[TOC]

0x00 Docker应用路径

1.组织结构

/var/lib/docker - docker 根(家)目录
描述:它是Docker中镜像、容器、容器配置的本地存储目录;

1
2
3
4
5
6
7
8
9
10
11
12
13
ls -lah 
drwx------. 2 root root 24 3月 22 13:39 builder
drwx--x--x. 4 root root 92 3月 22 13:39 buildkit
drwx------. 8 root root 4.0K 7月 5 21:28 containers #容器相关信息(运行时容器内部的一些配置)
drwx------. 3 root root 22 3月 22 13:39 image #镜像元数据相关信息
drwxr-x---. 3 root root 19 3月 22 13:39 network #网络模式相关信息
drwx------. 49 root root 8.0K 7月 5 21:41 overlay2 #容器Layer数据存储目录,
drwx------. 4 root root 32 3月 22 13:39 plugins #插件信息
drwx------. 2 root root 6 7月 2 11:21 runtimes
drwx------. 2 root root 6 3月 22 13:39 swarm #集群相关信息
drwx------. 2 root root 6 7月 5 21:42 tmp #临时目录(在拉去镜像时候会产生临时文件例如: GetImageBlob285307443)
drwx------. 2 root root 6 3月 22 13:39 trust #验证相关信息
drwx------. 7 root root 4.0K 6月 30 22:48 volumes #数据卷相关信息(容器持久化的相关信息在其中)

补充说明:

  • 1) 目前最新版本的 docker 默认优先采用 overlay2 作为存储驱动,对于已支持该驱动的 Linux 发行版,不需要任何进行任何额外的配置,可使用 lsmod | grep "overlay" 命令查看当前系统内核是否支持 overlay2,值得一提的是 devicemapper 存储驱动(本人常常被其摧残)已经在 docker 18.09 版本中被废弃,所以在高于此版本的Docker勿听百度的叫您去安装device-mapper-persistent-data因为完全没必要;


/var/lib/docker/image - 镜像元数据信息
描述: 该目录存储docker pull所下载的镜像的相关信息,并且在每次Docker pull 镜像时候都会检查repositories.json文件中索引本地是否存在该拉取镜像(image id / diff_ids),如果不存在则拉去下载,在Docker家目录中会看见sha256编码的目录名称它们实际是digest信息摘要;

Q:为什么要有digest?

答:如果一个镜像的镜像名和tag没有变,但是镜像的内容变了(layer变了),会造成相同的镜像名+tag得到了不同的镜像。digest是对manifest文件的sha256,当镜像的内容变化,即layer变化,相应的layer的sha256变化,以至manifest变化,从而保证了一个digest(不是镜像名+tag)对应一个镜像。
一句话理解: 一个digest 对应一个image Id

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
$ tree -f 1 /var/lib/docker/image/overlay2
# 当前 Docker 中存储两个images镜像
image
└── overlay2 # 镜像和容器的层信息(其也代表本使用的overlay2存储驱动)
├── distribution
│ ├── diffid-by-digest #[通过digest得到diffid] 可以通过Layer的diff id找到对应的digest,并且包含了生成该digest的源仓库
│ │ └── sha256
│ │ ├── 039b991354af4dcbc534338f687e27643c717bb57e11b87c2e81d50bdd0b2376
│ │ ├── 09a4142c5c9dde2fbf35e7a6e6475eba75a8c28540c375c80be7eade4b7cb438
│ └── v2metadata-by-diffid #[通过diffid得到digest] diffid-by-digest目录则与v2metadata-by-diffid相反
│ └── sha256
│ ├── 0683de2821778aa9546bf3d3e6944df779daba1582631b7ea3517bb36f9e4007
│ ├── 0f7493e3a35bab1679e587b41b353b041dca1e7043be230670969703f28a1d83
├── imagedb # 存储镜像元相关信息
│ ├── content # 该目录下存储了镜像的JSON格式描述信息
│ │ └── sha256
│ │ ├── 708bc6af7e5e539bdb59707bbf1053cc2166622f5e1b17666f0ba5829ca6aaea
│ │ └── f70734b6a266dcb5f44c383274821207885b549b75c8e119404917a61335981a
│ └── metadata # 该目录保存每个镜像的parent镜像ID
│ └── sha256
├── layerdb # 层元数据
│ ├── mounts # 容器运行时挂载的layer目录 lowerdir、upperdir
│ ├── sha256
│ │ ├── b9835d6a62886d4e85b65abb120c0ea44ff1b3d116d7a707620785d4664d8c1a # 唯一层ID
│ │ │ ├── cache-id #内容为一个uuid,指向Layer本地的真正存储位置
│ │ │ ├── diff #该Layer层的差异 id
│ │ │ ├── parent #存储了当前Layer层的父层chain_id
│ │ │ ├── size #该层的大小
│ │ │ └── tar-split.json.gz # tar-split.json.gz,layer压缩包的split文件,通过这个文件可以还原layer的tar包,https://github.com/vbatts/tar-split
│ │ └── d9b567b77bcdb9d8944d3654ea9bb5f6f4f7c4d07a264b2e40b1bb09af171dd3
│ │ ├── cache-id
│ │ ├── diff
│ │ ├── parent
│ │ ├── size
│ │ └── tar-split.json.gz
│ └── tmp
└── repositories.json #该文件存储了本地的所有images列表(镜像元数据信息),可以通过`docker images --digests`找到对应的镜像名称
21 directories, 119 files

# digest 和 image id 的对应
$jq "." /var/lib/docker/overlay2/image/repositories.json
{
"Repositories": {
"alpine": {
"alpine:latest": "sha256:a24bb4013296f61e89ba57005a7b3e52274d8edd3ae2077d04395f806b63d83e",
"alpine@sha256:185518070891758909c9f839cf4ca393ee977ac378609f700f60a771a2dfe321": "sha256:a24bb4013296f61e89ba57005a7b3e52274d8edd3ae2077d04395f806b63d83e"
}
}
}

补充知识:

  • (1) 通过 overlayfs 联合挂载的技术将镜像的多层 layer 挂载为一层, 下面从 docker 官方文档 Use the OverlayFS storage driver 里偷来的一张图片分别介绍各 Dir 的作用:

    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
    31
    32
    33
    34
    # 容器ID
    docker run -ti -d docker-search.4pd.io/ubuntu:18.04 top
    c70488b4ca7dfc882664272f5e3c6fe7dd6de1ce36c692b6c52f8002d76ca5ee

    $pwd
    /var/lib/docker/image/overlay2/layerdb/mounts

    # 容器的文件系统分为三层:
    * init层:启动容器时的参数
    * r/o层:也就是镜像层
    * r/w层:可读写层

    #运行一个容器会返回一个容器id. 通过这个id我们能在*/var/lib/docker/image/overlay2/layerdb/mounts*目录下找到该容器的信息:
    $tree
    ├── c70488b4ca7dfc882664272f5e3c6fe7dd6de1ce36c692b6c52f8002d76ca5ee
    │   ├── init-id # 内容:a66b1fa59d833d7d217ed3c2325079077c69822a21b739bd338f9c0dad227c03-init
    │   ├── mount-id # 内容:a66b1fa59d833d7d217ed3c2325079077c69822a21b739bd338f9c0dad227c03
    │   └── parent # 内容:36a83751a1193f6edf65d21f62fe7988394d562d71904ee20cb4aa72f14148bd
    └── e35b0d4511220a727e89d90a957a25460349ad143072e6b4efdde9a82c039807
    ├── init-id # init层的id
    ├── mount-id # r/w层的id
    └── parent # 基于哪个layer层存放的是Digest摘要

    # docker 如何使用的overlayFS?我们可以通过mount -l 查看一下,存储驱动是怎么挂载的目录;
    $mount -l
    overlay on /var/lib/docker/overlay2/a66b1fa59d833d7d217ed3c2325079077c69822a21b739bd338f9c0dad227c03/merged type overla y
    (rw,relatime,seclabel,
    lowerdir=/var/lib/docker/overlay2/l/C7RLSUT3FAOYOZQ44X5MLUFGXS:/var/lib/docker/overlay2/l/WFV3HGS7BXAK6C2VIPHHGMGRVC:/var/lib/docker/overlay2/l/UO6DNPRKCHHWTV6TWJEFQLAE74:/var/lib/docker/overlay2/l/OUYDRT4WTUXB4J27RBSO72TM6E:/var/lib/docker/overlay2/l/XSKIKL3ZDUGHFLHXAQJEEQLRCV:/var/lib/docker/overlay2/l/NXRKPTSOTIHHPL7YH7YUYZKOCE,
    upperdir=/var/lib/docker/overlay2/a66b1fa59d833d7d217ed3c2325079077c69822a21b739bd338f9c0dad227c03/diff,
    workdir=/var/lib/docker/overlay2/a66b1fa59d833d7d217ed3c2325079077c69822a21b739bd338f9c0dad227c03/work
    )
    # 其中lowerdir就是image layer的四层加上init层,upperdir层是r/w层是diff目录也就是挂载点,workdir是挂载之后的工作目录。
    # 此时您可能会困惑WFV3HGS7BXAK6C2VIPHHGMGRVC这些有是啥,这些是为了避免mount命令太长而导致的错误,所有故意搞短一点,
    # 实际上您看一哈`/var/lib/docker/overlay2/l/`也即是link的缩写这里面就是一坨指向真正层数据的软链。
    WeiyiGeek.

    WeiyiGeek.

  • (2) 文件 repositories.json 存储镜像信息主要是name和image id的对应digest和image id的对应而镜像imageid又包含了image config 以及各层的Digest;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    # cat image/overlay2/repositories.json  | python -m json.tool
    {
    "Repositories": {
    "docker.io/ubuntu": {
    "docker.io/ubuntu:18.04": "sha256:775349758637aff77bf85e2ff0597e86e3e859183ef0baba8b3e8fc8d3cba51c",
    "docker.io/ubuntu@sha256:6e9f67fa63b0323e9a1e587fd71c561ba48a034504fb804fd26fd8800039835d": "sha256:775349758637aff77bf85e2ff0597e86e3e859183ef0baba8b3e8fc8d3cba51c"
    }
    }
    }

    # image id和digest的联系digest是manifest的sha256.
    $docker images --digests
    REPOSITORY TAG DIGEST IMAGE ID CREATED SIZE
    docker-search.4pd.io/ubuntu 18.04 sha256:134c7fe821b9d359490cd009ce7ca322453f4f2d018623f849e580a89a685e5d 775349758637 5 weeks ago 64.2 MB

    # 因为manifest在本地没有,所有我们可以通过registry的结果去获取经过sha256sum算法生成摘要与docker images --digests中的DIGEST进行对比
    $curl -H "Accept:application/vnd.docker.distribution.manifest.v2+json" https://docker-search.4pd.io/v2/ubuntu/manifests/18.04 | sha256sum
    134c7fe821b9d359490cd009ce7ca322453f4f2d018623f849e580a89a685e5d -

    # 通过sha256 image config就能得到imageid:
    $sha256sum image/overlay2/imagedb/content/sha256/775349758637aff77bf85e2ff0597e86e3e859183ef0baba8b3e8fc8d3cba51c
    775349758637aff77bf85e2ff0597e86e3e859183ef0baba8b3e8fc8d3cba51c image/overlay2/imagedb/content/sha256/775349758637aff77bf85e2ff0597e86e3e859183ef0baba8b3e8fc8d3cba51c
  • (3) 在pull镜像的时候显示的是各个layer的digest信息而在image config存的是diffid,所以注意区分manifest的layer的表达和image config的layer的表达中不是一个东西;
    • 在Registry上拉取Layer的时候需要在请求头加入media type来确定拉取什么格式, 为了在网络上传输的更加快呢所有media type一般会指定压缩格式的比如gzip的,具体有哪些格式,见media type
      1
      2
      3
      4
      5
      6
      7
      8
      # Media Types
      application/vnd.docker.distribution.manifest.v1+json: # schema1 (existing manifest format)
      application/vnd.docker.distribution.manifest.v2+json: # New image manifest format (schemaVersion = 2)
      application/vnd.docker.distribution.manifest.list.v2+json: # Manifest list, aka “fat manifest”
      application/vnd.docker.container.image.v1+json: # Container config JSON
      application/vnd.docker.image.rootfs.diff.tar.gzip: # “Layer”, as a gzipped tar
      application/vnd.docker.image.rootfs.foreign.diff.tar.gzip: # “Layer”, as a gzipped tar that should never be pushed
      application/vnd.docker.plugin.v1+json: # Plugin config JSON
    • 当docker发现本地不存在某个layer的时候,就会通过manifest里面的digest + mediaType(一般是”application/vnd.docker.image.rootfs.diff.tar.gzip”)去registry拉对应的leyer, 然后在image id存的对应的diff id就是上面拿到的tar.gz包解压为tar包的id。
      1
      2
      3
      4
      5
      6
      # curl -H "Accept:application/vnd.docker.image.rootfs.diff.tar.gzip" https://docker-search.4pd.io/v2/ubuntu/blobs/sha256:7ddbc47eeb70dc7f08e410a667948b87ff3883024eb41478b44ef9a81bf400c -o layer1.tar.gz

      # sha256sum layer1.tar.gz
      7ddbc47eeb70dc7f08e410a6667948b87ff3883024eb41478b44ef9a81bf400c layer1.tar.gz
      # sha256sum layer1.tar
      cc967c529ced563b7746b663d98248bc571afdb3c012019d7f54d6c092793b8b layer1.tar
    • /var/lib/docker/image/overlay2/distribution 上面layer digest和diff id的互相对应关系


