文前说明
作为码农中的一员,需要不断的学习,我工作之余将一些分析总结和学习笔记写成博客与大家一起交流,也希望采用这种方式记录自己的学习之旅。
本文仅供学习交流使用,侵权必删。
不用于商业目的,转载请注明出处。
1. 概述
- 分层结构使镜像和容器的创建、共享以及分发变得非常高效,这些都要归功于 Docker 存储驱动(storage driver)。正是存储驱动实现了多层数据的堆叠并为用户提供一个单一的合并之后的统一视图。
- Docker 支持多种存储驱动,有 aufs、devicemapper、overlay2、zfs 和 vfs 等等。它们都能实现分层的架构,同时又有各自的特性。
- Docker 安装时会根据当前系统的配置选择默认的驱动。默认驱动具有最好的稳定性。
- 通过 docker info 查看默认的存储驱动。
Server Version: 1.13.1
Storage Driver: overlay2
Backing Filesystem: xfs
aufs
- UnionFS 是一种为 Linux 操作系统设计的用于把多个文件系统『联合』到同一个挂载点的文件系统服务。
- aufs 即 Advanced UnionFS 是 UnionFS 的升级版,能够提供更优秀的性能和效率。
- aufs 作为联合文件系统,它能够将不同文件夹中的层联合(Union)到了同一个文件夹中,这些文件夹在 aufs 中称作分支,整个『联合』的过程被称为联合挂载(Union Mount)。
- 每一个镜像层或者容器层都是 /var/lib/docker/ 目录下的一个子文件夹。
- 每一个镜像层都是建立在另一个镜像层之上的,同时所有的镜像层都是只读的,只有每个容器最顶层的容器层才可以被用户直接读写,所有的容器都建立在一些底层服务(Kernel)上,包括命名空间、控制组、rootfs 等等,这种容器的组装方式提供了非常大的灵活性,只读的镜像层通过共享也能够减少磁盘的占用。
- aufs 是 Docker 使用的存储驱动的一种,除了 aufs 之外,Docker 还支持了不同的存储驱动,包括 aufs、devicemapper、overlay2、zfs 和 vfs 等等,在最新的 Docker 中,overlay2 取代了 aufs 成为了推荐的存储驱动,但是在没有 overlay2 驱动的机器上仍然会使用 aufs 作为 Docker 的默认驱动。
overlay2
- Docker 默认的存储目录是 /var/lib/docker。
[root@localhost docker]# pwd
/var/lib/docker
[root@localhost docker]# ll
total 8
drwx------. 3 root root 78 May 14 19:24 containers
drwx------. 3 root root 22 May 7 18:26 image
drwxr-x---. 3 root root 19 May 7 18:26 network
drwx------. 20 root root 4096 May 14 19:24 overlay2
drwx------. 4 root root 32 May 7 18:26 plugins
drwx------. 2 root root 6 May 7 18:26 swarm
drwx------. 3 root root 37 May 13 13:45 tmp
drwx------. 2 root root 6 May 7 18:26 trust
drwx------. 27 root root 4096 May 13 13:45 volumes
- 是可以在 daemon.json 配置文件中修改存储驱动的。
{
"storage-driver": "overlay2",
"storage-opts": [
"overlay2.override_kernel_check=true"
]
}
- 其中 image 目录中存放的是存储的元数据。
- 因为镜像(image)是由多个镜像层(layer)组合而成的,镜像层(layer)是一个共享的层,可能会有多个镜像(image)会指向某个镜像层(layer)。因此 image 目录下又分为了 imagedb 和 layerdb 目录。
[root@localhost docker]# tree -L 3 image/
image/
└── overlay2
├── distribution
├── imagedb
│ ├── content
│ └── metadata
├── layerdb
│ ├── mounts
│ ├── sha256
│ └── tmp
└── repositories.json
9 directories, 1 file
- image 目录下按每个存储驱动的名字创建一个目录,这里为 overlay2。
[root@localhost docker]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
sdcvm-postgres latest 9b4a60d30e64 47 hours ago 445 MB
sdcos sdcos7 9df994b93bcf 5 weeks ago 350 MB
- 通过 docker images 查看到 postgres 的镜像 ID 为 9b4a60d30e64,在 imagedb 目录下查找。
[root@localhost sha256]# pwd
/var/lib/docker/image/overlay2/imagedb/content/sha256
[root@localhost sha256]# ll | grep 9b4a60d30e64
-rw-------. 1 root root 6316 May 13 13:45 9b4a60d30e64b8c7c48e436c38cefe7dabb6bd92aea95e8453c2a2c6754cf4d5
- 文件中包含了镜像与镜像层的对应关系。
- 从上到下是从镜像层的底层到镜像层的顶层。522eaa81ad19c... 是最底层。
......
"rootfs":{"type":"layers","diff_ids":
[
"sha256:522eaa81ad19cff9d9d64c7511c6288dc9695306f3e4f496d0cbd3379f31b743",
"sha256:bba9c8b6f7bf7db18623881d535ed612033daecada485fbc1443f676ebe681d9",
"sha256:e5f61ba5d0872fea5d70e0edf94d5dd204017bc31dc00698425de060f394b963",
......
]}
......
- 再通过 layer ID 查找存储的对应。
[root@localhost 522eaa81ad19cff9d9d64c7511c6288dc9695306f3e4f496d0cbd3379f31b743]# pwd
/var/lib/docker/image/overlay2/layerdb/sha256/522eaa81ad19cff9d9d64c7511c6288dc9695306f3e4f496d0cbd3379f31b743
[root@localhost 522eaa81ad19cff9d9d64c7511c6288dc9695306f3e4f496d0cbd3379f31b743]# ll
total 740
-rw-r--r--. 1 root root 64 May 7 18:27 cache-id
-rw-r--r--. 1 root root 71 May 7 18:27 diff
-rw-r--r--. 1 root root 9 May 7 18:27 size
-rw-r--r--. 1 root root 741803 May 7 18:27 tar-split.json.gz
[root@localhost 522eaa81ad19cff9d9d64c7511c6288dc9695306f3e4f496d0cbd3379f31b743]# cat cache-id
7c217baf83ec2dd3b91d18813f638a90c38683ce9890d4195ce7557fbd2c1005[root@localhost 522eaa81ad19cff9d9d64c7511c6288dc9695306f3e4f496d0cbd3379f31b743]#
- 最终可以查找到对应的 rootfs。
- 最底层只有 diff 和 link 文件,高层还包含 lower、merged、work 等目录。
- link 文件内容是 l 目录里面的缩写链接名,实际就是对应 diff 目录。
- diff 目录是容器的可读可写层,初始为空。
[root@localhost overlay2]# pwd
/var/lib/docker/overlay2
[root@localhost overlay2]# ll | grep 7c217baf83ec2dd3b91d18813f638a90c38683ce9890d4195ce7557fbd2c1005
drwx------. 3 root root 30 May 7 18:27 7c217baf83ec2dd3b91d18813f638a90c38683ce9890d4195ce7557fbd2c1005
[root@localhost overlay2]# cd 7c217baf83ec2dd3b91d18813f638a90c38683ce9890d4195ce7557fbd2c1005/
[root@localhost 7c217baf83ec2dd3b91d18813f638a90c38683ce9890d4195ce7557fbd2c1005]# ll
total 4
drwxr-xr-x. 17 root root 224 May 7 18:27 diff
-rw-r--r--. 1 root root 26 May 7 18:27 link
- merged 是容器的挂载点。
[root@localhost 1067c0c749b276951102faf9b44b0cc6b3bc061d1560d739e95cc719368e9401]# ll
total 8
drwxr-xr-x. 3 root root 17 May 13 13:45 diff
-rw-r--r--. 1 root root 26 May 13 13:45 link
-rw-r--r--. 1 root root 115 May 13 13:45 lower
drwx------. 2 root root 6 May 13 13:45 merged
drwx------. 2 root root 6 May 13 13:45 work
merged、容器层(upperdir)、镜像层(lowerdir)之间的关系
merged、容器层(upperdir)、镜像层(lowerdir)之间的关系- 如果 upperdir 和 lowerdir 有同名文件时会使用 upperdir 的文件。
- 读文件的时候,文件不在 upperdir 则从 lowerdir 读,
- 写文件的时候,不在 uppderdir 在 lowerdir,则从 lowerdir 里面拷贝到 upperdir,不管文件多大,拷贝完再写,删除或者重命名镜像层的文件都只是在容器层生成 whiteout 文件标志。
- overlay2 支持多个容器访问相同文件时公用 page cache,在拷贝和修改的时候 overlay2 比 aufs 更快,因为 aufs 的层级多搜索会有延迟,而 overlay2 会有缓存机制。
- 优化方面可以用 ssd,频繁的 I/O 操作,可以通过挂载 Volumes 来做,绕过存储驱动,可以多个容器共享数据,持久化数据。
2. 数据卷(Data Volume)
- 有状态容器都有数据持久化需求。Docker 采用 aufs 分层文件系统时,文件系统的改动都是发生在最上面的容器层。在容器的生命周期内,它是持续的,包括容器在被停止后。但是,当容器被删除后,该数据层也随之被删除了。因此,Docker 采用 volume (卷)的形式来向容器提供持久化存储。
- 数据卷本质上是 Docker 主机文件系统中的目录或文件,能够直接被 mount 到容器的文件系统中。具有以下的特点。
- 数据卷是目录或文件,而非没有格式化的磁盘(块设备)。
- 容器可以读写卷(volume)中的数据。
- 卷(volume)数据可以被永久地保存,即使使用它的容器已经销毁。
- 默认情况下,容器不使用任何 volume,此时,容器的数据被保存在容器之内,它只在容器的生命周期内存在,会随着容器的被删除而被删除。当然,也可以使用 docker commit 命令将它持久化为一个新的镜像。
- 一个数据卷(data volume)是容器中绕过 Union 文件系统的一个特定的目录。它被设计用来保存数据,而不管容器的生命周期。因此,当你删除一个容器时,Docker 肯定不会自动地删除一个 volume。
- Docker 提供了两种类型的卷(volume),bind mount 和 docker managed volume
bind mount
- 将主机上已存在的目录或文件 mount 到容器。(主机上的目录可以是一个本地目录,也可以在一个 NFS share 内,或者在一个已经格式化好了的块设备上)
- 格式:-v <host path>:<container path>。
- 容器内对 /webapp 的操作都会反映到主机上的 /src/webapp 目录内。只是,重新启动容器时,可以再次使用同样的方式来将 /src/webapp 目录挂载到新的容器内,这样就可以实现数据持久化的目标。
[root@localhost ]# docker run -d -P --name web2 -v /src/webapp:/webapp training/webapp python app.py
- 挂载主机上的一个文件到容器内的一个文件,需要注意,主机中的源文件必须存在,不然会当作一个新目录挂载给容器。
[root@localhost ]# docker run --rm -it -v ~/.bash_history:/root/.bash_history ubuntu /bin/bash
- 挂载时还可以指定数据的读写权限,默认是可读可写,也可以指定只读。(ro 设置了只读权限,在容器中无法对挂载数据进行修改,只有主机有权限修改数据,提供了安全性)
[root@localhost ]# docker run -d -P --name web2 -v /src/webapp:/webapp:ro training/webapp python app.py
docker managed volume
- bind mount 需要指定主机文件系统的特定路径,这限制了容器的可移植性,当需要将容器迁移到其他主机,而该主机没有要 mount 的数据或者数据不在相同的路径时,操作会失败。移植性更好的方式是 docker managed volume。
- docker managed volume 与 bind mount 在使用上最大的区别是不需要指定 mount 源。通过 -v 告诉 Docker 需要一个数据卷,并将其 mount 到 /webapp。
[root@localhost ]# docker run -d -P --name web -v /webapp training/webapp python app.py
- 通过执行 docker inspect 可以查看到容器配置信息。
"Mounts": [
{
"Name": "f143b7f379fb6d012a08656fc950bf6df4bf5a5b90c72f310644aa997620122b",
"Source": "/var/lib/docker/volumes/f143b7f379fb6d012a08656fc950bf6df4bf5a5b90c72f310644aa997620122b/_data",
"Destination": "/webapp",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
],
- 容器被删除后,_data 目录及其中的内容都还会保留下来,但是,新启动的容器无法再使用这个目录,已有的数据不能自动地被重复使用。
- docker volume 只能查看 docker managed volume,查看不了 bind mount。具体的信息还是只能通过 docker inspect 查看。
3. 卷容器(volume container)
- 如果要在容器之间共享数据,最好是使用 volume container。这种容器中不会跑应用,而只是挂载一个或多个卷。
[root@localhost ]# docker create -v /dbdata --name dbstore training/webapp /bin/true
- 其他容器可以通过 -volumes-from 使用 dbstore 这个 volume container。
[root@localhost ]# docker run -d -P --name web3 --volumes-from dbstore training/webapp python app.py
- 其好处在于可以不管其目录的临时性而不断地重复使用它。
- 与 bind mount 相比,不必为每一个容器指定主机路径。所有路径都在 volume container 中定义好了。
data-packed volume container
- 在 Dockerfile 构建镜像时。
- 先通过 ADD 将静态文件添加到容器目录中。
- 再通过 VOLUME 创建 docker managed volume,指定为 ADD 添加的目录。因为已添加,会将已有数据复制到 volume 中。容器能够正确的读取 volume 中的数据,而 data-packed volume container 又是自包含的,不依赖主机提供数据,具有很强的移植性,非常适合只使用静态数据的场景。
4. 数据卷的基本操作
操作 | 说明 |
---|---|
volume create | 创建数据卷。 |
volume inspect | 显示一个或多个卷的详细信息。 |
volume ls | 显示数据卷列表。 |
volume prune | 删除所有未使用的本地卷。 |
volume rm | 删除一个或多个数据卷。 |
4.1 创建数据卷
- 创建一个数据卷。
- 命令格式:docker volume create [OPTIONS] [VOLUME]
参数 | 说明 |
---|---|
--driver , -d | 指定卷驱动程序名称,默认本地。 |
--label | 为卷设置元数据。 |
--name | 设置卷名称。 |
--opt , -o | 设置驱动程序特定选项。 |
[root@localhost ]# docker volume create hello
hello
[root@localhost ]# docker run -d -v hello:/world busybox ls /world
[root@localhost ]# docker volume create --driver fake \
--opt tardis=blue \
--opt timey=wimey \
foo
4.2 数据卷详细信息
- 显示一个或多个卷的详细信息。
- 命令格式:docker volume inspect [OPTIONS] VOLUME [VOLUME...]
参数 | 说明 |
---|---|
--format , -f | 使用给定的 GO 模板格式化输出。 |
[root@localhost ]# docker volume create
85bffb0677236974f93955d8ecc4df55ef5070117b0e53333cc1b443777be24d
[root@localhost ]# docker volume inspect 85bffb0677236974f93955d8ecc4df55ef5070117b0e53333cc1b443777be24d
[
{
"Name": "85bffb0677236974f93955d8ecc4df55ef5070117b0e53333cc1b443777be24d",
"Driver": "local",
"Mountpoint": "/var/lib/docker/volumes/85bffb0677236974f93955d8ecc4df55ef5070117b0e53333cc1b443777be24d/_data",
"Status": null
}
]
[root@localhost ]# docker volume inspect --format '{{ .Mountpoint }}' 85bffb0677236974f93955d8ecc4df55ef5070117b0e53333cc1b443777be24d
/var/lib/docker/volumes/85bffb0677236974f93955d8ecc4df55ef5070117b0e53333cc1b443777be24d/_data
4.3 数据卷列表
- 显示数据卷列表。
- 命令俄式:docker volume ls [OPTIONS]
参数 | 说明 |
---|---|
--filter , -f | 提供筛选值(例如 dangling=true)。 |
--format | 使用给定的 GO 模板格式化输出。 |
--quiet , -q | 简要输出,只显示卷名称。 |
[root@localhost ]# docker run -d -v tyler:/tmpwork busybox
f86a7dd02898067079c99ceacd810149060a70528eff3754d0b0f1a93bd0af18
[root@localhost ]# docker volume ls -f dangling=true
DRIVER VOLUME NAME
local rosemary
format
占位符 | 说明 |
---|---|
.Name | 卷名称。 |
.Driver | 卷驱动。 |
.Scope | 卷范围(local, global)。 |
.Mountpoint | 主机上卷的装入点。 |
.Labels | 分配给卷的所有标签。 |
.Label | 此卷的特定标签的值。例如 {{.Label "project.version"}}。 |
[root@localhost ]# docker volume ls --format "{{.Name}}: {{.Driver}}"
vol1: local
vol2: local
vol3: local
4.4 删除所有未使用的本地卷
- 删除所有未使用的本地卷。
- 命令格式:docker volume prune [OPTIONS]
参数 | 说明 |
---|---|
--filter | 提供筛选值(例如 label=)。 |
--force , -f | 不提示确认。 |
[root@localhost ]# 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:
07c7bdf3e34ab76d921894c2b834f073721fccfbbcba792aa7648e3a7a664c2e
my-named-vol
Total reclaimed space: 36 B
4.5 删除数据卷
在删除容器的同时删除卷
- 可以使用 docker rm -v 命令在删除容器时删除该容器的卷。
[root@localhost ]# docker run -d -P --name web5 -v /webapp training/webapp python app.py
69199905a74cb360935e32f4e99f7f11319f6aa36033a920aa0bae25874f5c69
[root@localhost ]# docker volume ls
DRIVER VOLUME NAME
local 5341c03f3b94f13f4c86d88ccb0f3b63487adf30dea7ae6b2d06e947235e7330
local 838f4dd99721a9445be22a6b42d35e04cb43ad145ecf26107a9025f428587f76
local vol1
[root@localhost ]# docker rm -vf web5
web5
[root@localhost ]# docker volume ls
DRIVER VOLUME NAME
local 5341c03f3b94f13f4c86d88ccb0f3b63487adf30dea7ae6b2d06e947235e7330
local vol1
批量删除孤单卷
- 使用 docker run -v 启动的容器被删除以后,在主机上会遗留下来孤单的卷。
- 命令格式:docker volume rm [OPTIONS] VOLUME [VOLUME...]
参数 | 说明 |
---|---|
--force , -f | 强制删除一个或多个卷。 |
[root@localhost ]# docker volume ls -qf dangling=true
244a23f3ab11f17345a68e77f96bb46a8dbaf445760dd86ab0faa07dfbd84236
c864cfac232e8728b1805abc8c363d324124b38e6297544a8cbbf61d883c7e46
f143b7f379fb6d012a08656fc950bf6df4bf5a5b90c72f310644aa997620122b
[root@localhost ]# docker volume rm $(docker volume ls -qf dangling=true)
244a23f3ab11f17345a68e77f96bb46a8dbaf445760dd86ab0faa07dfbd84236
c864cfac232e8728b1805abc8c363d324124b38e6297544a8cbbf61d883c7e46
f143b7f379fb6d012a08656fc950bf6df4bf5a5b90c72f310644aa997620122b
[root@localhost ]# docker volume ls
DRIVER VOLUME NAME
local 5341c03f3b94f13f4c86d88ccb0f3b63487adf30dea7ae6b2d06e947235e7330
local vol1
网友评论