【あえての深堀調査】Dockerイメージとは何か?調べてイメージを作ってみる
【あえての深堀調査】Dockerイメージとは何か?調べてイメージを作ってみる
はじめに
三菱UFJインフォメーションテクノロジーの谷川と申します。
社内アプリケーションのインフラ保守・開発やCI/CDフローの改善などに従事しています。
また最近はAIを活用したツールの開発にも一部携わっています。
本記事は社内のアドベントカレンダーで投稿した内容です。弊社内で実施されたアドベントカレンダーの記事がどんなものだったかをご紹介できたらと思います!
(社内でのアドベントカレンダー企画については、下記の記事をご覧ください)
過去記事 : アドベントカレンダー企画を会社内でやってみての振り返り
概要
あなたはDockerイメージを作ることができますか?
Dockerfileを書いて、docker build
でビルドできるので、普段コンテナを扱っている人なら難しくはないと思います。
ですが、それは恐らくFROM
にベースイメージを指定して、そこに何かを足すことがほとんどではないでしょうか?
本当の意味で「Dockerイメージを作れるよ!」と言いたかったので、Dockerイメージの実態を調べてゼロからDockerイメージを作ってみよう、という試みです。
この記事の概要
この記事ではDockerの内部構造を解き明かすために、dockerコマンドを使わずにイメージを作ります。
セキュリティを高めるためにログインシェルが不要ならDistrolessを使うことになりますし、余計なものが何もいらなければscratch(空のイメージ)を利用します。
Dockerイメージとは
Dockerイメージを作るにあたり、Dockerイメージを構成する要素について説明します。
Dockerfile
Dockerfileとは、Dockerイメージのビルドに利用する命令セットです。
例えば、Javaのアプリケーションを稼働させるイメージを、RedHatのUBIをベースイメージから作る場合は、以下のようなDockerfileを記載します。
FROM redhat/ubi8:8.9
RUN dnf install -y java-17-openjdk
WORKDIR /app
COPY penguinApp.jar /app/penguinApp.jar
ENTRYPOINT ["java", "-jar", "/app/penguin.jar"]
Dockerfileを書くためのベストプラクティス
公式ドキュメントに詳細があります。
イメージを扱う上で大切なことが色々書いているので、一度は目を通しておきたいですが、要点としては以下です。
- 1つのコンテナに対して、目的(プロセス)は1つとする
- 無駄なものを作らず、限りあるリソースを有効活用する
簡単に見えますが、これが意外と難しく...本記事では後者に焦点を当てます。
レイヤ
「無駄なものを作らない」で、まず挙げられるのは「レイヤ」です。
Dockerイメージは「レイヤ」の集合として構成されています。
Dockerfileの命令1行が、大体1レイヤに相当します。
例えば、先ほどのDockerfileを元に作ったイメージは、以下のようなレイヤ構成となります。
作成するDockerイメージのレイヤは極力減らす
ガイド中のベストプラクティスでも触れられていますが、レイヤの数は極力減らすべきです。
レイヤ数を少なくすることには、以下のメリットがあります。
- イメージサイズの縮小
- コンテナ起動やビルド処理時間の短縮
- キャッシュの効率化
ちなみに、レイヤを増やす命令はCOPY
,ADD
,RUN
の3つです。
レイヤを増やさないためには、以下を意識すると良いでしょう。
- 可能であれば、1行にまとめる
-
RUN
が連続する場合、&でまとめる(RUN mkdir /app & cd /app
)
-
- レイヤを増やさない命令を活用する
- 先ほどのDockerfile内の
WORKDIR
はRUN mkdir /app & cd /app
に相当します。
WORKDIR
に置き換えることで、前のレイヤのメタデータとして持てるので、レイヤを減らせます。
- 先ほどのDockerfile内の
(メンテナンス性を高めるために、意図的にレイヤを分離することもあります)
Dockerイメージの構成を見る
イメージはtarファイルに出力できるため、それを解凍することで、内部構造を確認していきます。
ここではRedHatのUBIイメージを利用していきます。
docker save redhat/ubi8:8.9 > ./ubi.tar
tar xf ./ubi.tar
解凍結果
blobs/
└── sha256/
├── 053de0cbf9f05272ca7279809c748f87bf619ab9a2c0d6dc4fc902df60959f16
├── 388e2b6b84a27760caca69f14e4a104a742c222bde88e87b2cc7d92ff378858f
├── 7cb2198f105bc6e59faaa598b914fb470c8dc156e75a85234addf42d0c4fd740
└── c70762f1df383623777503c532e9cd575d9c75d41edbfaa4739e6a10aeab30a7
index.json
manifest.json
oci-layout
repositories
※もしもレイヤが多い場合は、その分レイヤとメタデータが増えるため、sha256直下のファイル数が増えます。
manifest.json
を確認すると、7cb2198f105bc6e59faaa598b914fb470c8dc156e75a85234addf42d0c4fd740
がメインのレイヤのようです。加えてこちらもtarファイルのようなので、解凍していきます。
//manifest.json
[
{
"Config": "blobs/sha256/388e2b6b84a27760caca69f14e4a104a742c222bde88e87b2cc7d92ff378858f",
"RepoTags": [
"redhat/ubi8:8.9"
],
"Layers": [
"blobs/sha256/7cb2198f105bc6e59faaa598b914fb470c8dc156e75a85234addf42d0c4fd740"
],
"LayerSources": {
"sha256:7cb2198f105bc6e59faaa598b914fb470c8dc156e75a85234addf42d0c4fd740": {
"mediaType": "application/vnd.oci.image.layer.v1.tar",
"size": 212797440,
"digest": "sha256:7cb2198f105bc6e59faaa598b914fb470c8dc156e75a85234addf42d0c4fd740"
}
}
}
]
ここではレイヤやメタデータ等の情報の在処を扱っているようです。
解凍すると、Dockerコンテナ内のファイル構造が見られました。
lrwxrwxrwx 1 root root 7 6月 21 2021 bin -> usr/bin
dr-xr-xr-x 2 root root 6 6月 21 2021 boot
drwxr-xr-x 2 root root 6 11月 1 2023 dev
drwxr-xr-x 47 root root 4096 11月 1 2023 etc
drwxr-xr-x 2 root root 6 6月 21 2021 home
lrwxrwxrwx 1 root root 7 6月 21 2021 lib -> usr/lib
lrwxrwxrwx 1 root root 9 6月 21 2021 lib64 -> usr/lib64
drwx------ 2 root root 6 11月 1 2023 lost+found
drwxr-xr-x 2 root root 6 6月 21 2021 media
drwxr-xr-x 2 root root 6 6月 21 2021 mnt
drwxr-xr-x 2 root root 6 6月 21 2021 opt
drwxr-xr-x 2 root root 6 11月 1 2023 proc
dr-xr-x--- 3 root root 213 11月 1 2023 root
drwxr-xr-x 4 root root 33 11月 1 2023 run
lrwxrwxrwx 1 root root 8 6月 21 2021 sbin -> usr/sbin
drwxr-xr-x 2 root root 6 6月 21 2021 srv
drwxr-xr-x 2 root root 6 11月 1 2023 sys
drwxrwxrwt 2 root root 58 11月 1 2023 tmp
drwxr-xr-x 12 root root 144 11月 1 2023 usr
drwxr-xr-x 19 root root 249 11月 1 2023 var
サイズが0のディレクトリを除くと、以下のディレクトリに絞られました。UBIのイメージはレイヤ1つで構成されるため、イメージの実態はこれらのディレクトリ群であることが分かりました。
$ du -h --max-depth=1 ./ | grep -v ^0
3.1M ./etc
60K ./root
8.0K ./tmp
200M ./usr
13M ./var
216M ./
なんだかDockerイメージって、ブラックボックスな印象でしたが、中にはJSONとファイルシステムしか存在しないようです。(もっとバイナリとかあるのかと想像していました)
実際につくってみた
作るために必要なもの
docker save
で出力したtarはdocker load
で読み込めます。
つまり、これまでの情報があれば、ゼロから作れるだろう...ということで、作ってみます。
試行錯誤した結果、コンテナを作るために、最低限必要なものは以下だとわかりました。
blobs/
└── sha256/
├── レイヤのデータ(tar)
└── レイヤのメタデータ(json)
manifest.json # イメージのメタデータ。構成情報が書かれている
repositories # リポジトリ名とタグ名の情報を内包。(manifest.jsonに"RepoTags"を設定したから必要だったのかも)
手順
稼働確認のためにHelloWorldを表示するシングルバイナリを配置し、これをメインプロセスとする「penguin/original:1.0」というコンテナイメージを作ってみます。
-
ディレクトリを作成する
$ mkdir -m 755 -p ./original_image/blobs/sha256 $ cd ./original_image
-
バイナリファイルをアーカイブする(【sha256sumの値①】を後手順で利用します)
※バイナリがカレントディレクトリにあると仮定 $ tar cf .binary.tar ./hello $ sha256sum ./binary.tar $ mv ./binary.tar ./blobs/sha256/【sha256sumの値①】
-
configを作成する(【sha256sumの値②】を後手順で利用します)
$ vi ./json
./jsonに、以下の内容(JSON)を記載する
{ "architecture": "amd64", "created": "0001-01-01T00:00:00Z", "os": "linux", "diff_ids": [ "sha256:blobs/sha256/【sha256sumの値①】" ] }, "config": { "User": "0", "WorkingDir": "/" } "rootfs": { "type": "layers", "diff_ids": [ "sha256:【sha256sumの値①】" ] } }
$ sha256sum ./json $mv ./json ./blobs/sha256/【sha256sumの値②】
-
manifest.jsonを作る
$ vi manifest.json
中身は以下の通り
[ { "Config": "blobs/sha256/【sha256sumの値②】", "RepoTags": [ "penguin/original:1.0" ], "Layers": [ "blobs/sha256/【sha256sumの値①】" ] } ]
-
イメージ名やタグを設定するためのrepositriesを作る
$ vi repositories
内容は以下。
penguin/original
がイメージ名で1.0
がタグ{ "penguin/original": { "1.0": "【sha256sumの値①】" } }
-
すべてをアーカイブし、Dockerイメージとする。そしてロードする
$ tar cf /tmp/original_image.tar ./* $ docker load < /tmp/original_image.tar
以上で完了です。
実際にdocker images
で確認してみると、無事に読み込まれています!(metadataがないので作成日はN/Aになる)
$ docker images penguin/original:1.0
REPOSITORY TAG IMAGE ID CREATED SIZE
penguin/original 1.0 662936f8add1 N/A 2.02MB
コンテナも正常に動きました。
$ docker run --rm penguin/original:1.0 /hello
++(・0・)++ < Hello Penguin
さいごに
Dockerを初めて触るようになって5年以上経ちましたが、イメージの内部構造まで詳しく見る機会はなく...今回の検証を通して、よりDockerへの理解が深まりました。
今後はレイヤの構成も意識して、軽量かつメンテナンス性の高いイメージ運用に役立てたいです。
Discussion