docker基础整理(4)-- 文件系统(overlayfs)与overlay2 持久化存储,共享与备份还原

Docker文件系统

Docker镜像是一层层构建的,其文件系统是一种联合文件系统,这种文件系统不是传统意义上的linux文件系统(如ext4,xfs,direct-lvm,btrfs,zfs等),并不直接参与磁盘空间的划分,而是基于底层的linux文件系统,联合文件系统中各目录所呈现。因此对于容器来讲,其根目录是挂载了多个目录的合集(merged,df即可查看)。
Docker不同文件系统对应不同的存储驱动。通过存储驱动来管理镜像层和可写容器层的内容。每个存储驱动程序对实现的处理方式不同,但是基本所有驱动程序都使用可堆叠的镜像层和写时复制(CoW)策略(除VFS外)。

CoW(Copy-on-write)

写时复制是一种共享和复制文件的策略。我们知道docker镜像层是只读的,所有容器的改变都仅发生在其容器读写层,如果容器层需要修改镜像层文件时,会将镜像层文件复制到该层并进行修改。如果容器对镜像层文件读操作时,则它可以直接读取镜像层文件。
多个容器是可以共享镜像的,通常镜像已经包含完整的应用和配置了,容器间彼此的差异只在于自己的容器层,该层只包含了将该容器系统与其他容器区分开来的内容,甚至,有些容器使用的是完全一样的镜像。在这些场景中,写时复制就可以非常有效地节省IO和后续层大小。
Docker写时复制缺点:容器进程写入时,整个文件会被复制和编辑,即便只是大文件的一部分被修改,整个文件都需要被复制,会增加容器系统大小且消耗容器IO。因此不适合写密集型应用,如数据库。这种情况可以通过外挂数据卷提高写性能。
Docker文件系统,存储驱动和底层文件系统对应关系:
docker基础整理(4)-- 文件系统(overlayfs)与overlay2 持久化存储,共享与备份还原_第1张图片
本文仅介绍Overlayfs和对应的存储驱动Overlay2。Docker 18.09在主流linux系统中都默认使用overlay2驱动。
如果考虑修改为overlay2驱动,请确保Linux内核的版本4.0或更高版本,或者使用3.10.0-514及更高版本的RHEL或CentOS。
注意:更改存储驱动程序时,所有现有的镜像和容器都将无法访问。这是因为新存储驱动程序无法使用其镜像层。如果还原更改,则可以再次访问旧的就给镜像和容器,但是使用新驱动程序提取或创建的任何镜像和容器将无法访问。

# 修改docker存储驱动:
# systemctl stop docker
# cp -au /var/lib/docker /var/lib/docker.bak
# 创建或编辑/etc/docker/daemon.json,写入一些内容,注意json文件格式:
{
  "storage-driver": "overlay2"
}
# systemctl start docker

# docker info
Containers: 1
 Running: 1
 Paused: 0
 Stopped: 0
Images: 24
Server Version: 18.09.1
Storage Driver: overlay2
 Backing Filesystem: xfs
 ...

使用Overlayfs驱动程序 – Overlay2

OverlayFS

官方介绍: OverlayFS是一种联合文件系统,在单个Linux主机上分层两个目录,并将它们显示为单个目录。这些目录称为“ 层”,统一过程称为“ 联合安装”。OverlayFS指的是下层目录lowerdir和上层目录upperdir。统一视图通过其自己的目录公开merged。

  • 下层目录(lowerdir):镜像层,只读
  • 上层目录(upperdir):容器层,可读写
  • 统一目录:merged,联合挂载目录
    docker基础整理(4)-- 文件系统(overlayfs)与overlay2 持久化存储,共享与备份还原_第2张图片

overlay2

overlay2 是overlayfs的存储驱动,overlayfs还支持overlay驱动,但overlay2更加稳定且支持多层lower,是目前官方比较推荐的驱动。overlay2最多可支持128层lower层,较好的支持docker build 和docker commit,可以减少对inode的使用。
PS: overlay不支持多层lower,通过硬链接链接到上层lower,以引用与较低层共享的数据。这些硬链接的使用会导致inode的过度使用。

/var/lib/docker/storagedriver

接下来我们通过分析docker的存储目录(/var/lib/docker/overlay2)来进一步了解overlay2工作方式。
PS: /var/lib/docker/overlay2是通过docker管理的,不能随便手动删除。