/var/lib/docker/overlay2 - 镜像Layer内容存放目录
描述:镜像 layer 的内容都存放在一个 diff 的文件夹下(它是Layer本地真正的存储位置),diff 的上级目录就是以镜像 layer 的 digest 为名的目录;

1
2
3
4
5
6
7
8
9
$docker images --digests --format "table {{.ID}}" | grep -v "none"
IMAGE ID DIGEST
1e4467b07108 sha256:5d1d5407f353843ecf8b16524bc5565aa332e9e6a1297c73a92d3e754b8a636d
21f378ba43ec sha256:72b62ec9d0e542f8656d3b9fdb9d11a8c375d9d3396575f999648c50bcd650e7

$ls /var/lib/docker/overlay2 | egrep "14f7|75013"
14f71bfdc22c04347ee318693557642f9af04cc1035cd11d92692ec138a086ae
75013da532e8e1fca06ac64cc1c6cc61e3e67b7198eee411c70fab3322bc6df0
75013da532e8e1fca06ac64cc1c6cc61e3e67b7198eee411c70fab3322bc6df0-init

其中还有个 l 文件夹(link 的缩写),下面有一坨坨的硬链接文件指向上级目录的 layer 目录,l 下的文件都是一些比 digest 文件夹名短一些的,方面不至于 mount 的参数过长。

1
2
3
4
$tree /var/lib/docker/overlay2/l
.
├── 2R3OHCF7E7AQNZSMWJ7REQGQXF -> ../14f71bfdc22c04347ee318693557642f9af04cc1035cd11d92692ec138a086ae/diff
├── QYJMESMUPSLVLW2WLVFOXGGJFB -> ../75013da532e8e1fca06ac64cc1c6cc61e3e67b7198eee411c70fab3322bc6df0-init/diff


/var/lib/docker/containers/容器ID - 运行或者暂停的容器时相关信息
描述:

1
2
3
4
5
6
7
8
9
10
$ls -lah /var/lib/docker/containers/4a1a157c5e701af224f3a4c5fb04c47d5b6e90dedc7d2124e21ec9ac33f87991/
-rw-r-----. 1 root root 647K 7月 2 09:11 4a1a157c5e701af224f3a4c5fb04c47d5b6e90dedc7d2124e21ec9ac33f87991-json.log #标准输入的日志记录
drwx------. 2 root root 6 4月 8 11:35 checkpoints #容器检查信息
-rw-------. 1 root root 3.7K 7月 2 11:21 config.v2.json #容器设置信息
-rw-r--r--. 1 root root 1.7K 7月 2 11:21 hostconfig.json #容器主机设置信息
-rw-r--r--. 1 root root 13 6月 29 22:23 hostname # 主机名称
-rw-r--r--. 1 root root 171 7月 2 09:11 hosts # 主机域名绑定
drwx------. 3 root root 17 4月 8 11:35 mounts # 挂载的数据卷信息
-rw-r--r--. 1 root root 38 6月 29 22:23 resolv.conf #DNS 域设置
-rw-r--r--. 1 root root 71 6月 29 22:23 resolv.conf.hash


/var/lib/registry - 镜像仓库目录(非默认可自定义)

1
2
# Registry Container Create
$docker run -d --name registry -p 5000:5000 -v /var/lib/registry:/var/lib/registry registry

目录结构:

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
$tree -h registry/
registry/
└── [ 22] docker
└── [ 16] registry
└── [ 39] v2 #版本协议
├── [ 20] blobs #存储镜像的Layer 以及 Manifest 和 Image Config
│   └── [ 36] sha256
│   ├── [ 78] 53
│   │   └── [ 18] 5395625ce01dee311e2f7c879b0b148ac7525de7aad5080a518d7f7e5a99d368
│   │   └── [1.1M] data # 存储镜像的Layer
│   ├── [ 78] 8d
│   │   └── [ 18] 8dabce532312b587329fe225ef501051c60f81ffdb2c801a5da6348b9cab132e
│   │   └── [ 528] data # Manifest 是针对registry服务端的配置信息
│   └── [ 78] cb
│   └── [ 18] cb05b87d001253772ae9a212200de5eb8304ab9691c61589332a2f57e7059209
│   └── [1.4K] data #Image Config是针对本地镜像的描述
└── [ 22] repositories # 存储镜像在仓库相关的信息,方便pull时候与blobs 存储的镜像信息对应
└── [ 55] go-hello
├── [ 20] _layers
│   └── [ 150] sha256
│   ├── [ 18] 5395625ce01dee311e2f7c879b0b148ac7525de7aad5080a518d7f7e5a99d368
│   │   └── [ 71] link
│   └── [ 18] cb05b87d001253772ae9a212200de5eb8304ab9691c61589332a2f57e7059209
│   └── [ 71] link
├── [ 35] _manifests
│   ├── [ 20] revisions
│   │   └── [ 78] sha256
│   │   └── [ 18] 8dabce532312b587329fe225ef501051c60f81ffdb2c801a5da6348b9cab132e
│   │   └── [ 71] link
│   └── [ 21] tags
│   └── [ 34] scratch
│   ├── [ 18] current
│   │   └── [ 71] link
│   └── [ 20] index
│   └── [ 78] sha256
│   └── [ 18] 8dabce532312b587329fe225ef501051c60f81ffdb2c801a5da6348b9cab132e
│   └── [ 71] link
├──[ 6] _uploads # 文件夹是个临时的文件夹,主要用来存放 push 镜像过程中的文件数据上传完成后文件夹将会被清空;
└── _manifests/ # 由于太多截取一部分
├── [ 20] revisions # _revisions 目录里存放了该 repository 历史上上传版本的所有 sha256 编码信息。
│   └── [ 150] sha256
│   ├── [ 18] 8dabce532312b587329fe225ef501051c60f81ffdb2c801a5da6348b9cab132e
│   │   └── [ 71] link # 链接到对应的blobs目录中
│   └── [ 18] b96f0ada640f7d628a4f22766f5432cbb1a4dfa208b03dc6444f8c699da9533c
│   └── [ 71] link
└── [ 34] tags #镜像标记中存有 current 目录和 index 目录
├── [ 34] scratch
│   ├── [ 18] current # current 目录下的 link 文件保存了该 tag 目前的 manifest 文件的 sha256 编码,对应在 blobs 中的 sha256 目录下的 data 文件
│   │   └── [ 71] link
│   └── [ 20] index # index 目录则列出了该 tag 历史上传的所有版本的 sha256 编码信息
│   └── [ 78] sha256
│   └── [ 18] 8dabce532312b587329fe225ef501051c60f81ffdb2c801a5da6348b9cab132e
│   └── [ 71] link
└── [ 34] stage
├── [ 18] current
│   └── [ 71] link
└── [ 20] index
└── [ 78] sha256
└── [ 18] b96f0ada640f7d628a4f22766f5432cbb1a4dfa208b03dc6444f8c699da9533c
└── [ 71] link


