EP6 容器的数据卷
什么是数据卷?
Docker将运行环境所需的所有东西都打包到容器里,容器运行时产生的所有数据/文件,在容器销毁后都会和容器一起销毁。为了能持久化数据以及共享容器间的数据,Docker提出了Volume的概念。
卷(Volume)就是目录或文件,存在于一个或多个容器中,由Docker挂载到容器,但卷不属于联合文件系统(Union FileSystem),因此能够绕过UFS提供一些用于持续存储或共享数据的特性。
卷的设计目的就是数据的持久化,完全独立于容器的生存周期,因此Docker不会在容器删除时删除其挂载的数据卷
数据卷的特点:
- 数据卷可在容器之间共享或重用数据
- 卷中的更改可以直接生效
- 数据卷中的更改不会包含在镜像的更新中
- 数据卷的生命周期一直持续到没有容器使用它为止
数据挂载方式
目前Docker提供了三种不同的方式将数据从宿主机挂载到容器中:
volumes:Docker管理宿主机文件系统的一部分,默认位于/var/lib/docker/volumes目录中;(最常用的方式)bind mounts:意为着可以存储在宿主机系统的任意位置;(比较常用的方式)tmpfs:挂载存储在宿主机系统的内存中,而不会写入宿主机的文件系统;(一般都不会用的方式)
bind mount在不同的宿主机系统时不可移植的,比如Windows和Linux的目录结构是不一样的,bind mount所指向的host目录也不能一样。所以bind mount不能出现在Dockerfile中,因为这样Dockerfile就不可移植了。
使用bind mounts挂载数据
bind mounts挂载命令:docker run -it -v 宿主机目录:容器目录
-
在宿主机创建一个文件夹,文件夹中添加一个文件
% pwd /Users/chenrui/work/temp/dockerStudy/volume % touch testVolume.txt % ls testVolume.txt -
运行一个nginx容器,挂载
volume目录到nginx的/usr/share/nginx/html目录% docker run -it -d --name=volume_nginx -p 8081:80 -v /Users/chenrui/work/temp/dockerStudy/volume:/usr/share/nginx/html nginx:1.22.0 6acefa1d972c8e4b2c8827cb7be48aef8bfc127bde251006e4d886bce05e2aaa -
进入容器查看
/usr/share/nginx/html目录,发现该目录没有任何文件(默认情况下该目录应该有nginx默认的index页面)% docker exec -it volume_nginx bash root@6acefa1d972c:/# cd /usr/share/nginx/html/ root@6acefa1d972c:/usr/share/nginx/html# ls root@6acefa1d972c:/usr/share/nginx/html# -
此时,在宿主机的
work/temp/dockerStudy/volume目录下添加文件,再查看容器内发现在宿主机添加的文件同步到了容器内;同时在容器内操作文件,宿主机的文件也会同步修改# 宿主机 dockerStudy % touch testVolume.txt dockerStudy % ls testVolume.txt # 容器内 root@6acefa1d972c:/usr/share/nginx/html# ls testVolume.txt # 容器:修改文件 root@6acefa1d972c:/usr/share/nginx/html# echo "aaaa" > testVolume.txt # 宿主机 volume % cat testVolume.txt aaaa如果希望挂载的文件在容器内只读可以通过在挂在目录后面添加
:ro来设置目录只读(默认是rw)。eg.docker run -v 宿主机目录:容器目录:or -
通过
inspect查看卷绑定状态
% docker inspect volume_nginx
....
"Mounts": [
{
"Type": "bind",
"Source": "/Users/chenrui/work/temp/dockerStudy/volume",
"Destination": "/usr/share/nginx/html",
"Mode": "",
"RW": true,
"Propagation": "rprivate"
}
],
....
使用bind mounts方式挂载目录时,无论宿主机目录是否为空,Docker都会用宿主机目录覆盖容器挂载目录;这样会造成一些问题,比如:我们需要挂载的目录本身是有数据的,这些数据是容器启动的依赖项;此时使用bind mounts方式挂载就会导致启动容器时出现错误。遇到这种情况就需要使用容器卷来挂载数据了。
volumes常用命令
-
创建容器卷
# docker volume create demo_vol demo_vol -
查看容器卷
-
查看数据卷详情
[root@localhost ~]# docker volume inspect demo_vol [ { "CreatedAt": "2022-08-27T15:33:32+08:00", "Driver": "local", "Labels": {}, "Mountpoint": "/var/lib/docker/volumes/demo_vol/_data", "Name": "demo_vol", "Options": {}, "Scope": "local" } ]从
"Mountpoint": "/var/lib/docker/volumes/demo_vol/_data"可以看出数据卷在宿主机的位置
注意:在MacOs中,Docker是运行在单独的虚拟机上的,所以docker创建的Volume并不在mac的主机上而是在Docker虚拟机中.如果要在MacOs中查看容器卷可以使用nsenter1镜像
% docker run -it --privileged --pid=host --rm justincormack/nsenter1
/ # cd /var/lib/docker/volumes/demo_vol/_data/
/var/lib/docker/volumes/demo_vol/_data # ls
/var/lib/docker/volumes/demo_vol/_data #
4. 删除容器卷
% docker volume ls
DRIVER VOLUME NAME
local demo_vol
chenrui@chenruideMacBook-Pro ~ % docker volume rm demo_vol
demo_vol
chenrui@chenruideMacBook-Pro ~ % docker volume ls
DRIVER VOLUME NAME
5. 清理容器卷
% docker volume prune
WARNING! This will remove all local volumes not used by at least one container.
Are you sure you want to continue? [y/N] y
Deleted Volumes:
23a8ad685b0218b19523576f80e07484b8aef1a6afcb0eb2b9c999bbd592c353
6724bf4ba886185f2cb364396f335507faf5c67cf61d71e117c62645c21e107d
toolkit_mongodb
2964084adbf7186f7e1bd15ed92fe81270f2cda20ac7597d47209d322460671e
cc4e3783e9d2a1cdcdfc8ba077d45d9637158595f796d261dd6903865072d932
97b327e9b51756e90f5322801dd35227d85d20b67549f022cbb723e2c75788fe
f7ef3dde0a15cfb4b82dd8f8fcb1b47b2c9f38bd1dfac88e62505ee3ddd3ace2
316da39969e32004756be4b13a6b033eecb812de58ee901d857ced2df6335496
Total reclaimed space: 315MB
chenrui@chenruideMacBook-Pro ~ % docker volume ls
DRIVER VOLUME NAME
使用volumes挂载数据
docker run提供两种方式绑定容器卷
-v/--volume,由(:)分隔的三个字段组成,卷名:容器路径:选项列表。选项列表可以ro/rw。
--mount,由多个键值对组成,由,分隔,每个由一个<key=<value>>元组组成。
type,值可以为 bind,volume,tmpfs。
source,对于命名卷,是卷名。对于匿名卷,这个字段被省略。可能被指定为 source 或 src。
destination,文件或目录将被挂载到容器中的路径。可以指定为 destination,dst 或 target。
启动容器并绑定卷
% docker run -it -d --name=volume_nginx -p 8081:80 -v ng_vol:/usr/share/nginx/html nginx:1.22.0
c756f40d4dd95b8bd4f99922c3e54e0ad8613ccc6253e9a4c521f1f3e7934938
通过inspect可以看到已经绑定了容器卷
查看卷可以看到ng_vol已经创建了
% docker volume ls
DRIVER VOLUME NAME
local ng_vol
% docker volume inspect ng_vol
[
{
"CreatedAt": "2022-08-27T08:40:06Z",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/ng_vol/_data",
"Name": "ng_vol",
"Options": null,
"Scope": "local"
}
]
查看宿主机中卷目录,发现这个卷中已经有了两个html文件,这是因为当我们的容器卷是空的,而挂载的容器目录非空时,docker会将容器挂载目录下的数据初始化到容器卷中
% docker run -it --privileged --pid=host --rm justincormack/nsenter1
/ # cd /var/lib/docker/volumes/ng_vol/_data
/var/lib/docker/volumes/ng_vol/_data # ls
50x.html index.html
进入容器修改内容
% docker exec -it volume_nginx bash
root@c756f40d4dd9:/# cd /usr/share/nginx/html/
root@c756f40d4dd9:/usr/share/nginx/html# echo "cy4ever" > ./helloDocker.txt
root@c756f40d4dd9:/usr/share/nginx/html# cat helloDocker.txt
cy4ever
在宿主机查看卷目录
% docker run -it --privileged --pid=host --rm justincormack/nsenter1
/ # cd /var/lib/docker/volumes/ng_vol/_data
/var/lib/docker/volumes/ng_vol/_data # ls
50x.html index.html
/var/lib/docker/volumes/ng_vol/_data # ls
50x.html helloDocker.txt index.html
/var/lib/docker/volumes/ng_vol/_data # cat helloDocker.txt
cy4ever
具名挂载和匿名挂载
具名挂载
具名挂载,就是指定文件夹名称,区别于指定路径挂载,这里的指定文件夹名称是在Docker指定的默认数据卷路径下的。
上文中「使用volumes挂在数据」就是使用的具名挂载,eg. -v 卷名称:容器挂载目录
匿名挂载
匿名挂载就是在指定数据卷的时候,不指定容器路径对应的主机路径,这样对应映射的主机路径就是默认的路径/var/lib/docker/volumes/中自动生成一个随机命名的文件夹。
匿名挂载不需要指定卷名称,docker会自动生成一个随机命名的卷名称,eg. -v 容器挂载目录