/var/lib/docker/overlay2

# ll overlay2/
total 16
drwx------ 3 root root 4096 Nov 18 10:11 4fe274f3a1eecee0c5913a7c6965354777c24f909dcbe4d31fc692086f43eb1c
drwx------ 5 root root 4096 Nov 14 12:09 c9cd2cc3092fd926b79b039de31487b998a4a57d9ef5affd2fbed7afb1768504
drwx------ 4 root root 4096 Nov 18 10:24 c9cd2cc3092fd926b79b039de31487b998a4a57d9ef5affd2fbed7afb1768504-init
drwxr-xr-x 2 root root 4096 Nov 15 17:28 l

4fe274f3a1eexxxxx目录是busybox(020584afccce)镜像的目录,由于busybox只有一层镜像,所以他的目录只有一个,如果拉取的是nginx这种多层镜像,其目录会有多个,可以测试下。
c9cd2cc309xxxxx和c9cd2cc309xxxxx-init是容器相关的目。
可以看到容器或镜像id和目录名不一致,没什么联系。

# docker images --no-trunc
REPOSITORY          TAG                 IMAGE ID                                                                  CREATED             SIZE
busybox             latest              sha256:020584afccce44678ec82676db80f68d50ea5c766b6e9d9601f7b5fc86dfb96d   2 weeks ago         1.22MB

# docker ps -a --no-trunc
CONTAINER ID                                                       IMAGE               COMMAND                                                         CREATED             STATUS              PORTS               NAMES
78c9475f102829afeb4a1c4e35b24516b8d33402c23eca4427444669381dd946   busybox             "/bin/sh -c 'while true; do echo hello world; sleep 1; done'"   4 days ago          Up 4 days                               b2

l目录是短链接的集合,这些链接是diff目录的缩短标识。这些标识符用于避免达到mount命令参数的大小限制。

# ll overlay2/l/
total 12
lrwxrwxrwx 1 root root 72 Nov 14 11:48 2TEXNS3YLN4AXUZGY7SMQIAJGV -> ../4fe274f3a1eecee0c5913a7c6965354777c24f909dcbe4d31fc692086f43eb1c/diff
lrwxrwxrwx 1 root root 72 Nov 14 12:09 GE5MIQDDL5VMH7WWNQ2XCXMPW3 -> ../c9cd2cc3092fd926b79b039de31487b998a4a57d9ef5affd2fbed7afb1768504/diff
lrwxrwxrwx 1 root root 77 Nov 14 12:09 ZZEGSCVLCGI6O6X4YQJNPZM5HM -> ../c9cd2cc3092fd926b79b039de31487b998a4a57d9ef5affd2fbed7afb1768504-init/diff

镜像层目录

# cd overlay2
# tree 4fe274f3a1eecee0c5913a7c6965354777c24f909dcbe4d31fc692086f43eb1c/ -L 2
overlay2/4fe274f3a1eecee0c5913a7c6965354777c24f909dcbe4d31fc692086f43eb1c/
├── diff
│   ├── bin
│   ├── dev
│   ├── etc
│   ├── home
│   ├── root
│   ├── tmp
│   ├── usr
│   └── var
└── link
9 directories, 1 file

这个是镜像层,包括diff和link。link内容是该层对应diff目录的缩短标识符名称,diff目录中包含该层的内容目录。

# cat 4fe274f3a1eecee0c5913a7c6965354777c24f909dcbe4d31fc692086f43eb1c/link 
2TEXNS3YLN4AXUZGY7SMQIAJGV

容器初始化层目录

# tree c9cd2cc3092fd926b79b039de31487b998a4a57d9ef5affd2fbed7afb1768504-init/ -L 2
c9cd2cc3092fd926b79b039de31487b998a4a57d9ef5affd2fbed7afb1768504-init/
├── diff
│   ├── dev
│   ├── etc
│   ├── proc
│   └── sys
├── link
├── lower
└── work
    └── work
7 directories, 2 files

这个是容器初始化层,在镜像层的基础上更改了diff和link文件,增加了lower文件和work目录。

  • diff目录当然仅包含该层的内容,包含了每个容器都有的一些基本文件,如/etc/hosts, /etc/resolv.cnf;
  • link仅包含该层对应diff目录的缩短标识符名称;
  • lower包含指改层上一层的link短链接。
  • work目录是一个驻留在上层文件系统的目录,通常用于完成上、下层之间文件的原子复制,work是一个临时目录。