补充说明:

  • (1)在/var/lib/registry/docker/registry/v2/blobs/sha256/53/{Digest}目录下的data文件实际是一个压缩文件其文件格式vnd.docker.image.rootfs.diff.tar.gzip,我们可以使用tar命令将其解压;
    • 在Docker拉取镜像时候会下载该data文件,等待完成后docker-untar进程将会把data文件解压到/var/lib/docker/overlay2/${digest}/diff目录下
      1
      2
      3
      4
      5
      6
      $pwd
      /var/lib/registry/docker/registry/v2/blobs/sha256/53/5395625ce01dee311e2f7c879b0b148ac7525de7aad5080a518d7f7e5a99d368
      $mkdir layer
      $tar -xf data -C $_
      $ls layer/
      hello # 镜像中该Layer所存储文件
  • (2) 在/var/lib/registry/docker/registry/v2/blobs/sha256/8d/{Digest}目录下文data文件实际是Manifest记录了一个镜像所包含的 layer 信息,当我们 pull 镜像的时候会使用到这个文件;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    $pwd
    /var/lib/registry/docker/registry/v2/blobs/sha256/8d/8dabce532312b587329fe225ef501051c60f81ffdb2c801a5da6348b9cab132e
    $cat data
    {
    "schemaVersion": 2,
    # 镜像 Image Config
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "config": {
    "mediaType": "application/vnd.docker.container.image.v1+json",
    "size": 1472,
    "digest": "sha256:cb05b87d001253772ae9a212200de5eb8304ab9691c61589332a2f57e7059209"
    },
    # 镜像层Digest
    "layers": [
    {
    "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
    "size": 1106793,
    "digest": "sha256:5395625ce01dee311e2f7c879b0b148ac7525de7aad5080a518d7f7e5a99d368"
    }
    ]
    }
  • (3) 在/var/lib/registry/docker/registry/v2/blobs/sha256/cb/{digest}目录下的date文件实际存放的是Image config文件

    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
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    {
    "architecture": "amd64",
    # 未来根据这个image启动container时,config里面的配置就是运行container时的默认参数。
    "config": {
    "Hostname": "",
    "Domainname": "",
    "User": "",
    "AttachStdin": false,
    "AttachStdout": false,
    "AttachStderr": false,
    "Tty": false,
    "OpenStdin": false,
    "StdinOnce": false,
    "Env": [
    "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
    ],
    "Cmd": [
    "./hello"
    ],
    "Image": "sha256:52e976b8f1f3a361b88de03eb6cd8f114a9ce3bece123d31e8d04f116cb4d862",
    "Volumes": null,
    "WorkingDir": "",
    "Entrypoint": null,
    "OnBuild": null,
    "Labels": null
    },
    # 该镜像时临时容器的ID
    "container": "2247a541f3c65b40fe0b1af8d2bf8ac9d9d9d399e46b2f721a1e203484ad2de4",
    # 对比containner_config与config的内容,字段完全一致验证了config的作用。
    "container_config": {
    "Hostname": "2247a541f3c6",
    "Domainname": "",
    "User": "",
    "AttachStdin": false,
    "AttachStdout": false,
    "AttachStderr": false,
    "Tty": false,
    "OpenStdin": false,
    "StdinOnce": false,
    "Env": [
    "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
    ],
    "Cmd": [
    "/bin/sh",
    "-c",
    "#(nop) ",
    "CMD [\"./hello\"]"
    ],
    "Image": "sha256:52e976b8f1f3a361b88de03eb6cd8f114a9ce3bece123d31e8d04f116cb4d862",
    "Volumes": null,
    "WorkingDir": "",
    "Entrypoint": null,
    "OnBuild": null,
    "Labels": {}
    },
    "created": "2020-07-27T14:50:40.305884817Z",
    "docker_version": "19.03.9",
    # 构建该镜像的所有历史命令
    "history": [
    {
    "created": "2020-07-27T14:50:40.251387904Z",
    "created_by": "/bin/sh -c #(nop) COPY file:25386fc86439ade6abebd01bfc3fa9b402ce30141c2dd5a5539d00c3f623b541 in . "
    },
    {
    "created": "2020-07-27T14:50:40.305884817Z",
    "created_by": "/bin/sh -c #(nop) CMD [\"./hello\"]",
    "empty_layer": true
    }
    ],
    "os": "linux",
    # 该镜像包含的layer层的diff id
    "rootfs": {
    "type": "layers",
    "diff_ids": [
    "sha256:a3586d8823611b4ebdec8742bedc72e50aecc5da2ff04c3f40c5f955591d7637"
    ]
    }
    }
  • (4) 在/var/lib/registry/docker/registry/v2/repositories/{镜像名称}/_uploads/目录存放在push镜像过程中的文件数据,当镜像Layer上传完毕后会清空该文件夹;其中的 data 文件上传完毕后会移动到 blobs 目录下,并根据该文件的 sha256 值来进行散列存储到相应的目录下。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    # 上传过程中的目录结构:
    _uploads
    ├── [ 53] 0d6c996e-638f-4436-b2b6-54fa7ad430d2
    │ ├── [198M] data
    │ ├── [ 20] hashstates
    │ │ └── [ 15] sha256
    │ │ └── [ 108] 0
    │ └── [ 20] startedat
    └── [ 53] ba31818e-4217-47ef-ae46-2784c9222614
    ├── [571M] data
    ├── [ 20] hashstates
    │ └── [ 15] sha256
    │ └── [ 108] 0
    └── [ 20] startedat
    6 directories, 6 files

    Tips:上传完镜像之后_uploads 文件夹就会被清空,正常情况下这个文件夹是空的; 但也有异常的时候比如网络抖动导致上传意外中断,该文件夹就可能不为空。
  • (5) 在/var/lib/registry/docker/registry/v2/repositories/{镜像名称}/_mainfest/目录下是镜像上传完成之后由Registry来生成的,并且该目录下的文件都是一个名为Link的文本文件,其值指向blobs目录下与之对应的目录; 在该目录中包含镜像的tags 和 revisions 信息,每一个镜像的每一个Tag对应着于tag名称相同的目录;

    • 注意点: 镜像的 tag 并不存储在 image config 中而是以目录的形式来形成镜像的 tag,这一点比较奇妙这和我们 Dockerfile 中并不包含镜像名和 tag 一个道理?
      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
      31
      32
      33
      34
      35
      $tree -h 1 _manifests/
      _manifests/
      ├── [ 20] revisions
      │   └── [ 150] sha256
      │   ├── [ 18] 8dabce532312b587329fe225ef501051c60f81ffdb2c801a5da6348b9cab132e
      │   │   └── [ 71] link
      │   └── [ 18] b96f0ada640f7d628a4f22766f5432cbb1a4dfa208b03dc6444f8c699da9533c
      │   └── [ 71] link
      └── [ 34] tags
      ├── [ 34] scratch
      │   ├── [ 18] current
      │   │   └── [ 71] link
      │   └── [ 20] index
      │   └── [ 78] sha256
      │   └── [ 18] 8dabce532312b587329fe225ef501051c60f81ffdb2c801a5da6348b9cab132e
      │   └── [ 71] link
      └── [ 34] stage
      ├── [ 18] current
      │   └── [ 71] link
      └── [ 20] index
      └── [ 78] sha256
      └── [ 18] b96f0ada640f7d628a4f22766f5432cbb1a4dfa208b03dc6444f8c699da9533c
      └── [ 71] link

      # 补充说明
      - _revisions 目录里存放了该 repository 历史上上传版本的所有 sha256 编码信息。
      - tags/index 目录则列出了该 tag 历史上传的所有版本的 sha256 编码息
      - tags/current 目录下的 link 文件保存了该 tag 目前的 manifest 文件的 sha256 编码对应在 blobs 中的 sha256 目录下的 data 文件,在`拉取镜像时候会首先找到该目录下的links文件中的sha256值对应目录的manifest返回给客户端`;
      $cat /var/lib/registry/docker/registry/v2/repositories/go-hello/_manifests/tags/scratch/current/link
      sha256:8dabce532312b587329fe225ef501051c60f81ffdb2c801a5da6348b9cab132e
      $ls -lah /var/lib/registry/docker/registry/v2/blobs/sha256/8d/8dabce532312b587329fe225ef501051c60f81ffdb2c801a5da6348b9cab132e/
      总用量 4.0K
      drwxr-xr-x. 2 root root 18 8月 2 09:58 .
      drwxr-xr-x. 3 root root 78 8月 2 09:58 ..
      -rw-r--r--. 1 root root 528 8月 2 09:58 data


2.基础实例

描述:为了更好的了解目录及其文件作用,我们构建一个镜像来存储到本地;

1
2
3
4
5
6
7
8
9
10
cat > Dockerfile <<END
FROM alpine
LABEL name="test-image"
RUN echo "http://mirrors.ustc.edu.cn/alpine/v3.10/main" > /etc/apk/repositories; \
echo "http://mirrors.ustc.edu.cn/alpine/v3.10/community">> /etc/apk/repositories; \
apk -v add --no-cache bash; \
apk -v add --no-cache curl
COPY ./startService.sh /
CMD ["/bin/bash", "/startService.sh"]
END

构建过程输出如下:
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
$docker build -t test-image .
Sending build context to Docker daemon 3.072kB
Step 1/6 : FROM alpine
---> 3f53bb00af94 # 镜像id
Step 2/6 : LABEL name="test-image"
# ---> Running in 3bd6320fc291 # 运行的容器 id
# Removing intermediate container 3bd6320fc291 # 对运行的容器进行写后并移除该容器
---> bb97dd1fb1a1 # 镜像id
Step 3/6 : RUN apk -v add --no-cache bash
# ---> Running in f9987ff57ad7 # 运行的容器 id
fetch http://mirrors.ustc.edu.cn/alpine/v3.8/main/x86_64/APKINDEX.tar.gz
fetch http://mirrors.ustc.edu.cn/alpine/v3.8/community/x86_64/APKINDEX.tar.gz
(1/5) Installing ncurses-terminfo-base (6.1_p20180818-r1)
(2/5) Installing ncurses-terminfo (6.1_p20180818-r1)
(3/5) Installing ncurses-libs (6.1_p20180818-r1)
(4/5) Installing readline (7.0.003-r0)
(5/5) Installing bash (4.4.19-r1)
Executing bash-4.4.19-r1.post-install
Executing busybox-1.28.4-r2.trigger
OK: 18 packages, 136 dirs, 2877 files, 13 MiB
# Removing intermediate container f9987ff57ad7
---> a5635f1b1d00 # 镜像id
Step 4/6 : RUN apk -v add --no-cache curl
# ---> Running in c49fb2e4b311 # 运行的容器 id
fetch http://mirrors.ustc.edu.cn/alpine/v3.8/main/x86_64/APKINDEX.tar.gz
fetch http://mirrors.ustc.edu.cn/alpine/v3.8/community/x86_64/APKINDEX.tar.gz
(1/5) Installing ca-certificates (20171114-r3)
(2/5) Installing nghttp2-libs (1.32.0-r0)
(3/5) Installing libssh2 (1.8.0-r3)
(4/5) Installing libcurl (7.61.1-r1)
(5/5) Installing curl (7.61.1-r1)
Executing busybox-1.28.4-r2.trigger
Executing ca-certificates-20171114-r3.trigger
OK: 23 packages, 141 dirs, 3040 files, 15 MiB
# Removing intermediate container c49fb2e4b311
---> 9156d1521a2f # 镜像id
Step 5/6 : COPY ./startService.sh / # none
---> 704626646baf # 镜像id
Step 6/6 : CMD ["/bin/bash", "/startService.sh"]
# ---> Running in 1c5e6e861264 # 运行的容器 id
# Removing intermediate container 1c5e6e861264
---> 6cd0a66e83f1 # 镜像id
Successfully built 6cd0a66e83f1 # 最终生成的test-image镜像ID为 6cd0a66e83f1
Successfully tagged test-image:latest

构建过程如图所示:

WeiyiGeek.docker

WeiyiGeek.docker

首先查看镜像的基本信息然后进行分析本地目录:

1
2
3
4
5
$ docker images --digests test-image
REPOSITORY TAG DIGEST IMAGE ID CREATED SIZE
test-image latest <none> 6cd0a66e83f1 About an hour ago

# 如前面所述digest是有docker repository生成,因为本地构建完之后并没有推送至远程仓库,所以为None。

