Untar Docker Image

docker中容器的运行离不开image,一个image中都存放有哪些东西呢?这些东西又是以何种方式组织在一起的呢?今天让我们来撕开这层面纱,看看image到底是什么样。

导出tar包

首先我们使用docker save,将一个docker image保存成tar包,我们以nginx官方镜像为例

1
2
3
4
5
6
7
8
9
10
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx 1.11.3 4efb2fcdb1ab 15 months ago 183MB

$ docker save nginx:1.11.3 -o ./nginx.tar
$ tar -xvf ./nginx.tar -C ./nginx
...
$ ls -l ./
drwxrwxr-x 5 zhoub zhoub 4096 11月 28 16:55 nginx
-rw------- 1 zhoub zhoub 191400960 11月 28 16:52 nginx.tar

走近nginx目录

现在nginx.tar已经被解开了,接下来就要进入nginx目录一探究竟了。首先tree一下nginx目录,看看都有什么文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$ tree -L 2 ./nginx
./nginx
├── 01d69dac180439f10b5f2f06b71fa19c9f89fb85cb5fd543dba9bb2ca9b620ef
│   ├── json
│   ├── layer
│   ├── layer.tar
│   └── VERSION
├── 4efb2fcdb1ab05fb03c9435234343c1cc65289eeb016be86193e88d3a5d84f6b.json
├── 80cdba09b091f72276c031bd64488c3da5f82022812bc995f0859abe0855a872
│   ├── json
│   ├── layer
│   ├── layer.tar
│   └── VERSION
├── c575e5db41b92dbf9e1814b8ea9cf989ef650e37cfcef12fa402e630db6a581f
│   ├── json
│   ├── layer
│   ├── layer.tar
│   └── VERSION
├── manifest.json
└── repositories

从目录结构上来看,有三个总体的描述文件repositoriesmanifest.json4efb2fcdb1ab05fb03c9435234343c1cc65289eeb016be86193e88d3a5d84f6b.json,还有三个目录,这三个目录的结构基本相同都由jsonlayerVERSION组成。

通过这些名字,我们先简单猜测一下,image由很多layer组成,上层layer基于下层layer构建而成,下层layer基于基层layer构成。到底image是不是按我们的猜测构成的呢,让我们来看一看总体描述的三个文件。

manifest.json:

1
{"Config":"4efb2fcdb1ab05fb03c9435234343c1cc65289eeb016be86193e88d3a5d84f6b.json","RepoTags":["nginx:1.11.3"],"Layers":["80cdba09b091f72276c031bd64488c3da5f82022812bc995f0859abe0855a872/layer.tar","c575e5db41b92dbf9e1814b8ea9cf989ef650e37cfcef12fa402e630db6a581f/layer.tar","01d69dac180439f10b5f2f06b71fa19c9f89fb85cb5fd543dba9bb2ca9b620ef/layer.tar"]}]

通过manifest.json文件我们可以看出,nginx:1.11.3这个image的配置文件是4efb2fcdb1ab05fb03c9435234343c1cc65289eeb016be86193e88d3a5d84f6b.json,layer一共有三个分别是80cdba09b091f72276c031bd64488c3da5f82022812bc995f0859abe0855a872/layer.tarc575e5db41b92dbf9e1814b8ea9cf989ef650e37cfcef12fa402e630db6a581f/layer.tar01d69dac180439f10b5f2f06b71fa19c9f89fb85cb5fd543dba9bb2ca9b620ef/layer.tar

