Docker存储(未完成)
Docker存储
容器的数据存放大致可分两个层:可读可写层、只读层。只读层数据来自于镜像和容器的初始化文件系统,可读可写层则存放自容器启动后产生的数据,而可读可写层的数据会随容器移除后消失。我们这一章主要讲Docker的持久化存储、外部存储的挂载。
数据分层
上面提到容器数据分只读层和可写层,而镜像由多个只读层叠加而来:Dockerfile的每一个指令构建出的每一层都是一个只读层;容器运行起来后会生成一个只属于这个容器的初始化的文件系统到只读层,和可读可写层,所有容器运行后产生的新增、更改的文件,都存放在可读可写层。
可知docker存储也是写时复制(copy on write, cow)的,只是相比较于虚拟磁盘,其对象级别是文件级别的。而叠加文件的概念则叫做UnionFS(Docker使用的具体实现为OverlayFS,当前默认为overlay2驱动)。
容器的数据分层目录
WorkDir [工作层] overlay工作所需的临时层,对于只读overlayFS(镜像)这一层是没任何东西的
MergedDir [container mount] 使用 Union FS 将以下两层合并后给容器最终呈现为统一视图
UpperDir [container layer] 容器的可读写层(容器运行后文件系统产生的改变),即当前层
LowerDir [image layer] 所有依赖的镜像层(包括父镜像在内的每一个层)和容器初始化数据
# docker container inspect voltest | jq '.[0]["GraphDriver"]["Data"]'
{
"LowerDir": "/var/lib/docker/overlay2/f10194482c48fbd6253e770982c5e92e8e643eeb33661b13cd7f243f1083d819-init/diff:/var/lib/docker/overlay2/5f40de21ddee67350d2827a02b7b9a4141a1439546d372b62a6a4655e01337c0/diff:/var/lib/docker/overlay2/9b7deb924ec81993dbd5d4e600b5311724887927f2a8537591b93a970a0fc768/diff:/var/lib/docker/overlay2/82d5943471b936c74ba474d536c365a6c6f9b3d3c1306aaaf46b910cb051999f/diff:/var/lib/docker/overlay2/4c263174de3b66769ca3aaf97b99a36190aab551d3518d0396043552545c9eae/diff:/var/lib/docker/overlay2/4054dac8af2d07177f5c8df2b32b461ea1f129a4586cdc4cf5355f5ac9b4f9d5/diff:/var/lib/docker/overlay2/7d916f5e8ca923977ef446753032dfcdd9ac0aa7ffb8b0912987005c2008dd74/diff",
"MergedDir": "/var/lib/docker/overlay2/f10194482c48fbd6253e770982c5e92e8e643eeb33661b13cd7f243f1083d819/merged",
"UpperDir": "/var/lib/docker/overlay2/f10194482c48fbd6253e770982c5e92e8e643eeb33661b13cd7f243f1083d819/diff",
"WorkDir": "/var/lib/docker/overlay2/f10194482c48fbd6253e770982c5e92e8e643eeb33661b13cd7f243f1083d819/work"
},
LowerDir包含的目录,有其镜像的文件系统,还有容器初始化的基础配置(网络配置文件等)。
UpperDir、MergeDir、WorkDir所指定的目录就是容器层,这个目录随着容器创建而创建,随着容器销毁而删除。UpperDir包含所有更改的文件,MergeDir是LowerDir和UpperDir的融合层,是容器中真正看到的文件系统。
容器是轻量的,可以随意创建和删除而没有任何负担。这样的想法很美好,但是有个问题:数据也会随着容器的删除而消失,即使不删除它们,管理起来也很困难;联合文件系统性能较原生差,也无法让多个容器共享相同(可读可写)数据。官方并没有朝着管理容器层的方向发展,而是另外提供数据持久化的办法。
来源:Docker OverlayFS 文件系统原理及容器镜像分析 | CHEGVA
overlay2 是如何存储文件的?
overlay2 和 AUFS 类似,它将所有目录称之为层(layer),overlay2 的目录是镜像和容器分层的基础,而把这些层统一展现到同一的目录下的过程称为联合挂载(union mount)。overlay2 把目录的下一层叫作
lowerdir,上一层叫作upperdir,联合挂载后的结果叫作merged。overlay2 文件系统最多支持 128 个层数叠加,也就是说你的 Dockerfile 最多只能写 128 行,不过这在日常使用中足够了。
在manifest文件中,LowerDir、MergedDir、UpperDir和WorkDir分别表示联合文件系统的不同部分。
- LowerDir: 这些是overlay文件系统的只读层,对于Docker来说,它们是按顺序组装的镜像层。
- UpperDir: 这是overlay文件系统的可读写层,对于Docker来说,它相当于包含容器所做更改的特定层。
- WorkDir: 这是overlay所需的临时工作基础空目录,当文件在合并目录中被写入或更改时,“workdir”目录用于存储内部程序运行时产生的临时文件等数据,然后以原子操作的方式将它们移动到“upperdir”中,对于只读的OverlayFS,可能会在“upperdir”中省略“workdir”目录,挂载后内容会被清空,在使用过程中其内容用户不可见。
- MergedDir: 这是overlay文件系统联合挂载完成后给用户呈现的统一视图。merged目录里面本身并没有任何实体文件,给我们展示的只是参与联合挂载的目录里面文件而已,挂载点中的所有文件都只是lowerdir和upperdir的文件映射,真正的文件还是在lower和upper中。Docker在运行容器时实际上会chroot到这个目录。
持久化存储
持久化存储和临时的容器层有着很大的区别:持久化存储不需要写时复制,不加入联合文件系统,本质只是目录或文件映射,因此理论上其性能是比容器层更好的,十分接近原生性能的。
在容器创建时使用持久化存储
关于持久化存储,在创建容器时有两个选项可以实现相同效果,以及一个专门指定临时卷的选项:
{docker run | docker create} [option...]
-v, --volume str
--mount str
--tmpfs str
------------------------------------------------------
-v 本地路径:挂载路径[:[ro][,host-src][,z|Z]] #绑定挂载
-v 挂载路径 #匿名卷
-v 卷名:挂载路径[:ro] #命名卷
------------------------------------------------------
--mount type=bind,src=本地路径,dst=挂载路径[,ro] #绑定挂载
--mount type=volume,src=卷名,dst=挂载路径[,ro][,volume-nocopy=true|false][,volume-driver=][,volume-opt=] #命名卷
--mount type=volume,dst=挂载路径[opt...] #匿名卷
--mount type=tmpfs,dst=挂载路径[,tmpfs-size=空间大小][,tmpfs-mode=权限] #临时文件系统
------------------------------------------------------
--tmpfs 挂载路径[:[size=空间大小][,mode=权限]] # 临时文件系统
对于--mount和-v,官方更支持使用前者,因为除了以上列出的参数,--mount支持更复杂的挂载选项,而-v仅仅比--mount多支持一个z的挂载选项,这是一个针对selinux的选项。因此如果使用并不复杂的时候下使用-v没有任何问题。
绑定挂载 bind mount
绑定挂载相当于目录映射, 将docker主机的本地路径(文件/目录皆可)映射到容器内路径,使容器可以直接读写docker主机的本地文件资源。通过绑定挂载可以很轻易地将原生应用迁移到容器,或将本地的文件资源交由容器管理(反过来也是)。
例:
docker run -d -v /var/www/html:/var/www/html --name web httpd
# 等同于:
docker run -d --name web --mount type=bind,src=/var/www/html,dst=/var/www/html httpd
容器卷 volume
docker将文件系统中的一个目录作为其存放数据(镜像和容器的只读、可读可写层)及容器、镜像、网络等配置信息的区域,这个目录或者说这个区域叫做 Docker area。容器卷也存放于此。
容器卷与绑定挂载的区别在于,容器卷只需要名字,而不需要指定特定路径,因此容器卷对没有文件映射需求的数据持久化是很有用的。而对于匿名卷来说,甚至不需要指定卷的名字,指定挂载路径就够了。
是的,容器卷分为命名卷和匿名卷,命名卷由用户指定卷的名称,匿名卷的名称由docker随机生成。容器卷可以在容器创建前就创建,也可以在创建容器的命令/docker-compose文件中指定。匿名卷在命令行甚至在镜像构建中就可以指定。
例:
# 命名卷
-v mydata:/var/lib/mysql/
--mount type=volume,src=mydata,dst=/var/lib/mysql/
# 匿名卷 -v选项仅指定目标路径,--mount选项不指定src参数
-v /var/mysql/data
--mount type=volume,dst=/var/mysql/data
临时文件系统/临时挂载 tmpfs mount
临时文件系统实际上并不是持久化存储,而是将内存空间映射到挂载点,可供高速读写。这些数据会在容器停止后立即消失,因此这些数据实际并没有被持久化,而相反是更易失的(容器停止并不清空容器层数据)。
tmpfs 对不想在主机和可写层存放的敏感数据,和希望每次启动容器都被重置的数据是很有用的,相反对高速读写的、有用的数据应当慎重考虑,因为容器因任何原因停止后,其数据都会立即消失。
例:
--tmpfs /data,size=4G
# 等同于:
--mount type=tmpfs,dst=/data,tmpfs-size=4G
只读挂载
绑定挂载和命名卷都可以使用只读选项,挂载后容器只能对该路径数据读取而不能修改(在容器中也是只读,并非指写时复制)。
# --volume
-v /var/www/html:/var/www/html:ro
# --mount
--mount type=volume,src=mydata,dst=/data,ro
挂载模板容器
创建一个容器并使用持久化存储,其他容器创建时使用--volumes-from选项,即可将前者使用的绑定挂载和存储卷挂载,以相同参数挂载到后者。tmpfs不能使用该选项。
基于此,我们可以专门创建一个容器进行挂载而不启动,其他容器使用--volumes-from选项获取该容器的挂载选项,这样即使是匿名卷也可以轻易做到多容器共享。
例:
docker create -v /var/www/hrml:/var/www/html --name web_mount alpine
docker run --volumes-from web_mount --name web1 nginx:myweb
docker run --volumes-from web_mount --name web2 nginx:myweb
容器卷管理 docker volume(未完成)
容器卷有脱离于容器的自己的生命周期,自然也有自己的管理命令。容器卷管理使用命令docker volume。
# 列出所有容器卷
docker volume ls [OPTIONS]
-f, --filter filter Provide filter values (e.g. "dangling=true")
--format string
-q, --quiet Only display volume names
------------------------------------------------------
# 创建容器卷
docker volume create [OPTIONS] [VOLUME]
-d, --driver string Specify volume driver name (default "local")
--label list Set metadata for a volume
-o, --opt map Set driver specific options (default map[])
------------------------------------------------
# 查看容器卷详细信息
docker volume inspect [OPTIONS] VOLUME [VOLUME...]
-f, --format string
------------------------------------------------
# 删除容器卷
docker volume rm [OPTIONS] VOLUME [VOLUME...]
-f, --force Force the removal of one or more volumes
------------------------------------------------
# 删除所有未被挂载的卷
docker volume prune [OPTIONS]
-a, --all Remove all unused volumes, not just anonymous ones
--filter filter Provide filter values (e.g. "label=<label>")
-f, --force Do not prompt for confirmation
创建容器卷
一次只能创建一个卷。
查看容器卷详细信息
docker volume inspect test | jq
[
{
"CreatedAt": "2024-06-22T14:19:44+08:00",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/test/_data",
"Name": "test",
"Options": null,
"Scope": "local"
}
]
删除容器卷
docker voluve rm <VOLUME> [...]
# 删除所有卷
docker volume rm `docker volume -q`
删除所有未被挂载的卷
谨慎使用。
docker volume prune
多机共享挂载(未完成)
NFS网络挂载

浙公网安备 33010602011771号