docker文件存储驱动

前言 Docker 卷与持久化数据存储

创建数据卷

1
2
3
[root@imwl-124 ~]# docker volume create myvolume
myvolume
[root@imwl-124 ~]# docker run -d --name=nginx-volume -v /usr/share/nginx/html nginx

查看数据卷

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
[root@imwl-124 ~]# docker volume ls
DRIVER VOLUME NAME
local c8321c3696c23379d98432e0df407bef4ee809ecba5a89ece5628fd3d71b2ba8
local myvolume

[root@imwl-124 ~]# docker volume inspect myvolume
[
{
"CreatedAt": "2021-04-06T08:29:03Z",
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/myvolume/_data",
"Name": "myvolume",
"Options": {},
"Scope": "local"
}
]

[root@imwl-124 ~]# docker volume inspect c8321c3696c23379d98432e0df407bef4ee809ecba5a89ece5628fd3d71b2ba8
[
{
"CreatedAt": "2021-04-06T08:29:37Z",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/c8321c3696c23379d98432e0df407bef4ee809ecba5a89ece5628fd3d71b2ba8/_data",
"Name": "c8321c3696c23379d98432e0df407bef4ee809ecba5a89ece5628fd3d71b2ba8",
"Options": null,
"Scope": "local"
}
]

使用数据卷

1
docker run -d --name=nginx --mount source=myvolume,target=/usr/share/nginx/html nginx

其中创建 + 使用 等同于

docker run -d --name=nginx-volume -v /usr/share/nginx/html nginx

旧容器删除,数据卷并不会删除

删除数据卷

1
docker volume rm myvolume

容器与容器之间数据共享

使用 volumes-from 参数可以在启动新的容器时来挂载已经存在的容器的卷,volumes-from 参数后面跟已经启动的容器名称

1
2
3
4
5
docker volume create log-vol

docker run --mount source=log-vol,target=/tmp/log --name=log-producer -it busybox

docker run -it --name consumer --volumes-from log-producer  busybox

主机与容器之间数据共享

-v HOST_PATH:CONTIANAER_PATH

1
docker run -v /data:/usr/local/data -it busybox

原理

1
2
3
4
5
6
7
8
9
10
[root@imwl-124 ~]# ls -l /var/lib/docker/volumes
total 32
drwxr-xr-x 3 root root 4096 Apr 6 08:29 c8321c3696c23379d98432e0df407bef4ee809ecba5a89ece5628fd3d71b2ba8
-rw------- 1 root root 32768 Apr 6 08:29 metadata.db
drwxr-xr-x 3 root root 4096 Apr 6 08:29 myvolume

[root@imwl-124 ~]# ls -l /var/lib/docker/volumes/myvolume/_data/
total 8
-rw-r--r-- 1 root root 494 Dec 15 13:59 50x.html
-rw-r--r-- 1 root root 612 Dec 15 13:59 index.html

Docker 卷的实现原理是在主机的 /var/lib/docker/volumes 目录下,根据卷的名称创建相应的目录,然后在每个卷的目录下创建 _data 目录,在容器启动时如果使用 --mount 参数,Docker 会把主机上的目录直接映射到容器的指定目录下,实现数据持久化

分类

  1. AUFS : 现在很少使用
  2. Devicemapper : 常用在 Red HatCentOS 系统中
  3. OverlayFS : Docker 目前推荐选择

Devicemapper

DockerDevicemapper 模式有两种:

  1. loop-lvm : 主要用来开发和测试使用
  2. direct-lvm : 推荐在生产环境中使用

direct-lvm

当前生产环境 使用的 模式

loop-lvm 仅配置 “storage-driver”: “devicemapper”, 不配置 “storage-opts”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[root@test-196 ~]# cat /etc/docker/daemon.json
{
"bip": "172.27.0.1/16",
"fixed-cidr": "172.27.0.0/16",
"insecure-registries": ["127.0.0.1/8"],
"storage-driver": "devicemapper",
"exec-opts": ["native.cgroupdriver=systemd"],
"storage-opts": [
"dm.basesize=20G",
"dm.thinpooldev=/dev/mapper/docker-thinpool",
"dm.use_deferred_removal=true",
"dm.use_deferred_deletion=true"
]
}

查看

1
2
3
4
5
6
7
8
9
[root@test-196 ~]# docker info |grep Driver
Storage Driver: devicemapper # 使用 devicemapper
Logging Driver: json-file
Cgroup Driver: systemd

[root@test-196 ~]# docker info |grep Pool
Pool Name: docker-thinpool # direct-lvm 模式已经配置成功
Pool Blocksize: 524.3kB
Thin Pool Minimum Free Space: 51GB

Devicemapper 原理

Devicemapper 是一种映射块设备的技术框架, 提供了一种将物理块设备映射到虚拟块设备的机制。

Devicemapper 将主要的工作部分分为 用户空间 和 内核空间

用户空间负责配置具体的设备映射策略与相关的内核空间控制逻辑,例如逻辑设备 dm-a 如何与物理设备 sda 相关联,怎么建立逻辑设备和物理设备的映射关系等。