此时/var/lib/docker/image目录发送如下变化

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
image/
└── overlay2
├── distribution
│ ├── diffid-by-digest
│ │ └── sha256
│ │ └── cd784148e3483c2c86c50a48e535302ab0288bebd587accf40b714fffd0646b3
│ └── v2metadata-by-diffid # 元数据由diff id
│ └── sha256
│ └── 7bff100f35cb359a368537bb07829b055fe8e0b1cb01085a3a628ae9c187c7b8
├── imagedb # 镜像数据库
│ ├── content
│ │ └── sha256 # 构建镜像时各Layer层的镜像ID
│ │ ├── 3f53bb00af943dfdf815650be70c0fa7b426e56a66f5e3362b47a129d57d5991 # Alpine
│ │ ├── 6cd0a66e83f133a2bad37103ed03f6480330fa3c469368eb5871320996d3b924 # test-Images
│ │ ├── 704626646baf8bdea82da237819cded076a0852eb97dba2fc731569dd85ae836
│ │ ├── 9156d1521a2fd50d972e1e1abc30d37df7c8e8f7825ca5955170f3b5441b3341
│ │ ├── a5635f1b1d0078cd926f21ef3ed77b357aa899ac0c8bf80cae51c37129167e3a
│ │ └── bb97dd1fb1a10b717655594950efb4605ff0d3f2f631feafc4558836c2b34c3c
│ └── metadata # 元数据 (在构建test-image之前,metadata目录为空,因为alpine没有parent) 在build之后新增了5个目录,分别对应docker build test-image所产生的5个镜像,每一层以上一层为parent,即parent文件中存储了上一层image id。
│ └── sha256
│ ├── 6cd0a66e83f133a2bad37103ed03f6480330fa3c469368eb5871320996d3b924
│ │ ├── lastUpdated
│ │ └── parent # 源(父)镜像id
│ ├── 704626646baf8bdea82da237819cded076a0852eb97dba2fc731569dd85ae836
│ │ └── parent
│ ├── 9156d1521a2fd50d972e1e1abc30d37df7c8e8f7825ca5955170f3b5441b3341
│ │ └── parent
│ ├── a5635f1b1d0078cd926f21ef3ed77b357aa899ac0c8bf80cae51c37129167e3a
│ │ └── parent
│ └── bb97dd1fb1a10b717655594950efb4605ff0d3f2f631feafc4558836c2b34c3c
│ └── parent
├── layerdb # 层数据库
│ ├── mounts # 当由镜像生成容器时,该目录下会生成容器的可读可写两个layer,可读即为由镜像生成,而可写就是未来对容器的修改都会放在
│ ├── sha256 # 镜像 Layer的diff id(但是实际只有四层)
│ │ ├── 0e88764cdf90e8a5d6597b2d8e65b8f70e7b62982b0aee934195b54600320d47
│ │ │ ├── cache-id
│ │ │ ├── diff
│ │ │ ├── parent
│ │ │ ├── size
│ │ │ └── tar-split.json.gz
│ │ ├── 7bff100f35cb359a368537bb07829b055fe8e0b1cb01085a3a628ae9c187c7b8
│ │ │ ├── cache-id
│ │ │ ├── diff
│ │ │ ├── size
│ │ │ └── tar-split.json.gz
│ │ ├── 80fe1abae43103e3be54ac2813114d1dea6fc91454a3369104b8dd6e2b1363f5
│ │ │ ├── cache-id
│ │ │ ├── diff
│ │ │ ├── parent
│ │ │ ├── size
│ │ │ └── tar-split.json.gz
│ │ └── db7c15c2f03f63a658285a55edc0a0012ccd0033f4695d4b428b1b464637e655
│ │ ├── cache-id
│ │ ├── diff
│ │ ├── parent
│ │ ├── size
│ │ └── tar-split.json.gz
│ └── tmp
└── repositories.json

在imagedb的contentmetadata下增加了build过程中产生的镜像(镜像ID能够一一对应),在layerdb下增加了三个Layer,目前还看不出来关联关系,后续分析。

  • Step1.先从repositories.json文件看起

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    $cat image/overlay2/repositories.json | python -m json.tool
    {
    "Repositories": {
    "alpine": {
    "alpine:latest": "sha256:3f53bb00af943dfdf815650be70c0fa7b426e56a66f5e3362b47a129d57d5991",
    "alpine@sha256:46e71df1e5191ab8b8034c5189e325258ec44ea739bba1e5645cff83c9048ff1": "sha256:3f53bb00af943dfdf815650be70c0fa7b426e56a66f5e3362b47a129d57d5991"
    },
    # 镜像ID : 增加了test-image该项包含其tag和id。
    "test-image": {
    "test-image:latest": "sha256:6cd0a66e83f133a2bad37103ed03f6480330fa3c469368eb5871320996d3b924"
    }
    }
    }
  • Step2.以test-image为例(目前的最底层镜像),查看其描述信息:

    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
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    {
    "architecture": "amd64",
    "config": {
    "ArgsEscaped": true,
    "AttachStderr": false,
    "AttachStdin": false,
    "AttachStdout": false,
    "Cmd": [
    "/bin/bash",
    "/startService.sh"
    ],
    "Domainname": "",
    "Entrypoint": null,
    "Env": [
    "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
    ],
    "Hostname": "",
    "Image": "sha256:704626646baf8bdea82da237819cded076a0852eb97dba2fc731569dd85ae836",
    "Labels": {
    "name": "test-image"
    },
    "OnBuild": null,
    "OpenStdin": false,
    "StdinOnce": false,
    "Tty": false,
    "User": "",
    "Volumes": null,
    "WorkingDir": ""
    },
    # 容器ID可以与图XX中生成该image的容器ID对应上即最后一次构建镜像时的容器ID;
    "container": "1c5e6e861264654f79a190eba5157dd4dedce59ab3de098a3625fb4e5b6f1d98",
    # 容器配置状态实际是执行完dockerfile中命令之后的容器状态;
    "container_config": {
    "ArgsEscaped": true,
    "AttachStderr": false,
    "AttachStdin": false,
    "AttachStdout": false,
    "Cmd": [
    "/bin/sh",
    "-c",
    "#(nop) ",
    "CMD [\"/bin/bash\" \"/startService.sh\"]"
    ],
    "Domainname": "",
    "Entrypoint": null,
    "Env": [
    "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
    ],
    "Hostname": "1c5e6e861264",
    "Image": "sha256:704626646baf8bdea82da237819cded076a0852eb97dba2fc731569dd85ae836",
    "Labels": {
    "name": "test-image"
    },
    "OnBuild": null,
    "OpenStdin": false,
    "StdinOnce": false,
    "Tty": false,
    "User": "",
    "Volumes": null,
    "WorkingDir": ""
    },
    "created": "2019-01-01T02:29:19.701494089Z",
    "docker_version": "18.09.0",
    "history": [
    {
    "created": "2018-12-21T00:21:29.97055571Z",
    "created_by": "/bin/sh -c #(nop) ADD file:2ff00caea4e83dfade726ca47e3c795a1e9acb8ac24e392785c474ecf9a621f2 in / "
    },
    {
    "created": "2018-12-21T00:21:30.122610396Z",
    "created_by": "/bin/sh -c #(nop) CMD [\"/bin/sh\"]",
    "empty_layer": true
    },
    {
    "created": "2019-01-01T02:29:06.530296297Z",
    "created_by": "/bin/sh -c #(nop) LABEL name=test-image",
    "empty_layer": true
    },
    {
    "created": "2019-01-01T02:29:14.182236016Z",
    "created_by": "/bin/sh -c apk -v add --no-cache bash"
    },
    {
    "created": "2019-01-01T02:29:19.327280058Z",
    "created_by": "/bin/sh -c apk -v add --no-cache curl"
    },
    {
    "created": "2019-01-01T02:29:19.549474383Z",
    "created_by": "/bin/sh -c #(nop) COPY file:fff66db7f2d773b25215edcc9d5697d84813835e3b731e5a6afe9a9b9647ecec in / "
    },
    {
    "created": "2019-01-01T02:29:19.701494089Z",
    "created_by": "/bin/sh -c #(nop) CMD [\"/bin/bash\" \"/startService.sh\"]",
    "empty_layer": true
    }
    ],
    "os": "linux",
    # test-image镜像包含的Layer的diff id (四层)
    # 在 LABEL name="test-image"和CMD ["/bin/bash", "/startService.sh"] 这两行并未产生新的Layer由于其并未改变文件系统中任何文件只是写在了Image-Config中;
    "rootfs": {
    "diff_ids": [
    "sha256:7bff100f35cb359a368537bb07829b055fe8e0b1cb01085a3a628ae9c187c7b8",
    "sha256:b1ddbff022577cd249a074285a1a7eb76d7c9139132ba5aa4272fc115dfa9e36",
    "sha256:9edc93f4dcf640f272ed73f933863dbefae6719745093d09c6c6908f402b1c34",
    "sha256:a6c8828ba4b58628284f783d3c918ac379ae2aba0830f4c926a330842361ffb6"
    ],
    "type": "layers"
    }
    }

事实上如果我们认为镜像是一个打包的静态OS,那么Layer可以认为是描述该OS的fs变化,即文件系统中文件或者目录发生的改变,很明显上述两行命令并不会引起fs的变化只是会写入该镜像的config中,在生成容器时读取即可自然也就不存在diff id

WeiyiGeek.diff-id

WeiyiGeek.diff-id

  • Step3.其次目前Layer已经增加至4个,这与我们上一节中看到的test-image的rootfs配置中有4个layer diif id能够对应上,然后很显然除了第一层的”7bff100f35cb”能对应上其他三个完全不同;
    1
    ChainID(A) = DiffID(A) = sha256:7bff100f35cb359a368537bb07829b055fe8e0b1cb01085a3a628ae9c187c7b8
    进一步研究才知道这里目录名其实是layer的chain id,而非diff id,关于这两这个的区别,我们可以理解为diff id用来描述单个变化chain id用来便于一些列的变化

重点:diff id和chain id之间的计算公式可以在image-spec中看到。

1
2
3
ChainID(A) = DiffID(A)
ChainID(A|B) = Digest(ChainID(A) + " " + DiffID(B))
ChainID(A|B|C) = Digest(ChainID(A|B) + " " + DiffID(C))

此处以test-image的rootfs验证分析他们是如何关联的:
1
2
3
4
5
6
7
8
9
10
"rootfs": {
# 文件系统差异标识
"diff_ids": [
"sha256:7bff100f35cb359a368537bb07829b055fe8e0b1cb01085a3a628ae9c187c7b8", # 该层可以对应上dir
"sha256:b1ddbff022577cd249a074285a1a7eb76d7c9139132ba5aa4272fc115dfa9e36", # 通过基础层 + diff_ids 获得Layer层摘要
"sha256:9edc93f4dcf640f272ed73f933863dbefae6719745093d09c6c6908f402b1c34",
"sha256:a6c8828ba4b58628284f783d3c918ac379ae2aba0830f4c926a330842361ffb6"
],
"type": "layers"
}

