docker(6-10)

文章目录

  • docker(6-10)
    • [第6章 存储](#第6章 存储)
      • [038 Docker的两类存储资源](#038 Docker的两类存储资源)
      • [039 Data Volume之bind mount](#039 Data Volume之bind mount)
      • [040 Data Volume之docker managed volume](#040 Data Volume之docker managed volume)
      • [041 如何共享数据](#041 如何共享数据)
      • [042 用volume container共享数据](#042 用volume container共享数据)
      • [043 data-packed volume container](#043 data-packed volume container)
      • [044 volume生命周期管理](#044 volume生命周期管理)
      • 实战:安装mysql
    • [第7章 容器监控](#第7章 容器监控)
    • [第8章 容器日志](#第8章 容器日志)
      • [Docker logs](#Docker logs)
    • [第9章 Docker-compose](#第9章 Docker-compose)
    • [第10章 docker图形界面管理](#第10章 docker图形界面管理)
      • [DockerUI 容器管理器的安装与使用](#DockerUI 容器管理器的安装与使用)
      • [Docker 图形化界面管理工具 Portainer](#Docker 图形化界面管理工具 Portainer)

docker(6-10)

第6章 存储

038 Docker的两类存储资源

我们从本章开始讨论 Docker 存储。

Docker 为容器提供了两种存放数据的资源:

  1. 由 storage driver 管理的镜像层和容器层。
  2. Data Volume。

我们会详细讨论它们的原理和特性。

storage driver

在前面镜像章节我们学习到 Docker 镜像的分层结构,简单回顾一下。

容器由最上面一个可写的容器层,以及若干只读的镜像层组成,容器的数据就存放在这些层中。这样的分层结构最大的特性是 Copy-on-Write:

  1. 新数据会直接存放在最上面的容器层。
  2. 修改现有数据会先从镜像层将数据复制到容器层,修改后的数据直接保存在容器层中,镜像层保持不变。
  3. 如果多个层中有命名相同的文件,用户只能看到最上面那层中的文件。

分层结构使镜像和容器的创建、共享以及分发变得非常高效,而这些都要归功于 Docker storage driver。正是 storage driver 实现了多层数据的堆叠并为用户提供一个单一的合并之后的统一视图。

Docker 支持多种 storage driver,有 AUFS、Device Mapper、Btrfs、OverlayFS、VFS 和 ZFS。它们都能实现分层的架构,同时又有各自的特性。对于 Docker 用户来说,具体选择使用哪个 storage driver 是一个难题,因为:

  1. 没有哪个 driver 能够适应所有的场景。
  2. driver 本身在快速发展和迭代。

不过 Docker 官方给出了一个简单的答案:
优先使用 Linux 发行版默认的 storage driver

Docker 安装时会根据当前系统的配置选择默认的 driver。默认 driver 具有最好的稳定性,因为默认 driver 在发行版上经过了严格的测试。

运行docker info查看CentOS的默认 driver:

bash 复制代码
[root@docker ~ 11:29:13]# docker info
Client: Docker Engine - Community
 Version:    26.1.3
 Context:    default
 Debug Mode: false
 Plugins:
  buildx: Docker Buildx (Docker Inc.)
    Version:  v0.14.0
    Path:     /usr/libexec/docker/cli-plugins/docker-buildx
  compose: Docker Compose (Docker Inc.)
    Version:  v2.27.0
    Path:     /usr/libexec/docker/cli-plugins/docker-compose

Server:
 Containers: 1
  Running: 1
  Paused: 0
  Stopped: 0
 Images: 10
 Server Version: 26.1.3
 #查看
 Storage Driver: overlay2
  Backing Filesystem: xfs
  Supports d_type: true
  Using metacopy: false
  Native Overlay Diff: true
  userxattr: false
 Logging Driver: json-file
 Cgroup Driver: cgroupfs
 Cgroup Version: 1
 Plugins:
  Volume: local
  Network: bridge host ipvlan macvlan null overlay
  Log: awslogs fluentd gcplogs gelf journald json-file local splunk syslog
 Swarm: inactive
 Runtimes: io.containerd.runc.v2 runc
 Default Runtime: runc
 Init Binary: docker-init
 containerd version: 8b3b7ca2e5ce38e8f31a34f35b2b68ceb8470d89
 runc version: v1.1.12-0-g51d5e94
 init version: de40ad0
 Security Options:
  seccomp
   Profile: builtin
 Kernel Version: 4.18.0-553.6.1.el8.x86_64
 Operating System: CentOS Stream 8
 OSType: linux
 Architecture: x86_64
 CPUs: 4
 Total Memory: 7.486GiB
 Name: docker
 ID: 425d4420-fa3e-4fcb-8bb1-4d92f2970921
 Docker Root Dir: /var/lib/docker
 Debug Mode: false
 Experimental: false
 Insecure Registries:
  127.0.0.0/8
 Registry Mirrors:
  https://19adffc09b4f4fcbad0603a171dd0419.mirror.swr.myhuaweicloud.com/
 Live Restore Enabled: false

CentOS Stream 8 用的overlay2,底层文件系统是xfs,各层数据存放在 /var/lib/docker。

对于某些容器,直接将数据放在由 storage driver 维护的层中是很好的选择,比如那些无状态的应用。无状态意味着容器没有需要持久化的数据,随时可以从镜像直接创建。

比如 busybox,它是一个工具箱,我们启动 busybox 是为了执行诸如 wget,ping 之类的命令,不需要保存数据供以后使用,使用完直接退出,容器删除时存放在容器层中的工作数据也一起被删除,这没问题,下次再启动新容器即可。

但对于另一类应用这种方式就不合适了,它们有持久化数据的需求,容器启动时需要加载已有的数据,容器销毁时希望保留产生的新数据,也就是说,这类容器是有状态的。

这就要用到 Docker 的另一种存储机制:Data Volume,下一节我们讨论。

039 Data Volume之bind mount

storage driver 和 data volume 是容器存放数据的两种方式,上一节我们学习了 storage driver,本节开始讨论 Data Volume。

Data Volume 本质上是 Docker Host 文件系统中的目录或文件,能够直接被 mount 到容器的文件系统中。Data Volume 有以下特点:

  1. Data Volume 是目录或文件,而非没有格式化的磁盘(块设备)。
  2. 容器可以读写 volume 中的数据。
  3. volume 数据可以被永久的保存,即使使用它的容器已经销毁。

好,现在我们有数据层(镜像层和容器层)和 volume 都可以用来存放数据,具体使用的时候要怎样选择呢?考虑下面几个场景:

  1. Database 软件 vs Database 数据
  2. Web 应用 vs 应用产生的日志
  3. 数据分析软件 vs input/output 数据
  4. Apache Server vs 静态 HTML 文件

相信大家会做出这样的选择:

  1. 前者放在数据层中。因为这部分内容是无状态的,应该作为镜像的一部分。
  2. 后者放在 Data Volume 中。这是需要持久化的数据,并且应该与镜像分开存放。

还有个大家可能会关心的问题:如何设置 voluem 的容量?

因为 volume 实际上是 docker host 文件系统的一部分,所以 volume 的容量取决于文件系统当前未使用的空间,目前还没有方法设置 volume 的容量。

在具体的使用上,docker 提供了两种类型的 volume:bind mount 和 docker managed volume。

bind mount

bind mount 是将 host 上已存在的目录或文件 mount 到容器。

例如 docker host 上有目录 $HOME/htdocs:

bash 复制代码
[root@docker ~ 11:29:18]# pwd
/root
[root@docker ~ 11:29:31]# mkdir htdocs
[root@docker ~ 11:29:38]# cd htdocs/
[root@docker htdocs 11:29:40]# vim index.html
[root@docker htdocs 11:30:00]# cd ..
[root@docker ~ 11:30:27]# cat htdocs/index.html
<html><body><h1>This is a file in host file system !</h1></body></html>

[root@docker ~ 11:30:35]# docker ps
CONTAINER ID   IMAGE             COMMAND             CREATED          STATUS          PORTS                                       NAMES
d7bddbe3787c   tomcat-with-web   "catalina.sh run"   53 minutes ago   Up 53 minutes   0.0.0.0:8080->8080/tcp, :::8080->8080/tcp   loving_davinci

通过 -v 将其 mount 到 httpd 容器:

bash 复制代码
[root@docker ~ 11:30:44]# docker run -d -p 80:80 -v ~/htdocs:/usr/local/apache2/htdocs httpd
0509b95e7e5a4de795c6a225cb35edf2c5390e02e5f2f3ea0a07ea2d0365e93d

-v 的格式为 <host path>:<container path>。 /usr/local/apache2/htdocs 就是 apache server 存放静态文件的地方。由于 /usr/local/apache2/htdocs 已经存在,原有数据会被隐藏起来,取而代之的是 host $HOME/htdocs/ 中的数据,这与 linux mount 命令的行为是一致的。

bash 复制代码
[root@docker ~ 11:31:30]# curl 127.0.0.1
<html><body><h1>This is a file in host file system !</h1></body></html>

curl 显示当前主页确实是 $HOME/htdocs/index.html 中的内容。更新一下,看是否能生效:

bash 复制代码
[root@docker ~ 11:31:40]# echo "updated index page!">~/htdocs/index.html
[root@docker ~ 11:32:50]# curl 127.0.0.1
updated index page!

host 中的修改确实生效了,bind mount 可以让 host 与容器共享数据。这在管理上是非常方便的。

下面我们将容器销毁,看看对 bind mount 有什么影响:

bash 复制代码
[root@docker ~ 11:33:25]# docker ps
CONTAINER ID   IMAGE             COMMAND              CREATED          STATUS          PORTS                                       NAMES
0509b95e7e5a   httpd             "httpd-foreground"   2 minutes ago    Up 2 minutes    0.0.0.0:80->80/tcp, :::80->80/tcp           practical_mcnulty
d7bddbe3787c   tomcat-with-web   "catalina.sh run"    56 minutes ago   Up 56 minutes   0.0.0.0:8080->8080/tcp, :::8080->8080/tcp   loving_davinci
[root@docker ~ 11:33:32]# docker rm -f 05
05
[root@docker ~ 11:33:42]# cat ~/htdocs/index.html
updated index page!

可见,即使容器没有了,bind mount 也还在。这也合理,bind mount 是 host 文件系统中的数据,只是借给容器用用,哪能随便就删了啊。

另外,bind mount 时还可以指定数据的读写权限,默认是可读可写,可指定为只读:

bash 复制代码
[root@docker ~ 11:34:25]# docker run -d -p 80:80 -v ~/htdocs:/usr/local/apache2/htdocs:ro httpd
17e041a12cd0efb0a08a67b0d35da76b2697a07f075c8fed0af7a676e1faeb1e
[root@docker ~ 11:35:05]# docker exec -it 17 bash
root@17e041a12cd0:/usr/local/apache2# echo hhh > htdocs/index.html
bash: htdocs/index.html: Read-only file system
root@17e041a12cd0:/usr/local/apache2# exit
exit

ro 设置了只读权限,在容器中是无法对 bind mount 数据进行修改的。只有 host 有权修改数据,提高了安全性。

除了 bind mount 目录,还可以单独指定一个文件:

bash 复制代码
# 删除上一个容器不然80端口冲突
[root@docker ~ 11:35:59]# docker ps
CONTAINER ID   IMAGE             COMMAND              CREATED          STATUS          PORTS                                       NAMES
17e041a12cd0   httpd             "httpd-foreground"   58 seconds ago   Up 57 seconds   0.0.0.0:80->80/tcp, :::80->80/tcp           funny_edison
d7bddbe3787c   tomcat-with-web   "catalina.sh run"    58 minutes ago   Up 58 minutes   0.0.0.0:8080->8080/tcp, :::8080->8080/tcp   loving_davinci
[root@docker ~ 11:36:02]# docker rm -f 17
17

[root@docker ~ 11:36:13]# docker run -d -p 80:80 -v ~/htdocs/index.html:/usr/local/apache2/htdocs/new_index.html httpd
4a34d1892712bda172f19bfcaf266d9ec370ce31e3e56805c2834e386f21eb5e
[root@docker ~ 11:37:07]# curl 127.0.0.1
<html><body><h1>It works!</h1></body></html>
[root@docker ~ 11:37:14]# curl 127.0.0.1/new_index.html
updated index page!

使用 bind mount 单个文件的场景是:只需要向容器添加文件,不希望覆盖整个目录。在上面的例子中,我们将 html 文件加到 apache 中,同时也保留了容器原有的数据。

使用单一文件有一点要注意:host 中的源文件必须要存在,不然会当作一个新目录 bind mount 给容器。

mount point 有很多应用场景,比如我们可以将源代码目录 mount 到容器中,在 host 中修改代码就能看到应用的实时效果。再比如将 mysql 容器的数据放在 bind mount 里,这样 host 可以方便地备份和迁移数据。

bind mount 的使用直观高效,易于理解,但它也有不足的地方:bind mount 需要指定 host 文件系统的特定路径,这就限制了容器的可移植性,当需要将容器迁移到其他 host,而该 host 没有要 mount 的数据或者数据不在相同的路径时,操作会失败。

移植性更好的方式是 docker managed volume,下一节我们讨论。

040 Data Volume之docker managed volume

docker managed volume 与 bind mount 在使用上的最大区别是不需要指定 mount 源,指明 mount point 就行了。还是以 httpd 容器为例:

bash 复制代码
[root@docker ~ 09:55:35]# docker run -d -p 80:80 -v /usr/local/apache2/htdocs httpd
e10590d4e2d651df91b0bfca59b23c4e5850b1ba53ce685ba5ef7495b3a748fe

我们通过 -v 告诉 docker 需要一个 data volume,并将其 mount 到 /usr/local/apache2/htdocs。那么这个 data volume 具体在哪儿呢?

这个答案可以在容器的配置信息中找到,执行 docker inspect 命令:

bash 复制代码
[root@docker ~]# docker inspect c75f36c4bf32  #docker inspect后面跟的是容器ID
...
        "Mounts": [
            {
                "Type": "volume",
                "Name": "1cc915096121dea197bbbb93fc90e6491d530e0c9e2ace1de07e2a635e569d16",
                "Source": "/var/lib/docker/volumes/1cc915096121dea197bbbb93fc90e6491d530e0c9e2ace1de07e2a635e569d16/_data",
                "Destination": "/usr/local/apache2/htdocs",
                "Driver": "local",
                "Mode": "",
                "RW": true,
                "Propagation": ""
            }
        ],
...      

docker inspect 的输出很多,我们感兴趣的是 Mounts 这部分,这里会显示容器当前使用的所有 data volume,包括 bind mount 和 docker managed volume。

Source 就是该 volume 在 host 上的目录。

原来,每当容器申请 mount docker manged volume 时,docker 都会在/var/lib/docker/volumes 下生成一个目录(例子中是 "/var/lib/docker/volumes/1cc915096121dea197bbbb93fc90e6491d530e0c9e2ace1de07e2a635e569d16/_data ),这个目录就是 mount 源。

下面继续研究这个 volume,看看里面有些什么东西:

bash 复制代码
[root@docker ~]# ls -l /var/lib/docker/volumes/1cc915096121dea197bbbb93fc90e6491d530e0c9e2ace1de07e2a635e569d16/_data
total 4
-rw-r--r-- 1 501 ftp 26 Sep 17 21:08 index.html
[root@docker ~]#
[root@docker ~]# curl 127.0.0.1:80
<html><body><h1>It works!</h1></body></html>

volume 的内容跟容器原有 /usr/local/apache2/htdocs 完全一样,这是怎么回事呢?

这是因为:如果 mount point 指向的是已有目录,原有数据会被复制到 volume 中。

但要明确一点:此时的 /usr/local/apache2/htdocs 已经不再是由 storage driver 管理的层数据了,它已经是一个 data volume。我们可以像 bind mount 一样对数据进行操作,例如更新数据:

bash 复制代码
[root@docker ~]# echo "update volume from host !" > /var/lib/docker/volumes/1cc915096121dea197bbbb93fc90e6491d530e0c9e2ace1de07e2a635e569d16/_data/index.html
[root@docker ~]#
[root@docker ~]# curl 127.0.0.1:80
update volume from host !

简单回顾一下 docker managed volume 的创建过程:

  1. 容器启动时,简单的告诉 docker "我需要一个 volume 存放数据,帮我 mount 到目录 /abc"。
  2. docker 在 /var/lib/docker/volumes 中生成一个随机目录作为 mount 源。
  3. 如果 /abc 已经存在,则将数据复制到 mount 源,
  4. 将 volume mount 到 /abc

除了通过 docker inspect 查看 volume,我们也可以用 docker volume 命令:

bash 复制代码
[root@docker ~]# docker volume ls
DRIVER    VOLUME NAME
local     1cc915096121dea197bbbb93fc90e6491d530e0c9e2ace1de07e2a635e569d16
[root@docker ~]#
[root@docker ~]# docker volume inspect 1cc915096121dea197bbbb93fc90e6491d530e0c9e2ace1de07e2a635e569d16
[
    {
        "CreatedAt": "2024-09-17T21:04:33+08:00",
        "Driver": "local",
        "Labels": {
            "com.docker.volume.anonymous": ""
        },
        "Mountpoint": "/var/lib/docker/volumes/1cc915096121dea197bbbb93fc90e6491d530e0c9e2ace1de07e2a635e569d16/_da
        "Name": "1cc915096121dea197bbbb93fc90e6491d530e0c9e2ace1de07e2a635e569d16",
        "Options": null,
        "Scope": "local"
    }
]
[root@docker ~]# 

目前,docker volume 只能查看 docker managed volume,还看不到 bind mount;同时也无法知道 volume 对应的容器,这些信息还得靠docker inspect

我们已经学习了两种 data volume 的原理和基本使用方法,下面做个对比:

  1. 相同点:两者都是 host 文件系统中的某个路径。
  2. 不同点:
bind mount docker managed volume
volume 位置 可任意指定 /var/lib/docker/volumes/...
对已有mount point 影响 隐藏并替换为 volume 原有数据复制到 volume
是否支持单个文件 支持 不支持,只能是目录
权限控制 可设置为只读,默认为读写权限 无控制,均为读写权限
移植性 移植性弱,与 host path 绑定 移植性强,无需指定 host 目录

下节讨论如何通过 data volume 实现容器与 host,容器与容器共享数据。

041 如何共享数据

数据共享是 volume 的关键特性,本节我们详细讨论通过 volume 如何在容器与 host 之间,容器与容器之间共享数据。

容器与 host 共享数据

我们有两种类型的 data volume,它们均可实现在容器与 host 之间共享数据,但方式有所区别。

对于 bind mount 是非常明确的:直接将要共享的目录 mount 到容器。具体请参考前面 httpd 的例子,不再赘述。

docker managed volume 就要麻烦点。由于 volume 位于 host 中的目录,是在容器启动时才生成,所以需要将共享数据拷贝到 volume 中。请看下面的例子:

bash 复制代码
[root@docker ~ 09:55:35]# docker run -d -p 80:80 -v /usr/local/apache2/htdocs httpd
e10590d4e2d651df91b0bfca59b23c4e5850b1ba53ce685ba5ef7495b3a748fe
[root@docker ~ 09:55:38]# curl 127.0.0.1:80
<html><body><h1>It works!</h1></body></html>
[root@docker ~ 09:55:48]# ls
anaconda-ks.cfg
[root@docker ~ 09:56:06]# mkdir htdocs
[root@docker ~ 09:56:14]# cd htdocs/
[root@docker htdocs 09:56:18]# touch index.html
[root@docker htdocs 09:56:27]# echo hhh > index.html
[root@docker htdocs 09:56:35]# docker cp ~/htdocs/index.html e1:/usr/local/apache2/htdocs/index.html
Successfully copied 2.05kB to e1:/usr/local/apache2/htdocs/index.html
[root@docker htdocs 09:57:19]# curl 127.0.0.1:80
hhh

docker cp 可以在容器和 host 之间拷贝数据,当然我们也可以直接通过 Linux 的 cp 命令复制到 /var/lib/docker/volumes/xxx。

思考:容器中的文件拷贝到host os如何操作

容器之间共享数据

第一种方法是将共享数据放在 bind mount 中,然后将其 mount 到多个容器。还是以 httpd 为例,不过这次的场景复杂些,我们要创建由三个 httpd 容器组成的 web server 集群,它们使用相同的 html 文件,操作如下:

  1. 将 $HOME/htdocs mount 到三个 httpd 容器。

    bash 复制代码
    [root@docker ~ 09:57:34]# docker run --name web1 -d -p 80 -v ~/htdocs:/usr/local/apache2/htdocs httpd
    e3bedd9cd864a1f0d9e88a9a096ff8a0612d5a815e804d7e9e9a6dd017d05d14
    [root@docker ~ 09:58:15]# docker run --name web2 -d -p 80 -v ~/htdocs:/usr/local/apache2/htdocs httpd
    50512a82e4288f164bd4aab03948668b41e7dcc97883a4f886314b67d5fae924
    [root@docker ~ 09:58:21]# docker run --name web3 -d -p 80 -v ~/htdocs:/usr/local/apache2/htdocs httpd
    0b913e8768757ac0ee6b8a57d5cccd6f443ff90a7f14750abe87d78cda32c85a
  2. 查看当前主页内容。

    bash 复制代码
    [root@docker ~ 09:58:27]# docker ps
    CONTAINER ID   IMAGE     COMMAND              CREATED          STATUS          PORTS                                     NAMES
    0b913e876875   httpd     "httpd-foreground"   6 seconds ago    Up 5 seconds    0.0.0.0:32770->80/tcp, :::32770->80/tcp   web3
    50512a82e428   httpd     "httpd-foreground"   11 seconds ago   Up 11 seconds   0.0.0.0:32769->80/tcp, :::32769->80/tcp   web2
    e3bedd9cd864   httpd     "httpd-foreground"   17 seconds ago   Up 17 seconds   0.0.0.0:32768->80/tcp, :::32768->80/tcp   web1
    e10590d4e2d6   httpd     "httpd-foreground"   2 minutes ago    Up 2 minutes    0.0.0.0:80->80/tcp, :::80->80/tcp         beautiful_tesla
    [root@docker ~ 09:58:32]# curl 127.0.0.1:32768
    hhh
    [root@docker ~ 09:58:53]# curl 127.0.0.1:32769
    hhh
    [root@docker ~ 09:59:01]# curl 127.0.0.1:32770
    hhh
  3. 修改 volume 中的主页文件,再次查看并确认所有容器都使用了新的主页。

    bash 复制代码
    [root@docker ~ 09:59:05]# echo "This is a new index page for web cluster">~/htdocs/index.html
    [root@docker ~ 09:59:46]# curl 127.0.0.1:32770
    This is a new index page for web cluster
    [root@docker ~ 09:59:49]# curl 127.0.0.1:32769
    This is a new index page for web cluster
    [root@docker ~ 09:59:52]# curl 127.0.0.1:32768
    This is a new index page for web cluster

另一种在容器之间共享数据的方式是使用 volume container,下节讨论。

042 用volume container共享数据

volume container 是专门为其他容器提供 volume 的容器。它提供的卷可以是 bind mount,也可以是 docker managed volume。下面我们创建一个 volume container:

bash 复制代码
[root@docker ~ 10:25:26]# docker create --name vc_data -v ~/htdocs/:/usr/local/apache2/htdocs -v /other/userful/tools busybox
e6dcf69353571327003989fde8efccc8af7e8ce70a866b436f247e641b62572e
[root@docker ~ 10:26:17]# docker ps -a
CONTAINER ID   IMAGE     COMMAND              CREATED          STATUS          PORTS                                     NAMES
e6dcf6935357   busybox   "sh"                 8 seconds ago    Created                                                   vc_data
0b913e876875   httpd     "httpd-foreground"   27 minutes ago   Up 27 minutes   0.0.0.0:32770->80/tcp, :::32770->80/tcp   web3
50512a82e428   httpd     "httpd-foreground"   28 minutes ago   Up 28 minutes   0.0.0.0:32769->80/tcp, :::32769->80/tcp   web2
e3bedd9cd864   httpd     "httpd-foreground"   28 minutes ago   Up 28 minutes   0.0.0.0:32768->80/tcp, :::32768->80/tcp   web1
e10590d4e2d6   httpd     "httpd-foreground"   30 minutes ago   Up 30 minutes   0.0.0.0:80->80/tcp, :::80->80/tcp         beautiful_tesla
e2863e8744a1   httpd     "httpd-foreground"   31 minutes ago   Created                                                   inspiring_matsumoto

我们将容器命名为 vc_data(vc 是 volume container 的缩写)。注意这里执行的是 docker create 命令,这是因为 volume container 的作用只是提供数据,它本身不需要处于运行状态。容器 mount 了两个 volume:

  1. bind mount,存放 web server 的静态文件。
  2. docker managed volume,存放一些实用工具(当然现在是空的,这里只是做个示例)。

通过 docker inspect 可以查看到这两个 volume。

bash 复制代码
[root@docker ~ 10:26:40]# docker inspect vc_data
[
    {
        "Id": "e6dcf69353571327003989fde8efccc8af7e8ce70a866b436f247e641b62572e",
        "Created": "2025-11-21T02:26:17.437534014Z",
        "Path": "sh",
        "Args": [],
        "State": {
            "Status": "created",
            "Running": false,
            "Paused": false,
            "Restarting": false,
            "OOMKilled": false,
            "Dead": false,
            "Pid": 0,
            "ExitCode": 0,
            "Error": "",
            "StartedAt": "0001-01-01T00:00:00Z",
            "FinishedAt": "0001-01-01T00:00:00Z"
        },
        "Image": "sha256:08ef35a1c3f050afbbd64194ffd1b8d5878659f5491567f26d1c814513ae9649",
        "ResolvConfPath": "",
        "HostnamePath": "",
        "HostsPath": "",
        "LogPath": "",
        "Name": "/vc_data",
        "RestartCount": 0,
        "Driver": "overlay2",
        "Platform": "linux",
        "MountLabel": "",
        "ProcessLabel": "",
        "AppArmorProfile": "",
        "ExecIDs": null,
        "HostConfig": {
            "Binds": [
                "/root/htdocs/:/usr/local/apache2/htdocs"
            ],
            "ContainerIDFile": "",
            "LogConfig": {
                "Type": "json-file",
                "Config": {}
            },
            "NetworkMode": "bridge",
            "PortBindings": {},
            "RestartPolicy": {
                "Name": "no",
                "MaximumRetryCount": 0
            },
            "AutoRemove": false,
            "VolumeDriver": "",
            "VolumesFrom": null,
            "ConsoleSize": [
                26,
                88
            ],
            "CapAdd": null,
            "CapDrop": null,
            "CgroupnsMode": "host",
            "Dns": [],
            "DnsOptions": [],
            "DnsSearch": [],
            "ExtraHosts": null,
            "GroupAdd": null,
            "IpcMode": "private",
            "Cgroup": "",
            "Links": null,
            "OomScoreAdj": 0,
            "PidMode": "",
            "Privileged": false,
            "PublishAllPorts": false,
            "ReadonlyRootfs": false,
            "SecurityOpt": null,
            "UTSMode": "",
            "UsernsMode": "",
            "ShmSize": 67108864,
            "Runtime": "runc",
            "Isolation": "",
            "CpuShares": 0,
            "Memory": 0,
            "NanoCpus": 0,
            "CgroupParent": "",
            "BlkioWeight": 0,
            "BlkioWeightDevice": [],
            "BlkioDeviceReadBps": [],
            "BlkioDeviceWriteBps": [],
            "BlkioDeviceReadIOps": [],
            "BlkioDeviceWriteIOps": [],
            "CpuPeriod": 0,
            "CpuQuota": 0,
            "CpuRealtimePeriod": 0,
            "CpuRealtimeRuntime": 0,
            "CpusetCpus": "",
            "CpusetMems": "",
            "Devices": [],
            "DeviceCgroupRules": null,
            "DeviceRequests": null,
            "MemoryReservation": 0,
            "MemorySwap": 0,
            "MemorySwappiness": null,
            "OomKillDisable": false,
            "PidsLimit": null,
            "Ulimits": [],
            "CpuCount": 0,
            "CpuPercent": 0,
            "IOMaximumIOps": 0,
            "IOMaximumBandwidth": 0,
            "MaskedPaths": [
                "/proc/asound",
                "/proc/acpi",
                "/proc/kcore",
                "/proc/keys",
                "/proc/latency_stats",
                "/proc/timer_list",
                "/proc/timer_stats",
                "/proc/sched_debug",
                "/proc/scsi",
                "/sys/firmware",
                "/sys/devices/virtual/powercap"
            ],
            "ReadonlyPaths": [
                "/proc/bus",
                "/proc/fs",
                "/proc/irq",
                "/proc/sys",
                "/proc/sysrq-trigger"
            ]
        },
        "GraphDriver": {
            "Data": {
                "LowerDir": "/var/lib/docker/overlay2/bce2a485f99fb85c6fce2a77cb514577ad0cfef072f7d603f608268c12920735-init/diff:/var/lib/docker/overlay2/a6428197c2e79a4f7d0bb82bdd212b81de0948872ad198e3116011ee8a97b654/diff",
                "MergedDir": "/var/lib/docker/overlay2/bce2a485f99fb85c6fce2a77cb514577ad0cfef072f7d603f608268c12920735/merged",
                "UpperDir": "/var/lib/docker/overlay2/bce2a485f99fb85c6fce2a77cb514577ad0cfef072f7d603f608268c12920735/diff",
                "WorkDir": "/var/lib/docker/overlay2/bce2a485f99fb85c6fce2a77cb514577ad0cfef072f7d603f608268c12920735/work"
            },
            "Name": "overlay2"
        },
        "Mounts": [
            {
                `查看`
                "Type": "bind",
                "Source": "/root/htdocs",
                "Destination": "/usr/local/apache2/htdocs",
                "Mode": "",
                "RW": true,
                "Propagation": "rprivate"
            },
            {
                `查看`
                "Type": "volume",
                "Name": "d6ecd0cb3f752a1a6aec94786a72e4bce53238026386d79b5bf47d7c13a8bbb7",
                "Source": "/var/lib/docker/volumes/d6ecd0cb3f752a1a6aec94786a72e4bce53238026386d79b5bf47d7c13a8bbb7/_data",
                "Destination": "/other/userful/tools",
                "Driver": "local",
                "Mode": "",
                "RW": true,
                "Propagation": ""
            }
        ],
        "Config": {
            "Hostname": "e6dcf6935357",
            "Domainname": "",
            "User": "",
            "AttachStdin": false,
            "AttachStdout": true,
            "AttachStderr": true,
            "Tty": false,
            "OpenStdin": false,
            "StdinOnce": false,
            "Env": [
                "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
            ],
            "Cmd": [
                "sh"
            ],
            "Image": "busybox",
            "Volumes": {
                "/other/userful/tools": {}
            },
            "WorkingDir": "",
            "Entrypoint": null,
            "OnBuild": null,
            "Labels": {}
        },
        "NetworkSettings": {
            "Bridge": "",
            "SandboxID": "",
            "SandboxKey": "",
            "Ports": {},
            "HairpinMode": false,
            "LinkLocalIPv6Address": "",
            "LinkLocalIPv6PrefixLen": 0,
            "SecondaryIPAddresses": null,
            "SecondaryIPv6Addresses": null,
            "EndpointID": "",
            "Gateway": "",
            "GlobalIPv6Address": "",
            "GlobalIPv6PrefixLen": 0,
            "IPAddress": "",
            "IPPrefixLen": 0,
            "IPv6Gateway": "",
            "MacAddress": "",
            "Networks": {
                "bridge": {
                    "IPAMConfig": null,
                    "Links": null,
                    "Aliases": null,
                    "MacAddress": "",
                    "NetworkID": "",
                    "EndpointID": "",
                    "Gateway": "",
                    "IPAddress": "",
                    "IPPrefixLen": 0,
                    "IPv6Gateway": "",
                    "GlobalIPv6Address": "",
                    "GlobalIPv6PrefixLen": 0,
                    "DriverOpts": null,
                    "DNSNames": null
                }
            }
        }
    }
]

其他容器可以通过 --volumes-from 使用 vc_data 这个 volume container:

bash 复制代码
#通过vc_data容器创建新的容器
[root@docker ~ 10:27:30]# docker run --name web11 -d -p 80 --volumes-from vc_data httpd
c92c85cf621b98f0ff0980b301429e8b8c89c49d4d4c38f867e41b89bd94e7b2
[root@docker ~ 10:27:35]# docker run --name web12 -d -p 80 --volumes-from vc_data httpd
cf4d0caa7ba638799dfbf3a84dd7b2a0233b3c7001eab1c7579200ca0fc95ed2
[root@docker ~ 10:27:42]# docker run --name web13 -d -p 80 --volumes-from vc_data httpd
9e7d0682ba53c7137bda85e87e87b3e2f4c504c593438414de79ac0815c20e38

三个 httpd 容器都使用了 vc_data,看看它们现在都有哪些 volume,以 web11 为例:

bash 复制代码
[root@docker ~ 10:27:57]# docker inspect web11
[
    {
        "Id": "c92c85cf621b98f0ff0980b301429e8b8c89c49d4d4c38f867e41b89bd94e7b2",
        "Created": "2025-11-21T02:27:35.654059203Z",
        "Path": "httpd-foreground",
        "Args": [],
        "State": {
            "Status": "running",
            "Running": true,
            "Paused": false,
            "Restarting": false,
            "OOMKilled": false,
            "Dead": false,
            "Pid": 3403,
            "ExitCode": 0,
            "Error": "",
            "StartedAt": "2025-11-21T02:27:35.949193095Z",
            "FinishedAt": "0001-01-01T00:00:00Z"
        },
        "Image": "sha256:c00bfb4edfeb0206fa0e988e51b588a442deca46cb4ea69b3cd7e08f93b429ae",
        "ResolvConfPath": "/var/lib/docker/containers/c92c85cf621b98f0ff0980b301429e8b8c89c49d4d4c38f867e41b89bd94e7b2/resolv.conf",
        "HostnamePath": "/var/lib/docker/containers/c92c85cf621b98f0ff0980b301429e8b8c89c49d4d4c38f867e41b89bd94e7b2/hostname",
        "HostsPath": "/var/lib/docker/containers/c92c85cf621b98f0ff0980b301429e8b8c89c49d4d4c38f867e41b89bd94e7b2/hosts",
        "LogPath": "/var/lib/docker/containers/c92c85cf621b98f0ff0980b301429e8b8c89c49d4d4c38f867e41b89bd94e7b2/c92c85cf621b98f0ff0980b301429e8b8c89c49d4d4c38f867e41b89bd94e7b2-json.log",
        "Name": "/web11",
        "RestartCount": 0,
        "Driver": "overlay2",
        "Platform": "linux",
        "MountLabel": "",
        "ProcessLabel": "",
        "AppArmorProfile": "",
        "ExecIDs": null,
        "HostConfig": {
            "Binds": null,
            "ContainerIDFile": "",
            "LogConfig": {
                "Type": "json-file",
                "Config": {}
            },
            "NetworkMode": "bridge",
            "PortBindings": {
                "80/tcp": [
                    {
                        "HostIp": "",
                        "HostPort": ""
                    }
                ]
            },
            "RestartPolicy": {
                "Name": "no",
                "MaximumRetryCount": 0
            },
            "AutoRemove": false,
            "VolumeDriver": "",
            "VolumesFrom": [
                "vc_data"
            ],
            "ConsoleSize": [
                26,
                88
            ],
            "CapAdd": null,
            "CapDrop": null,
            "CgroupnsMode": "host",
            "Dns": [],
            "DnsOptions": [],
            "DnsSearch": [],
            "ExtraHosts": null,
            "GroupAdd": null,
            "IpcMode": "private",
            "Cgroup": "",
            "Links": null,
            "OomScoreAdj": 0,
            "PidMode": "",
            "Privileged": false,
            "PublishAllPorts": false,
            "ReadonlyRootfs": false,
            "SecurityOpt": null,
            "UTSMode": "",
            "UsernsMode": "",
            "ShmSize": 67108864,
            "Runtime": "runc",
            "Isolation": "",
            "CpuShares": 0,
            "Memory": 0,
            "NanoCpus": 0,
            "CgroupParent": "",
            "BlkioWeight": 0,
            "BlkioWeightDevice": [],
            "BlkioDeviceReadBps": [],
            "BlkioDeviceWriteBps": [],
            "BlkioDeviceReadIOps": [],
            "BlkioDeviceWriteIOps": [],
            "CpuPeriod": 0,
            "CpuQuota": 0,
            "CpuRealtimePeriod": 0,
            "CpuRealtimeRuntime": 0,
            "CpusetCpus": "",
            "CpusetMems": "",
            "Devices": [],
            "DeviceCgroupRules": null,
            "DeviceRequests": null,
            "MemoryReservation": 0,
            "MemorySwap": 0,
            "MemorySwappiness": null,
            "OomKillDisable": false,
            "PidsLimit": null,
            "Ulimits": [],
            "CpuCount": 0,
            "CpuPercent": 0,
            "IOMaximumIOps": 0,
            "IOMaximumBandwidth": 0,
            "MaskedPaths": [
                "/proc/asound",
                "/proc/acpi",
                "/proc/kcore",
                "/proc/keys",
                "/proc/latency_stats",
                "/proc/timer_list",
                "/proc/timer_stats",
                "/proc/sched_debug",
                "/proc/scsi",
                "/sys/firmware",
                "/sys/devices/virtual/powercap"
            ],
            "ReadonlyPaths": [
                "/proc/bus",
                "/proc/fs",
                "/proc/irq",
                "/proc/sys",
                "/proc/sysrq-trigger"
            ]
        },
        "GraphDriver": {
            "Data": {
                "LowerDir": "/var/lib/docker/overlay2/8c10e3dd93771e977464a85922e367ef2bd8997251b560340acfd368f89572db-init/diff:/var/lib/docker/overlay2/ab9371ee0c09ca05df9673ef482abaff5b2b8e59460c81386a1616e8716a7adc/diff:/var/lib/docker/overlay2/ea141e734453a14b7ed9d12c9dd9ddaf2a5880cf9143d67f01f7251b4f8f6513/diff:/var/lib/docker/overlay2/b02292cbc66f2683b4e6cfde8f115063d7cbe7906e24d5727c62a6a28f94c455/diff:/var/lib/docker/overlay2/08c21803335e73c0caf4547cfdd37cc5e20aa077a19f567f7e226f0a0f9fac12/diff:/var/lib/docker/overlay2/500323ce618e7efafe8ba544187103b5dd981599711ce08c5c2b1b551331547b/diff:/var/lib/docker/overlay2/49fc8804b7277fd85e10201ecf4fb9f36b393ee87a38c6840d6d75f9f040d0a8/diff",
                "MergedDir": "/var/lib/docker/overlay2/8c10e3dd93771e977464a85922e367ef2bd8997251b560340acfd368f89572db/merged",
                "UpperDir": "/var/lib/docker/overlay2/8c10e3dd93771e977464a85922e367ef2bd8997251b560340acfd368f89572db/diff",
                "WorkDir": "/var/lib/docker/overlay2/8c10e3dd93771e977464a85922e367ef2bd8997251b560340acfd368f89572db/work"
            },
            "Name": "overlay2"
        },
        "Mounts": [
            {
                `查看,与vc_data一样`
                "Type": "bind",
                "Source": "/root/htdocs",
                "Destination": "/usr/local/apache2/htdocs",
                "Mode": "",
                "RW": true,
                "Propagation": "rprivate"
            },
            {
                `查看,与vc_data一样`
                "Type": "volume",
                "Name": "d6ecd0cb3f752a1a6aec94786a72e4bce53238026386d79b5bf47d7c13a8bbb7",
                "Source": "/var/lib/docker/volumes/d6ecd0cb3f752a1a6aec94786a72e4bce53238026386d79b5bf47d7c13a8bbb7/_data",
                "Destination": "/other/userful/tools",
                "Driver": "local",
                "Mode": "",
                "RW": true,
                "Propagation": ""
            }
        ],
        "Config": {
            "Hostname": "c92c85cf621b",
            "Domainname": "",
            "User": "",
            "AttachStdin": false,
            "AttachStdout": false,
            "AttachStderr": false,
            "ExposedPorts": {
                "80/tcp": {}
            },
            "Tty": false,
            "OpenStdin": false,
            "StdinOnce": false,
            "Env": [
                "PATH=/usr/local/apache2/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
                "HTTPD_PREFIX=/usr/local/apache2",
                "HTTPD_VERSION=2.4.65",
                "HTTPD_SHA256=58b8be97d9940ec17f7656c0c6b9f41b618aac468b894b534148e3296c53b8b3",
                "HTTPD_PATCHES="
            ],
            "Cmd": [
                "httpd-foreground"
            ],
            "Image": "httpd",
            "Volumes": null,
            "WorkingDir": "/usr/local/apache2",
            "Entrypoint": null,
            "OnBuild": null,
            "Labels": {},
            "StopSignal": "SIGWINCH"
        },
        "NetworkSettings": {
            "Bridge": "",
            "SandboxID": "6277c0b4a167c1f7fe07cb373248540c90624a4ff6aec805ad232dd6f9147108",
            "SandboxKey": "/var/run/docker/netns/6277c0b4a167",
            "Ports": {
                "80/tcp": [
                    {
                        "HostIp": "0.0.0.0",
                        "HostPort": "32771"
                    },
                    {
                        "HostIp": "::",
                        "HostPort": "32771"
                    }
                ]
            },
            "HairpinMode": false,
            "LinkLocalIPv6Address": "",
            "LinkLocalIPv6PrefixLen": 0,
            "SecondaryIPAddresses": null,
            "SecondaryIPv6Addresses": null,
            "EndpointID": "e1efc57f286b8215e15176a496c14af5a8e715debcad4d56a15485b0949e2fbc",
            "Gateway": "172.17.0.1",
            "GlobalIPv6Address": "",
            "GlobalIPv6PrefixLen": 0,
            "IPAddress": "172.17.0.6",
            "IPPrefixLen": 16,
            "IPv6Gateway": "",
            "MacAddress": "02:42:ac:11:00:06",
            "Networks": {
                "bridge": {
                    "IPAMConfig": null,
                    "Links": null,
                    "Aliases": null,
                    "MacAddress": "02:42:ac:11:00:06",
                    "NetworkID": "7878e6067538a7e3ade22ad36a0e88c7cd7aac1c0a619a7d901d525b018c4192",
                    "EndpointID": "e1efc57f286b8215e15176a496c14af5a8e715debcad4d56a15485b0949e2fbc",
                    "Gateway": "172.17.0.1",
                    "IPAddress": "172.17.0.6",
                    "IPPrefixLen": 16,
                    "IPv6Gateway": "",
                    "GlobalIPv6Address": "",
                    "GlobalIPv6PrefixLen": 0,
                    "DriverOpts": null,
                    "DNSNames": null
                }
            }
        }
    }
]

web11容器使用的就是 vc_data 的 volume,而且连 mount point 都是一样的。验证一下数据共享的效果:

bash 复制代码
[root@docker ~ 10:28:02]# docker ps
CONTAINER ID   IMAGE     COMMAND              CREATED          STATUS          PORTS                                     NAMES
9e7d0682ba53   httpd     "httpd-foreground"   29 seconds ago   Up 29 seconds   0.0.0.0:32773->80/tcp, :::32773->80/tcp   web13
cf4d0caa7ba6   httpd     "httpd-foreground"   34 seconds ago   Up 33 seconds   0.0.0.0:32772->80/tcp, :::32772->80/tcp   web12
c92c85cf621b   httpd     "httpd-foreground"   40 seconds ago   Up 40 seconds   0.0.0.0:32771->80/tcp, :::32771->80/tcp   web11
0b913e876875   httpd     "httpd-foreground"   29 minutes ago   Up 29 minutes   0.0.0.0:32770->80/tcp, :::32770->80/tcp   web3
50512a82e428   httpd     "httpd-foreground"   29 minutes ago   Up 29 minutes   0.0.0.0:32769->80/tcp, :::32769->80/tcp   web2
e3bedd9cd864   httpd     "httpd-foreground"   30 minutes ago   Up 30 minutes   0.0.0.0:32768->80/tcp, :::32768->80/tcp   web1
e10590d4e2d6   httpd     "httpd-foreground"   32 minutes ago   Up 32 minutes   0.0.0.0:80->80/tcp, :::80->80/tcp         beautiful_tesla
[root@docker ~ 10:28:15]# echo "This is from a volume container!"> ~/htdocs/index.html
[root@docker ~ 10:28:49]# curl 127.0.0.1:32771
This is from a volume container!
[root@docker ~ 10:29:05]# curl 127.0.0.1:32772
This is from a volume container!
[root@docker ~ 10:29:08]# curl 127.0.0.1:32773
This is from a volume container!

可见,三个容器已经成功共享了 volume container 中的 volume。

下面我们讨论一下 volume container 的特点:

  1. 与 bind mount 相比,不必为每一个容器指定 host path,所有 path 都在 volume container 中定义好了,容器只需与 volume container 关联,实现了容器与 host 的解耦
  2. 使用 volume container 的容器其 mount point 是一致的,有利于配置的规范和标准化,但也带来一定的局限,使用时需要综合考虑。

另一种在容器之间共享数据的方式是 data-packed volume container,下一节讨论。

043 data-packed volume container

在上一节的例子中 volume container 的数据归根到底还是在 host 里,有没有办法将数据完全放到 volume container 中,同时又能与其他容器共享呢?

当然可以,通常我们称这种容器为 data-packed volume container。其原理是将数据打包到镜像中,然后通过 docker managed volume 共享。

我们用下面的 Dockfile 构建镜像:

bash 复制代码
[root@docker ~]# vim Dockerfile
FROM busybox:latest
ADD htdocs /usr/local/apache2/htdocs
VOLUME /usr/local/apache2/htdocs

ADD 将静态文件添加到容器目录 /usr/local/apache2/htdocs。
VOLUME 的作用与 -v 等效,用来创建 docker managed volume,mount point 为 /usr/local/apache2/htdocs,因为这个目录就是 ADD 添加的目录,所以会将已有数据拷贝到 volume 中。

修改文本内容

bash 复制代码
[root@docker ~]# echo "This content is from a data packed volume container!" > htdocs/index.html

build 新镜像 datapacked:

bash 复制代码
[root@docker ~]# docker build -t datapacked .

用新镜像创建 data-packed volume container:

bash 复制代码
[root@docker ~]# docker create --name vc_data datapacked
55dc035deeb31907830ea45f7af5e209c7e48078d59732798c529e7445d2a196

因为在 Dockerfile 中已经使用了 VOLUME 指令,这里就不需要指定 volume 的 mount point 了。启动 httpd 容器并使用 data-packed volume container:

bash 复制代码
[root@docker ~]# docker run -d -p 80:80 --volumes-from vc_data httpd
98db78fc661441ec7ce0a2b76d62675fccf3389b7768cc9744b852645323c33b
[root@docker ~]#
[root@docker ~]# curl 127.0.0.1:80
This content is from a data packed volume container!

容器能够正确读取 volume 中的数据。data-packed volume container 是自包含的,不依赖 host 提供数据,具有很强的移植性,非常适合 只使用 静态数据的场景,比如应用的配置信息、web server 的静态文件等。

容器数据共享就讨论到这里,下一节我们学习如何对 data volume 的生命周期进行管理

044 volume生命周期管理

Data Volume 中存放的是重要的应用数据,如何管理 volume 对应用至关重要。前面我们主要关注的是 volume 的创建、共享和使用,本节将讨论如何备份、恢复、迁移和销毁 volume。

备份

因为 volume 实际上是 host 文件系统中的目录和文件,所以 volume 的备份实际上是对文件系统的备份。

还记得前面我们是如何搭建本地 Registry 的吗?

bash 复制代码
[root@docker ~ 11:24:01]# docker run -d -p 5000:5000 -v /myregistry:/var/lib/registry registry:2
Unable to find image 'registry:2' locally
2: Pulling from library/registry
44cf07d57ee4: Pull complete
bbbdd6c6894b: Pull complete
8e82f80af0de: Pull complete
3493bf46cdec: Pull complete
6d464ea18732: Pull complete
Digest: sha256:a3d8aaa63ed8681a604f1dea0aa03f100d5895b6a58ace528858a7b332415373
Status: Downloaded newer image for registry:2
9abba18cf2df8bdcbdeeec36343394a91deac17a87a5f88a58b13e8e5666f60d

所有的本地镜像都存在 host 的 /myregistry 目录中,我们要做的就是定期备份这个目录。

恢复

volume 的恢复也很简单,如果数据损坏了,直接用之前备份的数据拷贝到 /myregistry 就可以了。

迁移

如果我们想使用更新版本的 Registry,这就涉及到数据迁移,方法是:

  1. docker stop 当前 Registry 容器。

  2. 启动新版本容器并 mount 原有 volume。

    docker run -d -p 5000:5000 -v /myregistry:/var/lib/registry registry:latest

当然,在启用新容器前要确保新版本的默认数据路径是否发生变化。

销毁

可以删除不再需要的 volume,但一定要确保知道自己正在做什么,volume 删除后数据是找不回来的。

docker 不会销毁 bind mount,删除数据的工作只能由 host 负责。对于 docker managed volume,在执行 docker rm 删除容器时可以带上 -v 参数,docker 会将容器使用到的 volume 一并删除,但前提是没有其他容器 mount 该 volume,目的是保护数据,非常合理。

如果删除容器时没有带 -v 呢?这样就会产生孤儿 volume,好在 docker 提供了 volume 子命令可以对 docker managed volume 进行维护。请看下面的例子:

bash 复制代码
[root@docker ~ 11:24:13]# docker volume ls
DRIVER    VOLUME NAME
local     4de6569c355e22b4beaf3b27ef93517ab76a98778522fe012fc93034fff25a34
local     8bd164e5df776b723d4f45d31712900f4acbe2d4c9603d8cd21a4a53d15b3fd7
local     55f9f946dc99ea41c5e03d9d8085fcdec7fed29dfe2cdac777fe0aa3c12d8671
local     031019b758261f75d7682b61cd87fa2896e29765a6404810d90c27c658628386
local     d6ecd0cb3f752a1a6aec94786a72e4bce53238026386d79b5bf47d7c13a8bbb7
local     e906e1c1e81141bbd73c5ba4af8eeeffef6131272b1bac2024d4c04cc6a7aaab

容器 bbox 使用的 docker managed volume 可以通过 docker volume ls 查看到。

bash 复制代码
[root@docker ~ 11:24:41]# docker rm -f $(docker ps -aq)
9abba18cf2df

#因为没有使用 `-v`,volume 遗留了下来。对于这样的孤儿 volume,可以用 `docker volume rm` 删除:
[root@docker ~ 11:24:47]# docker volume ls
DRIVER    VOLUME NAME
local     4de6569c355e22b4beaf3b27ef93517ab76a98778522fe012fc93034fff25a34
local     8bd164e5df776b723d4f45d31712900f4acbe2d4c9603d8cd21a4a53d15b3fd7
local     55f9f946dc99ea41c5e03d9d8085fcdec7fed29dfe2cdac777fe0aa3c12d8671
local     031019b758261f75d7682b61cd87fa2896e29765a6404810d90c27c658628386
local     d6ecd0cb3f752a1a6aec94786a72e4bce53238026386d79b5bf47d7c13a8bbb7
local     e906e1c1e81141bbd73c5ba4af8eeeffef6131272b1bac2024d4c04cc6a7aaab
[root@docker ~ 11:24:59]# docker volume rm $(docker volume ls -q)
4de6569c355e22b4beaf3b27ef93517ab76a98778522fe012fc93034fff25a34
8bd164e5df776b723d4f45d31712900f4acbe2d4c9603d8cd21a4a53d15b3fd7
55f9f946dc99ea41c5e03d9d8085fcdec7fed29dfe2cdac777fe0aa3c12d8671
031019b758261f75d7682b61cd87fa2896e29765a6404810d90c27c658628386
d6ecd0cb3f752a1a6aec94786a72e4bce53238026386d79b5bf47d7c13a8bbb7
e906e1c1e81141bbd73c5ba4af8eeeffef6131272b1bac2024d4c04cc6a7aaab
[root@docker ~ 11:25:26]# docker volume ls
DRIVER    VOLUME NAME

小结

本章我们学习了以下内容:

  1. docker 为容器提供了两种存储资源:数据层和 Data Volume。
  2. 数据层包括镜像层和容器层,由 storage driver 管理。
  3. Data Volume 有两种类型:bind mount 和 docker managed volume。
  4. bind mount 可实现容器与 host 之间,容器与容器之间共享数据。
  5. volume container 是一种具有更好移植性的容器间数据共享方案,特别是 data-packed volume container。
  6. 最后我们学习了如何备份、恢复、迁移和销毁 Data Volume。

实战:安装mysql

docker hub上查找mysql镜像

bash 复制代码
[root@docker ~]# docker search mysql

从华为云加速器拉取mysql镜像到本地标签为5.7

bash 复制代码
[root@docker ~ 11:44:46]# docker pull mysql:5.7
5.7: Pulling from library/mysql
20e4dcae4c69: Pull complete
1c56c3d4ce74: Pull complete
e9f03a1c24ce: Pull complete
68c3898c2015: Pull complete
6b95a940e7b6: Pull complete
90986bb8de6e: Pull complete
ae71319cb779: Pull complete
ffc89e9dfd88: Pull complete
43d05e938198: Pull complete
064b2d298fba: Pull complete
df9a4d85569b: Pull complete
Digest: sha256:4bc6bc963e6d8443453676cae56536f4b8156d78bae03c0145cbe47c2aad73bb
Status: Downloaded newer image for mysql:5.7
docker.io/library/mysql:5.7

使用mysql镜像

简单版
bash 复制代码
[root@docker ~ 11:48:55]# docker run -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123 -d mysql:5.7
78a58a27c2c3ba814f1e51c65e7d43f0fc5f64ed53e05d66b07d2de9a2d3c22b

[root@docker ~ 11:49:04]# docker ps
CONTAINER ID   IMAGE       COMMAND                  CREATED         STATUS         PORTS                                                  NAMES
78a58a27c2c3   mysql:5.7   "docker-entrypoint.s..."   9 seconds ago   Up 8 seconds   0.0.0.0:3306->3306/tcp, :::3306->3306/tcp, 33060/tcp   happy_grothendieck

[root@docker ~ 11:49:26]# docker exec -it 78 bash
bash-4.2# mysql -uroot -p123
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 2
Server version: 5.7.44 MySQL Community Server (GPL)

Copyright (c) 2000, 2023, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql>

建库建表插入数据

bash 复制代码
#创建数据库叫db01
mysql> create database db01;
Query OK, 1 row affected (0.00 sec)

#使用db01;
mysql> use db01;
Database changed

mysql> create table tables1(id int,name varchar(20));
Query OK, 0 rows affected (0.01 sec)

mysql> insert into tables1 values(1,'dyx');
Query OK, 1 row affected (0.00 sec)

mysql> select * from tables1;
+------+------+
| id   | name |
+------+------+
|    1 | dyx  |
+------+------+
1 row in set (0.01 sec)

外部windows连接运行在docker上的mysql容器实例服务

安装navicat160_premium_cs_x64.exe,一直下一步直到安装完成

双击桌面图标打开

bash 复制代码
[root@docker ~ 13:52:25]# docker exec -it 78 bash
bash-4.2# mysql -uroot -p123
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 7
Server version: 5.7.44 MySQL Community Server (GPL)

Copyright (c) 2000, 2023, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> show variabled like 'character%';
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'variabled like 'character%'' at line 1
mysql> show variables like 'character%';
+--------------------------+----------------------------+
| Variable_name            | Value                      |
+--------------------------+----------------------------+
| character_set_client     | latin1                     |
| character_set_connection | latin1                     |
| character_set_database   | latin1                     |
| character_set_filesystem | binary                     |
| character_set_results    | latin1                     |
| character_set_server     | latin1                     |
| character_set_system     | utf8                       |
| character_sets_dir       | /usr/share/mysql/charsets/ |
+--------------------------+----------------------------+
8 rows in set (0.01 sec)

mysql> q
    ->
    -> ;
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'q' at line 1
mysql> eixt
    -> ;
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'eixt' at line 1
mysql> exit
Bye
bash-4.2#
bash-4.2# read escape sequence
[root@docker ~ 13:59:09]# docker ps
CONTAINER ID   IMAGE       COMMAND                  CREATED       STATUS       PORTS                                                  NAMES
78a58a27c2c3   mysql:5.7   "docker-entrypoint.s..."   2 hours ago   Up 2 hours   0.0.0.0:3306->3306/tcp, :::3306->3306/tcp, 33060/tcp   happy_grothendieck

[root@docker ~ 13:59:19]# docker rm -f 78
78

#再用同样的方式创建一个mysql,数据还在么?
[root@docker ~ 14:00:21]# docker run -d -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123 mysql:5.7
0ff47ec15ef5ce2e2bb3b8781634da19cb120fcf436ef2d7d1b09ed8fafff7ab
实战版

新建mysql实例

bash 复制代码
[root@docker ~ 14:20:05]# docker run -d -p 3306:3306 --privileged=true -v /dyx/mysql/log:/var/log/mysql -v /dyx/mysql/data:/var/lib/mysql -v /dyx/mysql/conf:/etc/mysql/conf.d -e MYSQL_ROOT_PASSWORD=123456 --name mysql1 mysql:5.7
974b22a5e4f5dfb34e32703f6f924898793d1239e0982b0d2d931e1d839b5e95

新建my.cnf,通过容器卷同步给mysql容器实例

bash 复制代码
[root@docker ~ 14:22:09]# cd /dyx/mysql/conf/
[root@docker conf 14:22:16]# ls
#实现mysql支持中文
[root@docker conf 14:22:18]# vim my.cnf
[client]
default_character_set=utf8
[mysqld]
collation_server = utf8_general_ci
character_set_server = utf8

重新启动mysql容器实例再重新进入并查看字符编码

bash 复制代码
[root@docker conf 14:23:15]# docker restart mysql1
mysql1
[root@docker conf 14:23:28]# docker exec -it 97 bash
bash-4.2# mysql -uroot -p123456
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 2
Server version: 5.7.44 MySQL Community Server (GPL)

Copyright (c) 2000, 2023, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> show variables like 'character%';
+--------------------------+----------------------------+
| Variable_name            | Value                      |
+--------------------------+----------------------------+
| character_set_client     | utf8                       |
| character_set_connection | utf8                       |
| character_set_database   | utf8                       |
| character_set_filesystem | binary                     |
| character_set_results    | utf8                       |
| character_set_server     | utf8                       |
| character_set_system     | utf8                       |
| character_sets_dir       | /usr/share/mysql/charsets/ |
+--------------------------+----------------------------+
8 rows in set (0.00 sec)

#创建数据库叫db01
mysql> create database db01;
Query OK, 1 row affected (0.00 sec)

 #使用db01;
mysql> use db01;
Database changed

mysql> create table table1(id int,name varchar(20));
Query OK, 0 rows affected (0.01 sec)

mysql> insert into table1 values(1,'dyx');
Query OK, 1 row affected (0.00 sec)

mysql> select * from table1;
+------+------+
| id   | name |
+------+------+
|    1 | dyx  |
+------+------+
1 row in set (0.00 sec)


#还能插入中文
mysql> select * from table1;
+------+-----------+
| id   | name      |
+------+-----------+
|    1 | dyx       |
|    2 | 邓云馨    |
+------+-----------+
2 rows in set (0.00 sec)

mysql> exit
Bye
bash-4.2# exit
exit

假如当前容器实例删除,再重新来一次,之前创建的db01实例还有吗??赶紧动起来尝试一下吧!!!

再次删库跑路!!!

bash 复制代码
[root@docker conf 14:27:34]# docker ps
CONTAINER ID   IMAGE       COMMAND                  CREATED         STATUS         PORTS                                                  NAMES
974b22a5e4f5   mysql:5.7   "docker-entrypoint.s..."   5 minutes ago   Up 4 minutes   0.0.0.0:3306->3306/tcp, :::3306->3306/tcp, 33060/tcp   mysql1
[root@docker conf 14:27:47]# docker rm -f 97
97

重新创建容器,看看数据还在不在

bash 复制代码
[root@docker conf 14:27:51]# docker run -d -p 3306:3306 --privileged=true -v /dyx/mysql/log:/var/log/mysql -v /dyx/mysql/data:/var/lib/mysql -v /dyx/mysql/conf:/etc/mysql/conf.d -e MYSQL_ROOT_PASSWORD=123456 --name mysql1 mysql:5.7
acde7362df4dd0dcfd2114fa654d6ec3d8097e273570afa1df1edbfa20a11d12

发现数据还在

第7章 容器监控

Docker自带的监控子命令

当Docker部署规模逐步变大后,可视化监控容器环境的性能和健康状态将会变得越来越重要。

ps

docker ps 是我们早已熟悉的命令了,方便我们查看当前运行的容器。前面已经有大量示例,这里就不赘述了。

bash 复制代码
[root@docker ~ 15:04:14]# docker ps
CONTAINER ID   IMAGE       COMMAND                  CREATED          STATUS          PORTS                                                  NAMES
acde7362df4d   mysql:5.7   "docker-entrypoint.s..."   36 minutes ago   Up 36 minutes   0.0.0.0:3306->3306/tcp, :::3306->3306/tcp, 33060/tcp   mysql1
[root@docker ~ 15:04:25]# docker top
"docker top" requires at least 1 argument.
See 'docker top --help'.

Usage:  docker top CONTAINER [ps OPTIONS]

Display the running processes of a container
top

查看容器的进程

bash 复制代码
[root@docker ~ 15:04:36]# docker top ac
UID                 PID                 PPID                C                   STIME               TTY                 TIME                CMD
systemd+            6653                6632                0                   14:27               ?                   00:00:01            mysqld
[root@docker ~ 15:04:41]# docker top mysql1
UID                 PID                 PPID                C                   STIME               TTY                 TIME                CMD
systemd+            6653                6632                0                   14:27               ?                   00:00:01            mysqld

#命令后面还可以跟上 Linux 操作系统 `ps` 命令的参数显示特定的信息,比如 `-au`。
[root@docker ~ 15:04:58]# docker top mysql1 -au
USER                PID                 %CPU                %MEM                VSZ                 RSS                 TTY                 STAT                START               TIME                COMMAND
systemd+            6653                0.0                 2.8                 1360648             225724              ?                   Ssl                 14:27               0:01                mysqld
stats

列出容器资源使用率

bash 复制代码
[root@docker ~ 15:05:10]# docker stats
CONTAINER ID   NAME      CPU %     MEM USAGE / LIMIT     MEM %     NET I/O          BLOCK I/O     PIDS
acde7362df4d   mysql1    0.01%     204.7MiB / 7.486GiB   2.67%     6.2kB / 11.1kB   0B / 30.4MB   30
CONTAINER ID   NAME      CPU %     MEM USAGE / LIMIT     MEM %     NET I/O          BLOCK I/O     PIDS
acde7362df4d   mysql1    0.01%     204.7MiB / 7.486GiB   2.67%     6.2kB / 11.1kB   0B / 30.4MB   30
CONTAINER ID   NAME      CPU %     MEM USAGE / LIMIT     MEM %     NET I/O          BLOCK I/O     PIDS
acde7362df4d   mysql1    0.04%     204.7MiB / 7.486GiB   2.67%     6.2kB / 11.1kB   0B / 30.4MB   30
CONTAINER ID   NAME      CPU %     MEM USAGE / LIMIT     MEM %     NET I/O          BLOCK I/O     PIDS
acde7362df4d   mysql1    0.04%     204.7MiB / 7.486GiB   2.67%     6.2kB / 11.1kB   0B / 30.4MB   30
CONTAINER ID   NAME      CPU %     MEM USAGE / LIMIT     MEM %     NET I/O          BLOCK I/O     PIDS
acde7362df4d   mysql1    0.03%     204.7MiB / 7.486GiB   2.67%     6.2kB / 11.1kB   0B / 30.4MB   30
CONTAINER ID   NAME      CPU %     MEM USAGE / LIMIT     MEM %     NET I/O          BLOCK I/O     PIDS
acde7362df4d   mysql1    0.03%     204.7MiB / 7.486GiB   2.67%     6.2kB / 11.1kB   0B / 30.4MB   30
CONTAINER ID   NAME      CPU %     MEM USAGE / LIMIT     MEM %     NET I/O          BLOCK I/O     PIDS
acde7362df4d   mysql1    0.04%     204.7MiB / 7.486GiB   2.67%     6.2kB / 11.1kB   0B / 30.4MB   30
CONTAINER ID   NAME      CPU %     MEM USAGE / LIMIT     MEM %     NET I/O          BLOCK I/O     PIDS
acde7362df4d   mysql1    0.04%     204.7MiB / 7.486GiB   2.67%     6.2kB / 11.1kB   0B / 30.4MB   30
CONTAINER ID   NAME      CPU %     MEM USAGE / LIMIT     MEM %     NET I/O          BLOCK I/O     PIDS
acde7362df4d   mysql1    0.06%     204.7MiB / 7.486GiB   2.67%     6.2kB / 11.1kB   0B / 30.4MB   30
CONTAINER ID   NAME      CPU %     MEM USAGE / LIMIT     MEM %     NET I/O          BLOCK I/O     PIDS
acde7362df4d   mysql1    0.06%     204.7MiB / 7.486GiB   2.67%     6.2kB / 11.1kB   0B / 30.4MB   30
CONTAINER ID   NAME      CPU %     MEM USAGE / LIMIT     MEM %     NET I/O          BLOCK I/O     PIDS
acde7362df4d   mysql1    0.03%     204.7MiB / 7.486GiB   2.67%     6.2kB / 11.1kB   0B / 30.4MB   30
CONTAINER ID   NAME      CPU %     MEM USAGE / LIMIT     MEM %     NET I/O          BLOCK I/O     PIDS
acde7362df4d   mysql1    0.03%     204.7MiB / 7.486GiB   2.67%     6.2kB / 11.1kB   0B / 30.4MB   30
CONTAINER ID   NAME      CPU %     MEM USAGE / LIMIT     MEM %     NET I/O          BLOCK I/O     PIDS
acde7362df4d   mysql1    0.03%     204.7MiB / 7.486GiB   2.67%     6.2kB / 11.1kB   0B / 30.4MB   30
CONTAINER ID   NAME      CPU %     MEM USAGE / LIMIT     MEM %     NET I/O          BLOCK I/O     PIDS
acde7362df4d   mysql1    0.03%     204.7MiB / 7.486GiB   2.67%     6.2kB / 11.1kB   0B / 30.4MB   30
CONTAINER ID   NAME      CPU %     MEM USAGE / LIMIT     MEM %     NET I/O          BLOCK I/O     PIDS
acde7362df4d   mysql1    0.03%     204.7MiB / 7.486GiB   2.67%     6.2kB / 11.1kB   0B / 30.4MB   30
q
CONTAINER ID   NAME      CPU %     MEM USAGE / LIMIT     MEM %     NET I/O          BLOCK I/O     PIDS
acde7362df4d   mysql1    0.03%     204.7MiB / 7.486GiB   2.67%     6.2kB / 11.1kB   0B / 30.4MB   30
CONTAINER ID   NAME      CPU %     MEM USAGE / LIMIT     MEM %     NET I/O          BLOCK I/O     PIDS
acde7362df4d   mysql1    0.04%     204.7MiB / 7.486GiB   2.67%     6.2kB / 11.1kB   0B / 30.4MB   30
CONTAINER ID   NAME      CPU %     MEM USAGE / LIMIT     MEM %     NET I/O          BLOCK I/O     PIDS
acde7362df4d   mysql1    0.04%     204.7MiB / 7.486GiB   2.67%     6.2kB / 11.1kB   0B / 30.4MB   30
^C

默认会显示一个实时变化的列表,展示每个容器的 CPU 使用率,内存使用量和可用量,网络和磁盘的 IO 数据。

注意:容器启动时如果没有特别指定内存 limit,stats 命令会显示 host 的内存总量,但这并不意味着每个 container 都能使用到这么多的内存。

cAdvisor

cAdvisor 是 google 开发的容器监控工具。

bash 复制代码
[root@docker ~ 15:07:28]# docker run --volume=/:/rootfs:ro --volume=/var/run:/var/run:rw --volume=/sys:/sys:ro --volume=/var/lib/docker/:/var/lib/docker:ro --publish=8080:8080 --detach=true --name=cadvisor google/cadvisor:latest
Unable to find image 'google/cadvisor:latest' locally
latest: Pulling from google/cadvisor
ff3a5c916c92: Pull complete
44a45bb65cdf: Pull complete
0bbe1a2fe2a6: Pull complete
Digest: sha256:815386ebbe9a3490f38785ab11bda34ec8dacf4634af77b8912832d4f85dca04
Status: Downloaded newer image for google/cadvisor:latest
68d1510e27fad50939948f3f22d83e9dfcfea96d831d49137eb7301f8ed03e6c

也可以使用镜像hub.c.163.com/xbingo/cadvisor:latest。

通过 http://[Host_IP]:8080 访问 cAdvisor。首次打开比较慢,系统需要收集数据并绘制图表。

点击Docker Containers进去看容器具体信息



第8章 容器日志

高效的监控和日志管理对保持生产系统持续稳定地运行以及排查问题至关重要。

在微服务架构中,由于容器的数量众多以及快速变化的特性使得记录日志和监控变得越来越重要。考虑到容器短暂和不固定的生命周期,当我们需要 debug 问题时有些容器可能已经不存在了。因此,一套集中式的日志管理系统是生产环境中不可或缺的组成部分。

Docker logs

对于一个运行的容器,Docker 会将日志发送到 容器标准输出设备(STDOUT)和标准错误设备(STDERR),STDOUT 和 STDERR 实际上就是容器的控制台终端。

举个例子,用下面的命令运行 httpd 容器:

bash 复制代码
[root@docker ~ 15:08:51]# docker run -p 80:80 httpd
AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 172.17.0.4. Set the 'ServerName' directive globally to suppress this message
AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 172.17.0.4. Set the 'ServerName' directive globally to suppress this message
[Fri Nov 21 07:14:51.699320 2025] [mpm_event:notice] [pid 1:tid 1] AH00489: Apache/2.4.65 (Unix) configured -- resuming normal operations
[Fri Nov 21 07:14:51.699438 2025] [core:notice] [pid 1:tid 1] AH00094: Command line: 'httpd -D FOREGROUND'
ls
pwd
^C[Fri Nov 21 07:15:03.061336 2025] [mpm_event:notice] [pid 1:tid 1] AH00491: caught SIGTERM, shutting down

我们在启动日志的时候没有用 -d 参数,httpd 容器以前台方式启动,日志会直接打印在当前的终端窗口。

如果加上 -d 参数以后台方式运行容器,我们就看不到输出的日志了。

bash 复制代码
[root@docker ~ 15:15:14]# docker run -d -p 80:80 httpd
7a44bd648fb85d7bab7c9a8cdb48f67fe13fb48f15cce0ac747f07cec92c3bb4

这种情况下如果要查看容器的日志,有两种方法:

  1. attach 到该容器。
  2. docker logs 命令查看日志。

先来看 attach 的方法。运行 docker attach 命令。

bash 复制代码
[root@docker ~ 15:15:22]# docker attach 7a
172.17.0.1 - - [21/Nov/2025:07:16:01 +0000] "GET / HTTP/1.1" 200 45
172.17.0.1 - - [21/Nov/2025:07:16:06 +0000] "GET / HTTP/1.1" 200 45
172.17.0.1 - - [21/Nov/2025:07:16:07 +0000] "GET / HTTP/1.1" 200 45
^C[Fri Nov 21 07:16:10.504308 2025] [mpm_event:notice] [pid 1:tid 1] AH00491: caught SIGTERM, shutting down

attach 到了 httpd 容器,但并没有任何输出,这是因为当前没有新的日志信息。

为了产生一条新的日志,可以在 host 的另一个命令行终端执行 curl localhost

终端B:

bash 复制代码
[root@docker ~ 15:15:55]# curl localhost
<html><body><h1>It works!</h1></body></html>
[root@docker ~ 15:16:01]# curl localhost
<html><body><h1>It works!</h1></body></html>
[root@docker ~ 15:16:06]# curl localhost
<html><body><h1>It works!</h1></body></html>

这时,attach 的终端就会打印出新的日志。

attach 的方法在实际使用中不太方便,因为:

  1. 只能看到 attach 之后的日志,以前的日志不可见。
  2. 退出 attach 状态比较麻烦(Ctrl+p 然后 Ctrl+q 组合键),一不小心很容器将容器杀掉(比如按下 Ctrl+C)。

查看容器日志推荐的方法是用 docker logs 命令。

bash 复制代码
#可以显示全部的日志
[root@docker ~ 15:16:10]# docker logs 7a
AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 172.17.0.4. Set the 'ServerName' directive globally to suppress this message
AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 172.17.0.4. Set the 'ServerName' directive globally to suppress this message
[Fri Nov 21 07:15:22.925326 2025] [mpm_event:notice] [pid 1:tid 1] AH00489: Apache/2.4.65 (Unix) configured -- resuming normal operations
[Fri Nov 21 07:15:22.925440 2025] [core:notice] [pid 1:tid 1] AH00094: Command line: 'httpd -D FOREGROUND'
172.17.0.1 - - [21/Nov/2025:07:16:01 +0000] "GET / HTTP/1.1" 200 45
172.17.0.1 - - [21/Nov/2025:07:16:06 +0000] "GET / HTTP/1.1" 200 45
172.17.0.1 - - [21/Nov/2025:07:16:07 +0000] "GET / HTTP/1.1" 200 45
[Fri Nov 21 07:16:10.504308 2025] [mpm_event:notice] [pid 1:tid 1] AH00491: caught SIGTERM, shutting down

docker logs 能够打印出自容器启动以来完整的日志,并且 -f 参数可以继续打印出新产生的日志,效果上与 Linux 命令 tail -f 一样。

第9章 Docker-compose

我们知道使用一个 Dockerfile 模板文件,可以让用户很方便的定义一个单独的应用容器。然而,在日常工作中,经常会碰到需要多个容器相互配合来完成某项任务的情况。例如要实现一个 Web 项目,除了 Web 服务容器本身,往往还需要再加上后端的数据库服务容器,甚至还包括负载均衡容器等。如果每个容器都要按顺序手动启停,那么维护工作量将会很大,而且工作效率也很低。

Docker Compose 可以轻松、高效地管理容器,它是一个用于定义和运行多容器的管理工具。

它通过一个单独的 docker-compose.yml 模板文件(YAML 格式)定义一组相关联资源集。

Compose 中有两个重要的概念:

  • 服务 (service):一个应用的容器,实际上可以包括若干运行相同镜像的容器实例。
  • 项目 (project):由一组关联的应用容器组成的一个完整业务单元,在 docker-compose.yml 文件中定义。

Compose 的默认管理对象是项目,通过子命令对项目中的一组容器进行便捷地生命周期管理。

Compose 项目由 Python 编写(后用Go语言重写),实现上调用了 Docker 服务提供的 API 来对容器进行管理。因此,只要所操作的平台支持 Docker API,就可以在其上利用 Compose 来进行编排管理。

命令说明

bash 复制代码
[root@docker ~ 15:58:55]# docker compose -h
Flag shorthand -h has been deprecated, please use --help

Usage:  docker compose [OPTIONS] COMMAND

Define and run multi-container applications with Docker

Options:
      --all-resources              Include all resources, even those not used by
                                   services
      --ansi string                Control when to print ANSI control characters
                                   ("never"|"always"|"auto") (default "auto")
      --compatibility              Run compose in backward compatibility mode
      --dry-run                    Execute command in dry run mode
      --env-file stringArray       Specify an alternate environment file
  -f, --file stringArray           Compose configuration files
      --parallel int               Control max parallelism, -1 for unlimited
                                   (default -1)
      --profile stringArray        Specify a profile to enable
      --progress string            Set type of progress output (auto, tty, plain,
                                   quiet) (default "auto")
      --project-directory string   Specify an alternate working directory
                                   (default: the path of the, first specified,
                                   Compose file)
  -p, --project-name string        Project name

Commands:
  attach      Attach local standard input, output, and error streams to a service's running container
  build       Build or rebuild services
  config      Parse, resolve and render compose file in canonical format
  cp          Copy files/folders between a service container and the local filesystem
  create      Creates containers for a service
  down        Stop and remove containers, networks
  events      Receive real time events from containers
  exec        Execute a command in a running container
  images      List images used by the created containers
  kill        Force stop service containers
  logs        View output from containers
  ls          List running compose projects
  pause       Pause services
  port        Print the public port for a port binding
  ps          List containers
  pull        Pull service images
  push        Push service images
  restart     Restart service containers
  rm          Removes stopped service containers
  run         Run a one-off command on a service
  scale       Scale services
  start       Start services
  stats       Display a live stream of container(s) resource usage statistics
  stop        Stop services
  top         Display the running processes
  unpause     Unpause services
  up          Create and start containers
  version     Show the Docker Compose version information
  wait        Block until the first service container stops
  watch       Watch build context for service and rebuild/refresh containers when files are updated

Run 'docker compose COMMAND --help' for more information on a command.

version

bash 复制代码
[root@docker ~ 17:16:00]# docker compose version
Docker Compose version v2.27.0

Compose 模板

模板文件是使用 Compose 的核心,涉及到的指令关键字也比较多。但大家不用担心,这里面大部分指令跟 docker run 相关参数的含义都是类似的。

默认的模板文件名称为 docker-compose.yml,格式为 YAML 格式。

模板文件结构

  • version:用来定义模板文件的版本,不同版本的模板,格式也不一样。
  • 资源列表:用来定义资源清单,包括service、secret、network、volume等。
  • 注释行: # 开头的注释行。

示例:使用版本2模板,定义一个使用httpd镜像的services。

复制代码
version: "2"

services:
  webapp:
    image: httpd

详细结构参考官方

Compose文档

实战-Wordpress

回顾一下如果用docker run改如何操作:

bash 复制代码
#先下载镜像
[root@docker ~ 15:46:49]# docker pull wordpress
Using default tag: latest
latest: Pulling from library/wordpress
0e4bc2bd6656: Already exists
5ff292d92bef: Pull complete
30d3fccd4e77: Pull complete
2e42a5718777: Pull complete
1786c40e6ef6: Pull complete
d546c1d12a7f: Pull complete
64709930f968: Pull complete
d1a33af9a2e4: Pull complete
b95356b5f483: Pull complete
8e3cf1a078fa: Pull complete
dba783666fd7: Pull complete
387c70da0b5e: Pull complete
f3c8c18e18ce: Pull complete
2da9ad8b7fb7: Pull complete
4f4fb700ef54: Pull complete
e4aedbf72206: Pull complete
175dfdd2b654: Pull complete
78e81f73c2d7: Pull complete
22803a624815: Pull complete
336946dd099b: Pull complete
83f77dfb5823: Pull complete
7ac08dbbed07: Pull complete
b237a14d49b4: Pull complete
53cbf50969c4: Pull complete
Digest: sha256:40f6374af39dd7dcefa84c0d8a3c7a5884b6e955d1c190b6ffb54d9e2398e7d6
Status: Downloaded newer image for wordpress:latest
docker.io/library/wordpress:latest


#运行数据库
[root@docker ~ 16:00:32]# docker run -itd --name db --restart always -v /db:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123 -e MYSQL_DATABASE=wordpress mysql:5.7
38434b037efeae4f60fe0cf4b8e57c79fe343d7d29f5417a82c2162cdc470318

#运行WordPress
[root@docker ~ 16:00:37]# docker run -itd --name blog -v /web:/var/www/html -p 80:80 --link db -e WORDPRESS_DB_HOST=db -e WORDPRESS_DB_USER=root -e WORDPRESS_DB_PASSWORD=123 -e WORDPRESS_DB_NAME=wordpress wordpress
6dbac2a065afbf5449a26d77a2992f17a1080424f24931406d00e1df0130a85d

#查看容器
[root@docker ~ 16:02:50]# docker ps
CONTAINER ID   IMAGE                    COMMAND                  CREATED          STATUS          PORTS                                                  NAMES
6dbac2a065af   wordpress                "docker-entrypoint.s..."   5 seconds ago    Up 4 seconds    0.0.0.0:80->80/tcp, :::80->80/tcp                      blog
38434b037efe   mysql:5.7                "docker-entrypoint.s..."   2 minutes ago    Up 2 minutes    3306/tcp, 33060/tcp                                    db
68d1510e27fa   google/cadvisor:latest   "/usr/bin/cadvisor -..."   54 minutes ago   Up 54 minutes   0.0.0.0:8080->8080/tcp, :::8080->8080/tcp              cadvisor
acde7362df4d   mysql:5.7                "docker-entrypoint.s..."   2 hours ago      Up 2 hours      0.0.0.0:3306->3306/tcp, :::3306->3306/tcp, 33060/tcp   mysql1

测试效果:

通过docker compose来统一管理这两个容器呢?

假设新建一个名为 wordpress 的文件夹,然后进入这个文件夹,创建 docker-compose.yml 文件

bash 复制代码
#先删除前面的实验环境
[root@docker ~ 16:02:54]# docker rm -f 6d
6d
[root@docker ~ 16:04:14]# docker rm -f 38
38

# 通过docker compose实现多个容器一起启动
[root@docker ~ 16:04:32]# mkdir wordpress
[root@docker ~ 16:05:10]# cd wordpress/

[root@docker wordpress 16:16:10]# vim docker-compose.yml
[root@docker wordpress 16:19:41]# cat docker-compose.yml
services:
        #服务名字,相当于docker run的时候指定的一个名称
        blog:
           #必选,镜像的名字
           image: wordpress:latest
           restart: always
           links:
                 - db
           #可选,等价于 docker run 里的 -p 选项指定端口映射
           ports:
                 - "80:80"
           #可选,等价于 docker run 里的 --env 选项设置环境变量
           environment:
                        - WORDPRESS_DB_HOST=db
                        - WORDPRESS_DB_USER=root
                        - WORDPRESS_DB_PASSWORD=123
                        - WORDPRESS_DB_NAME=wordpress
        db:
           image: mysql:5.7
           restart: always
           environment:
                        - MYSQL_ROOT_PASSWORD=123
                        - MYSQL_DATABASE=wordpress

#检测语法
[root@docker wordpress 16:13:42]# docker compose config -q

#后端运行
[root@docker wordpress 16:19:37]# docker compose up -d
[+] Running 3/3
 ✔ Network wordpress_default   Created                                             0.2s
 ✔ Container wordpress-db-1    Started                                             0.3s
 ✔ Container wordpress-blog-1  Started                                             0.7s

查看现象

bash 复制代码
[root@docker wordpress 16:19:54]# docker ps
CONTAINER ID   IMAGE                    COMMAND                  CREATED             STATUS             PORTS                                                  NAMES
0e523316e5f7   wordpress:latest         "docker-entrypoint.s..."   21 seconds ago      Up 20 seconds      0.0.0.0:80->80/tcp, :::80->80/tcp                      wordpress-blog-1
d471b0628ec9   mysql:5.7                "docker-entrypoint.s..."   21 seconds ago      Up 20 seconds      3306/tcp, 33060/tcp                                    wordpress-db-1
68d1510e27fa   google/cadvisor:latest   "/usr/bin/cadvisor -..."   About an hour ago   Up About an hour   0.0.0.0:8080->8080/tcp, :::8080->8080/tcp              cadvisor
acde7362df4d   mysql:5.7                "docker-entrypoint.s..."   2 hours ago         Up 2 hours         0.0.0.0:3306->3306/tcp, :::3306->3306/tcp, 33060/tcp   mysql1

第10章 docker图形界面管理

DockerUI 容器管理器的安装与使用

简介:

DockerUI是一个易用且轻量化的 Docker 管理工具,通过 Web 界面的操作,更方便对于 Docker 指令不熟悉的用户更容易操作 Docker 。

功能:

  • Docker主机管理:数据卷管理,镜像管理,容器管理,构建管理,仓库配置管理,网络配置管理
  • Docker Swarm集群管理:集群概要信息,节点管理,Service管理,任务管理,密码管理,配置管理

镜像:

我今天分享的这个镜像是来自于这位大佬@joinsunsoft 的,他发布在Docker Hub的镜像地址为:https://hub.docker.com/r/joinsunsoft/docker.ui

安装

启动容器并映射8999端口:

bash 复制代码
[root@docker ~ 16:35:15]# docker run -d --name docker.ui --restart always -v /var/run/docker.sock:/var/run/docker.sock -p 8999:8999 joinsunsoft/docker.ui
Unable to find image 'joinsunsoft/docker.ui:latest' locally
latest: Pulling from joinsunsoft/docker.ui
213ec9aee27d: Pull complete
362df81e5664: Pull complete
c3f864fca442: Pull complete
5a55080a5af0: Pull complete
497ed8376dfb: Pull complete
4642bc86bdfb: Pull complete
af43397a88d3: Pull complete
Digest: sha256:1b70b86be661e931e112d8b59850eeac927966679c52b46064d67df2c489d7a5
Status: Downloaded newer image for joinsunsoft/docker.ui:latest
fd07a348f96ba884355a08e5916ac0a5846ae362083c0ea76a2f7684e48ee706

启动效果

你可以访问:http://192.168.108.30:8999

默认用户名密码:ginghan /123456

DockerUI的主界面(概览),它展示的信息还是很多的,除了容器相关,还有资源占用情况等信息。不得不说还是国人更懂国人,UI界面还是很漂亮的对吧~

Docker 图形化界面管理工具 Portainer

Portainer 是一个 Docker 图形化管理工具,可以通过 Web UI 轻松的管理容器、镜像、网络、卷。同时上手难度也更大一些

Portainer 分为社区版和商业版,本文安装的是社区版(Portainer CE),该版本免费,比较适合个人用户使用

安装

1.创建存储卷

bash 复制代码
[root@docker ~ 16:36:47]# docker volume create portainer_data
portainer_data

2.通过docker安装Portainer

bash 复制代码
[root@docker ~ 17:13:45]# docker run -d -p 8000:8000 -p 9443:9443 --name portainer --restart=always -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer-ce:latest
Unable to find image 'portainer/portainer-ce:latest' locally
latest: Pulling from portainer/portainer-ce
a48e87497a08: Pull complete
7e764748a0e3: Pull complete
04f56cab0724: Pull complete
666c509ccde9: Pull complete
6109bb7d1248: Pull complete
da18f9659f11: Pull complete
e80185cdbc79: Pull complete
4f4fb700ef54: Pull complete
Digest: sha256:83fab9ec8e05a9ca17c64d2288fef25f498124eccdbcd12eaff9238870884630
Status: Downloaded newer image for portainer/portainer-ce:latest
f4aefa2b1b729b6e57a6f239a85b0cd319e217ca1c49cff1ea0bab6cef778bc8

9443 端口默认会启用 SSL,如果需要直接通过 http 访问,需要加上 -p 9000:9000 访问 9000 端口

这里,-v /var/run/docker.sock:/var/run/docker.sock 参数使得Portainer能够访问Docker守护进程,从而能够管理容器。-v portainer_data:/data 参数则用于持久化Portainer的数据。

访问

通过 http://ip:9000 或者 https://ip:9443 访问 Portainer,首次访问需要创建管理员账号

0a5af0: Pull complete

497ed8376dfb: Pull complete

4642bc86bdfb: Pull complete

af43397a88d3: Pull complete

Digest: sha256:1b70b86be661e931e112d8b59850eeac927966679c52b46064d67df2c489d7a5

Status: Downloaded newer image for joinsunsoft/docker.ui:latest

fd07a348f96ba884355a08e5916ac0a5846ae362083c0ea76a2f7684e48ee706

复制代码
**启动效果**

你可以访问:http://192.168.108.30:8999

默认用户名密码:**ginghan**/**123456**

[外链图片转存中...(img-FmfGnDhT-1764848344285)]

DockerUI的主界面(概览),它展示的信息还是很多的,除了容器相关,还有资源占用情况等信息。不得不说还是国人更懂国人,UI界面还是很漂亮的对吧~

[外链图片转存中...(img-75aYP1bU-1764848344285)]

### Docker 图形化界面管理工具 Portainer

Portainer 是一个 Docker 图形化管理工具,可以通过 Web UI 轻松的管理容器、镜像、网络、卷。同时上手难度也更大一些

Portainer 分为社区版和商业版,本文安装的是社区版(Portainer CE),该版本免费,比较适合个人用户使用



**安装**

1.创建存储卷

```bash
[root@docker ~ 16:36:47]# docker volume create portainer_data
portainer_data

2.通过docker安装Portainer

bash 复制代码
[root@docker ~ 17:13:45]# docker run -d -p 8000:8000 -p 9443:9443 --name portainer --restart=always -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer-ce:latest
Unable to find image 'portainer/portainer-ce:latest' locally
latest: Pulling from portainer/portainer-ce
a48e87497a08: Pull complete
7e764748a0e3: Pull complete
04f56cab0724: Pull complete
666c509ccde9: Pull complete
6109bb7d1248: Pull complete
da18f9659f11: Pull complete
e80185cdbc79: Pull complete
4f4fb700ef54: Pull complete
Digest: sha256:83fab9ec8e05a9ca17c64d2288fef25f498124eccdbcd12eaff9238870884630
Status: Downloaded newer image for portainer/portainer-ce:latest
f4aefa2b1b729b6e57a6f239a85b0cd319e217ca1c49cff1ea0bab6cef778bc8

9443 端口默认会启用 SSL,如果需要直接通过 http 访问,需要加上 -p 9000:9000 访问 9000 端口

这里,-v /var/run/docker.sock:/var/run/docker.sock 参数使得Portainer能够访问Docker守护进程,从而能够管理容器。-v portainer_data:/data 参数则用于持久化Portainer的数据。

访问

通过 http://ip:9000 或者 https://ip:9443 访问 Portainer,首次访问需要创建管理员账号

相关推荐
普罗米修斯Aaron_Swartz2 小时前
云主机(容器)内实现本地VNC访问
网络·docker
Crazy________2 小时前
01 k8s核心minikube搭建单点集群,kubectl高频命令
云原生·容器·kubernetes
LSL666_3 小时前
docker概述
运维·docker·容器
cqsztech4 小时前
如何在Oracle linux9.6 安装docker软件
数据库·docker·oracle
码灵5 小时前
docker export 和 docker commit的区别
docker
一条懒鱼6665 小时前
K8S-Configmap资源
云原生·容器·kubernetes
jthou@hotmail.com5 小时前
远程服务器 Docker 环境配置指南
运维·服务器·docker
cqsztech5 小时前
自己如何动手创建oracle 19c 19.3标准版2 DOCKER 镜像
数据库·docker·oracle
路边草随风5 小时前
使用SparkLauncher发布k8s作业
云原生·容器·spark·kubernetes