image的配置文件中都对image进行了哪些配置呢?让我们来看一看
4efb2fcdb1ab05fb03c9435234343c1cc65289eeb016be86193e88d3a5d84f6b.json

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
{
"architecture":"amd64",
"author":"NGINX Docker Maintainers \"docker-maint@nginx.com\"",
"config":{
"Hostname":"2da0903ff372",
"Domainname":"",
"User":"",
"AttachStdin":false,
"AttachStdout":false,
"AttachStderr":false,
"ExposedPorts":{"443/tcp":{},"80/tcp":{}},
"Tty":false,
"OpenStdin":false,
"StdinOnce":false,
"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","NGINX_VERSION=1.11.3-1~jessie"],
"Cmd":["nginx","-g","daemon off;"],
"ArgsEscaped":true,
"Image":"sha256:27ca9d70a764c6955e354f5fdc706b03ed47601213ac9fc638a8943fcc7680f8",
"Volumes":null,
"WorkingDir":"",
"Entrypoint":null,
"OnBuild":[],
"Labels":{}
},
"container":"25ab8b143580751e438246801e7ba720f6f84ee9faca3522532e7fbe1ed7021f",
"container_config":{
"Hostname":"2da0903ff372",
"Domainname":"",
"User":"",
"AttachStdin":false,
"AttachStdout":false,
"AttachStderr":false,
"ExposedPorts":{"443/tcp":{},"80/tcp":{}},
"Tty":false,
"OpenStdin":false,
"StdinOnce":false,
"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","NGINX_VERSION=1.11.3-1~jessie"],
"Cmd":["/bin/sh","-c","#(nop) CMD [\"nginx\" \"-g\" \"daemon off;\"]"],
"ArgsEscaped":true,
"Image":"sha256:27ca9d70a764c6955e354f5fdc706b03ed47601213ac9fc638a8943fcc7680f8",
"Volumes":null,
"WorkingDir":"",
"Entrypoint":null,
"OnBuild":[],
"Labels":{}
},
"created":"2016-08-23T18:51:23.709520142Z",
"docker_version":"1.10.3",
"history":[
{"created":"2016-07-28T17:47:54.990622865Z","created_by":"/bin/sh -c #(nop) ADD file:0e0565652aa852f62033d99f84892216020d30f64521ded5e72d4940bc4c9697 in /"},
{"created":"2016-07-28T17:47:55.809686499Z","created_by":"/bin/sh -c #(nop) CMD [\"/bin/bash\"]","empty_layer":true},
{"created":"2016-08-23T18:49:31.945397165Z","author":"NGINX Docker Maintainers \"docker-maint@nginx.com\"","created_by":"/bin/sh -c #(nop) MAINTAINER NGINX Docker Maintainers \"docker-maint@nginx.com\"","empty_layer":true},
{"created":"2016-08-23T18:49:33.057946615Z","author":"NGINX Docker Maintainers \"docker-maint@nginx.com\"","created_by":"/bin/sh -c #(nop) ENV NGINX_VERSION=1.11.3-1~jessie","empty_layer":true},
{"created":"2016-08-23T18:51:19.236946594Z","author":"NGINX Docker Maintainers \"docker-maint@nginx.com\"","created_by":"/bin/sh -c apt-key adv --keyserver hkp://pgp.mit.edu:80 --recv-keys 573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62 \t\u0026\u0026 echo \"deb http://nginx.org/packages/mainline/debian/ jessie nginx\" \u003e\u003e /etc/apt/sources.list \t\u0026\u0026 apt-get update \t\u0026\u0026 apt-get install --no-install-recommends --no-install-suggests -y \t\t\t\t\t\tca-certificates \t\t\t\t\t\tnginx=${NGINX_VERSION} \t\t\t\t\t\tnginx-module-xslt \t\t\t\t\t\tnginx-module-geoip \t\t\t\t\t\tnginx-module-image-filter \t\t\t\t\t\tnginx-module-perl \t\t\t\t\t\tnginx-module-njs \t\t\t\t\t\tgettext-base \t\u0026\u0026 rm -rf /var/lib/apt/lists/*"},
{"created":"2016-08-23T18:51:21.574094006Z","author":"NGINX Docker Maintainers \"docker-maint@nginx.com\"","created_by":"/bin/sh -c ln -sf /dev/stdout /var/log/nginx/access.log \t\u0026\u0026 ln -sf /dev/stderr /var/log/nginx/error.log"},
{"created":"2016-08-23T18:51:22.632895061Z","author":"NGINX Docker Maintainers \"docker-maint@nginx.com\"","created_by":"/bin/sh -c #(nop) EXPOSE 443/tcp 80/tcp","empty_layer":true},
{"created":"2016-08-23T18:51:23.709520142Z","author":"NGINX Docker Maintainers \"docker-maint@nginx.com\"","created_by":"/bin/sh -c #(nop) CMD [\"nginx\" \"-g\" \"daemon off;\"]","empty_layer":true}
],
"os":"linux",
"rootfs":{
"type":"layers",
"diff_ids":[
"sha256:2f71b45e4e254ddceb187b1467f5471f0e14d7124ac2dd7fdd7ddbc76e13f0e5",
"sha256:d7953e5e5bba5c637ce6eb81a4528a777c4265386cb12e9bff8124ef97538746",
"sha256:69ecf026ff94793ab573754612e6fa40b28331a47c3584de0b32d39a88959c37"
]
}
}