链ID计算公式:因为chainid的一层是依赖上一层的所以最后算出来的rootfs是统一的;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 公式 
Chaninid(1) = diffid(1) chainid(n) = sha256(chain(n-1) diffid(n) )
ChainID(A|B) = Digest(ChainID(A) + " " + DiffID(B))

# 链ID 与 差异ID
ChainID(A) = sha256:7bff100f35cb359a368537bb07829b055fe8e0b1cb01085a3a628ae9c187c7b8
DiffID(B) = sha256:b1ddbff022577cd249a074285a1a7eb76d7c9139132ba5aa4272fc115dfa9e36
DiffID(C) = sha256:9edc93f4dcf640f272ed73f933863dbefae6719745093d09c6c6908f402b1c34
DiffID(D) = sha256:a6c8828ba4b58628284f783d3c918ac379ae2aba0830f4c926a330842361ffb6

# 结果:
# ChainID(A|B) = sha256:db7c15c2f03f63a658285a55edc0a0012ccd0033f4695d4b428b1b464637e655
$echo -n "sha256:7bff100f35cb359a368537bb07829b055fe8e0b1cb01085a3a628ae9c187c7b8 sha256:b1ddbff022577cd249a074285a1a7eb76d7c9139132ba5aa4272fc115dfa9e36" | sha256sum -
db7c15c2f03f63a658285a55edc0a0012ccd0033f4695d4b428b1b464637e655 - # sha256下面引用

#chainID(A|B|C) = sha256:0e88764cdf90e8a5d6597b2d8e65b8f70e7b62982b0aee934195b54600320d47
$echo -n "sha256:db7c15c2f03f63a658285a55edc0a0012ccd0033f4695d4b428b1b464637e655 sha256:9edc93f4dcf640f272ed73f933863dbefae6719745093d09c6c6908f402b1c34" | sha256sum -
0e88764cdf90e8a5d6597b2d8e65b8f70e7b62982b0aee934195b54600320d47 -

# 计算流程如上
# chainID(A|B|C|D) = sha256:80fe1abae43103e3be54ac2813114d1dea6fc91454a3369104b8dd6e2b1363f5

WeiyiGeek.ChainID

WeiyiGeek.ChainID

  • Step4.在 imagedb/overlay2/destribution 目录中初始文件如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    [root@docker-learn overlay2]$tree distribution/
    distribution/
    ├── diffid-by-digest
    │ └── sha256
    │ └── cd784148e3483c2c86c50a48e535302ab0288bebd587accf40b714fffd0646b3
    └── v2metadata-by-diffid
    └── sha256
    └── 7bff100f35cb359a368537bb07829b055fe8e0b1cb01085a3a628ae9c187c7b8
    4 directories, 2 files
    • 目前来看,destribution目录和只有alpine镜像时并没有什么区别,这是因为digest是由镜像仓库生成,本地构建的镜像在没有push到仓库之前,自然也就没有digest。
      使用docker push命令将test-image推送至dockerhub。
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      [root@docker-learn distribution]# docker push backbp/test-image:lasted
      The push refers to repository [docker.io/backbp/test-image] # 从上层依次传输到下层
      a6c8828ba4b5: Pushed
      9edc93f4dcf6: Pushed
      b1ddbff02257: Pushed
      7bff100f35cb: Mounted from library/alpine
      lasted: digest: sha256:3dc66a43c28ea3e994e4abf6a2d04c7027a9330e8eeab5c609e4971a8c58f0b0 size: 1156

      # 此时 distribution/ 文件如下:
      distribution/
      ├── diffid-by-digest
      │ └── sha256
      │ ├── 2826782ee82560ec5f90a8a9da80880d48dd4036763f5250024fab5b3ef8e8cf
      │ ├── 8e905c02e6908fbb0e591cea285470208920d32408735bd6a8fcaf85ffba9089
      │ ├── a5bec9983f6902f4901b38735db9c427190ffcb3734c84ee233ea391da81081b
      │ └── cd784148e3483c2c86c50a48e535302ab0288bebd587accf40b714fffd0646b3
      └── v2metadata-by-diffid
      └── sha256
      ├── 7bff100f35cb359a368537bb07829b055fe8e0b1cb01085a3a628ae9c187c7b8 # push 4 - ChianID(A)
      ├── 9edc93f4dcf640f272ed73f933863dbefae6719745093d09c6c6908f402b1c34 # push 2 - DiffID(C)
      ├── a6c8828ba4b58628284f783d3c918ac379ae2aba0830f4c926a330842361ffb6 # push 1 - DiffID(D)
      └── b1ddbff022577cd249a074285a1a7eb76d7c9139132ba5aa4272fc115dfa9e36 # push 3 - DiffID(B)


补充知识点:
Q: 1.docker tag命令发生了什么?

答:我们还是采用上面的test-image生成一个新的tag为例进行实验查看得知,两个tag的镜像都指向同一个image id,所以这个命令相当于只有修改了repositories.json类比于软链接;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[root@docker-learn overlay2]$cat repositories.json | python -m json.tool
{
"Repositories": {
"alpine": {
"alpine:latest": "sha256:3f53bb00af943dfdf815650be70c0fa7b426e56a66f5e3362b47a129d57d5991",
"alpine@sha256:46e71df1e5191ab8b8034c5189e325258ec44ea739bba1e5645cff83c9048ff1": "sha256:3f53bb00af943dfdf815650be70c0fa7b426e56a66f5e3362b47a129d57d5991"
},
"test-image": {
"test-image:latest": "sha256:6cd0a66e83f133a2bad37103ed03f6480330fa3c469368eb5871320996d3b924"
},
"test-image-tag": {
"test-image-tag:latest": "sha256:6cd0a66e83f133a2bad37103ed03f6480330fa3c469368eb5871320996d3b924"
}
}
}


Q: 2.Image Size是如何计算的?
答: 上述的alpine镜像和test-image镜像大小分别是:4.41M和9.88M。为了更准确的分析,可以使用docker inspect 镜像查看详细大小,分别为4413428和9876099。
再次查看每层Layer的大小(layerdb/layer chain id/size),分别为

1
2
3
4
5
6
7
8
9
# 每层大小
Layer1:4413428
Layer2:3815516
Layer3:1647117
Layer4:38

# 镜像计算
alpine 只有一层,所以image size与Layer size相等;
test-image 有四层,所以image size = sum(Layer1+Layer2+Layer3+Layer4)=9876099


Q: 3.Dockerfile 指令之apk del .build-deps对Layer层Size的影响?
答: 如下Dockerfile 指令优化并减少了生成镜像的大小,分析原因如下将一部分命令放在一个RUN指令中运行会在同一个Layer执行其指令中的多条语句从而不会在其它层进行执行,这样所做的好处就是节约空间,此处采用一个虚拟目录.build-deps的安装和卸载相对于上一层而言根本就不存在,这是由于OCI Image的原理所致,只需要记住的是每一个Layer记录的是与上一层Layer相比的变化

1
2
3
4
# 灵魂重点(DockerFile 优化必备)
RUN apk -v add --no-cache --virtual .build-deps gcc make && \
make clean && make && make install && \
apk del .build-deps


总结本章:

  • 1.在image/overlay2/imagedb目录中,保存的Image的配置信息在content目录中,并且以image id为文件名存储,Image之间关联信息在metadata中,以parent文件存储。我们根据image生成容器的时候可以生成了一个文件系统,但是上述这些信息并不包含fs的数据。
    因为真正的fs数据是存储在Layer中的即Layer的信息存储在layerdb目录下
  • 2.因此test-image的第二层Layer对应的目录为:layerdb/sha256/db7c15c2f03f63a658285a55edc0a0012ccd0033f4695d4b428b1b464637e655,与上一节中相比多了parent,包含了上一层Layer的chain id,查看该Layer的信息:
    1
    2
    3
    [root@docker-learn sha256]$ls db7c15c2f03f63a658285a55edc0a0012ccd0033f4695d4b428b1b464637e655/
    cache-id diff parent size tar-split.json.gz
    # parent = sha256:7bff100f35cb359a368537bb07829b055fe8e0b1cb01085a3a628ae9c187c7b8

0x01 Dockerd 语法参数

描述:通过前面Docker的学习我们知道了它是一个C/S架构, 即Daemon CLI (dockerd)它是docker的服务端它是管理容器的持久进程;

Docker为守护进程和客户端使用不同的二进制文件,要运行守护进程输入dockerd。

Dockerd基础语法参数:

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# 语法:容器的自给自足运行时。
Usage: dockerd COMMAND


# 参数选项
Options:
--add-runtime runtime Register an additional OCI compatible runtime (default [])
--allow-nondistributable-artifacts list Allow push of nondistributable artifacts to registry
--api-cors-header string Set CORS headers in the Engine API
--authorization-plugin list Authorization plugins to load
--bip string Specify network bridge IP
-b, --bridge string Attach containers to a network bridge
--cgroup-parent string Set parent cgroup for all containers
--cluster-advertise string Address or interface name to advertise
--cluster-store string URL of the distributed storage backend
--cluster-store-opt map Set cluster store options (default map[])
--config-file string Daemon configuration file (default "/etc/docker/daemon.json")
--containerd string containerd grpc 地址
--cpu-rt-period int 以微秒为单位限制CPU的实时周期
--cpu-rt-runtime int 以微秒为单位限制CPU的实时运行时间
--cri-containerd 以cri启动containerd
--data-root string Root directory of persistent Docker state (default "/var/lib/docker")
-D, --debug Enable debug mode
--default-address-pool pool-options Default address pools for node specific local networks
--default-gateway ip Container default gateway IPv4 address
--default-gateway-v6 ip Container default gateway IPv6 address
--default-ipc-mode string Default mode for containers ipc ("shareable" | "private") (default "private")
--default-runtime string Default OCI runtime for containers (default "runc")
--default-shm-size bytes Default shm size for containers (default 64MiB)
--default-ulimit ulimit Default ulimits for containers (default [])
--dns list DNS server to use
--dns-opt list DNS options to use
--dns-search list DNS search domains to use
--exec-opt list Runtime execution options
--exec-root string Root directory for execution state files (default "/var/run/docker")
--experimental 注意:在一台主机上运行多个守护进程被认为是“试验性的”。
--fixed-cidr string IPv4 subnet for fixed IPs
--fixed-cidr-v6 string IPv6 subnet for fixed IPs
-G, --group string Group for the unix socket (default "docker")
--help Print usage
-H, --host list Daemon socket(s) to connect to
--icc Enable inter-container communication (default true)
--init Run an init in the container to forward signals and reap processes
--init-path string Path to the docker-init binary
--insecure-registry list Enable insecure registry communication
--ip ip Default IP when binding container ports (default 0.0.0.0)
--ip-forward Enable net.ipv4.ip_forward (default true)
--ip-masq Enable IP masquerading (default true)
--iptables Enable addition of iptables rules (default true)
--ipv6 Enable IPv6 networking
--label list Set key=value labels to the daemon
--live-restore 当容器仍在运行时,启用docker的活还原
--log-driver string Default driver for container logs (default "json-file")
-l, --log-level string Set the logging level ("debug"|"info"|"warn"|"error"|"fatal") (default "info")
--log-opt map Default log driver options for containers (default map[])
--max-concurrent-downloads int 设置每次拉取的最大并发下载(默认为3)
--max-concurrent-uploads int 设置每个推送的最大并发上传数(默认为5)
--metrics-addr string Set default address and port to serve the metrics api on
--mtu int Set the containers network MTU
--network-control-plane-mtu int Network Control plane MTU (default 1500)
--no-new-privileges Set no-new-privileges by default for new containers
--node-generic-resource list Advertise user-defined resource
--oom-score-adjust int 为守护进程设置oom_score_adj(默认值为-500)
-p, --pidfile string Path to use for daemon PID file (default "/var/run/docker.pid")
--raw-logs Full timestamps without ANSI coloring
--registry-mirror list Preferred Docker registry mirror
--rootless Enable rootless mode; typically used with RootlessKit (experimental)
--seccomp-profile string Path to seccomp profile
--selinux-enabled Enable selinux support
--shutdown-timeout int Set the default shutdown timeout (default 15)
-s, --storage-driver string Storage driver to use
--storage-opt list Storage driver options
--swarm-default-advertise-addr string Set default address or interface for swarm advertised address
--tls Use TLS; implied by --tlsverify
--tlscacert string Trust certs signed only by this CA (default "~/.docker/ca.pem")
--tlscert string Path to TLS certificate file (default "~/.docker/cert.pem")
--tlskey string Path to TLS key file (default "~/.docker/key.pem")
--tlsverify Use TLS and verify the remote
--userland-proxy Use userland proxy for loopback traffic (default true)
--userland-proxy-path string Path to the userland proxy binary
--userns-remap string User/Group setting for user namespaces
-v, --version 版本信息


