问题:已经使用
docker rmi
删除镜像,/var/lib/docker/devicemapper/devicemapper/data
仍占用很大空间,且/var/lib/docker/devicemapper/devicemapper/data/mnt
有很多空目录。docker使用的版本是1.12.6,采用的文件系统是devicemapper + loop-lvm
。
什么是 Devicemapper
Devicemapper 是 Linux 内核提供的框架,从 Linux 内核 2.6.9 版本开始引入,Devicemapper 与 AUFS 不同,AUFS 是一种文件系统,而Devicemapper 是一种映射块设备的技术框架。
Devicemapper 提供了一种将物理块设备映射到虚拟块设备的机制,目前 Linux 下比较流行的 LVM (Logical Volume Manager 是 Linux 下对磁盘分区进行管理的一种机制)和软件磁盘阵列(将多个较小的磁盘整合成为一个较大的磁盘设备用于扩大磁盘存储和提供数据可用性)都是基于 Devicemapper 机制实现的。
Docker 是如何使用 Devicemapper 实现存储数据和镜像分层共享的呢?
Devicemapper是如何数据存储的?
当 Docker 使用 Devicemapper作为文件存储驱动时,Docker 将镜像和容器的文件存储在瘦供给池(thinpool)中,并将这些内容挂载在
/var/lib/docker/devicemapper/` 目录下。
这些目录储存 Docker 的容器和镜像相关数据,目录的数据内容和功能说明如下:
-
devicemapper
目录(/var/lib/docker/devicemapper/devicemapper/
):存储镜像和容器实际内容,该目录由一个或多个块设备构成。 -
metadata
目录(/var/lib/docker/devicemapper/metadata/
): 包含Devicemapper
本身配置的元数据信息, 以 json 的形式配置,这些元数据记录了镜像层和容器层之间的关联信息。 -
mnt
目录(/var/lib/docker/devicemapper/mnt/
):是容器的联合挂载点目录,未生成容器时,该目录为空,而容器存在时,该目录下的内容跟容器中一致。
如何在 Docker 中配置 Devicemapper
Docker 的 Devicemapper 模式有两种:第一种是 loop-lvm
模式,该模式主要用来开发和测试使用;第二种是direct-lvm
模式,该模式推荐在生产环境中使用。
默认使用loop-lvm
模式。
如何配置 Devicemapper 的 direct-lvm 模式
1.使用以下命令停止已经运行的 Docker:
$ sudo systemctl stop docker
2.编辑 /etc/docker/daemon.json
文件,如果该文件不存在,则创建该文件,并添加以下配置:
{
"storage-driver": "devicemapper",
"storage-opts": [
"dm.directlvm_device=/dev/xdf",
"dm.thinp_percent=95",
"dm.thinp_metapercent=1",
"dm.thinp_autoextend_threshold=80",
"dm.thinp_autoextend_percent=20",
"dm.directlvm_device_force=false"
]
}
其中 directlvm_device
指定需要用作 Docker 存储的磁盘路径,Docker 会动态为我们创建对应的存储池。例如这里我想把 /dev/xdf 设备作为我的 Docker 存储盘,directlvm_device 则配置为 /dev/xdf。
3.启动 Docker:
$ sudo systemctl start docker
4.验证 Docker 的文件驱动模式:
使用docker info
查看,当看到 Storage Driver 为 devicemapper,并且 Pool Name 为 docker-thinpool
时,这表示 Devicemapper 的 direct-lvm
模式已经配置成功。
OverlayFS 文件系统
overlay2
是目前Docker 官方推荐的文件系统,也是目前安装 Docker 时默认的文件系统。
overlay2 是如何存储文件的?
overlay2 和 AUFS 类似,它将所有目录称之为层(layer),overlay2 的目录是镜像和容器分层的基础,而把这些层统一展现到同一的目录下的过程称为联合挂载(union mount)。overlay2 把目录的下一层叫作lowerdir,上一层叫作upperdir,联合挂载后的结果叫作merged。
overlay2 文件系统最多支持 128 个层数叠加,也就是说你的 Dockerfile 最多只能写 128 行。
l目录包含缩短的层标识符作为符号链接。这些标识符用于避免达到mount命令参数的长度限制。
镜像层的 link 文件内容为该镜像层的短 ID,diff 文件夹为该镜像层的改动内容,lower 文件为该层的所有父层镜像的短 ID。diff 目录为容器的读写层,容器内修改的文件都会在 diff 中出现,merged 目录为分层文件联合挂载后的结果,也是容器内的工作目录。
通过docker image inspect
命令来查看某个镜像的层级关系
"GraphDriver": {
"Data": {
"LowerDir": "/var/lib/docker/overlay2/d648e843b67402e1d4b8d98d2914e5190b85ad95aa3ead1484b05461e11091c9/diff:/var/lib/docker/overlay2/ce041ea1c1f188ebcbafa6cada7a717d9b9ee060336e8578c8be7dfc21b12c8b/diff:/var/lib/docker/overlay2/bc05c005cf1a9aeb7793778f066afe06e2554c268d49e71c219f41cf5b610370/diff:/var/lib/docker/overlay2/1dbc7ba45976ff9b164248fd62a96c1b93abaa5034ae95f4d9193041950bb72b/diff",
"MergedDir": "/var/lib/docker/overlay2/895118b45ed7f1848b45df8e89747cdd8c3ee5ac081e01f5a5a99776f2e9ee08/merged",
"UpperDir": "/var/lib/docker/overlay2/895118b45ed7f1848b45df8e89747cdd8c3ee5ac081e01f5a5a99776f2e9ee08/diff",
"WorkDir": "/var/lib/docker/overlay2/895118b45ed7f1848b45df8e89747cdd8c3ee5ac081e01f5a5a99776f2e9ee08/work"
},
"Name": "overlay2"
},
#MergedDir 代表当前镜像层在 overlay2 存储下的目录
#LowerDir 代表当前镜像的父层关系,使用冒号分隔,冒号最后代表该镜像的最底层。
使用docker inspect
命令来查看容器的工作目录:
"GraphDriver": {
"Data": {
"LowerDir": "/var/lib/docker/overlay2/3200038cd409c964ac7de88544be62b63aaa4588b0f8373f2fc446c842633111-init/diff:/var/lib/docker/overlay2/895118b45ed7f1848b45df8e89747cdd8c3ee5ac081e01f5a5a99776f2e9ee08/diff:/var/lib/docker/overlay2/d648e843b67402e1d4b8d98d2914e5190b85ad95aa3ead1484b05461e11091c9/diff:/var/lib/docker/overlay2/ce041ea1c1f188ebcbafa6cada7a717d9b9ee060336e8578c8be7dfc21b12c8b/diff:/var/lib/docker/overlay2/bc05c005cf1a9aeb7793778f066afe06e2554c268d49e71c219f41cf5b610370/diff:/var/lib/docker/overlay2/1dbc7ba45976ff9b164248fd62a96c1b93abaa5034ae95f4d9193041950bb72b/diff",
"MergedDir": "/var/lib/docker/overlay2/3200038cd409c964ac7de88544be62b63aaa4588b0f8373f2fc446c842633111/merged",
"UpperDir": "/var/lib/docker/overlay2/3200038cd409c964ac7de88544be62b63aaa4588b0f8373f2fc446c842633111/diff",
"WorkDir": "/var/lib/docker/overlay2/3200038cd409c964ac7de88544be62b63aaa4588b0f8373f2fc446c842633111/work"
},
"Name": "overlay2"
},
#MergedDir 后面的内容即为容器层的工作目录
#LowerDir 为容器所依赖的镜像层目录
overlay2 如何读取、修改文件?
overlay2 的工作过程中对文件的操作分为读取文件和修改文件。
容器内进程读取文件分为以下三种情况。
- 文件在容器层中存在:当文件存在于容器层并且不存在于镜像层时,直接从容器层读取文件;
- 当文件在容器层中不存在:当容器中的进程需要读取某个文件时,如果容器层中不存在该文件,则从镜像层查找该文件,然后读取文件内容;
- 文件既存在于镜像层,又存在于容器层:当我们读取的文件既存在于镜像层,又存在于容器层时,将会从容器层读取该文件。
overlay2 对文件的修改采用的是写时复制的工作机制,这种工作机制可以最大程度节省存储空间。具体的文件操作机制如下。
- 第一次修改文件:当我们第一次在容器中修改某个文件时,overlay2 会触发写时复制操作,overlay2 首先从镜像层复制文件到容器层,然后在容器层执行对应的文件修改操作。
overlay2 写时复制的操作将会复制整个文件,如果文件过大,将会大大降低文件系统的性能,因此当我们有大量文件需要被修改时,overlay2 可能会出现明显的延迟。好在,写时复制操作只在第一次修改文件时触发,对日常使用没有太大影响。
- 删除文件或目录:当文件或目录被删除时,overlay2 并不会真正从镜像中删除它,因为镜像层是只读的,overlay2 会创建一个特殊的文件或目录,这种特殊的文件或目录会阻止容器的访问。
清理/var/lib/docker/
如何清理docker呢?由于版本较古老,没有prune
命令。使用以下脚本来清理:
#!/bin/bash
# Remove dead containers (and their volumes)
docker ps -f status=dead --format '{{ .ID }}' | xargs -r docker rm -v
# Remove dangling volumes
docker volume ls -qf dangling=true | xargs -r docker volume rm
# Remove untagged ("<none>") images
docker images --digests --format '{{.Repository}}:{{.Tag}}@{{.Digest}}' | sed -rne 's/([^>]):<none>@/\1@/p' | xargs -r docker rmi
# Remove dangling images
docker images -qf dangling=true | xargs -r docker rmi
# Remove temporary files
rm -f /var/lib/docker/tmp/*
网友评论