舞台裏で何が起こっているのかを知ることは常に価値があります。ルートレス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コンテナーの基本を学びます。