基础选项讲解
  • docker 服务端调试模式Debug开启:
    1
    2
    3
    4
    5
    6
    7
    8
    # docker.service
    dockerd -D --debug

    # docker.service
    dockerd --experimental true

    # daemon.json file
    "experimental": true

  • 守护进程套接字选项:
    Docker守护进程可以通过三种不同类型的套接字侦听Docker引擎API请求:unix、tcp和fd,并且支持侦听多个套接字在同一时间使用多个-H选项。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    # (1) 默认情况下在/var/run/docker创建一个unix域套接字(或IPC套接字),需要root权限或docker组成员资格。
    # 如有需要远程访问Docker Deamon建议使用Nginx等进行配置HTTPS加密套接字(TLS1.0>=), 通常使用端口2375进行非加密,而使用端口2376与守护进程进行加密通信。
    # Run docker in daemon mode
    $ sudo <path to>/dockerd -H tcp://127.0.0.1:2375 -H unix:///var/run/docker.sock &
    $ sudo dockerd -H unix:///var/run/docker.sock -H tcp://192.168.59.106 -H tcp://10.10.10.2

    # (2) 在基于Systemd的系统上,您可以通过激活Systemd套接字与守护进程通信,使用dockerd -H fd://。
    使用fd://将工作完美的大多数设置,但你也可以指定个别套接字: dockerd -H fd://3,如果没有找到指定的套接字激活的文件,那么Docker将退出。
    # 您可以在Docker源树中找到使用Systemd套接字激活Docker和Systemd的示例。

    # (3) Docker客户机将使用DOCKER_HOST环境变量或者为客户机设置-H标志
    $ export DOCKER_HOST="tcp://0.0.0.0:2375" \
    export DOCKER_TLS_VERIFY=1 \
    && docker ps && docker --tlsverify ps

    $ docker -H tcp://127.0.0.1:2375 pull ubuntu
    $ docker -H :5555 pull ubuntu
    $ docker -H tcp://0.0.0.0:2375 ps

    # (4) 从Docker 18.09开始,Docker客户端支持通过SSH连接到远程守护进程可以配置公钥认证远程主机:
    $ docker -H ssh://me@example.com ps

  • 存储驱动器:
    描述:在Linux上Docker守护进程支持几种不同的图像层存储驱动程序:aufs、devicemapper、btrfs、zfs、overlay 和 overlay2
    1
    2
    3
    4
    5
    6
    aufs : 最早的存储驱动基于Linux补丁内核集(存在未知位置BUG),但是允许容器共享可执行内存和共享库内存
    devicemapper : 驱动程序使用瘦配置和写上复制(CoW)快照,默认情况下这些块设备是通过使用自动创建的稀疏文件的环回挂载来自动创建的(先已经被overlay2所替代)
    btrfs : 与上者类似但是不同的是它不能在设备之间共享可执行内存
    zfs : 该驱动程序可能不如btrfs快但在稳定性方面有较长的记录。由于只有一个副本,克隆之间的共享块只缓存一次。
    overlay : 是一个非常快的union文件系统。它现在已经合并到3.18.0的主Linux内核中。覆盖也支持页面缓存共享,这意味着访问同一个文件的多个容器可以共享一个页面缓存条目(或多个条目),这使得覆盖和aufs驱动程序一样高效。
    overlay2 : 使用相同的快速union文件系统,但是利用了Linux内核4.0中添加的其他特性,以避免过度使用inode。(调用dockerd -s overlay2来使用它。-当前默认)

基础示例:devicemapper

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
31
32
33
34
35
36
37
38
39
40
# 指定要用于thin-pool的自定义块存储设
$ sudo dockerd --storage-opt dm.thinpooldev=/dev/mapper/thin-pool
# 设置块设备进行存储
$ sudo dockerd --storage-opt dm.directlvm_device=/dev/xvdf
# 设置在块设备传递到用于存储的百分比。
$ sudo dockerd --storage-opt dm.thinp_percent=95
# 设置传入的块设备的百分比,用于元数据存储。
$ sudo dockerd --storage-opt dm.thinp_metapercent=1
# 设置lvm尝试自动扩展可用空间之前使用的空间百分比值[100 =禁用]
$ sudo dockerd --storage-opt dm.thinp_autoextend_threshold=80
# 设置值百分比值,当lvm尝试自动扩展可用空间时增加瘦池[100 =禁用]
$ sudo dockerd --storage-opt dm.thinp_autoextend_percent=20
# 指定要用于瘦池的自定义块大小。默认块大小为64K。
$ sudo dockerd --storage-opt dm.blocksize=512K
# 指定用于瘦池数据的自定义块设备
$ sudo dockerd \
--storage-opt dm.datadev=/dev/sdb1 \
# 指定用于瘦池元数据的自定义块设备。为了获得最佳性能,元数据应该位于与数据不同的轴上,甚至更好地位于SSD上。
--storage-opt dm.metadatadev=/dev/sdc1

# 基本设备大小可以在守护进程重启时增加,这将允许所有将来的映像和容器(基于这些新映像)都是新的基本设备大小。
$ sudo dockerd --storage-opt dm.basesize=50G #指定创建基本设备时要使用的大小,该设备限制图像和容器的大小
# 指定为瘦池使用的“data”设备创建回送文件时使用的大小。默认大小是100G,此选项配置devicemapper环回,不应在生产中使用。
$ sudo dockerd --storage-opt dm.loopdatasize=200G
# 指定为瘦池使用的“元数据”设备创建回送文件时使用的大小。默认大小是2G
$ sudo dockerd --storage-opt dm.loopmetadatasize=4G

# 为基本设备指定要使用的文件系统类型。支持的选项是“ext4”和“xfs”。默认值是“xfs”
$ sudo dockerd --storage-opt dm.fs=ext4
# 指定创建基础设备时要使用额外的mkfs参数。
$ sudo dockerd --storage-opt "dm.mkfsarg=-O ^has_journal"
# 指定安装瘦设备时使用的额外挂载选项
$ sudo dockerd --storage-opt dm.mountopt=nodiscard

# 如果libdm和内核驱动程序支持延迟设备删除机制,则支持使用延迟设备删除。
$ sudo dockerd --storage-opt dm.use_deferred_removal=true
# 允许对瘦池设备使用延迟设备删除。默认情况下,瘦池设备删除是同步的在删除一个容器之前,Docker守护进程会删除所有相关的设备。如果存储驱动程序不能删除设备,则容器删除失败,守护进程返回,下面避免了这样的情况。
$ sudo dockerd \
--storage-opt dm.use_deferred_deletion=true \
--storage-opt dm.use_deferred_removal=true

基础示例:Overlay2

