はじめに
コンテナは単なる「アプリを入れた箱」ではありません。
コンテナイメージには、
- レイヤー構造
- イメージ名の解決(URL解決)
- OSライブラリやパッケージ
といった仕組みが含まれています。この記事では Podmanを使いながら、コンテナイメージの内部構造を実際に確認していきます。
対象読者
- コンテナの仕組みを実際に触って理解したい方
参考教材
環境準備
サンプルの Dockerfile を取得します。
git clone https://github.com/redhat-developer-demos/intro-both
cd intro-both
取得したDockerfile(以下)は UBI9(ベースイメージ)をベースに、ファイルを5つ追加するだけのシンプルな構成です。
1. レイヤー構造を確認する
そもそも「レイヤー」とは何か
コンテナイメージはレイヤーとメタデータでk構成されていて、一般的には、各レイヤーはファイルシステムの差分として保存されます。
base imageを一番下として、差分がtarファイル(Linuxファイルシステム)として追加されていくイメージです。
┌─────────────────────────────┐
│ RUN echo > /tmp/newfile5 │ ← 自分が追加したレイヤー(2.56kB)
├─────────────────────────────┤
│ RUN echo > /tmp/newfile4 │ ← 自分が追加したレイヤー(2.56kB)
├─────────────────────────────┤
│ ...(newfile1〜3 も同様) │
├─────────────────────────────┤
│ ubi9/ubi ベースイメージ │ ← Red Hat が作った土台(236MB)
└─────────────────────────────┘
実際に、レイヤーがどう作られているか見ていきます。まずイメージをビルドします。
podman build -t ubi9-change .
次にレイヤー履歴を確認します。
podman history ubi9-change
実行結果:
ID CREATED CREATED BY SIZE
# 一番上(一番最後に追加されたレイヤー)
c81a452eb22f 1分前 /bin/sh -c echo "Hello world" > /tmp/newfile5 2.56kB ← podman images の IMAGE ID と同じ
69d9acf240e5 1分前 /bin/sh -c echo "Hello world" > /tmp/newfile4 2.56kB
3bf72b35c66c 1分前 /bin/sh -c echo "Hello world" > /tmp/newfile3 2.56kB
8f450cda9815 1分前 /bin/sh -c echo "Hello world" > /tmp/newfile2 2.56kB
164cc2f0f1d3 1分前 /bin/sh -c echo "Hello world" > /tmp/newfile 2.56kB
c2b5da997c99 8時間前 /bin/sh -c #(nop) MAINTAINER ... 0B
# 一番下(base)
<missing> 8時間前 /bin/sh -c #(nop) LABEL ... 236MB ← レジストリ側のレイヤー
...
上から新しいレイヤー、下がベースイメージになります。
c2b5da997c99のベースイメージの上から、どんどんレイヤーが追加されているのがわかりますね。
次に、作成されたイメージを見ていきます。
podman images
実行結果:
REPOSITORY TAG IMAGE ID SIZE
localhost/ubi9-change latest c81a452eb22f 236 MB ← ビルドしたイメージ
registry.access.redhat.com/ubi9/ubi latest c2b5da997c99 237 MB ← ベースイメージ
registry.access.redhat.com/ubi9/ubi-minimal latest 0cb0798c83aa 112 MB
ビルドしたイメージと、ベースイメージが作成されています。
2. イメージ名は自動的に解決される
Podman は省略されたイメージ名を補完します。
例えば、nginxは内部的に、docker.io/library/nginx:latest として解釈されます。
また、以下はすべて同じイメージを指します。省略してもpodmanがイメージ名を補完してくれるからです。
podman inspect registry.access.redhat.com/ubi9/ubi:latest
podman inspect registry.access.redhat.com/ubi9/ubi
podman inspect ubi9/ubi:latest
podman inspect ubi9/ubi
ただし、名前が衝突すると解決できない場合があります。
podman build -t ubi9:test .
# 実行結果
podman inspect ubi9 # 失敗
podman inspect ubi9:test # 成功
実運用では、フルパスレジストリ/ネームスペース/リポジトリ:タグ で指定するのが安全です。
3. イメージ内部のライブラリを確認する
コンテナの中にはアプリケーションだけでなく、OSライブラリも含まれています。
アプリケーションがどのライブラリに依存しているかは ldd コマンドで確認できます。
例1. Java(JVM)の依存ライブラリを確認する
Javaは「どこでも動く」と言われますが、Java実行環境のJVM自体は、OSライブラリ(glibc)に依存しています。
podman run -it registry.access.redhat.com/jboss-eap-7/eap70-openshift \
ldd /usr/lib/jvm/java-1.8.0-openjdk/jre/bin/java
出力例:
libpthread.so.0
libz.so.1
libc.so.6
ここで表示されるlibc.so.6は glibc の共有ライブラリです。
つまり、
Javaアプリ
↓
JVM(Javaの実行環境)
↓
glibc(JVMが依存するライブラリ)
という依存関係があります。
例2. curlの依存ライブラリを確認する
Red Hat の toolbox イメージを起動します。
podman run -it registry.access.redhat.com/ubi9/toolbox bash
依存ライブラリを確認します。
ldd /usr/bin/curl
出力例:
libssl.so.3(ライブラリファイル)
libcrypto.so.3(ライブラリファイル)
libc.so.6(ライブラリファイル)
依存ライブララリが出てきました。さらに、このライブラリがどのパッケージによって提供されているのかを調べます。
rpm -qf /lib64/libssl.so.3
#
を実行すると、openssl-libsが表示されました。
つまり、libssl.so.3はopenssl-libsパッケージによって提供されています。
openssl-libs
├── libssl.so.3
├── libcrypto.so.3
├── openssl.cnf
└── その他ファイル
curlはlibssl.so.3を利用していて、そのlibssl.so.3は openssl-libsパッケージに含まれています。
まとめ
コンテナイメージについて、普段は意識しない内部構造も、Podmanを使って実際に覗いてみることで、レイヤー構造やイメージ名の解決、さらにはイメージ内のライブラリやパッケージの依存関係まで確認することができました。
コンテナイメージは単なる「アプリを入れた箱」ではなく、レイヤーを積み重ねながら構築され、その中でライブラリやパッケージのなどの様々な部品が問題なく依存関係を持って取り込まれることで初めて機能するのだなと思いました。