docker文件存储驱动overlay2

背景

https://zhuanlan.zhihu.com/p/41958018

本系列Docker笔记将以基于TensorFlow的模型的训练与部署的具体场景为例,总结Docker的基本原理、安装、镜像制作、容器部署等。事实上,在学校实验室和自己的开源项目推进Docker使用已经快1年了,这篇文章之所以现在才开始写,纯粹是拖太久。

安装docker与nvidia-docker

首先,请以官网教程安装docker:

How to install docker on Ubuntu 16.04

如果你打算制作或者使用涉及到cuda或者cuddn等与GPU相关的镜像或容器,你需要安装nvidia-docker,请按照repo里的步骤安装nvidia-docker:

How to install nvidia-docker on Ubuntu 16.04

在完成两步安装后,需要更改docker守护进程默认的runtime参数,请将/etc/docker/daemon.json文件中键default-runtime对应的值修改为nvidia,然后通过sudo service docker restart重启docker服务,这一步操作是为了避免之后运行与cuda或者cudnn库相关的容器时每次都要指定runtime参数的情况。

请务必确认docker被正确安装,接下来我们将从基本概念开始介绍docker,一直到模型部署。

基本概念

首先引用百度百科的定义:

Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。容器是完全使用沙箱机制,相互之间不会有任何接口。

其次是官网的定义:

Docker is a platform for developers and sysadmins to develop, deploy, and run applications with containers.

其实百度百科的总结已经足够好了,更概括地,Docker是一个轻量级虚拟机制作、分发、部署工具。

Docker有两个非常重要的概念,他们分别是镜像(image)与容器(container),直观地,我们可以将容器类比为虚拟机,这个虚拟机可能是正在运行的,也可能是已经停止的,而镜像则是像配置文件一样定义了这些虚拟机如何运行。

而事实上事情要比上面的例子复杂很多,镜像和容器的本质一个文件系统:

在计算机中,文件系统(File System)是命名文件及放置文件的逻辑存储和恢复的系统。

我们首先将以Overlay2为例,详细介绍镜像与容器的区别和联系。

Overlay2

接下来将简要的介绍文件存储驱动overlay2,以便于更好的理解容器与镜像的关系。overlay2是Ubuntu上最新的Docker CE版本18.06.0上的默认存储驱动。上段提到,本质上镜像与容器都是文件系统,它们唯一的不同,就是镜像是只读的,而容器是可读可写的。

举个例子,我们通过以下命令获取Ubuntu的镜像:

➜  overlay2 docker pull ubuntu
Using default tag: latest
latest: Pulling from library/ubuntu
c64513b74145: Pull complete
01b8b12bad90: Pull complete
c5d85cf7a05f: Pull complete
b6b268720157: Pull complete
e12192999ff1: Pull complete
Digest: sha256:3f119dc0737f57f704ebecac8a6d8477b0f6ca1ca0332c7ee1395ed2c6a82be7
Status: Downloaded newer image for ubuntu:latest
➜  overlay2 docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
ubuntu              latest              735f80812f90        2 weeks ago         83.5MB

可以看出,Ubuntu镜像具有5层,这5层都是只读的,我们可以在这个目录看到他们:

➜  overlay2 pwd
/var/lib/docker/overlay2
➜  overlay2 ll
total 24K
drwx------ 4 root root 4.0K 8月  13 19:52 0ba50fa3b79a5dc66ebb8f2939e77128b0ab7c3989fc776bd4268af366bd148 5
drwx------ 3 root root 4.0K 8月  13 19:52 225c757add2a395c0cfc47e1bc4472bf8fccf9dedd42f76f99b21c7637cb2a7 6
drwx------ 4 root root 4.0K 8月  13 19:53 40b4fd1a0dea16978cffe5f26deee9a5834c76752db8c3b2a86057037a12b5f 5
drwx------ 4 root root 4.0K 8月  13 19:52 7bb93daf624b3fc798554d36b75940fced7713f0d165631131432e230718555 9
drwx------ 4 root root 4.0K 8月  13 19:52 ea274fcefed09dd48b0c6baa45e66bcd00887d4abbddbee1ef804c9dc7cfba4 e
drwxr-xr-x 2 root root 4.0K 8月  13 19:53 l