看完这个配置文件我惊到了,如此庞大的一个配置文件告诉我们dockerfile能配的我能配,dockerfile不能配的我也能配。。。这个配置文件具体就不分析了。。。

刚刚看完那个累心的配置文件,接下来让我们来说说这三个layer,哪一个layer是入口呢?

repositories:

1
{"nginx":{"1.11.3":"01d69dac180439f10b5f2f06b71fa19c9f89fb85cb5fd543dba9bb2ca9b620ef"}}

我们通过查看repositories可以看出1.11.3这个版本的layer入口是01d69dac180439f10b5f2f06b71fa19c9f89fb85cb5fd543dba9bb2ca9b620ef。那么三个layer之间的依赖关系怎样呢?让我们进入各个layer一探究竟吧。

layer 01d69dac180439f10b5f2f06b71fa19c9f89fb85cb5fd543dba9bb2ca9b620ef

该layer中的json文件描述的本layer的配置信息

json:

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
{
"id":"01d69dac180439f10b5f2f06b71fa19c9f89fb85cb5fd543dba9bb2ca9b620ef",
"parent":"c575e5db41b92dbf9e1814b8ea9cf989ef650e37cfcef12fa402e630db6a581f",
"created":"2016-08-23T18:51:23.709520142Z",
"container":"25ab8b143580751e438246801e7ba720f6f84ee9faca3522532e7fbe1ed7021f",
"container_config":{
"Hostname":"2da0903ff372",
"Domainname":"",
"User":"",
"AttachStdin":false,
"AttachStdout":false,
"AttachStderr":false,
"ExposedPorts":{"443/tcp":{},"80/tcp":{}},
"Tty":false,
"OpenStdin":false,
"StdinOnce":false,
"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","NGINX_VERSION=1.11.3-1~jessie"],
"Cmd":["/bin/sh","-c","#(nop) CMD [\"nginx\" \"-g\" \"daemon off;\"]"],
"ArgsEscaped":true,
"Image":"sha256:27ca9d70a764c6955e354f5fdc706b03ed47601213ac9fc638a8943fcc7680f8",
"Volumes":null,
"WorkingDir":"",
"Entrypoint":null,
"OnBuild":[],
"Labels":{}
},
"docker_version":"1.10.3",
"author":"NGINX Docker Maintainers \"docker-maint@nginx.com\"",
"config":{
"Hostname":"2da0903ff372",
"Domainname":"",
"User":"",
"AttachStdin":false,
"AttachStdout":false,
"AttachStderr":false,
"ExposedPorts":{"443/tcp":{},"80/tcp":{}},
"Tty":false,
"OpenStdin":false,
"StdinOnce":false,
"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","NGINX_VERSION=1.11.3-1~jessie"],
"Cmd":["nginx","-g","daemon off;"],
"ArgsEscaped":true,
"Image":"sha256:27ca9d70a764c6955e354f5fdc706b03ed47601213ac9fc638a8943fcc7680f8",
"Volumes":null,
"WorkingDir":"",
"Entrypoint":null,
"OnBuild":[],
"Labels":{}
},
"architecture":"amd64",
"os":"linux"
}