容器层目录

# tree c9cd2cc3092fd926b79b039de31487b998a4a57d9ef5affd2fbed7afb1768504/ -L 2
c9cd2cc3092fd926b79b039de31487b998a4a57d9ef5affd2fbed7afb1768504/
├── diff
├── link
├── lower
├── merged
│   ├── bin
│   ├── dev
│   ├── etc
│   ├── home
│   ├── proc
│   ├── root
│   ├── sys
│   ├── tmp
│   ├── usr
│   └── var
└── work
    └── work
14 directories, 2 files

这个是容器层,在容器初始化层的基础上增加了merged目录。
merged就是lower(镜像和容器层的diff)和upper(容器层的diff)的合并,即正在运行中的容器的挂载点,通过df或者mount可看到。

# df -h
Filesystem      Size  Used Avail Use% Mounted on
devtmpfs        462M     0  462M   0% /dev
tmpfs           493M     0  493M   0% /dev/shm
tmpfs           493M   56M  437M  12% /run
tmpfs           493M     0  493M   0% /sys/fs/cgroup
/dev/vda1        25G  8.1G   16G  35% /
overlay          25G  8.1G   16G  35% /var/lib/docker/overlay2/c9cd2cc3092fd926b79b039de31487b998a4a57d9ef5affd2fbed7afb1768504/merged
shm              64M     0   64M   0% /var/lib/docker/containers/78c9475f102829afeb4a1c4e35b24516b8d33402c23eca4427444669381dd946/mounts/shm
tmpfs            99M     0   99M   0% /run/user/0
# shm是容器的共享挂载点

# mount | grep overlay2
overlay on /var/lib/docker/overlay2/c9cd2cc3092fd926b79b039de31487b998a4a57d9ef5affd2fbed7afb1768504/merged type overlay (rw,relatime,
lowerdir=/var/lib/docker/overlay2/l/ZZEGSCVLCGI6O6X4YQJNPZM5HM:/var/lib/docker/overlay2/l/2TEXNS3YLN4AXUZGY7SMQIAJGV,
upperdir=/var/lib/docker/overlay2/c9cd2cc3092fd926b79b039de31487b998a4a57d9ef5affd2fbed7afb1768504/diff,
workdir=/var/lib/docker/overlay2/c9cd2cc3092fd926b79b039de31487b998a4a57d9ef5affd2fbed7afb1768504/work)

存储卷和挂载目录

前面已经了解到,docker容器不适合存储数据,应该使用存储卷或挂载目录存储数据。
存储卷: 由Docker创建和管理。可以单独通过docker volume create命令创建卷,或者可以在容器或服务创建期间顺便通过-v|–mount命令创建卷。卷存储在/var/lib/docker/volumes,建议仅docker进程可对其修改。
挂载目录: 可以是宿主机上的任何目录,甚至可能是重要的系统文件或目录。Docker主机或Docker容器上的非Docker进程可以随时对其进行修改。挂载目录使用宿主机文件系统,因此读写速率会快些,但是容器中的进程可以更改挂载目录,如创建删除修改等,可能会干扰其他非Docker进程。
tmpfs: 仅存储在宿主机的内存中,并且永远不会写入宿主机的文件系统中,可以用它来存储非持久状态或敏感信息。

存储卷或挂载目录都是绑定存储的方法,不过存储卷使用起来较挂载目录有以下优势:

  • 卷更容易管理,共享,备份,迁移;
  • 卷在Linux和Windows容器上均可工作;
  • 卷驱动程序可以将卷存储在远程主机或云上,支持加密卷内容或添加其他功能。

区别:

  • 如果将空卷挂载到非空docker目录,则docker目录内容复制到空卷;
  • 如果将空或非空目录挂载到非空的docker目录,则docker目录会被覆盖。

-v|–mount

这两个选项都是常用的绑定的参数,目前主要区别就是–mount比-v语法更明确。
-v|–volume: ,包含3个字段,有特定的顺序
–mount: 。包含多个键值对,以逗号分隔,每个键值对都由一个=元组组成,无特定顺序。
如果将不存在的卷挂载到docker目录,系统则自动创建卷并挂载。但如果将不存在的主机目录挂载到docker目录,则挂载报错,需要手动创建。