以及这个文件夹:

➜  l pwd
/var/lib/docker/overlay2/l
➜  l ll
total 20K
lrwxrwxrwx 1 root root 72 8月  13 19:52 IBZJKDU6Z76YR2ZDYWHDVUVHEZ -> ../0ba50fa3b79a5dc66ebb8f2939e77128 b0ab7c3989fc776bd4268af366bd1485/diff
lrwxrwxrwx 1 root root 72 8月  13 19:52 NRJEUKQPNXJQSQBLGCULIHRT77 -> ../7bb93daf624b3fc798554d36b75940fc ed7713f0d165631131432e2307185559/diff
lrwxrwxrwx 1 root root 72 8月  13 19:52 UXY6233J7FGSMGWJ2KJKU4Z6U3 -> ../225c757add2a395c0cfc47e1bc4472bf 8fccf9dedd42f76f99b21c7637cb2a76/diff
lrwxrwxrwx 1 root root 72 8月  13 19:52 VYQ3FKAYCIQNRXABOBPQ3ACEEH -> ../ea274fcefed09dd48b0c6baa45e66bcd 00887d4abbddbee1ef804c9dc7cfba4e/diff
lrwxrwxrwx 1 root root 72 8月  13 19:53 ZFT2GFUH6ZW3BMC3A4VY7S6HZV -> ../40b4fd1a0dea16978cffe5f26deee9a5 834c76752db8c3b2a86057037a12b5f5/diff

可以发现,全部都是到各层diff之间的软链接,以IBZJKDU6Z76YR2ZDYWHDVUVHEZ为例,我们观察一下这个链接目录:

➜  l cd IBZJKDU6Z76YR2ZDYWHDVUVHEZ
➜  IBZJKDU6Z76YR2ZDYWHDVUVHEZ ll
total 4.0K
drwxr-xr-x 3 root root 4.0K 7月  27 06:20 etc

发现除了etc文件夹之外空空如也,再以NRJEUKQPNXJQSQBLGCULIHRT77为例,观察一下这个符号链接目录的内容:

➜  NRJEUKQPNXJQSQBLGCULIHRT77 ll
total 16K
drwxr-xr-x 4 root root 4.0K 7月  27 06:20 etc
drwxr-xr-x 2 root root 4.0K 7月  27 06:20 sbin
drwxr-xr-x 3 root root 4.0K 7月  25 04:51 usr
drwxr-xr-x 3 root root 4.0K 7月  25 04:53 var

可以发现以上内容。事实上,每层的diff即是文件系统在统一挂载时的挂载点,我们可以再进一步地观察下一层,UXY6233J7FGSMGWJ2KJKU4Z6U3的内容:

➜  UXY6233J7FGSMGWJ2KJKU4Z6U3 ll
total 76K
drwxr-xr-x  2 root root 4.0K 7月  25 04:53 bin
drwxr-xr-x  2 root root 4.0K 4月  24 16:34 boot
drwxr-xr-x  4 root root 4.0K 7月  25 04:51 dev
drwxr-xr-x 29 root root 4.0K 7月  25 04:53 etc
drwxr-xr-x  2 root root 4.0K 4月  24 16:34 home
drwxr-xr-x  8 root root 4.0K 7月  25 04:51 lib
drwxr-xr-x  2 root root 4.0K 7月  25 04:52 lib64
drwxr-xr-x  2 root root 4.0K 7月  25 04:51 media
drwxr-xr-x  2 root root 4.0K 7月  25 04:51 mnt
drwxr-xr-x  2 root root 4.0K 7月  25 04:51 opt
drwxr-xr-x  2 root root 4.0K 4月  24 16:34 proc
drwx------  2 root root 4.0K 7月  25 04:53 root
drwxr-xr-x  4 root root 4.0K 7月  25 04:51 run
drwxr-xr-x  2 root root 4.0K 7月  25 04:53 sbin
drwxr-xr-x  2 root root 4.0K 7月  25 04:51 srv
drwxr-xr-x  2 root root 4.0K 4月  24 16:34 sys
drwxrwxrwt  2 root root 4.0K 7月  25 04:53 tmp
drwxr-xr-x 10 root root 4.0K 7月  25 04:51 usr
drwxr-xr-x 11 root root 4.0K 7月  25 04:53 var

