これは何?
コンテナイメージの実体を見てみる話です。
概念と使い方はわかるけど、「コンテナイメージっていったい何者なんだろう」と思っていたので中を見てみました。
認識違いがあったら教えて頂けると非常に嬉しいです。
環境とか
- Docker version 18.09.1, build 4c52b90
題材として適当なコンテナイメージを用意する
$ docker pull node/8.15.0-alpine
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
node 8.15.0-alpine 5c0c5c94503f 4 weeks ago 66.3MB
コンテナイメージをtarにする
$ docker save node:8.15.0-alpine > node.tar
$ ls -ltr
-rwxrwxrwx 1 xxx xxx 69734400 Jan 26 14:37 node.tar
tarを解凍してみる
$ tar -xvf node.tar
5491dce778832e33c284cd8185100e76d6daa18f8cbc32458c706776894127fc/
5491dce778832e33c284cd8185100e76d6daa18f8cbc32458c706776894127fc/VERSION
5491dce778832e33c284cd8185100e76d6daa18f8cbc32458c706776894127fc/json
5491dce778832e33c284cd8185100e76d6daa18f8cbc32458c706776894127fc/layer.tar
5c0c5c94503f7310f50e73e8bcaa584eab5e5256ac34b9745589b64bb8fa09bb.json
84f3cb797bec406c8818fd5b1deaad92efd6776bb451c974175373102cfe72ac/
84f3cb797bec406c8818fd5b1deaad92efd6776bb451c974175373102cfe72ac/VERSION
84f3cb797bec406c8818fd5b1deaad92efd6776bb451c974175373102cfe72ac/json
84f3cb797bec406c8818fd5b1deaad92efd6776bb451c974175373102cfe72ac/layer.tar
eaabbaf2e7fe2603c758103f3f9397a82ea9c1f39e421735753afef370323a1f/
eaabbaf2e7fe2603c758103f3f9397a82ea9c1f39e421735753afef370323a1f/VERSION
eaabbaf2e7fe2603c758103f3f9397a82ea9c1f39e421735753afef370323a1f/json
eaabbaf2e7fe2603c758103f3f9397a82ea9c1f39e421735753afef370323a1f/layer.tar
manifest.json
repositories
これがコンテナイメージの実体のようです。
上でdocker images
した時に表示されたイメージIDと同じものがありますね。
種類を分けるとこんな感じでしょうか。
-
buildされたコンテナイメージの情報
- 5c0c5c94503f7310f50e73e8bcaa584eab5e5256ac34b9745589b64bb8fa09bb.json
-
レイヤーの情報
- 5491dce778832e33c284cd8185100e76d6daa18f8cbc32458c706776894127fc
- 84f3cb797bec406c8818fd5b1deaad92efd6776bb451c974175373102cfe72ac
- eaabbaf2e7fe2603c758103f3f9397a82ea9c1f39e421735753afef370323a1f
-
メタ情報
- manifest.json
- repositories
buildされたコンテナイメージの情報
めちゃくちゃ長いです。たくさん情報持ってるんですね。
レイヤー名とかも確認できますね。
{
"architecture":"amd64",
"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",
"NODE_VERSION=8.15.0",
"YARN_VERSION=1.12.3"
],
"Cmd":["node"],
"ArgsEscaped":true,
"Image":"sha256:e615719cc465120b3f6a8a76ad4a39464a3d65be67f99c0bd90b4a847a00d651",
"Volumes":null,
"WorkingDir":"",
"Entrypoint":null,
"OnBuild":null,
"Labels":null
},
"container":"d29ae8e85f8f1b00dbc79c2160e202969205c1429dcc31553b18585f27248ff7",
"container_config":{
"Hostname":"d29ae8e85f8f",
"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",
"NODE_VERSION=8.15.0",
"YARN_VERSION=1.12.3"
],
"Cmd":[
"/bin/sh",
"-c",
"#(nop) ",
"CMD [\"node\"]"
],
"ArgsEscaped":true,
"Image":"sha256:e615719cc465120b3f6a8a76ad4a39464a3d65be67f99c0bd90b4a847a00d651",
"Volumes":null,
"WorkingDir":"",
"Entrypoint":null,
"OnBuild":null,
"Labels":{}
},
"created":"2018-12-27T01:40:51.334534029Z",
"docker_version":"18.06.1-ce",
"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":"2018-12-27T01:22:27.078806811Z",
"created_by":"/bin/sh -c #(nop) ENV NODE_VERSION=8.15.0",
"empty_layer":true
},
{
"created":"2018-12-27T01:40:46.64036283Z",
"created_by":"/bin/sh -c addgroup -g 1000 node \u0026\u0026 adduser -u 1000 -G node -s /bin/sh -D node \u0026\u0026 apk add --no-cache libstdc++ \u0026\u0026 apk add --no-cache --virtual .build-deps binutils-gold curl g++
gcc gnupg libgcc linux-headers make python \u0026\u0026 for key in 94AE36675C464D64BAFA68DD7434390BDBE9B9C5 FD3A5288F042B6850C66B31F09FE44734EB7990E 71DCFD284A79C3B38668286BC97EC7A07EDE3FC1 DD8F2338BAE7501E3DD5AC78C273792F7D83545D C4F0DFFF4E8C1A8236409D08E73BC641CC11F4C8 B9AE9905FFD7803F25714661B63B535A4C206CA9 77984A986EBC2AA786BC0F66B01FBB92821C587A 8FCCA13FEF1D0C2E91008E09770F7A9A5AE15600 4ED778F539E3634C779C87C6D7062848A1AB005C A48C2BEE680E841632CD4E44F07496B3EB3C1762 B9E2F5981AA6E0CD28160D9FF13993A75599653C ; do gpg --batch --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys \"$key\" || gpg --batch --keyserver hkp://ipv4.pool.sks-keyservers.net --recv-keys \"$key\" || gpg --batch --keyserver hkp://pgp.mit.edu:80 --recv-keys \"$key\" ; done \u0026\u0026 curl -fsSLO --compressed \"https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION.tar.xz\" \u0026\u0026 curl -fsSLO --compressed \"https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc\" \u0026\u0026 gpg --batch --decrypt --output SHASUMS256.txt SHASUMS256.txt.asc \u0026\u0026 grep \" node-v$NODE_VERSION.tar.xz\\$\" SHASUMS256.txt | sha256sum -c - \u0026\u0026 tar -xf \"node-v$NODE_VERSION.tar.xz\" \u0026\u0026 cd \"node-v$NODE_VERSION\" \u0026\u0026 ./configure \u0026\u0026 make -j$(getconf _NPROCESSORS_ONLN) \u0026\u0026 make install \u0026\u0026 apk del .build-deps \u0026\u0026 cd .. \u0026\u0026 rm -Rf \"node-v$NODE_VERSION\" \u0026\u0026 rm \"node-v$NODE_VERSION.tar.xz\" SHASUMS256.txt.asc SHASUMS256.txt"
},
{
"created":"2018-12-27T01:40:46.898832094Z",
"created_by":"/bin/sh -c #(nop) ENV YARN_VERSION=1.12.3",
"empty_layer":true
},
{
"created":"2018-12-27T01:40:51.183517333Z",
"created_by":"/bin/sh -c apk add --no-cache --virtual .build-deps-yarn curl gnupg tar \u0026\u0026 for key in 6A010C5166006599AA17F08146C2130DFD2497F5 ; do gpg --batch --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys \"$key\" || gpg --batch --keyserver hkp://ipv4.pool.sks-keyservers.net --recv-keys \"$key\" || gpg --batch --keyserver hkp://pgp.mit.edu:80 --recv-keys \"$key\" ; done \u0026\u0026 curl -fsSLO --compressed \"https://yarnpkg.com/downloads/$YARN_VERSION/yarn-v$YARN_VERSION.tar.gz\" \u0026\u0026 curl -fsSLO --compressed \"https://yarnpkg.com/downloads/$YARN_VERSION/yarn-v$YARN_VERSION.tar.gz.asc\" \u0026\u0026 gpg --batch --verify yarn-v$YARN_VERSION.tar.gz.asc yarn-v$YARN_VERSION.tar.gz \u0026\u0026 mkdir -p /opt \u0026\u0026 tar -xzf yarn-v$YARN_VERSION.tar.gz -C /opt/ \u0026\u0026 ln -s /opt/yarn-v$YARN_VERSION/bin/yarn /usr/local/bin/yarn
\u0026\u0026 ln -s /opt/yarn-v$YARN_VERSION/bin/yarnpkg /usr/local/bin/yarnpkg \u0026\u0026 rm yarn-v$YARN_VERSION.tar.gz.asc yarn-v$YARN_VERSION.tar.gz \u0026\u0026 apk del .build-deps-yarn"
},
{
"created":"2018-12-27T01:40:51.334534029Z",
"created_by":"/bin/sh -c #(nop) CMD [\"node\"]",
"empty_layer":true
}
],
"os":"linux",
"rootfs":{
"type":"layers",
"diff_ids":[
"sha256:7bff100f35cb359a368537bb07829b055fe8e0b1cb01085a3a628ae9c187c7b8",
"sha256:734b6a5256135b58db8ecef74e164d2682722f2b208467bf484392ee87509645",
"sha256:1f3213370fe13af42ae5e55610b7e64d7c995edf4f95ebdfa4671456812c38dd"
]
}
}
レイヤーの情報
レイヤーと思われるtarの中身を見てみます。
- 5491dce778832e33c284cd8185100e76d6daa18f8cbc32458c706776894127fc/layer.tar
- 84f3cb797bec406c8818fd5b1deaad92efd6776bb451c974175373102cfe72ac/layer.tar
- eaabbaf2e7fe2603c758103f3f9397a82ea9c1f39e421735753afef370323a1f/layer.tar
5491dce778832e33c284cd8185100e76d6daa18f8cbc32458c706776894127fc/layer.tar
$ tar -xvf 5491dce778832e33c284cd8185100e76d6daa18f8cbc32458c706776894127fc/layer.tar
bin/
bin/arch
bin/ash
bin/base64
bin/bbconfig
~~~
etc/alpine-release
etc/apk/
etc/apk/arch
etc/apk/keys/
etc/apk/keys/alpine-devel@lists.alpinelinux.org-4a6a0840.rsa.pub
~~~
var/run
var/spool/
var/spool/cron/
var/spool/cron/crontabs
var/tmp/
果てしないものが展開されましたので中略しています。
途中でalpine
という文言もチラホラ見えたので、これはOS?
eaabbaf2e7fe2603c758103f3f9397a82ea9c1f39e421735753afef370323a1f/layer.tar
$ tar -xvf eaabbaf2e7fe2603c758103f3f9397a82ea9c1f39e421735753afef370323a1f/layer.tar
etc/
etc/apk/
etc/apk/protected_paths.d/
etc/apk/world
etc/group
~~~
usr/local/lib/node_modules/npm/node_modules/stringify-package/index.js
usr/local/lib/node_modules/npm/node_modules/stringify-package/package.json
usr/local/lib/node_modules/npm/node_modules/strip-ansi/
usr/local/lib/node_modules/npm/node_modules/strip-ansi/index.js
usr/local/lib/node_modules/npm/node_modules/strip-ansi/license
~~~
usr/sbin/
usr/share/
var/
var/cache/
var/cache/misc/
これも果てしないので中略。
ただ、思いっきりnode_modules
を展開していたのでわかりやすかったです。nodeのレイヤーですね。
メタ情報
manifest.json
とrepositories
には、メタ情報が入ってるようです。
[
{
"Config":"5c0c5c94503f7310f50e73e8bcaa584eab5e5256ac34b9745589b64bb8fa09bb.json",
"RepoTags":["node:8.15.0-alpine"],
"Layers":[
"5491dce778832e33c284cd8185100e76d6daa18f8cbc32458c706776894127fc/layer.tar",
"eaabbaf2e7fe2603c758103f3f9397a82ea9c1f39e421735753afef370323a1f/layer.tar",
"84f3cb797bec406c8818fd5b1deaad92efd6776bb451c974175373102cfe72ac/layer.tar"
]
}
]
{
"node":{
"8.15.0-alpine":"84f3cb797bec406c8818fd5b1deaad92efd6776bb451c974175373102cfe72ac"
}
}
実際のDockerfileとの対応
開発者の皆様がDockerfileを上げてくれているので、node:8.15.0-alpine
のDockerfileを見てみます。
- [nodejs/docker-node - docker-node/8/alpine/Dockerfile]
(https://github.com/nodejs/docker-node/blob/86b9618674b01fc5549f83696a90d5bc21f38af0/8/alpine/Dockerfile)
このDockerfileは以下の6つの命令から成ります。
- FROM
- ENV
- RUN
- ENV
- RUN
- CMD
Dockerfileをbuildする際にレイヤーを新たに作り出す命令というのはRUN
、ADD
、COPY
の3つだけですから、tarを解凍して確認できた3つのレイヤーは
- FROM(alpineのベースイメージのレイヤー)
- RUN
- RUN
と対応していると思われます。(たぶん)
まとめ
- コンテナイメージをtarにすれば人間が中身を確認できる
- 内容は大きく3種類
- コンテナイメージ情報json
- 各レイヤーのリソースや設定情報もろもろ
- メタ情報json
- レイヤーの中には、当たり前だが各プロダクトを構成するファイルがちゃんと詰め込まれている
感想
知らなくてもコンテナ使う上では困らないと思いますが、なんだかよくわからないまま使うのはイヤなので見てみました。
レイヤー情報などわかりやすく格納されていて、かなり納得できました。
同時に、あらためてコンテナおよびDockerの便利さを再確認できました。