内核空间则负责用户空间配置的关联关系实现,例如当 IO 请求到达虚拟设备 dm-a 时,内核空间负责接管 IO 请求,然后处理和过滤这些 IO 请求并转发到具体的物理设备 sda 上。

映射设备 通过 映射表 关联到具体的 物理目标设备 。事实上,映射设备不仅可以通过映射表关联到物理目标设备,也可以关联到虚拟目标设备,然后虚拟目标设备再通过映射表关联到物理目标设备。

devicemapper核心概念
瘦供给(Thin Provisioning) : 需要多少磁盘空间,存储驱动就帮我们分配多少磁盘空间

Docker 使用了瘦供给的 snapshot 技术, Docker 将镜像和容器的文件存储在瘦供给池(thinpool )中,并将这些内容挂载在 /var/lib/docker/devicemapper/ 目录下。

1
2
3
4
5
6
[root@test-249 devicemapper]# pwd
/var/lib/docker/devicemapper
[root@test-249 devicemapper]# ll
total 32
drwx------ 2 root root 24576 Apr 7 14:40 metadata # 包含 Devicemapper 本身配置的元数据信息, 以 json 的形式配置,这些元数据记录了镜像层和容器层之间的关联信息。
drwxr-xr-x 23 root root 4096 Apr 7 14:40 mnt # 是容器的联合挂载点目录,未生成容器时,该目录为空,而容器存在时,该目录下的内容跟容器中一致

Devicemapper存储模型

这个 Ubuntu 镜像一共有四层,每一层镜像都是下一层的快照,镜像的最底层是基础设备的快照。当容器运行时,容器是基于镜像的快照。Devicemapper 实现镜像分层的根本原理就是快照

快照是数据在某一个时间点的存储状态。快照的主要作用是对数据进行备份,当存储设备发生故障时,可以使用已经备份的快照将数据恢复到某一个时间点,而 Docker 中的数据分层存储也是基于快照实现的。

overlay2

要想使用 overlay2Docker版本必须高于17.06.02`。

如果你的操作系统是 RHELCentOSLinux 内核版本必须使用 3.10.0-514 或者更高版本,其他 Linux 发行版的内核版本必须高于 4.0(例如 Ubuntu 或 Debian),可以使用 uname -a 查看当前系统的内核版本。

overlay2 最好搭配 xfs 文件系统使用,并且使用 xfs 作为底层文件系统时,d_type 必须开启,可以使用以下命令验证 d_type 是否开启:

1
2
[imwl@imwl-u ~]$  xfs_info /var/lib/docker | grep ftype
naming =version 2 bsize=4096 ascii-ci=0 ftype=1

ftype=1 时,表示 d_type 已经开启。 sudo mkfs.xfs -f -n ftype=1 /path/to/disk

示例

挂载 /var/lib/docker 到 /dev/vdb1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$ lsblk
NAME   MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
vda    253:0    0  500G  0 disk
└─vda1 253:1    0  500G  0 part /
vdb    253:16   0  500G  0 disk
└─vdb1 253:17   0    8G  0 part

$ sudo mkfs.xfs -f -n ftype=1 /dev/vdb1

$ sudo echo "/dev/vdb1 /var/lib/docker xfs defaults,pquota 0 0" >> /etc/fstab

$ sudo mount -a

$ lsblk
NAME   MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
vda    253:0    0  500G  0 disk
└─vda1 253:1    0  500G  0 part /
vdb    253:16   0  500G  0 disk
└─vdb1 253:17   0    8G  0 part /var/lib/docker

$ xfs_info /var/lib/docker | grep ftype
naming   =version 2              bsize=4096   ascii-ci=0 ftype=1

使用

修改 /etc/docker/daemon.json

1
2
3
4
5
6
7
{
"storage-driver": "overlay2",
"storage-opts": [
"overlay2.size=20G",
"overlay2.override_kernel_check=true"
]
}

限制每个容器根目录大小为 20G

重启 docker

1
$ sudo systemctl start docker

docker_info 节选

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

Server:
Containers: 1
Running: 1
Paused: 0
Stopped: 0
Images: 1
Server Version: 20.10.6
Storage Driver: overlay2
Backing Filesystem: xfs
Supports d_type: true
Native Overlay Diff: true
userxattr: false
Logging Driver: json-file
Cgroup Driver: cgroupfs
Cgroup Version: 1

storage Driver 已经变为 overlay2,并且 d_type 也是 true

原理

overlay2AUFS 类似,它将所有目录称之为层(layer),overlay2 的目录是镜像和容器分层的基础,而把这些层统一展现到同一的目录下的过程称为联合挂载(union mount)。overlay2 把目录的下一层叫作 lowerdir,上一层叫作 upperdir,联合挂载后的结果叫作 merged

overlay2 将镜像层和容器层都放在单独的目录,并且有唯一 ID ,每一层仅存储发生变化的文件,最终使用联合挂载技术将容器层和镜像层的所有文件统一挂载到容器中,使得容器中看到完整的系统文件。

AUFS

配置

AUFS原理

1
2
3
{
"storage-driver": "aufs"
}

每一个目录在 AUFS 中都叫作分支,而在 Docker 中则称之为层(layer),但最终呈现给用户的则是一个普通单层的文件系统,我们把多层以单一层的方式呈现出来的过程叫作联合挂载。