可以发现这一层仿佛就是一个Ubuntu了。到这里我们可以知道,镜像是由多个层组织并定义的,这些层本质上是文件,这些文件是只读的,每层具体的文件存放在层标识符下的diff目录下。接下来我们将介绍他们是如何被组织起来的。

回过头来,我们继续观察层标识符目录:

➜  overlay2 pwd
/var/lib/docker/overlay2
➜  overlay2 ll
total 24K
drwx------ 4 root root 4.0K 8月  13 19:52 0ba50fa3b79a5dc66ebb8f2939e77128b0ab7c3989fc776bd4268af366bd148 5
drwx------ 3 root root 4.0K 8月  13 19:52 225c757add2a395c0cfc47e1bc4472bf8fccf9dedd42f76f99b21c7637cb2a7 6
drwx------ 4 root root 4.0K 8月  13 19:53 40b4fd1a0dea16978cffe5f26deee9a5834c76752db8c3b2a86057037a12b5f 5
drwx------ 4 root root 4.0K 8月  13 19:52 7bb93daf624b3fc798554d36b75940fced7713f0d165631131432e230718555 9
drwx------ 4 root root 4.0K 8月  13 19:52 ea274fcefed09dd48b0c6baa45e66bcd00887d4abbddbee1ef804c9dc7cfba4 e
drwxr-xr-x 2 root root 4.0K 8月  13 19:53 l

接着我们进入225c757add2a395c0cfc47e1bc4472bf8fccf9dedd42f76f99b21c7637cb2a7这个目录,观察一下目录结构:

➜  225c757add2a395c0cfc47e1bc4472bf8fccf9dedd42f76f99b21c7637cb2a76 tree . -L 2
.
├── diff
│   ├── bin
│   ├── boot
│   ├── dev
│   ├── etc
│   ├── home
│   ├── lib
│   ├── lib64
│   ├── media
│   ├── mnt
│   ├── opt
│   ├── proc
│   ├── root
│   ├── run
│   ├── sbin
│   ├── srv
│   ├── sys
│   ├── tmp
│   ├── usr
│   └── var
└── link

20 directories, 1 file

好像没什么特别的,接着我们进入0ba50fa3b79a5dc66ebb8f2939e77128b0ab7c3989fc776bd4268af366bd148这个目录,观察一下目录结构:

➜  overlay2 cd 0ba50fa3b79a5dc66ebb8f2939e77128b0ab7c3989fc776bd4268af366bd1485

➜  0ba50fa3b79a5dc66ebb8f2939e77128b0ab7c3989fc776bd4268af366bd1485 tree .
.
├── diff
│   └── etc
│       └── apt
│           └── sources.list
├── link
├── lower
└── work

4 directories, 3 files
➜  0ba50fa3b79a5dc66ebb8f2939e77128b0ab7c3989fc776bd4268af366bd1485 cat lower
l/VYQ3FKAYCIQNRXABOBPQ3ACEEH:l/NRJEUKQPNXJQSQBLGCULIHRT77:l/UXY6233J7FGSMGWJ2KJKU4Z6U3#
➜  0ba50fa3b79a5dc66ebb8f2939e77128b0ab7c3989fc776bd4268af366bd1485 cat link
IBZJKDU6Z76YR2ZDYWHDVUVHEZ#