1
2
3
4
# 覆盖允许overlay2的Linux内核版本检查。
$ overlay2.override_kernel_check
# 设置容器的默认最大大小。仅当后备fs是xfs并使用pquota挂载选项挂载时才支持它。在这些条件下用户可以传递小于后台fs大小的任何大小。
$ sudo dockerd -s overlay2 --storage-opt overlay2.size=1G


  • Docker运行时执行选项
    Docker守护进程依赖于OCI兼容的运行时(通过containerd守护进程调用)作为它到Linux内核名称空间、cgroups和SELinux的接口。
    默认情况下,Docker守护进程会自动启动containerd。如果您想控制containerd的启动,请手动启动containerd并使用—containerd标志将路径传递给containerd套接字。例如:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    $ sudo dockerd --containerd /var/run/dev/docker-containerd.sock

    # 可以通过配置文件或使用——add-runtime命令行参数向守护进程注册运行时。
    $ sudo dockerd --add-runtime runc=runc --add-runtime custom=/usr/local/bin/my-runc-replacement

    # 选项指定容器的cgroups的管理,该示例将cgroupdriver设置为systemd
    $ sudo dockerd --exec-opt native.cgroupdriver=systemd # 设置此选项适用于守护进程启动的所有容器。
    # 将使hyperv成为Windows上的默认隔离技术。如果在守护进程启动时没有指定隔离值,那么在Windows客户机上,默认为hyperv,在Windows服务器上,默认为process。
    $ dockerd --exec-opt isolation=hyperv

  • 守护进程DNS选项
    1
    2
    3
    4
    # 为所有Docker容器设置DNS服务器,请使用:
    $ sudo dockerd --dns 8.8.8.8
    # 为所有Docker容器设置DNS搜索域,请使用:
    $ sudo dockerd --dns-search example.com

  • 允许推入不可分发的工件
    1
    2
    3
    To override this behavior for specific registries, use the --allow-nondistributable-artifacts option in one of the following forms:
    --allow-nondistributable-artifacts myregistry:5000 tells the Docker daemon to push nondistributable artifacts to myregistry:5000.
    --allow-nondistributable-artifacts 10.1.0.0/16 tells the Docker daemon to push nondistributable artifacts to all registries whose resolved IP address is within the subnet described by the CIDR syntax.

  • Insecure registries
    默认情况下,Docker假设所有注册,但是在本地(参见下面的本地注册),注册是安全的。与不安全的注册表通信是不可能的,如果Docker假设注册表是安全的。为了与不安全的注册表通信,Docker守护进程需要—insecure注册表在以下两种形式之一:
    1
    2
    --insecure-registry myregistry:5000 tells the Docker daemon that myregistry:5000 should be considered insecure.
    --insecure-registry 10.1.0.0/16 tells the Docker daemon that all registries whose domain resolve to an IP address is part of the subnet described by the CIDR syntax, should be considered insecure.

  • Node discovery
    选项指定这个特定守护进程实例在向集群发布自身时应该使用的主机:端口或接口:端口组合。
    守护进程使用libkv公布集群中的节点。一些键值后端支持相互TLS。要配置守护进程使用的客户端TLS设置,可以使用 ——cluster-store-opt 标志进行配置,指定PEM编码文件的路径。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    $ sudo dockerd \
    --cluster-advertise 192.168.1.2:2376 \
    --cluster-store etcd://192.168.1.2:2379 \
    --cluster-store-opt kv.cacertfile=/path/to/ca.pem \ # 指定要信任的带有PEM编码CA证书的本地文件的路径。
    --cluster-store-opt kv.certfile=/path/to/cert.pem \ # 指定具有PEM编码证书的本地文件的路径。
    --cluster-store-opt kv.keyfile=/path/to/key.pem # 使用PEM编码的私钥指定本地文件的路径。

    # 当前支持的集群存储选项是
    discovery.heartbeat = 心跳检测默认默认值是20秒。
    discovery.ttl = 指定TTL(生存时间)(以秒为单位),如果在配置的TTL值内没有接收到有效的心跳,则发现模块使用该TTL为节点超时。如果没有配置,默认值是60秒。
    kv.path = 指定的键/值存储的路径。

  • 杂项选项
    描述:IP伪装使用地址转换来允许没有公共IP的容器与Internet上的其他机器进行对话。这可能会干扰一些网络拓扑,可以使用–ip-masq=false禁用。
    Docker支持Docker数据目录(/var/lib/ Docker)/var/lib/Docker/tmp的软链接。DOCKER_TMPDIR和数据目录可以这样设置:
    1
    2
    3
    4
    5
    6
    7
    8
    DOCKER_TMPDIR=/mnt/disk2/tmp /usr/local/bin/dockerd -D -g /var/lib/docker -H unix:// > /var/lib/docker-machine/docker.log 2>&1
    # or
    export DOCKER_TMPDIR=/mnt/disk2/tmp
    /usr/local/bin/dockerd -D -g /var/lib/docker -H unix:// > /var/lib/docker-machine/docker.log 2>&1


    # Docker的访问授权可以通过授权插件来扩展
    $ sudo dockerd --authorization-plugin=plugin1 --authorization-plugin=plugin2

  • 守护进程指标
    –metrics-addr 选项接受一个tcp地址来为度量API提供服务。要在localhost:9323上提供度量API,您需要指定——metrsts -addr 127.0.0.1:9323,允许您对127.0.0.1:9323/metrics的API发出请求,以接收 prometheus的度量。端口9323是与Docker度量相关联的默认端口,以避免与其他prometheus出口商和服务发生冲突。
    1
    2
    3
    4
    5
    # 如果你运行的是prometheus服务器你可以将这个地址添加到你的scrape配置中
    scrape_configs:
    - job_name: 'docker'
    static_configs:
    - targets: ['127.0.0.1:9323']


命令选项:

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
--api-cors-header="":CORS 头部域,默认不允许 CORS,要允许任意的跨域访问,可以指定为 "*"
--authorization-plugin="":载入认证的插件;
-b="":将容器挂载到一个已存在的网桥上。指定为 none 时则禁用容器的网络,与 --bip 选项互斥;
--bip="":让动态创建的 docker0 网桥采用给定的 CIDR 地址; 与 -b 选项互斥;
--cgroup-parent="":指定 cgroup 的父组,默认 fs cgroup 驱动为 /docker,systemd cgroup 驱动为 system.slice;
--cluster-store="":构成集群(如 Swarm)时,集群键值数据库服务地址;
--cluster-advertise="":构成集群时,自身的被访问地址,可以为 host:port 或 interface:port;
--cluster-store-opt="":构成集群时,键值数据库的配置选项;
--config-file="/etc/docker/daemon.json":daemon 配置文件路径;
--containerd="":containerd 文件的路径;
-D, --debug=true|false:是否使用 Debug 模式。缺省为 false
--default-gateway="":容器的 IPv4 网关地址,必须在网桥的子网段内;
--default-gateway-v6="":容器的 IPv6 网关地址;
--default-ulimit=[]:默认的 ulimit 值;
--disable-legacy-registry=true|false:是否允许访问旧版本的镜像仓库服务器;
--dns="":指定容器使用的 DNS 服务器地址;添加 DNS 服务器到容器的 /etc/resolv.conf 中,让容器用这个服务器来解析所有不在 /etc/hosts 中的主机名。
--dns-opt="":DNS 选项;
--dns-search=[]:DNS 搜索域;当设定搜索域为 .example.com 时,在搜索一个名为 host 的主机时,DNS 不仅搜索 host,还会搜索 host.example.com。
--exec-opt=[]:运行时的执行选项;
--exec-root="":容器执行状态文件的根路径,默认为 /var/run/docker;
--fixed-cidr="":限定分配 IPv4 地址范围;
--fixed-cidr-v6="":限定分配 IPv6 地址范围;
-G, --group="":分配给 unix 套接字的组,默认为 docker;
-g, --graph="":Docker 运行时的根路径,默认为 /var/lib/docker;
-H, --host=[]:指定命令对应 Docker daemon 的监听接口,可以为 unix 套接字 unix:///path/to/socket,文件句柄 fd://socketfd 或 tcp 套接字 tcp://[host[:port]],默认为 unix:///var/run/docker.sock;
--icc=true|false:是否启用容器间以及跟 daemon 所在主机的通信。默认为 true
--insecure-registry=[]:允许访问给定的非安全仓库服务;
--ip="":绑定容器端口时候的默认 IP 地址。缺省为 0.0.0.0;
--ip-forward=true|false:是否检查启动在 Docker 主机上的启用 IP 转发服务,默认开启。注意关闭该选项将不对系统转发能力进行任何检查修改;
--ip-masq=true|false:是否进行地址伪装,用于容器访问外部网络,默认开启;
--iptables=true|false:是否允许 Docker 添加 iptables 规则。缺省为 true
--ipv6=true|false:是否启用 IPv6 支持,默认关闭;
-l, --log-level="debug|info|warn|error|fatal":指定日志输出级别;
--label="[]":添加指定的键值对标注;
--log-driver="json-file|syslog|journald|gelf|fluentd|awslogs|splunk|etwlogs|gcplogs|none":指定日志后端驱动,默认为 json-file;
--log-opt=[]:日志后端的选项;
--mtu=VALUE:指定容器网络的 mtu;
-p="":指定 daemon 的 PID 文件路径。缺省为 /var/run/docker.pid;
--raw-logs:输出原始,未加色彩的日志信息;
--registry-mirror=<scheme>://<host>:指定 docker pull 时使用的注册服务器镜像地址;
-s, --storage-driver="":指定使用给定的存储后端;
--selinux-enabled=true|false:是否启用 SELinux 支持。缺省值为 false。SELinux 目前尚不支持 overlay 存储驱动;
--storage-opt=[]:驱动后端选项;
--tls=true|false:是否对 Docker daemon 启用 TLS 安全机制,默认为否;
--tlscacert=/.docker/ca.pem:TLS CA 签名的可信证书文件路径;
--tlscert=/.docker/cert.pem:TLS 可信证书文件路径;
--tlscert=/.docker/key.pem:TLS 密钥文件路径;
--tlsverify=true|false:启用 TLS 校验,默认为否;
--userland-proxy=true|false:是否使用用户态代理来实现容器间和出容器的回环通信,默认为 true
--userns-remap=default|uid:gid|user:group|user|uid:指定容器的用户命名空间,默认是创建新的 UID 和 GID 映射到容器内进程。

dockerd 简单的启动示例1脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/usr/bin/dockerd 
--config-file /etc/docker/daemon.json
-g /export/docker
-H fd://
-H tcp://0.0.0.0:2375
-H unix:///var/run/docker.sock

# Docker运行时执行选项
--containerd=/run/containerd/containerd.sock
--exec-opt native.cgroupdriver=systemd #cgroupfs

# 当容器仍在运行时,启用docker的活还原
--live-retore

# 存储引擎为devicemapper及相关参数
-s devicemapper
--storage-opt dm.loopdatasize=500G
--storage-opt dm.loopmetadatasize=4G
--storage-opt dm.basesize=100G
--storage-opt dm.fs=ext4

--insecure-registry https://index.docker.io/v1/
--biq 172.17.42.1/16
--fixed-cidr 172.17.0.0/16

示例2:脚本的Docker守护进程的一个单独的“bootstrap”实例没有网络:

1
2
3
4
5
6
7
8
$ sudo dockerd \
-H unix:///var/run/docker-bootstrap.sock \
-p /var/run/docker-bootstrap.pid \
--iptables=false \
--ip-masq=false \
--bridge=none \
--data-root=/var/lib/docker-bootstrap \
--exec-root=/var/run/docker-bootstrap

补充说明:

  • 重新加载配置行为 :在守护进程运行时,可以重新配置一些选项,而不需要重新启动进程。我们在Linux中使用SIGHUP信号来重新加载,在Windows中使用一个全局事件
    当前支持的选项列表,可以重新配置如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    # debug: it changes the daemon to debug mode when set to true.
    # cluster-store: it reloads the discovery store with the new address.
    # cluster-store-opts: it uses the new options to reload the discovery store.
    # cluster-advertise: it modifies the address advertised after reloading.
    # labels: it replaces the daemon labels with a new set of labels.
    # live-restore: Enables keeping containers alive during daemon downtime.
    # max-concurrent-downloads: it updates the max concurrent downloads for each pull.
    # max-concurrent-uploads: it updates the max concurrent uploads for each push.
    # default-runtime: it updates the runtime to be used if not is specified at container creation. It defaults to “default” which is the runtime shipped with the official docker packages.
    # runtimes: it updates the list of available OCI runtimes that can be used to run containers.
    # authorization-plugin: it specifies the authorization plugins to use.
    # allow-nondistributable-artifacts: Replaces the set of registries to which the daemon will push nondistributable artifacts with a new set of registries.
    # insecure-registries: it replaces the daemon insecure registries with a new set of insecure registries. If some existing insecure registries in daemon’s configuration are not in newly reloaded insecure resgitries, these existing ones will be removed from daemon’s config.
    # registry-mirrors: it replaces the daemon registry mirrors with a new set of registry mirrors. If some existing registry mirrors in daemon’s configuration are not in newly reloaded registry mirrors, these existing ones will be removed from daemon’s config.
    # shutdown-timeout: it replaces the daemon’s existing configuration timeout with a new timeout for shutting down all containers.
    # features: it explicitly enables or disables specific features.

0x02 Docker 配置文件

描述:本章节记录了docker的一些主要的配置文件路径及其功能,并对其文件内容进行解析备注;

Configure Path
  • /root/.docker/config.json Docker认证+启动配置(注意您使用的docker版本)
  • /etc/docker/deamon.json Docker 启动参数配置文件(主要文件CentOS)
    • /etc/docker/key.json Docker 认证相关配置(主要是存储仓库认证鉴权信息)
  • /lib/systemd/system/docker.service