# 卷案例
# docker volume create vol_test
# docker run -it --name b1 --rm  --mount source=vol_test,target=/mnt busybox  sh
# docker run -it --name b2 --rm  -v vol_test:/mnt busybox  sh
# docker volume inspect vol_test
[
    {
        "CreatedAt": "2019-11-19T09:54:19+08:00",
        "Driver": "local",
        "Labels": null,
        "Mountpoint": "/var/lib/docker/volumes/vol_test/_data",
        "Name": "vol_test",
        "Options": null,
        "Scope": "local"
    }
]

# docker volume rm vol_test
# docker run -it --name b3 --rm -v /data busybox  sh #创建匿名卷并挂载
# 卷有匿名卷,就是创建容器不指定source,系统会随机命名并创建卷,这个卷容器删了它也就被删了

# 挂载目录案例
docker run -d --name web1  --rm  --mount type=bind,source=/home/web,target=/usr/share/nginx/html,readonly nginx
docker run -d --name web2  --rm  -v  /home/web:/usr/share/nginx/html:ro nginx

共享存储

docker共享存储方式包括直接绑定存储和通过驱动插件绑定存储(支持管理远程卷,加密或其他集群功能)。

直接绑定存储

绑定本地目录/卷(参考上面命令);
绑定远程共享目录
# 首先创建nfs目录,步骤省略
# 绑定nfs目录
mount -t nfs -o vers=4 172.20.2.192:/www/static /www/static
docker run -d --name app --rm --mount type=bind,source=/www/static,target=/usr/share/nginx/html, readonly nginx
mount -t nfs -o vers=4,minorversion=0,noresvport 39f9xxxxx-yac20.cn-shanghai.nas.aliyuncs.com:/ /www/static # 还可以挂载阿里云文件系统(nas),阿里云nas仅支持云服务器内网访问
docker run -d --name app -rm --mount type=bind,source=/www/static,target=/usr/share/nginx/html,readonly nginx

第三方驱动插件绑定存储:

# 安装插件,这里仅演示vieux/sshfs插件
docker plugin install --grant-all-permissions vieux/sshfs
# 创建共享卷
docker volume create --driver vieux/sshfs   -o [email protected]:/home/static   -o password=systemidc   sshvolume
# 绑定容器
docker run -d --name app --rm -v sshvolume:/usr/share/nginx/html nginx
# docker volume ls
DRIVER               VOLUME NAME
local                myvolume
local                portainer_data
vieux/sshfs:latest   sshvolume
# docker volume inspect sshvolume
[
    {
        "CreatedAt": "0001-01-01T00:00:00Z",
        "Driver": "vieux/sshfs:latest",
        "Labels": {},
        "Mountpoint": "/mnt/volumes/47975a3bf025c6e24b635aaab522a1a4",
        "Name": "sshvolume",
        "Options": {
            "password": "systemidc",
            "sshcmd": "[email protected]:/home/static"
        },
        "Scope": "local"
    }
]

密码会直接暴露远程主机ssh密码,建议使用sshkey创建volume,具体见docker-volume-sshfs

备份还原卷

这里主要介绍卷的备份迁移,目录可以直接复制挂载。

# 备份卷,通过--volumes-from CONTAINER这个神奇的选项创建中转服务器
# 命令解释:启动一个中转容器,将源容器source_web 的/usr/share/nginx/html/ 下的文件打包为static.tgz, 放在/backup,也就是当前主机目录。
docker run --rm --volumes-from source_web -v $(pwd):/backup busybox tar zcf /backup/static.tgz -C /usr/share/nginx/html/ ./
# 复制tgz包到其他主机
# 在目标主机中创建目标容器
docker run -d -v webvol:/usr/share/nginx/html --name target_web nginx
# 将备份文件导入目标容器中
docker run --rm --volumes-from target_web -v $(pwd):/backup busybox tar zxf /backup/static.tgz -C /usr/share/nginx/html/

参考与扩展

官方存储驱动介绍:https://docs.docker.com/v18.09/storage/storagedriver/
Docker生产环境实践指南:https://www.kancloud.cn/liu21st/hellod/861043

你可能感兴趣的:(docker)