可以看出,link文件描述了该层标识符的精简版,而lower文件描述了层序的组织关系。接着我们通过以下命令启动一个容器:

➜  0ba50fa3b79a5dc66ebb8f2939e77128b0ab7c3989fc776bd4268af366bd1485 docker run -it ubuntu

然后通过以下命令观察overlay2联合挂载情况:

root@7d01751deb92:/# mount | grep overlay
overlay on / type overlay (rw,relatime,lowerdir=/var/lib/docker/overlay2/l/FSK5KQSBSQH67GQ5IEWQKL4YPF:/var/lib/docker/overlay2/l/ZFT2GFUH6ZW3BMC3A4VY7S6HZV:/var/lib/docker/overlay2/l/IBZJKDU6Z76YR2ZDYWHDVUVHEZ:/var/lib/docker/overlay2/l/VYQ3FKAYCIQNRXABOBPQ3ACEEH:/var/lib/docker/overlay2/l/NRJEUKQPNXJQSQBLGCULIHRT77:/var/lib/docker/overlay2/l/UXY6233J7FGSMGWJ2KJKU4Z6U3,upperdir=/var/lib/docker/overlay2/3895f4ddbd45f65e509ed996d39536d1737647bf1b70c2b9c82b6765b2e376da/diff,workdir=/var/lib/docker/overlay2/3895f4ddbd45f65e509ed996d39536d1737647bf1b70c2b9c82b6765b2e376da/work)
root@7d01751deb92:/#

我们可以观察到一些关键信息,例如lowerdir,可以看到是这些层标识符:

FSK5KQSBSQH67GQ5IEWQKL4YPF

ZFT2GFUH6ZW3BMC3A4VY7S6HZV
IBZJKDU6Z76YR2ZDYWHDVUVHEZ
VYQ3FKAYCIQNRXABOBPQ3ACEEH
NRJEUKQPNXJQSQBLGCULIHRT77
UXY6233J7FGSMGWJ2KJKU4Z6U3

这时我们再观察overlay2文件夹,发现在该文件夹和l文件夹都多出了2个标识符:

➜  overlay2 ls | grep 389
3895f4ddbd45f65e509ed996d39536d1737647bf1b70c2b9c82b6765b2e376da
3895f4ddbd45f65e509ed996d39536d1737647bf1b70c2b9c82b6765b2e376da-init
➜  l ll | grep 389
lrwxrwxrwx 1 root root 77 8月  13 20:26 FSK5KQSBSQH67GQ5IEWQKL4YPF -> ../3895f4ddbd45f65e509ed996d39536d1 737647bf1b70c2b9c82b6765b2e376da-init/diff
lrwxrwxrwx 1 root root 72 8月  13 20:26 O5NQ7PKEES3VHMHNZAZHE54M2C -> ../3895f4ddbd45f65e509ed996d39536d1 737647bf1b70c2b9c82b6765b2e376da/diff

这一层是动态生成的,观察其目录结构:

➜  3895f4ddbd45f65e509ed996d39536d1737647bf1b70c2b9c82b6765b2e376da-init tree . -L 4
.
├── diff
│   ├── dev
│   │   └── console
│   └── etc
│       ├── hostname
│       ├── hosts
│       ├── mtab -> /proc/mounts
│       └── resolv.conf
├── link
├── lower
└── work
    └── work

5 directories, 7 files

可以看出,它主要是一些配置文件构成的层。而不带init后缀的3895f4ddbd45f65e509ed996d39536d1737647bf1b70c2b9c82b6765b2e376da的情况就比较特殊了:

➜  3895f4ddbd45f65e509ed996d39536d1737647bf1b70c2b9c82b6765b2e376da tree . -L 2
.
├── diff
├── link
├── lower
├── merged
│   ├── bin
│   ├── boot
│   ├── dev
│   ├── etc
│   ├── home
│   ├── lib
│   ├── lib64
│   ├── media
│   ├── mnt
│   ├── opt
│   ├── proc
│   ├── root
│   ├── run
│   ├── sbin
│   ├── srv
│   ├── sys
│   ├── tmp
│   ├── usr
│   └── var
└── work
    └── work

23 directories, 2 files
➜  3895f4ddbd45f65e509ed996d39536d1737647bf1b70c2b9c82b6765b2e376da cat lower
l/FSK5KQSBSQH67GQ5IEWQKL4YPF:l/ZFT2GFUH6ZW3BMC3A4VY7S6HZV:l/IBZJKDU6Z76

回忆一下225c757add2a395c0cfc47e1bc4472bf8fccf9dedd42f76f99b21c7637cb2a7这个标识符:

➜  225c757add2a395c0cfc47e1bc4472bf8fccf9dedd42f76f99b21c7637cb2a76 tree . -L 2
.
├── diff
│   ├── bin
│   ├── boot
│   ├── dev
│   ├── etc
│   ├── home
│   ├── lib
│   ├── lib64
│   ├── media
│   ├── mnt
│   ├── opt
│   ├── proc
│   ├── root
│   ├── run
│   ├── sbin
│   ├── srv
│   ├── sys
│   ├── tmp
│   ├── usr
│   └── var
└── link

20 directories, 1 file

可以看出,3895f4ddbd45f65e509ed996d39536d1737647bf1b70c2b9c82b6765b2e376da这标识符多了一个文件夹merged,与225c757add2a395c0cfc47e1bc4472bf8fccf9dedd42f76f99b21c7637cb2a76diff文件夹相似,它正是容器的可读可写层。讲到这里,我们在回头来观察overlay2联合挂载情况:

root@7d01751deb92:/# mount | grep overlay
overlay on / type overlay (rw,relatime,lowerdir=/var/lib/docker/overlay2/l/FSK5KQSBSQH67GQ5IEWQKL4YPF:/var/lib/docker/overlay2/l/ZFT2GFUH6ZW3BMC3A4VY7S6HZV:/var/lib/docker/overlay2/l/IBZJKDU6Z76YR2ZDYWHDVUVHEZ:/var/lib/docker/overlay2/l/VYQ3FKAYCIQNRXABOBPQ3ACEEH:/var/lib/docker/overlay2/l/NRJEUKQPNXJQSQBLGCULIHRT77:/var/lib/docker/overlay2/l/UXY6233J7FGSMGWJ2KJKU4Z6U3,upperdir=/var/lib/docker/overlay2/3895f4ddbd45f65e509ed996d39536d1737647bf1b70c2b9c82b6765b2e376da/diff,workdir=/var/lib/docker/overlay2/3895f4ddbd45f65e509ed996d39536d1737647bf1b70c2b9c82b6765b2e376da/work)
root@7d01751deb92:/#

我们可以看出,overlay2将lowerdirupperdirworkdir联合挂载,形成最终的merged挂载点,其中lowerdir是镜像只读层,upperdir是容器可读可写层,workdir是执行涉及修改lowerdir执行copy_up操作的中转层(例如,upperdir中不存在,需要从lowerdir中进行复制,该过程暂未详细了解,遇到了再分析),接着我们可以做一个实验,我们在容器中通过以下命令创建一个文件:

root@7d01751deb92:/# touch test.txt

接下来我们观察容器的可读写层,与镜像的只读层:

➜  3895f4ddbd45f65e509ed996d39536d1737647bf1b70c2b9c82b6765b2e376da ll diff
total 0
-rw-r--r-- 1 root root 0 8月  13 20:54 test.txt
➜  3895f4ddbd45f65e509ed996d39536d1737647bf1b70c2b9c82b6765b2e376da ll merged
total 76K
drwxr-xr-x 2 root root 4.0K 7月  25 04:53 bin
drwxr-xr-x 2 root root 4.0K 4月  24 16:34 boot
drwxr-xr-x 1 root root 4.0K 8月  13 20:26 dev
drwxr-xr-x 1 root root 4.0K 8月  13 20:26 etc
drwxr-xr-x 2 root root 4.0K 4月  24 16:34 home
drwxr-xr-x 8 root root 4.0K 7月  25 04:51 lib
drwxr-xr-x 2 root root 4.0K 7月  25 04:52 lib64
drwxr-xr-x 2 root root 4.0K 7月  25 04:51 media
drwxr-xr-x 2 root root 4.0K 7月  25 04:51 mnt
drwxr-xr-x 2 root root 4.0K 7月  25 04:51 opt
drwxr-xr-x 2 root root 4.0K 4月  24 16:34 proc
drwx------ 2 root root 4.0K 7月  25 04:53 root
drwxr-xr-x 1 root root 4.0K 7月  27 06:20 run
drwxr-xr-x 1 root root 4.0K 7月  27 06:20 sbin
drwxr-xr-x 2 root root 4.0K 7月  25 04:51 srv
drwxr-xr-x 2 root root 4.0K 4月  24 16:34 sys
-rw-r--r-- 1 root root    0 8月  13 20:54 test.txt
drwxrwxrwt 2 root root 4.0K 7月  25 04:53 tmp
drwxr-xr-x 1 root root 4.0K 7月  25 04:51 usr
drwxr-xr-x 1 root root 4.0K 7月  25 04:53 var
➜  3895f4ddbd45f65e509ed996d39536d1737647bf1b70c2b9c82b6765b2e376da ll ../225c757add2a395c0cfc47e1bc4472bf8fccf9dedd42f76f99b21c7637cb2a76/diff
total 76K
drwxr-xr-x  2 root root 4.0K 7月  25 04:53 bin
drwxr-xr-x  2 root root 4.0K 4月  24 16:34 boot
drwxr-xr-x  4 root root 4.0K 7月  25 04:51 dev
drwxr-xr-x 29 root root 4.0K 7月  25 04:53 etc
drwxr-xr-x  2 root root 4.0K 4月  24 16:34 home
drwxr-xr-x  8 root root 4.0K 7月  25 04:51 lib
drwxr-xr-x  2 root root 4.0K 7月  25 04:52 lib64
drwxr-xr-x  2 root root 4.0K 7月  25 04:51 media
drwxr-xr-x  2 root root 4.0K 7月  25 04:51 mnt
drwxr-xr-x  2 root root 4.0K 7月  25 04:51 opt
drwxr-xr-x  2 root root 4.0K 4月  24 16:34 proc
drwx------  2 root root 4.0K 7月  25 04:53 root
drwxr-xr-x  4 root root 4.0K 7月  25 04:51 run
drwxr-xr-x  2 root root 4.0K 7月  25 04:53 sbin
drwxr-xr-x  2 root root 4.0K 7月  25 04:51 srv
drwxr-xr-x  2 root root 4.0K 4月  24 16:34 sys
drwxrwxrwt  2 root root 4.0K 7月  25 04:53 tmp
drwxr-xr-x 10 root root 4.0K 7月  25 04:51 usr
drwxr-xr-x 11 root root 4.0K 7月  25 04:53 var

可以发现,新创建的文件被存在了上述位置,而此时如果我们通过以下命令:

docker commit CONTAINER_ID

提交容器更改,则会将该容器的当前可读可写层转化为只读层,更新镜像。总结一下,镜像大体上,可以认为是多个只读层通过某些特定的方式组织起来,而容器则是在其之上的一个可读写层,我们可以保存一个可读写层的更改,将它转化为一个只读层。

以上是对镜像与容器,及其存储驱动overlay2的一个简要概述。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值