1.~/.config.json
描述:该配置文件是root用户下设定的 docker configure 参数, 优先级高于/etc/docker/deamon.json;

1
2
3
4
5
6
7
8
9
10
11
$vim /root/.docker/config.json
{
"auths": {
"https://index.$docker.io/v1/": {
"auth": "********" //账号:密码
}
},
"HttpHeaders": {
"User-Agent": "$docker-Client/18.09.5 (linux)"
}
}


2./etc/docker/daemon.json
描述:Docker Engine V1.12 之后版本,用户可以自行创建 daemon.json 文件对 Docker Engine 进行配置和调整。在Linux中使用/etc/docker/daemon.json(Linux) 与在Windows中%programdata%\docker\config\daemon.json(Windows)来配置 Daemon.json, 可能在您的系统没有daemon.json目录此时你可以创建一个即可;

要点如下:

  • 该文件作为 Docker Engine 的配置管理文件, 里面几乎涵盖了所有 docker 命令行启动可以配置的参数。
  • 不管是在哪个平台以何种方式启动, Docker 默认都会来这里读取配置,使用户可以统一管理不同系统下的 docker daemon 配置(优先级低于用户配置~/.docker/config.json)。
  • 相关参数的使用说明,可以参阅 man dockerd 帮助信息,或者参阅官方文档。

Docker 官方 daemon-configuration-file参数一览:https://docs.docker.com/engine/reference/commandline/dockerd/#daemon-configuration-file

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167

// 镜像仓库鉴权信息(优先级没有用户配置文件高)
"auths": {
"https://registry.weiyigek.top/v2": {"auth": "VlJFpmQE43Sw=="},
"localhost": {"auth": "cm9vdDoxMjM0NTY="}
},

// 注册镜像-加速镜像站配置
"registry-mirrors": [
"https://xlx9erfu.mirror.aliyuncs.com",
"https://docker.mirrors.ustc.edu.cn"
],

// 私有镜像仓库的 IP 地址或者域名(harbor或者docker)不进行HTTPS认证
"insecure-registries" : [
"https://harbor.weiyigeek.top",
"192.168.10.200:5000"
],

// 私有仓库registry仓库不对外分发的产品
"allow-nondistributable-artifacts": ["192.168.10.200:5000"],

// docker 客户端 User-Agent 设置
"HttpHeaders": {
"User-Agent": "Docker-Client/19.03.5 (linux)"
},

// Docker守护进程的监听接口以及存储守护程序的pid文件
"hosts": ["/var/run/docker.sock"],
"pidfile":"/var/run/docker.pid",


// 存储位置更改
"graph": "/disk/docker",
// 更改 Docker 的默认存储位置(指定挂载磁盘分区目录限定容器使用磁盘的大小)
"data-root": "/data/docker",


// 容器运行时相关配置
"containerd": "/run/containerd/containerd.sock",
"containerd-namespace": "docker",
"containerd-plugin-namespace": "docker-plugins",


// 存储驱动采用 overlay2
"storage-driver": "overlay2",
//存储驱动程序选项
"storage-opts": [
"overlay2.override_kernel_check=true",
"overlay2.size=1G"
],

// 存储驱动采用 devicemapper 时候
"storage-driver": "devicemapper",
"storage-opts": [
"dm.thinpooldev=/dev/mapper/thin-pool", #指定要用于瘦池的自定义块存储设备
"dm.use_deferred_deletion=true",
"dm.use_deferred_removal=true"
],

// 容器日志文件分割每个文件最大500MB
"log-driver":"json-file",
"log-opts": {
"max-size":"500m",
"max-file":"3",
"labels": "production_status",
"env": "os,customer"
},



// 容器中ulimits限制(基于宿主机进行ulimits来设置)
"default-ulimits": {
"nofile": {
"Name": "nofile",
"Hard": 1024000,
"Soft": 1024000
},
"nproc": {
"Name": "nproc",
"Hard": 1024000,
"Soft": 1024000
},
"core": {
"Name": "core",
"Hard": -1,
"Soft": -1
}
},

// 标志指定的选项配置运行时。把docker更改驱动为systemtd或者cgroupfs
"exec-opts": ["native.cgroupdriver=cgroupfs"],
// Docker运行时执行选项,以下是一个例子将通过配置2个运行时
"default-runtime": "runc",
"runtimes": {
"runc": {
"path": "runc"
},
"custom": {
"path": "/usr/local/bin/my-runc-replacement",
"runtimeArgs": [
"--debug"
]
}
},


//永久绑定到某个固定的 IP 地址
"ip": "0.0.0.0",
//禁止访问所有端口(来关闭容器之间的通信)
"icc": false,
//启用iptables规则添加(默认为true)
"iptables": true,
//设置默认桥接到新建立的桥接网卡(将容器附加到网桥)
"bridge": "bridge0",


// 容器中使用的DNS解析地址相关配置,设定容器DNS额外扩展、容器的搜索域
"dns" :[
"114.114.114.114",
"8.8.8.8"
],
"dns-opts": [],
"dns-search": [],

//此时 dockerd 会在日志中输入更多信息供分析
"debug": true,

//注意:在一台主机上运行多个守护进程被认为是“试验性的”。
"experimental": "enabled",

//为守护进程设置oom_score_adj(默认值为-500)
"oom-score-adjust": -1000,

//设置每次拉取的最大并发下载(默认为3)
"max-concurrent-downloads": 3,

//设置每个推送的最大并发上传数(默认为5)
"max-concurrent-uploads": 5,

//设置该选项保证 docker daemon重启时容器不重启(需要设置 docker.server 只杀死docker进程,而不是cgroup中的所有进程)
"live-restore": true,

// 支持IPV6
"ipv6": true,
"fixed-cidr-v6": "2001:db8:1::/64",

// 允许您设置默认的 cgroup 父级 用于容器。 如果未设置此选项,则默认为 /docker为了 fs cgroup 驱动程序和 system.slice用于 systemd cgroup 驱动程序。
"cgroup-parent": "/custom",

// 设置不使用用户态代理来实现容器间和出容器的回环通信,
"userland-proxy": false,
"userland-proxy-path": "/usr/libexec/docker-proxy",

// 设置不为新容器设置新权限
"no-new-privileges": false,

// 启用 SELinux 支持, SELinux 目前尚不支持 overlay 存储驱动, redhat 与 centOS支持设置,ubuntu需要自行安装;
"selinux-enabled": true,

// 配置dockerd API的TLS认证
"tls": true, // 默认 false, 启动TLS认证开关
"tlscacert": "/etc/docker/ssl/ca.pem", // 默认 ~/.docker/ca.pem,通过CA认证过的的certificate文件路径
"tlscert": "/etc/docker/ssl/cert.pem", // 默认 ~/.docker/cert.pem ,TLS的certificate文件路径
"tlskey": "/etc/docker/ssl/key.pem", // 默认~/.docker/key.pem,TLS的key文件路径
"tlsverify": true, // 默认false,使用TLS并做后台进程与客户端通讯的验证
}


3.docker.service

  • 基于Docker-CE17.12版本以及之后, build 481bc77156
    docker服务配置文件路径:/lib/systemd/system/docker.service
    1
    ExecStart=/usr/bin/dockerd --registry-mirror=https://xlx9erfu.mirror.aliyuncs.com

服务重启

1
2
3
4
5
6
#修改配置文件后进行平滑重启
$sudo kill -SIGHUP $(pidof dockerd)

#通用启动
$sudo systemctl daemon-reload #重载守护
$sudo systemctl restart docker

补充说明:

1
2
3
4
5
#Docker Server/Client:
Version: 1.12.6
API version: 1.24
- /etc/systemd/system/docker.service.requires/flanneld.service
- /etc/docker/

Q:如何配置Docker Deamon能被Docker Client远程链接?
答: 我们需要在docker.service配置文件中进行更改dockerd的启动参数中添加-H tcp://0.0.0.0:2375

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# 修改启动参数
nano /usr/lib/systemd/system/docker.service
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock -H tcp://0.0.0.0:2375

# 重载守护进程与重启docker
systemctl daemon-reload
systemctl restart docker

# 查看监听情况
netstat -tlnp
# Active Internet connections (only servers)
# Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
# tcp6 0 0 :::2375 :::* LISTEN 11389/dockerd

# 简单验证远程访问
curl http://127.0.0.1:2375/containers/json | jq
[
{
"Id": "25d2d645bfc9e6530039d6aac890f69dd9af33f8f966adc2d7287b74964678e3",
"Names": [
"/test1"
],
"Image": "test1",
"ImageID": "sha256:5ec0e2b89f7aadb6178c17b3db73aba2e209f9556a436562de7f32b077b776bd",
"Command": "top -b -d 2",
"Created": 1592451272,
"Ports": [],
"Labels": {
"Author": "WeiyiGeek",
"Description": "Test Dockerfile"
},
"State": "running",
"Status": "Up 35 minutes",
"HostConfig": {
"NetworkMode": "default"
},
"NetworkSettings": {
"Networks": {
"bridge": {
"IPAMConfig": null,
"Links": null,
"Aliases": null,
"NetworkID": "257f6a8500710d76efba6a1c9be8c0f10b4308afb481baf1e9ba77cf98f596bd",
"EndpointID": "ac4518815359da7b8182167dfeeec728c0ea51accd1736719005f1596797e944",
"Gateway": "172.17.0.1",
"IPAddress": "172.17.0.2",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": "02:42:ac:11:00:02",
"DriverOpts": null
}
}
},
"Mounts": []
}
]

WeiyiGeek.Dockerd-TCP

WeiyiGeek.Dockerd-TCP


容器 HTTP/HTTPS proxy 设置
Docker 守护程序在其启动环境中使用 HTTP_PROXY、HTTPS_PROXY 和 NO_PROXY 环境变量来配置 HTTP 或 HTTPS 代理行为。 您无法使用 daemon.json 文件配置这些环境变量。

如果您在 HTTP 或 HTTPS 代理服务器后面,例如在公司设置中,则需要在 Docker systemd 服务文件中添加此配置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 1.为 docker 服务创建一个 systemd 插入目录:
sudo mkdir -p /etc/systemd/system/docker.service.d

# 2.创建一个名为 /etc/systemd/system/docker.service.d/http-proxy.conf 的文件,其中添加了 HTTP_PROXY 环境变量:
[Service]
Environment="HTTP_PROXY=http://proxy.example.com:80"
Environment="HTTPS_PROXY=https://proxy.example.com:443"
# 如果您有内部 Docker 注册表需要在不使用代理的情况下联系,您可以通过 NO_PROXY 环境变量指定它们。
Environment="NO_PROXY=localhost,127.0.0.1,docker-registry.example.com,.corp"

# 3.Flush changes and restart Docker
sudo systemctl daemon-reload
sudo systemctl restart docker

# 4.验证配置是否已加载并与您所做的更改相匹配,例如: