舞台裏で何が起こっているのかを知ることは常に価値があります。ルートレスPodmanコンテナの内部で何が起こるかを見てみましょう。各コンポーネントについて説明してから、関連するすべての手順を詳しく説明します。
この例では、既にBuildahを実行しているコンテナーを実行して、コンテナーイメージを構築しようとします。まず、Containerfile
という単純なDockerfileを作成します ubi8イメージをプルし、コンテナーで実行していることを通知するコマンドを実行します:
$ mkdir containers
$ cat > ~/Containerfile << _EOF
FROM ubi8
RUN echo “in buildah container”
_EOF
次に、次のPodmanコマンドを使用してコンテナを実行します。
$ podman run --device /dev/fuse -v ~/Containerfile:/Containerfile:Z \
-v ~/containers:/var/lib/containers:Z buildah buildah bud /
このコマンドは、追加のデバイス/dev/fuse
を追加します 、コンテナ内でBuildahを実行するために必要です。 Containerfile
にボリュームマウントします Buildahがそれを見つけて、SELinuxフラグ:Z
を使用できるようにします。 Podmanにラベルを変更するように指示します。 Buildahのコンテナストレージをコンテナの外部で処理するために、ローカルのcontainers
もマウントします。 上で作成したディレクトリ。そして最後に、Buildahコマンドを実行します。
このコマンドを実行したときに表示される実際の出力は次のとおりです。
$ podman run -ti --device /dev/fuse -v ~/Containerfile:/Containerfile:Z -v ~/containers:/var/lib/containers:Z buildah/stable buildah bud /
Trying to pull docker.io/buildah/stable...
denied: requested access to the resource is denied
Trying to pull registry.fedoraproject.org/buildah/stable...
manifest unknown: manifest unknown
Trying to pull quay.io/buildah/stable...
Getting image source signatures
Copying blob 907e338ec93d done
Copying blob a3ed95caeb02 done
Copying blob a3ed95caeCob02 done
Copying blob a3ed95caeb02 skipped: already exists
Copying blob d318c91bf2a8 done
Copying blob e721a8015139 done
Copying blob a3ed95caeb02 done
Copying blob 8dd367492bc7 done
Writing manifest to image destination
Storing signatures
STEP 1: FROM ubi8
Getting image source signatures
Copying blob c65691897a4d done
Copying blob 641d7cc5cbc4 done
Copying config 11f9dba4d1 done
Writing manifest to image destination
Storing signatures
STEP 2: RUN echo "in buildah container"
in buildah container
STEP 3: COMMIT
Getting image source signatures
Copying blob 6866631b657e skipped: already exists
Copying blob 48905dae4010 skipped: already exists
Copying blob 5f70bf18a086 skipped: already exists
Copying config 9c54016647 done
Writing manifest to image destination
Storing signatures
9c5401664748e032b43b8674dba90e9b853d6b47b679d056cb2a1e3118f9dab7
それでは、Podmanコマンド内で実際に何が起こっているのかを深く掘り下げてみましょう。
ユーザーとマウントの名前空間を設定するとき、Podmanは最初にユーザーの名前空間がすでに構成されているかどうかを確認します。これは、ユーザーに対して実行中の一時停止プロセスがあるかどうかを確認することによって行われます。すべてのルートレスコンテナは同じユーザー名前空間で実行する必要があるため、一時停止プロセスの役割はユーザー名前空間を存続させることです。そうでない場合、いくつかのこと(別のコンテナからネットワーク名前空間を共有するなど)は不可能になります。
ルートレスが特定のタイプのファイルシステムをマウントし、複数のUIDとGIDにアクセスできるようにするには、ユーザー名前空間が必要です。
一時停止プロセスが存在する場合、そのユーザー名前空間が結合されます。マルチスレッドプログラムはユーザーの名前空間を変更できないため、このアクションはGoランタイムが開始する前の実行の非常に早い段階で実行されます。ただし、一時停止プロセスがしない 存在する場合、Podmanは/etc/subuid
を読み取ります および/etc/subgid
ファイル、Podmanコマンドを実行しているユーザーのユーザー名またはUIDを探します。 Podmanはエントリを見つけると、コンテンツとユーザーの現在のUID / GIDを使用して、それらのユーザー名前空間を生成します。
たとえば、ユーザーがUID 1000として実行されており、USER:100000:65536
のエントリがある場合 、Podmanはsetuidおよびsetgidアプリを実行します。/usr/bin/newuidmap
および/usr/bin/newgidmap
、ユーザー名前空間を構成します。次に、ユーザー名前空間は次のマッピングを取得します。
0 3267 1
1 100000 65536
次のコマンドを実行すると、ユーザーの名前空間を確認できることに注意してください。
$ podman unshare cat /proc/self/uid_map
次に、Podmanは、名前空間を存続させるための一時停止プロセスを作成します。これにより、すべてのコンテナーが同じコンテキストから実行され、同じマウントを確認できます。次のPodmanプロセスは、最初に名前空間を作成しなくても、名前空間に直接参加します。ただし、ユーザースペースを作成できなかった場合、Podmanは、ユーザー名前空間がなくてもコマンドを実行できるかどうかを確認します。 podman version
のようないくつかのコマンド 必要ありません。それ以外の場合、ユーザー名前空間のないコマンドは失敗します。
次に、Podmanはコマンドラインオプションを処理し、それらが正しいことを確認します。 podman-help
を使用できます およびpodman run --help
使用可能なオプションを一覧表示し、詳細な説明についてはマニュアルページを使用してください。
最後に、Podmanはコンテナストレージをマウントするためのマウント名前空間を作成します。
イメージをプルするとき、Podmanはコンテナイメージがbuildah/stable
かどうかをチェックします ローカルコンテナストレージに存在します。含まれている場合、Podmanはネットワークをセットアップします(次のセクションを参照)。ただし、コンテナイメージが存在しない場合、Podmanは、/etc/containers/registries.conf
で定義された検索レジストリを使用してプルする候補イメージのリストを作成します。 。
containers/image
ライブラリは、これらの候補画像をregistries.conf
で定義された順序で一度に1つずつプルするために使用されます。 。正常にプルされた最初の画像が使用されます。
containers/image
スクリプトはDNSを使用してレジストリのIPアドレスを検索します。- このスクリプトTCPは、
httpd
を介してIPアドレスに接続します ポート(80)。 containers/image
コンテナ画像。/buildah/stable:latest - スクリプトが画像を見つけられない場合は、代わりに次のレジストリを使用して手順1に戻ります。ただし、画像が 見つかった場合、
containers/image
を使用して画像の各レイヤーのプルを開始します ライブラリ。
この例では、buildah/stable
quay.io/buildah/stable
で見つかりました 。 containers/image
スクリプトは、quay.io/buildah/stable
に7つのレイヤーがあることを検出します そして、それらすべてをコンテナレジストリからホストに同時にコピーし始めます。それらを同時にコピーすると効率的です。
各レイヤーがホストにコピーされると、Podmanはcontainers/storage
を呼び出します 図書館。 containers/storage
スクリプトは、レイヤーを順番に、レイヤーごとに再アセンブルします。 ~/.local/share/containers/storage
にオーバーレイマウントポイントを作成します 前のレイヤーの上に。前のレイヤーがない場合は、最初のレイヤーが作成されます。
注: ルートレスポッドマンでは、実際にはfuse-overlayfs
を使用します レイヤーを作成するための実行可能ファイル。 Rootfullはカーネルのoverlayfs
を使用します 運転者。現在、カーネルはルートレスユーザーがオーバーレイファイルシステムをマウントすることを許可していませんが、FUSEファイルシステムをマウントすることはできます。
次に、containers/storage
レイヤーの内容を新しいストレージレイヤーに解凍します。レイヤーはタール化されていないため、containers/storage
tarball内のファイルのUID/GIDをホームディレクトリに変換します。 tarファイルで指定されたUIDまたはGIDがユーザー名前空間にマップされていない場合、このプロセスは失敗する可能性があることに注意してください。 ルートレスポッドマンが私の画像をプルできないのはなぜですか?をご覧ください。
次に、Podmanが画像に基づいて新しいコンテナを作成します。これを実現するために、Podmanはコンテナをデータベースに追加してから、containers/storage
に問い合わせます。 c/storage
に新しいコンテナを作成してマウントするためのライブラリ 。新しいコンテナレイヤーは、最終的な読み取り/書き込みレイヤーとして機能し、画像の上にマウントされます。
次に、ネットワークを設定する必要があります。これを実現するために、Podmanは/usr/bin/slirp4netns
を見つけて実行します。 コンテナネットワークを設定します。ルートレスPodmanでは、この機能はroot以外のユーザーには許可されていないため、コンテナー用の完全な個別のネットワークを作成することはできません。ルートレスポッドマンでは、slirp4netns
を使用します ホストネットワークを構成し、コンテナのVPNをシミュレートします。
注: ルートフルコンテナでは、PodmanはCNIプラグインを使用してブリッジを構成します。
ユーザーが-p 8080:80
のようなポートマッピングを指定した場合 、slirpnetns
ポート8080でホストネットワークをリッスンし、コンテナプロセスがポート80にバインドできるようにします。slirp4netns
コマンドは、コンテナが存在する新しいネットワーク名前空間内に挿入されるタップデバイスを作成します。各パケットはslirp4netns
から読み戻されます ユーザースペースでTCP/IPスタックをエミュレートします。コンテナネットワークの名前空間外の各接続は、非特権ユーザーがホストネットワークの名前空間で実行できるソケット操作で変換されます。
ボリュームを処理するために、Podmanはすべてのコンテナストレージを読み取ります。使用済みのSELinuxラベルを収集し、opencontainers/selinux
を使用してコンテナーを実行するための新しい未使用のラベルを作成します。 ライブラリ。
ユーザーがコンテナにマウントする2つのボリュームを指定し、Podmanにコンテンツのラベルを変更するように要求したため、Podmanはopencontainers/selinux
を使用します。 SELinuxラベルをボリュームのソースファイル/ディレクトリに再帰的に適用します。次に、Podmanはopencontainers/runtime-tools
を使用します Open Containers Initiative(OCI)ランタイム仕様をアセンブルするライブラリ:
- Podmanは
runtime-tools
に指示します 機能、環境、名前空間などのハードコードされたデフォルトを仕様に追加します。 - Podmanは、
buildah/stable
からプルダウンされたOCIイメージ仕様を使用します 作業ディレクトリ、エントリポイント、追加の環境変数など、仕様のコンテンツを設定するための画像。 - Podmanはユーザーの入力を受け取り、
runtime-tools
を使用します 各ボリュームの仕様にフィールドを追加するライブラリ。コンテナのコマンドをbuildah bud /
に設定します。 。
この例では、ユーザーがPodmanにデバイス/dev/fuse
を使用したいと伝えました。 コンテナの内側。ルートフルコンテナでは、PodmanはOCIランタイムに/dev/fuse
を作成するように指示します。 コンテナ内のデバイスですが、ルートレスのPodmanユーザーはデバイスを作成できません。そのため、Podmanは代わりにOCI仕様にマウント/dev/fuse
をバインドするように指示します。 ホストからコンテナへ。
conmon
ボリュームが処理されると、Podmanはデフォルトのconmon
を見つけて実行します コンテナの場合/usr/bin/conmon
。この情報は、/usr/share/containers/libpod.conf
から読み取られます。 。次に、Podmanはconmon
に通知します libpod.conf
にもリストされているOCIランタイムを使用するための実行可能ファイル;通常、/usr/bin/runc
または/usr/bin/crun
。 Podmanはconmon
にも指示します podman container cleanup $CTRID
を実行します コンテナが終了するときのコンテナの場合。
Conmonは、コンテナを監視するときに次のことを行います。
- ConmonはOCIランタイムを実行し、OCIスペックファイルへのパスを渡し、
containers/storage
のコンテナーレイヤーマウントポイントをポイントします。 。このマウントポイントはrootfsと呼ばれます。 - Conmonは、コンテナが終了するまでコンテナを監視し、終了コードを報告します。
- Conmonは、ユーザーがコンテナに接続するときに処理し、コンテナのSTDOUTとSTDERRをストリーミングするためのソケットを提供します。
- STDOUTとSTDERRは、
podman logs
のファイルにも記録されます。 。
conmon
を実行した後 、ただし、コンテナが-d
で実行されなかったため、OCIランタイムが開始する前に、Podmanは「接続」ソケットに接続します 。コンテナを実行する前にこれを行う必要があります。そうしないと、アタッチする前にコンテナが標準ストリームに書き込んだものがすべて失われるリスクがあります。コンテナが起動する前にそうすることで、すべてが得られます。
OCIランタイムの起動
OCIランタイムは、OCI仕様ファイルを読み取り、コンテナーを実行するようにカーネルを構成します。それ:
- コンテナの追加の名前空間を設定します。
- コンテナがcgroupsV2で実行されている場合は、cgroupsを構成します(cgroups V1はルートレスcgroupsをサポートしていません)。
- コンテナを実行するためのSELinuxラベルを設定します。
-
seccomp.json
を読み取ります ファイル(デフォルトは/usr/share/containers/seccomp.json
)そしてseccompルールを設定します。 - 環境変数を設定します。
- Bindは、指定された2つのボリュームをrootfsのパスにマウントします。宛先パスがrootfsに存在しない場合、OCIランタイムは宛先ディレクトリを作成します。
- rootをrootfsに切り替えます(rootfsを
/
にします コンテナ内)。 - コンテナプロセスをフォークします。
- 任意のOCIフックプログラムを実行し、それらにrootfsとコンテナのPID1を渡します。
- ユーザーが指定したコマンドを実行します
buildah bud /
コンテナのPID1を使用します。 - OCIランタイムを終了し、
conmon
を残します コンテナを監視します。
そして最後に、conmon
成功をPodmanに報告します。
buildah
の実行 コンテナの主要なプロセス
さて、最後のステップのグループです。これは、コンテナが最初のBuildahプロセスを起動したときに始まります。 (この例ではBuildahを使用したためです。)Buildahは、基になるcontainers/image
を共有します。 およびcontainers/storage
ライブラリはPodmanを使用しているため、実際には、Podmanがイメージをプルしてコンテナを生成するために使用した上記の手順のほとんどに従います。
Podmanはconmon
に接続します ソケットし、STDOUTの読み取り/書き込みをconmon
に続行します 。ユーザーがPodmanの-d
を指定した場合は注意してください フラグ、Podmanは終了しますが、conmon
コンテナの監視を継続します。
コンテナプロセスが終了すると、カーネルはSIGCHLDをconmon
に送信します。 処理する。次に、conmon
:
- コンテナの終了コードを記録します。
- コンテナのログファイルを閉じます。
- PodmanコマンドのSTDOUT/STDERRを閉じます。
podman container cleanup $CTRID
を実行します コマンド。
次に、Podmanコンテナのクリーンアップにより、slirp4netns
が停止されます。 ネットワークとcontainers/storage
に通知します すべてのコンテナマウントポイントをアンマウントします。ユーザーが--rm
を指定した場合 代わりに、コンテナは完全に削除されます。コンテナレイヤーがcontainers/storage
から削除されます 、およびコンテナ定義がDBから削除されます。
元のPodmanコマンドがフォアグラウンドで実行されていたため、Podmanはconmon
を待機します 終了するには、コンテナから終了コードを取得してから、コンテナの終了コードで終了します。
うまくいけば、この説明がすべての魔法を理解するのに役立つでしょう これは、rootlessPodmanコマンドを実行するときに内部で発生します。
コンテナは初めてですか? Containers Primerをダウンロードして、Linuxコンテナーの基本を学びます。