你必须知道的Docker数据卷(Volume)
一、将Docker数据挂载到容器
在Docker中,要想实现数据的持久化(所谓Docker的数据持久化即数据不随着Container的结束而结束),需要将数据从宿主机挂载到容器中。目前Docker提供了三种不同的方式将数据从宿主机挂载到容器中:
(1)volumes:Docker管理宿主机文件系统的一部分,默认位于 /var/lib/docker/volumes 目录中;(最常用的方式)
所有Container的数据都保存在了这个目录下边,由于没有在创建时指定卷,所以Docker帮我们默认创建许多匿名(就上面这一堆很长ID的名字)卷。
(2)bind mounts:意为着可以存储在宿主机系统的任意位置;(比较常用的方式)
但是,bind mount在不同的宿主机系统时不可移植的,比如Windows和Linux的目录结构是不一样的,bind mount所指向的host目录也不能一样。这也是为什么bind mount不能出现在Dockerfile中的原因,因为这样Dockerfile就不可移植了。
(3)tmpfs:挂载存储在宿主机系统的内存中,而不会写入宿主机的文件系统;(一般都不会用的方式)
二、Volume的基本使用
2.1管理卷
// 创建一个自定义容器卷 docker volume create edc-nginx-vol // 查看所有容器卷 docker volume ls // 查看指定容器卷详情信息 docker volume inspect edc-nginx-vol
输出如下:
root@debian:~# docker volume create edc-nginx-vol edc-nginx-vol root@debian:~# docker volume ls DRIVER VOLUME NAME local 88c39923317234d00b26d9ed1aef542423bfe61787344acadd8f35b0884d45e3 local edc-nginx-vol local root_data local x_data root@debian:~# docker volume inspect edc-nginx-vol [ { "CreatedAt": "2024-10-31T21:12:04+08:00", "Driver": "local", "Labels": {}, "Mountpoint": "/var/lib/docker/volumes/edc-nginx-vol/_data", "Name": "edc-nginx-vol", "Options": {}, "Scope": "local" } ]
2.2创建使用指定卷的容器
有了自定义容器卷,我们可以创建一个使用这个数据卷的容器,这里我们以nginx为例:
docker run -d -it --name=edc-nginx -p 8800:80 -v edc-nginx-vol:/usr/share/nginx/html nginx
其中,-v代表挂载数据卷,这里使用自定数据卷edc-nginx-vol,并且将数据卷挂载到 /usr/share/nginx/html (这个目录是yum安装nginx的默认网页目录)。如果没有通过-v指定,那么Docker会默认帮我们创建匿名数据卷进行映射和挂载。
创建好容器之后,我们可以进入容器里面看看:
root@debian:~# docker run -d -it --name=edc-nginx -p 8800:80 -v edc-nginx-vol:/usr/share/nginx/html nginx d067ff1b256163fcbab2779a944d47603c7346a2b451af792d95bcb85bab97dd root@debian:~# docker exec -it edc-nginx bash root@d067ff1b2561:/# cd /usr/share/nginx/html root@d067ff1b2561:/usr/share/nginx/html# ls 50x.html index.html
可以看到有两个默认页,这时我们新启动一个SSH连接到宿主机去到刚刚创建的数据卷里边看看:
root@debian:~# cd /var/lib/docker/volumes/edc-nginx-vol/_data root@debian:/var/lib/docker/volumes/edc-nginx-vol/_data# ls 50x.html index.html
可以看到,我们可以访问到容器里面的两个默认页面,由此可知,volume帮我们做的类似于一个软链接的功能。在容器里边的改动,我们可以在宿主机里感知,而在宿主机里面的改动,在容器里边可以感知到。
这时,如果我们手动stop并且remove当前nginx容器,我们会发现容器卷里面的文件还在,并没有被删除掉。
root@debian:/var/lib/docker/volumes/edc-nginx-vol/_data# docker stop edc-nginx edc-nginx root@debian:/var/lib/docker/volumes/edc-nginx-vol/_data# docker rm edc-nginx edc-nginx root@debian:/var/lib/docker/volumes/edc-nginx-vol/_data# ls 50x.html index.html
由此可以验证,在数据卷里边的东西是可以持久化的。如果下次还需要创建一个nginx容器,那么还是复用当前数据卷里面的文件。
此外,我们还可以启动多个nginx容器实例,并且共享同一个数据卷,复用性和扩展性较强。
2.3清理卷
如果不再使用自定义数据卷了,那么可以手动清理掉:
// 暂停容器实例 docker stop edc-nginx // 移除容器实例 docker rm edc-nginx // 删除自定义数据卷 docker volume rm edc-nginx-vol
root@debian:~# docker stop edc-nginx // 前面已经停止过容器 Error response from daemon: No such container: edc-nginx root@debian:~# docker rm edc-nginx // 前面已经删除过容器 Error: No such container: edc-nginx root@debian:~# docker volume rm edc-nginx-vol // 成功删除卷 edc-nginx-vol
三、Bind Mounts的基本使用
3.1使用卷创建一个容器
docker run -d -it --name=edc-nginx -v /app/wwwroot:/usr/share/nginx/html nginx
这里指定了将宿主机上的 /app/wwwroot 目录(如果没有会自动创建)挂载到 /usr/share/nginx/html (这个目录是yum安装nginx的默认网页目录)。
这时我们再次进入容器内部看看:
root@debian:~# docker exec -it edc-nginx bash root@12bbb8b84b25:/# cd /usr/share/nginx/html root@12bbb8b84b25:/usr/share/nginx/html# ls root@12bbb8b84b25:/usr/share/nginx/html#
可以看到,与volumes不同,bind mounts的方式会隐藏掉被挂载目录里面的内容(如果非空的话),这里是/usr/share/nginx/html 目录下的内容被隐藏掉了,因此我们看不到。
但是,我们可以将宿主机上的文件随时挂载到容器中:
Step1.新建一个index.html
root@debian:~# ls / app boot etc initrd.img lib lost+found mnt proc run srv tmp var vmlinuz.old bin dev home initrd.img.old lib64 media opt root sbin sys usr vmlinuz root@debian:~# cd /app/wwwroot root@debian:/app/wwwroot# nano index.html root@debian:/app/wwwroot# ls index.html
Step2.在容器中查看
root@12bbb8b84b25:/usr/share/nginx/html# ls index.html
3.2验证绑定
docker inspect edc-nginx
通过上述命令可以看到一大波配置,我们要关注的是:
"HostConfig": { "Binds": [ "/app/wwwroot:/usr/share/nginx/html" ], "ContainerIDFile": "",
3.3清理
同volumes一样,当我们清理掉容器之后,挂载目录里面的文件仍然还在,不会随着容器的结束而消失,从而实现数据持久化。
3.4应用案例
在服务治理组件中,服务发现组件是一个最常用的组件之一,Consul是一个流行的服务发现开源项目,Consul推荐我们使用配置文件的方式注册服务信息。因此,我们常常会将填写好服务注册配置文件放在宿主机的一个文件目录下将其挂载到Consul的容器指定目录下,如下所示:
docker run -d -p 8500:8500 --restart=always \ -v /app/data:/consul/data \ -v /app/conf:/consul/config \ -e CONSUL_BIND_INTERFACE='eth0' \ --privileged=true \ --name=consul_server_1 consul:1.4.4 agent -server -bootstrap-expect=3 -ui -node=consul_server_1 -client='0.0.0.0' \ -data-dir /consul/data \ -config-dir /consul/config \ -datacenter=xdp_dc;
可以看到,我们通过Bind Mounts的方式将宿主机上的/XiLife/consul/data/server1目录挂载到了容器的/consul/data目录下,还将/XiLife/consul/conf/server1目录挂载到了容器的/consul/config目录下,而容器下的两个目录/consul/data和/consul/config则是我们指定的存放agent数据和配置文件的地方。因此,宿主机上的配置文件的变化会及时反映到容器中,比如我们在宿主机上的目录下更新了配置文件,那么只需要reload一下Consul的容器实例即可:
oot@debian:~# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 3fb3675de102 consul:1.4.4 "docker-entrypoint.s…" 57 seconds ago Up 56 seconds 8300-8302/tcp, 8301-8302/udp, 8600/tcp, 8600/udp, 0.0.0.0:8500->8500/tcp, :::8500->8500/tcp consul_server_1 root@debian:~# docker exec consul_server_1 consul reload Configuration reload triggered
*.这里的consul-server是容器的名字,consul reload是重新加载的命令(非restart)。
四、小结
本文探索了Docker的数据卷及挂载数据到容器的两种主要方式Volumes和Bind Mounts,并介绍基本的使用方式和步骤,通过数据卷我们可以实现Docker的数据持久化,在实际应用中比较广泛。