我们可以看到parent字段指出了当前layer的父layer是c575e5db41b92dbf9e1814b8ea9cf989ef650e37cfcef12fa402e630db6a581f
当前layer中数据内容请见layer.tar

layer.tar:

1
2
3
4
5
6
7
$ tree ./layer
./layer
└── var
└── log
└── nginx
├── access.log -> /dev/stdout
└── error.log -> /dev/stderr

layer c575e5db41b92dbf9e1814b8ea9cf989ef650e37cfcef12fa402e630db6a581f

该layer中的json文件描述的本layer的配置信息

json:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
"id":"c575e5db41b92dbf9e1814b8ea9cf989ef650e37cfcef12fa402e630db6a581f",
"parent":"80cdba09b091f72276c031bd64488c3da5f82022812bc995f0859abe0855a872",
"created":"1970-01-01T08:00:00+08:00",
"container_config":{
"Hostname":"",
"Domainname":"",
"User":"",
"AttachStdin":false,
"AttachStdout":false,
"AttachStderr":false,
"Tty":false,
"OpenStdin":false,
"StdinOnce":false,
"Env":null,
"Cmd":null,
"Image":"",
"Volumes":null,
"WorkingDir":"",
"Entrypoint":null,
"OnBuild":null,
"Labels":null
}
}

该layer中的父layer是80cdba09b091f72276c031bd64488c3da5f82022812bc995f0859abe0855a872
当前layer中的数据内容请见layer.tar

layer.tar:

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
$ tree -L 2 ./layer
./layer
├── etc
│   ├── alternatives
│   ├── apt
│   ├── ca-certificates
│   ├── ca-certificates.conf
│   ├── default
│   ├── fonts
│   ├── group
│   ├── group-
│   ├── gshadow
│   ├── gshadow-
│   ├── init.d
│   ├── ld.so.cache
│   ├── logrotate.d
│   ├── nginx
│   ├── passwd
│   ├── passwd-
│   ├── perl
│   ├── rc0.d
│   ├── rc1.d
│   ├── rc2.d
│   ├── rc3.d
│   ├── rc4.d
│   ├── rc5.d
│   ├── rc6.d
│   ├── shadow
│   ├── shadow-
│   ├── ssl
│   ├── subgid
│   ├── subgid-
│   ├── subuid
│   ├── subuid-
│   └── ucf.conf
├── lib
│   └── x86_64-linux-gnu
├── tmp
├── usr
│   ├── bin
│   ├── lib
│   ├── local
│   ├── sbin
│   └── share
└── var
├── cache
├── lib
└── log

layer 80cdba09b091f72276c031bd64488c3da5f82022812bc995f0859abe0855a872

该layer中的json文件描述的本layer的配置信息
json:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{
"id":"80cdba09b091f72276c031bd64488c3da5f82022812bc995f0859abe0855a872",
"created":"1970-01-01T08:00:00+08:00",
"container_config":{
"Hostname":"",
"Domainname":"",
"User":"",
"AttachStdin":false,
"AttachStdout":false,
"AttachStderr":false,
"Tty":false,
"OpenStdin":false,
"StdinOnce":false,
"Env":null,
"Cmd":null,
"Image":"",
"Volumes":null,
"WorkingDir":"",
"Entrypoint":null,
"OnBuild":null,
"Labels":null
}
}

从配置中可以看出,没有parent字段了。说明这个layer应该就是base layer了。(我十分好奇nginx镜像是用哪个基础镜像做出来的?)
当前layer中的数据内容请见layer.tar
layer.tar:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$ tree -L 1 ./layer
./layer
├── bin
├── boot
├── dev
├── etc
├── home
├── lib
├── lib64
├── media
├── mnt
├── opt
├── proc
├── root
├── run
├── sbin
├── srv
├── sys
├── tmp
├── usr
└── var

1
2
$ cat ./layer/etc/debian_version
8.5

噢,原来nginx镜像是基于debian 8.5做出来的。