init コンテナとサイドカーの仕組み
v1.37 時点(2026年10月23日 確認)
目次
この記事では、準備や補助のために 1 つの Pod に入れるコンテナである init コンテナ と サイドカー の仕組みを、図で説明します。init コンテナはアプリケーションのコンテナより先に動いて終わり、サイドカーはアプリケーションのコンテナの横で動き続けます。2 つの違いと、Pod の中のコンテナが起動して止まる順番、コンテナどうしがファイルや通信を共有する方法がわかります。後半では kind のクラスタで init コンテナとサイドカーを持つ Pod を動かし、起動の順番と共有の様子を確かめる手順を示します。
Pod とコンテナの関係と、Pod の中のコンテナが共有するもの(ネットワークとボリューム)を扱った第8回の内容を前提にしています。restartPolicy による再起動と再起動までの待ち時間を扱った第9回の内容も使います。まだの方は先にこちらをどうぞ。
以下の図は、この回の後半で動かす Pod の中の 3 つのコンテナと、それらが共有するものを表しています。
図の init コンテナ init-content は Pod の起動時にいちばん先に動き、ボリュームにファイルを書いて終わります。そのあとサイドカー sidecar が起動し、続いてアプリケーションのコンテナ nginx が起動します。nginx は init コンテナが書いたファイルをボリュームから読んで配信し、sidecar は同じ Pod の中から localhost で nginx に問い合わせます。init コンテナとアプリケーションのコンテナは同時には動かないので、データの受け渡しはボリュームを通した一方向になります。
参考:Init Containers | Kubernetes、Sidecar Containers | Kubernetes、Pods | Kubernetes
init コンテナとサイドカーとは
第8回で見たとおり、Pod には 1 つ以上のコンテナを入れられます。公式ドキュメントはアプリケーションの処理を動かすコンテナを app container(以下、アプリケーションコンテナ)と呼び、init コンテナと区別しています。Pod にはアプリケーションコンテナのほかに、Pod の起動時に動く init コンテナも入れられます。v1.37 時点で、Pod のマニフェストに書くコンテナは次の 3 種類です。
| 種類 | 書く場所 | 動く期間 |
|---|---|---|
| アプリケーションコンテナ | spec.containers | ふつうの init コンテナがすべて終わり、サイドカーが起動したあと、Pod が動いているあいだ |
| init コンテナ | spec.initContainers | Pod の起動時に 1 つずつ順に動き、それぞれ終わりまで動いて止まる |
| サイドカー | spec.initContainers(コンテナごとの restartPolicy を Always にする) | Pod の起動時に起動し、Pod が動いているあいだ動き続ける |
参考:Pods | Kubernetes、Init Containers | Kubernetes、Sidecar Containers | Kubernetes、Glossary | Kubernetes
init コンテナ は アプリケーションコンテナが起動する前に動き、終わりまで動いて止まる、準備のためのコンテナ です。公式ドキュメントによると、init コンテナはアプリケーションコンテナとほとんど同じですが、次の 2 点が違います。1 つは、init コンテナは終わることを前提にした処理を動かし、終わりまで動いて止まることです。もう 1 つは、init コンテナがいくつもあるとき、それぞれが成功して終わってから次の init コンテナが始まることです。init コンテナにはアプリケーションのイメージに入っていない道具や、準備のための処理を入れておけます。
サイドカーコンテナ(以下、サイドカー)は 同じ Pod の中で、メインのアプリケーションコンテナと一緒に動く、補助のためのコンテナ です。公式ドキュメントによると、サイドカーはログの記録、監視、セキュリティ、データの同期などの機能を、アプリケーションのコードを直接変えずに加えるために使います。公式ドキュメントによると、たいていは 1 つの Pod にアプリケーションコンテナは 1 つだけです。たとえば公式ドキュメントの Pods のページには、共有のボリュームにあるファイルを配信する Web サーバーのコンテナと、そのファイルを外から取ってきて更新するサイドカーの例があります。
Kubernetes はサイドカーを init コンテナの特別な場合として実装しています。spec.initContainers に書いたコンテナに、コンテナごとの restartPolicy として Always を指定すると、そのコンテナはサイドカーとして扱われ、Pod の起動後も動き続けます。公式ドキュメントは起動時にだけ動くふつうの init コンテナを regular init containers と呼んで、サイドカーと区別しています。この回でも、サイドカーではない init コンテナを「ふつうの init コンテナ」と呼びます。v1.37 時点の公式ドキュメントによると、サイドカーの機能(SidecarContainers)は v1.28 で初めて入り、v1.33 から正式(GA)の機能です。今は無効にできません。
公式ドキュメントの Pods のページは、このほかに、動いている Pod の調べものをするために一時的に入れる ephemeral container(エフェメラルコンテナ)も挙げています。公式ドキュメントによると、ephemeral container は pod.spec に直接書いて足すのではなく、API の専用の窓口を通して作るもので、自動では再起動されません。この回では扱いません。Pod の不具合の原因の調べ方は第41回で扱います。
なぜ init コンテナとサイドカーが必要なのか
第8回で見たとおり、1 つの Pod に複数のコンテナを入れるのは、コンテナどうしが密に結び付いているときだけの、比較的高度な使い方です。公式ドキュメントによると、Pod が複数のコンテナを持てる主な理由は、メインのアプリケーションを手伝う補助のアプリケーションを支えることです。補助のアプリケーションの典型的な例として、データを取ってくるもの、データを送り出すもの、通信を中継するもの(プロキシ)が挙がっています。補助のアプリケーションとメインのアプリケーションは、多くの場合、共有のファイルシステム(ボリューム)か、localhost で互いにやり取りします。
参考:Communicate Between Containers in the Same Pod Using a Shared Volume | Kubernetes、Pods | Kubernetes
補助の処理をアプリケーションとは別のコンテナに分けると、それぞれを別々に扱えます。公式ドキュメントによると、サイドカーはアプリケーションのコードを直接変えずに機能を加えるためのもので、アプリケーションコンテナとは別の、独立したライフサイクルを持ちます。そのため、メインのアプリケーションに影響を与えずに、サイドカーを更新したり保守したりできます。
init コンテナはアプリケーションの準備を、アプリケーションのイメージから切り離すためにあります。公式ドキュメントによると、init コンテナはアプリケーションコンテナとは別のイメージを使うので、アプリケーションのイメージを作る人と Pod を動かす人は、1 つのイメージを一緒に作らなくても、別々に作業できます。公式ドキュメントは init コンテナを使う利点として、このほかに次の 4 つを挙げています。
- 準備にだけ使う道具(
sed、awk、python、digなど)を、アプリケーションのイメージに入れずに済む - アプリケーションコンテナには見せない Secret に、init コンテナだけがアクセスできるようにできる
- 決まった条件がそろうまで、アプリケーションコンテナの起動を止めておける
- 要らない道具を分けておき、アプリケーションのイメージの攻撃される箇所(attack surface)を減らせる
sed、awk、python、dig は一般に、文字の置き換えや加工、スクリプトの実行、名前の問い合わせなどに使う道具です。Secret はパスワードなどの秘密の情報を入れておくリソースで、第18回で扱います。
公式ドキュメントには init コンテナの使い方の例として、Service(第21回)ができるまで待つ、Git のリポジトリをボリュームにコピーする、設定ファイルの値を埋めて作る、などが挙がっています。
サイドカーの仕組みが要るのは、補助のコンテナを起動や停止の順番まで含めて扱いたいときです。組み込みのサイドカーができる前は、補助のコンテナもアプリケーションコンテナと同じく spec.containers に並べていました(末尾の補足)。公式ドキュメントもコンテナごとの restartPolicy に対応していない古いバージョンの Kubernetes で動かす必要があるときは、init でもサイドカーでもない複数のコンテナを Pod で動かすこともできる、としています。公式ドキュメントによると、spec.containers に並べたアプリケーションコンテナは、init コンテナが終わったあと、すべて並行して起動できます。また、init でもサイドカーでもない複数のコンテナを Pod で動かすやり方は、どのコンテナが先に起動したり止まったりするかを制御しなくてよい場合に向いている、とされています。たとえば、アプリケーションが起動してすぐにログを書き出したときに、ログを扱う補助のコンテナがまだ受け取れる状態になっていない、ということも起こり得ると考えられます。
参考:Init Containers | Kubernetes、Sidecar Containers | Kubernetes、Adopting Sidecar Containers | Kubernetes
公式ドキュメントは Kubernetes に組み込まれたサイドカーの利点として、次の 3 つを挙げています。
- ほかの init コンテナより先に起動するようにできる
- ほかのコンテナがすべて終わってから、最後に止まるようにできる
- Job(第16回)のように終わりまで動いて止まる Pod で、サイドカーが Pod の完了を妨げない
組み込みではない以前のやり方のサイドカーでは、Job の完了に特別な配慮が要るとされています。
参考:Adopting Sidecar Containers | Kubernetes、Sidecar Containers | Kubernetes
init コンテナとサイドカーの仕組み
マニフェストに書く場所
以下は、冒頭の図の Pod のマニフェストです。init コンテナ init-content がボリュームに index.html を書き、サイドカー sidecar が localhost で nginx に問い合わせ続けます。ボリュームはそのディレクトリをコンテナの中の決まったパスから見えるようにして使います。これを「ボリュームをマウントする」と言います。後半の「手元で試す」でも、このマニフェストを使います。
apiVersion: v1
kind: Pod
metadata:
name: multi-demo
spec:
initContainers:
- name: init-content
image: busybox:1.28
command: ['sh', '-c', 'echo "Hello from the init container" > /work-dir/index.html; sleep 10']
volumeMounts:
- name: html
mountPath: /work-dir
- name: sidecar
image: busybox:1.28
restartPolicy: Always
command: ['sh', '-c', 'while true; do wget -q -O- http://localhost:80/ || echo "nginx is not ready yet"; sleep 5; done']
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
volumes:
- name: html
emptyDir: {}参考:Configure Pod Initialization | Kubernetes、Sidecar Containers | Kubernetes、Pod | Kubernetes API Reference
このマニフェストは公式ドキュメントの 2 つの例をもとに、名前やコマンドなどを変えたものです。Configure Pod Initialization のページの例では、busybox:1.28 の init コンテナが emptyDir のボリュームを /work-dir にマウントして index.html を書き、nginx のコンテナが同じボリュームを /usr/share/nginx/html にマウントして配信します。例の init コンテナは wget で外のサイトからページを取ってきますが、この回では echo で書くことにして、クラスタの外につながらなくても動くようにしました。nginx のイメージは第8回と同じく Pods のページの例のとおり nginx:1.14.2 にしています。Sidecar Containers のページの例は Deployment で、initContainers の項目に restartPolicy: Always を書いてサイドカーにしています。
参考:Configure Pod Initialization | Kubernetes、Sidecar Containers | Kubernetes、Pods | Kubernetes
sidecar を init-content の後ろに書いたのは、ふつうの init コンテナが終わるまでサイドカーが待つ様子を見るためです。init コンテナは書いた順に動くので、sidecar は init-content が終わってから起動します。公式ドキュメントの利点の 1 つ目のとおり、sidecar を前に書けば、init-content より先に起動します。
参考:Sidecar Containers | Kubernetes、Adopting Sidecar Containers | Kubernetes
この回で新しく使うフィールドを、v1.37 時点の API リファレンスに沿って次の表にまとめます。
| フィールド | 内容 |
|---|---|
spec.initContainers | Pod に属する init コンテナの一覧。書いた順に、アプリケーションコンテナより先に動く。項目の書き方は spec.containers と同じ |
spec.initContainers[*].restartPolicy | コンテナごとの再起動の決まり。init コンテナで Always にすると、そのコンテナはサイドカーになる |
spec.volumes | Pod のコンテナがマウントできるボリュームの一覧 |
spec.volumes[*].emptyDir | Pod と同じ期間だけ存在する、一時的なディレクトリのボリューム。{} は一般に、YAML で中身が空の項目を表す書き方で、ここでは emptyDir の設定(medium や sizeLimit)を何も指定しないことを表す |
spec.containers[*].volumeMounts | コンテナのファイルシステムにマウントするボリューム。name でボリュームを指し、mountPath でコンテナの中のパスを決める |
公式ドキュメントによると、ボリュームを使うには、Pod に用意するボリュームを .spec.volumes に書き、コンテナのどこにマウントするかを volumeMounts に書きます。マウントする場所はコンテナごとに別々に指定します。この例では、同じボリューム html を、init-content は /work-dir に、nginx は /usr/share/nginx/html にマウントしています。パスが違っても、中身は同じディレクトリです。sidecar には volumeMounts を書いていないので、sidecar からはこのボリュームが見えません。
emptyDir は名前のとおり空の状態で作られるボリュームです。公式ドキュメントによると、emptyDir は Pod がノードに割り当てられたときに作られ、Pod のすべてのコンテナが同じファイルを読み書きできます。Pod がノードから消えると、emptyDir のデータは完全に消えます。一方で、コンテナが異常終了しても Pod はノードから消えないので、データは残ります。ボリュームの種類は第26回で扱います。
command の書き方は第9回と同じで、sh -c に渡した文字列を、シェルが ; で区切って順に実行します。init-content は echo で 1 行の文字を書き出し、> でその行を /work-dir/index.html に書き込み、10 秒待って終わります。> は一般に、コマンドが書き出す文字をファイルに書き込むシェルの記号です。10 秒待つのは、後半で init コンテナが動いている様子を観察しやすくするためです。sidecar は、while true; do ...; done で同じ処理を繰り返します(一般的なシェルの書き方です)。繰り返す処理は wget で http://localhost:80/ に問い合わせて、受け取った中身を書き出し、5 秒待つことです。wget は一般に、URL を指定してデータを取ってくるコマンドです。localhost は自分自身を指す名前で、同じ Pod のコンテナはネットワークを共有しているので(第8回)、http://localhost:80/ は同じ Pod の 80 番ポート、つまり nginx が待ち受けているポートを指します。wget に付けた -q -O- は公式ドキュメントの別の例でも busybox:1.28 で使われている指定です。一般に、-q は進み具合の表示を止め、-O- は受け取った中身をファイルではなく標準出力に書き出す指定です。問い合わせに失敗したときは、|| の後ろの echo で nginx is not ready yet を書き出します。|| は一般に、前のコマンドが失敗したときだけ後ろのコマンドを動かすシェルの記号です。
参考:Define a Command and Arguments for a Container | Kubernetes、HorizontalPodAutoscaler Walkthrough | Kubernetes、Pod | Kubernetes API Reference、Pods | Kubernetes
コンテナの名前は init コンテナも含めて Pod の中で一意にします。公式ドキュメントによると、ほかのコンテナと同じ名前のコンテナがあると、API がマニフェストを受け付けず、エラーを返します。また API リファレンスによると、Pod を作ったあとで init コンテナを足したり減らしたりはできません。第8回の補足のとおり、作ったあとで変えられるのは spec.initContainers[*].image などに限られます。
参考:Init Containers | Kubernetes、Pod | Kubernetes API Reference、Pods | Kubernetes
init コンテナの動き方
公式ドキュメントによると、Pod の起動時、kubelet はネットワークとストレージの準備ができるまで init コンテナの実行を待ちます。準備ができたら、kubelet は Pod の spec に書かれた順に init コンテナを動かします。それぞれの init コンテナが成功して終わってから、次の init コンテナが始まります。すべての init コンテナが終わると、kubelet はアプリケーションコンテナを用意して、いつもどおり動かします。条件がそろったあとは、Pod のアプリケーションコンテナはすべて並行して起動できます。この節の説明はふつうの init コンテナについてのものです。終わらないサイドカーがあるときは、ふつうの init コンテナがすべて終わり、サイドカーが起動したあとにアプリケーションコンテナが起動します(次の「サイドカーの動き方」で説明します)。
参考:Init Containers | Kubernetes、Sidecar Containers | Kubernetes、Pods | Kubernetes
init コンテナが動いているあいだの Pod の様子を、次の表にまとめます。
| 項目 | init コンテナが動いているあいだ |
|---|---|
| Pod のフェーズ(第9回) | Pending |
Pod の Initialized の condition | False。kubelet は init コンテナがすべて成功して終わったあとに True にする |
Pod の Ready の condition | すべての init コンテナが成功するまで Ready にならない |
| init コンテナの状態 | status.initContainerStatuses に、コンテナごとに記録される(アプリケーションコンテナの status.containerStatuses と同じ形) |
init コンテナが失敗したときに kubelet がどうするかは、Pod の spec.restartPolicy(第9回)で決まります。公式ドキュメントによると、ふつうの init コンテナには Pod の restartPolicy が適用されます。ただし Pod の restartPolicy が Always のとき、init コンテナには OnFailure が使われます。公式ドキュメントによると、init コンテナは成功すれば Pod の起動時に一度だけ動きます。Always のまま扱うと、成功して終わった init コンテナまで再起動されてしまうので、OnFailure が使われると考えられます。そのため、0 で終わって成功した init コンテナは、基本的には再起動されません(例外は少し後で説明します)。失敗したときの扱いを次の表にまとめます。
Pod の restartPolicy | init コンテナが失敗したとき |
|---|---|
Always | 成功するまで init コンテナを再起動する(init コンテナには OnFailure が使われる) |
OnFailure | 成功するまで init コンテナを再起動する |
Never | 再起動しない。Pod 全体が失敗したものとして扱われる |
失敗を繰り返す init コンテナの再起動にも、第9回で見た待ち時間が入ります。公式ドキュメントによると、最初の失敗のあとの再起動には、倍々に延びる待ち時間が入ります。第9回で引用した kubectl describe pod の例も CrashLoopBackOff になった 2 つ目の init コンテナの表示でした。kubectl get pods の STATUS 列には Init:CrashLoopBackOff が表示されることがあります。init コンテナが成功しないかぎり、アプリケーションコンテナは起動しません。
参考:Pod Lifecycle | Kubernetes、Debug Init Containers | Kubernetes、Init Containers | Kubernetes
公式ドキュメントは、init コンテナがいつまでも失敗し続けないように、Pod の activeDeadlineSeconds を使う方法も挙げています。activeDeadlineSeconds は、Pod の開始時刻(status.startTime。第5回)から数えて、Pod が失敗として扱われてコンテナが止められるまでの秒数の上限です。ただし、activeDeadlineSeconds は init コンテナが終わったあとも効き続け、正しく動いている Pod も止めてしまいます。そのため公式ドキュメントはアプリケーションを Job(終わりまで動いて止まる処理のためのリソース。第16回)として動かす場合だけに使うよう勧めています。
参考:Init Containers | Kubernetes、Pod | Kubernetes API Reference
init コンテナは一度成功しても、また動くことがあります。公式ドキュメントによると、Pod が再起動(restart)されると、すべての init コンテナがもう一度動きます。ここでいう Pod の再起動は第8回で見た Pod の作り直し(別の UID の新しい Pod になること)とは別のものです。同じ Pod のまま、init コンテナから動かし直すことを指すと考えられます。公式ドキュメントは、Pod が再起動される理由として次の 2 つを挙げています。
- Pod の土台になるコンテナ(Pod infrastructure container)が再起動された。まれなことで、ノードの管理者の権限(root)を持つ人が行う必要がある
- Pod の
restartPolicyがAlwaysのときにすべてのコンテナが終了し、init コンテナが完了した記録もガベージコレクションで消えていた
ガベージコレクションは、Kubernetes がクラスタの要らなくなったものを片付ける仕組みの総称です。ここでは、終了した init コンテナの記録が片付けられることを指すと考えられます。公式ドキュメントの Garbage Collection のページによると、kubelet は使われていないコンテナを 1 分ごとに片付けます。どちらの理由も、ふだん利用者が自分で起こすものではないと考えられます。それでも init コンテナはもう一度動くことがあるので、公式ドキュメントは、init コンテナの処理は何度動いても同じ結果になるように書く よう求めています。とくに emptyDir に書き込む処理は、書き出すファイルがすでにあるかもしれないことに備える必要があります。この回の init-content は > で index.html を書きます。> は一般に、ファイルがすでにあれば中身を置き換えるので、何度動いても同じ中身のファイルになります。
参考:Garbage Collection | Kubernetes、Init Containers | Kubernetes
ふつうの init コンテナには、lifecycle、livenessProbe、readinessProbe、startupProbe のフィールドを使えません。lifecycle は、コンテナを作った直後や止める直前に決まった処理を動かす設定です。Probe はコンテナが正常かどうかを kubelet が定期的に確かめる仕組みで、第34回で扱います。公式ドキュメントは、init コンテナは Pod が Ready になる前に終わりまで動く必要があり、Pod が動いているあいだ動き続けるサイドカーとは違う、と説明しています。readinessProbe を使えない理由としては、init コンテナでは準備ができたこと(readiness)を、終わったこととは別に決められないから、としています。
参考:Init Containers | Kubernetes、Container Lifecycle Hooks | Kubernetes
サイドカーの動き方
サイドカーも init コンテナなので、ふつうの init コンテナと同じく spec.initContainers の順番に沿って起動します。違うのは、kubelet がサイドカーの終わりを待たずに次へ進むことです。公式ドキュメントによると、サイドカーが動き始めて、kubelet がそのコンテナの started の状態を true にすると、kubelet は一覧の次の init コンテナを起動します。started が true になるのは、startup Probe を定義していなければ、コンテナの中でプロセスが動いているときです。startup Probe を定義していれば、それが成功したときです。
参考:Sidecar Containers | Kubernetes、Pod | Kubernetes API Reference
公式ドキュメントの Pods のページによると、サイドカーとして定義したコンテナは、メインのアプリケーションより先に起動し、Pod が終了するまで動き続けます。Sidecar Containers のページによると、サイドカーはほかの init コンテナやアプリケーションコンテナから独立していて、それらに影響を与えずに起動、停止、再起動できます。たとえば公式ドキュメントによると、サイドカーのイメージを変えると、Pod は再起動されず、サイドカーのコンテナが再起動されます。
第9回で見たとおり、サイドカーは Pod の restartPolicy に関係なく再起動されます。公式ドキュメントによると、サイドカーは Pod の restartPolicy を無視し、コンテナごとの restartPolicy(Always)に従います。第9回では、公式ドキュメントの Pod Lifecycle のページにある終了コードごとの比較の表から、アプリケーションコンテナの部分を引きました。同じ表にはサイドカーの列もあり、合わせると次のとおりです。
| 終了コード | Pod が Always | Pod が OnFailure | Pod が Never | サイドカー |
|---|---|---|---|---|
0(成功) | 再起動する | 再起動しない | 再起動しない | 必ず再起動する(アプリケーションコンテナが動いているあいだ) |
0 以外(失敗) | 再起動する | 再起動する | 再起動しない | 必ず再起動する(アプリケーションコンテナが動いているあいだ) |
Pod の Always、OnFailure、Never の列は、アプリケーションコンテナの扱いです。公式ドキュメントは Pod のコンテナの再起動に、倍々に延びる待ち時間が入ると説明しており、サイドカーもこれに当たります。API リファレンスによると、サイドカーは、アプリケーションコンテナがすべて終了するまで、終了するたびに再起動されます。アプリケーションコンテナがすべて終わると、サイドカーはすべて止められます。
参考:Pod | Kubernetes API Reference、Pod Lifecycle | Kubernetes、Adopting Sidecar Containers | Kubernetes
Pod が終了するときの順番も決まっています。公式ドキュメントによると、Pod にサイドカーがあると、kubelet は最後のアプリケーションコンテナが止まりきるまで、サイドカーに TERM シグナル(第8回)を送るのを待ちます。そのあとサイドカーは Pod の spec に書かれたのと逆の順番で止められます。サイドカーはほかのコンテナに必要とされなくなるまで動き続けるということです。アプリケーションコンテナが止まるのに時間がかかると、サイドカーが止まるのも遅れます。止まりきる前に猶予の時間(既定は 30 秒。第8回)が過ぎると、Pod は強制的な終了に移ることがあります。このとき、残っているコンテナは一斉に止められます。公式ドキュメントはこのときの猶予の時間を「短い」とだけ説明していて、この場面での長さは書かれていません。
参考:Pod Lifecycle | Kubernetes、Sidecar Containers | Kubernetes
サイドカーは最後に止められるので、止める番が来たときには猶予の時間がほとんど残っていないことがあります。公式ドキュメントによると、ほかのコンテナが猶予の時間を使い切ると、サイドカーは TERM シグナルを受けたあと、きれいに終わる前に KILL シグナル(強制的に止める合図)を受けます。そのため、Pod が終了するときにサイドカーが 0 以外の終了コードで終わっても、多くの場合、問題ではありません。公式ドキュメントは、Pod が終了するときのサイドカーの 0 以外の終了コードはよくあることで、Kubernetes の外のツール(external tooling)では基本的に無視してよい、としています。外のツールには、たとえばコンテナの終了コードを見張る監視のツールなどが当たると考えられます。
サイドカーはふつうの init コンテナと違って Probe を使えます。公式ドキュメントによると、サイドカーに readiness Probe を定義すると、その結果が Pod の Ready の判断に使われます。また、Job の Pod では、アプリケーションコンテナが終わったあと、サイドカーが Job の完了を妨げることはありません。公式ドキュメントには、Pod の restartPolicy が Never の Job にサイドカーを入れた例があります。
参考:Sidecar Containers | Kubernetes、Init Containers | Kubernetes
起動から終了までの順番
以下の図は、この回のマニフェストの Pod が起動してから削除されるまでに、コンテナが動く順番をコマ送りで表しています。図ではノードと kube-apiserver を省き、Pod の中だけを描いています。どのコンテナも、起動したり止めたりするのはノードの kubelet です。各箱の 3 行目はそのコマでのコンテナの状態です。そのコマで変わったものは緑の太字にし、動いていないコンテナは薄く描いています。
ふつうの init コンテナ(init-content)は終わるまで次へ進ませません。サイドカー(sidecar)は起動したところで次へ進ませ、そのあとも動き続けます。止まる順番は起動と逆で、サイドカーはアプリケーションコンテナより後に止まります。
参考:Init Containers | Kubernetes、Sidecar Containers | Kubernetes、Pod Lifecycle | Kubernetes、Volumes | Kubernetes
コンテナどうしが共有するもの
第8回で見たとおり、Pod の中のコンテナはネットワーク(IP アドレスとポート)とボリュームを共有できます。公式ドキュメントによると、init コンテナもアプリケーションコンテナと同じ資源(CPU、メモリ、ネットワーク)を使いますが、アプリケーションコンテナと直接やり取りはしません。init コンテナはアプリケーションコンテナが起動する前に止まるからです。そのため、init コンテナからのデータの受け渡しは一方向で、たとえば emptyDir のボリュームに情報を置く形になります。
参考:Init Containers | Kubernetes、Sidecar Containers | Kubernetes、Pods | Kubernetes
サイドカーはアプリケーションコンテナと同時に動くので、直接やり取りできます。公式ドキュメントによると、サイドカーは init コンテナと同じく常にネットワークを共有し、必要ならボリューム(ファイルシステム)も共有できます。この回のマニフェストでは、sidecar はネットワークを共有しているので、localhost で nginx に届きます。
ここまでの 3 種類のコンテナの違いを、次の表にまとめます。
| 項目 | ふつうの init コンテナ | サイドカー | アプリケーションコンテナ |
|---|---|---|---|
| 動く期間 | 起動時に 1 つずつ動き、終わる | 起動時に起動し、Pod の終了まで動き続ける | ふつうの init コンテナがすべて終わり、サイドカーが起動したあとに起動し、動き続ける |
| 再起動 | 失敗したときだけ(Pod が Never なら再起動しない) | 終了コードに関係なく必ず再起動する | Pod の restartPolicy に従う(コンテナごとに指定しなければ) |
| Probe | 使えない | 使える | 使える |
| アプリケーションコンテナとのやり取り | ボリュームを通した一方向だけ | ネットワークで直接。ボリュームも共有できる | 同じ Pod のほかのコンテナと localhost で通信できる |
参考:Init Containers | Kubernetes、Sidecar Containers | Kubernetes、Pod Lifecycle | Kubernetes、Pods | Kubernetes
手元で試す
ここまでの内容を手元の kind のクラスタで試す手順を示します。「マニフェストに書く場所」の Pod を作り、init コンテナ、サイドカー、アプリケーションコンテナが順に起動する様子を観察します。そのあと、init コンテナが書いたファイルを nginx が読めることと、サイドカーが localhost で nginx に届くことを確かめます。コマンドは macOS や Linux のシェル(bash や zsh)で実行する前提で書いています。Windows では WSL(Windows の中で Linux を動かす仕組み)などを使ってください。試す環境は次のとおりです。
| 項目 | 内容 |
|---|---|
| kind | v0.33.0 |
| Kubernetes | v1.37.0(kind v0.33.0 の既定のノードイメージ) |
| ノード構成 | シングルノード(クラスタ名 kind):コントロールプレーン 1 |
| 必要なもの | Docker、kubectl(v1.36〜v1.38) |
参考:Release v0.33.0 · kubernetes-sigs/kind、Version Skew Policy | Kubernetes
kind でクラスタを作る手順は、以下の記事で解説しています。この回ではシングルノードのクラスタを使います。Pod の中のコンテナは同じノードで動くので、この回の内容はノードの数に関係しないからです。
kind で検証用のクラスタを作るDocker のコンテナをノードにして、手元のマシンに Kubernetes のクラスタを作るツール kind の仕組みを図で説明します。kind create cluster で何が起きるかを押さえ、シングルノードとマルチノードのクラスタを作って、ノードの正体が Docker のコンテナであることを確かめる手順を紹介します。シングルノードのクラスタを用意する
まず、前の回のクラスタが残っていないかを確かめます。
$ kind get clusters
kind が表示されたら、作り直さずにそのまま使えるので、クラスタを作る次のコマンドは飛ばします。表示されなければ、クラスタを作ってから、もう一度一覧します。
$ kind create cluster
$ kind get clusters
kind get clusters に kind が表示されれば、クラスタができています。第3回で決めたとおり、ここからのコマンドには --context kind-kind を付けて、操作するクラスタをはっきりさせます。
続けて、ノードを一覧します。
$ kubectl get nodes --context kind-kind
ノードが 1 つ表示され、STATUS 列が Ready になっていれば準備ができています。Ready になっていなければ、少し待ってからもう一度実行します。
起動の順番を観察する
「マニフェストに書く場所」のマニフェストを、multi-demo.yaml という名前で、コマンドを実行するディレクトリに保存します。
apiVersion: v1
kind: Pod
metadata:
name: multi-demo
spec:
initContainers:
- name: init-content
image: busybox:1.28
command: ['sh', '-c', 'echo "Hello from the init container" > /work-dir/index.html; sleep 10']
volumeMounts:
- name: html
mountPath: /work-dir
- name: sidecar
image: busybox:1.28
restartPolicy: Always
command: ['sh', '-c', 'while true; do wget -q -O- http://localhost:80/ || echo "nginx is not ready yet"; sleep 5; done']
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
volumes:
- name: html
emptyDir: {}参考:Configure Pod Initialization | Kubernetes、Sidecar Containers | Kubernetes
Pod を作り、すぐに Pod の監視を始めます。第3回で見たとおり、--watch を付けると、一覧を表示したあとも変化を追い続けます。
$ kubectl apply -f multi-demo.yaml --context kind-kind
$ kubectl get pods --watch --context kind-kind
multi-demo の行が、状態が変わるたびに書き足されていくはずです。init コンテナを持つ Pod では、STATUS 列に init コンテナの進み具合が表示されます。公式ドキュメントの例(2 つの init コンテナを持つ Pod が、1 つ目の init コンテナを待っているときに、kubectl get -f myapp.yaml で表示した例)では、表示は次のとおりです。
公式ドキュメントの Debug Init Containers のページは、init コンテナを調べるときに STATUS 列に表示されることがある値を、次のように説明しています。
STATUS 列の値 | 意味 |
|---|---|
Init:N/M | Pod に init コンテナが M 個あり、そのうち N 個が終わった |
Init:Error | init コンテナが失敗した |
Init:CrashLoopBackOff | init コンテナが失敗を繰り返している |
Pending | Pod は、まだ init コンテナを動かし始めていない |
PodInitializing または Running | Pod は、init コンテナを動かし終えた |
第9回で見たとおり、STATUS 列は Pod のフェーズそのものではありません。表の Pending も「init コンテナをまだ動かし始めていない」という kubectl の表示で、init コンテナが動いているあいだも続くフェーズの Pending とは意味が違います。
参考:Pod Lifecycle | Kubernetes、Debug Init Containers | Kubernetes
手元では、STATUS 列が Pending や Init:0/2 から始まり、PodInitializing などを経て、Running になると考えられます。Init:0/2 の 2 は initContainers に書いた 2 つ(init-content と sidecar)と考えられますが、表の説明はふつうの init コンテナを前提にしたものです。サイドカーがどう数えられるかは、公式ドキュメントからは確かめられませんでした。そのため、途中で Init:1/2 などが表示されることも、されないこともあると考えられます。途中の値が違っていても、最後に Running になれば問題ありません。READY 列も、サイドカーを数に入れるかどうかが公式ドキュメントで確かめられないので、この手順では見るところにしません。init-content は 10 秒待ってから終わるので、Init:0/2 の行はしばらく続くはずです。クラスタを作り直したばかりなら、busybox:1.28 と nginx:1.14.2 のイメージの取得にも時間がかかります(第9回)。Running になったら、Ctrl+C を押して監視を止めます。
参考:Debug Init Containers | Kubernetes、Init Containers | Kubernetes、Images | Kubernetes
コンテナごとの状態と起動の時刻を確かめる
Pod を詳しく表示します。
$ kubectl describe pod multi-demo --context kind-kind
手元では、Init Containers: の下に init-content: と sidecar: が、Containers: の下に nginx: が並ぶと考えられます。公式ドキュメントの例(2 つの init コンテナを持つ別の Pod myapp-pod を表示した例)では、表示の一部は次のとおりです。
例は 1 つ目の init コンテナが動いている途中の表示で、Status: は Pod のフェーズの Pending です。init コンテナは Init Containers: の下に、アプリケーションコンテナは Containers: の下に並びます。手元の Pod は Running になったあとなので、表示は例と違うはずです。見るところを次の表にまとめます。
| 見るところ | 表示されると考えられること |
|---|---|
Init Containers: の init-content: | State: が Terminated で、Reason: は Completed、Exit Code: は 0(成功して終わったため) |
Init Containers: の sidecar: | State: が Running(サイドカーは initContainers に書いたので、ここに並ぶと考えられる) |
Containers: の nginx: | State: が Running |
参考:Debug Init Containers | Kubernetes、Init Containers | Kubernetes、Sidecar Containers | Kubernetes
Completed と Exit Code: 0 は公式ドキュメントの Debug Init Containers のページの例で、成功して終わった init コンテナに表示されている値です。
次に、起動の順番を時刻で確かめます。init-content が終わった時刻、sidecar が起動した時刻、nginx が起動した時刻を取り出します。-o jsonpath は第5回で使った、フィールドを指定して取り出す出力の形式です。[?(@.name=="init-content")] は、一覧の中から name が init-content の項目だけを選ぶ指定(filter)です。{"\n"} の手前に書いた init-content finished: などの文字はそのまま表示されます。
$ kubectl get pod multi-demo -o jsonpath='init-content finished: {.status.initContainerStatuses[?(@.name=="init-content")].state.terminated.finishedAt}{"\n"}sidecar started: {.status.initContainerStatuses[?(@.name=="sidecar")].state.running.startedAt}{"\n"}nginx started: {.status.containerStatuses[?(@.name=="nginx")].state.running.startedAt}{"\n"}' --context kind-kind
参考:JSONPath Support | Kubernetes、Pod | Kubernetes API Reference
取り出しているフィールドを次の表にまとめます。
| フィールド | 表すもの |
|---|---|
status.initContainerStatuses | init コンテナ(サイドカーを含む)ごとの状態の一覧 |
status.containerStatuses | アプリケーションコンテナごとの状態の一覧 |
state.terminated.finishedAt | 終了したコンテナが、最後に終了した時刻 |
state.running.startedAt | 動いているコンテナが、最後に起動(再起動)した時刻 |
参考:Pod | Kubernetes API Reference、Init Containers | Kubernetes
3 行の時刻は上から順に同じか後になっているはずです。kubelet は init-content が成功して終わってから sidecar を起動し、sidecar が起動してから nginx を起動するからです。sidecar や nginx が再起動していれば、表示されるのは再起動した時刻です。
参考:Init Containers | Kubernetes、Sidecar Containers | Kubernetes、Pod | Kubernetes API Reference
続けて、Pod の Initialized の condition を取り出します。
$ kubectl get pod multi-demo -o jsonpath='{.status.conditions[?(@.type=="Initialized")].status}{"\n"}' --context kind-kind
True が表示されると考えられます。公式ドキュメントは Initialized を「すべての init コンテナが成功して終わった」ことを表す condition と説明しています。ただしこの説明は終わらないサイドカーを前提にしていません。サイドカーがあるときに、いつ True になるかは公式ドキュメントに書かれていません。nginx が起動したあとなので True になっていると考えられますが、推測です。
ボリュームでファイルを共有していることを確かめる
nginx のコンテナの中で、init コンテナが書いたファイルを表示します。kubectl exec はコンテナの中でコマンドを動かすコマンドで、-c で動かすコンテナを指定します。-- の後ろがコンテナの中で動かすコマンドです。cat は一般に、ファイルの中身を表示するコマンドです。
$ kubectl exec multi-demo -c nginx --context kind-kind -- cat /usr/share/nginx/html/index.html
Hello from the init container の 1 行が表示されるはずです。init-content が自分の /work-dir に書いたファイルが、nginx の /usr/share/nginx/html から見えています。2 つのコンテナは同じ emptyDir のボリューム html を、別々のパスにマウントしているからです。init-content はもう終わっているので、Pod の中で今も動いているのは sidecar と nginx です。
参考:Volumes | Kubernetes、Configure Pod Initialization | Kubernetes
リファレンスによると、-c を書かなかった場合、kubectl exec は kubectl.kubernetes.io/default-container のアノテーションで指定されたコンテナか、Pod の最初のコンテナ(the first container in the pod)を選びます。コンテナが複数ある Pod では、-c でコンテナを指定するのが確実です。
localhost で通信していることを確かめる
サイドカーのログを表示します。kubectl logs の -c もログを表示するコンテナを指定するものです。リファレンスによると、Pod にコンテナが 1 つしかないときは、コンテナの名前を省略できます。この Pod はコンテナが複数あるので、-c sidecar を付けます。
$ kubectl logs multi-demo -c sidecar --context kind-kind
Hello from the init container の行が、5 秒ごとに書き足されて並んでいるはずです。sidecar が localhost:80 に問い合わせ、nginx が index.html の中身を返しているからです。最初の何行かは nginx is not ready yet などの失敗の行になっていることもあると考えられます。sidecar は nginx より先に起動するので、nginx がまだ待ち受けていないうちに問い合わせることがあるからです。サイドカーがアプリケーションコンテナより先に動くことを前提に、補助の処理を書く必要があるとわかります。
このログには init コンテナ、ボリューム、アプリケーションコンテナ、サイドカーの 4 つがすべて関わっています。init-content がボリュームに書いたファイルを、nginx が読んで返し、それを sidecar が localhost で受け取っています。sidecar にはボリュームをマウントしていないので、sidecar がファイルの中身を知る方法は、nginx に問い合わせることだけです。ログを追い続けたいときは、-f を付けると新しい行が出るたびに表示されます。止めるときは Ctrl+C を押します。
片付け
作った Pod を、マニフェストを指定して削除し、消えたことを確かめます。
$ kubectl delete -f multi-demo.yaml --context kind-kind
$ kubectl get pods --context kind-kind
削除すると、kubelet は先に nginx を止め、nginx が止まりきってから sidecar を止めます。kubectl delete は既定では、Pod が消えるまで待ってから終わります。猶予の時間(既定では 30 秒)が過ぎるまで待つことがあります。kubectl get pods の一覧に multi-demo が無ければ、削除できています。Pod がノードから消えたので、emptyDir のボリュームのデータも消えています。
参考:Pod Lifecycle | Kubernetes、kubectl delete | Kubernetes、Volumes | Kubernetes
最後に、クラスタを削除し、消えたことを確かめます。
$ kind delete cluster
$ kind get clusters
kind delete cluster は存在しないクラスタを指定してもエラーを返しません。kind get clusters に kind が表示されなくなったことを確かめます。kubeconfig に kind-kind のコンテキストが残っていたら、第3回の片付けと同じく kubectl config delete-context kind-kind で消します。
注意点
init コンテナとサイドカーでつまずきやすい点と、後から変えられない点、バージョンで変わる点を、次の表にまとめます。
| 項目 | 内容 |
|---|---|
サイドカーは initContainers に書く | containers に並べたコンテナは、起動や停止の順番を制御しない。サイドカーにするには initContainers の項目に restartPolicy: Always を書く |
| init コンテナは何度も動くことがある | Pod が再起動されると、すべての init コンテナがもう一度動く。何度動いても同じ結果になるように書き、emptyDir に書くときはファイルがすでにある場合に備える |
| init コンテナが失敗するとアプリケーションは起動しない | 成功するまで再起動される。Pod の restartPolicy が Never なら、Pod 全体が失敗として扱われる |
| ふつうの init コンテナは Probe を使えない | lifecycle、livenessProbe、readinessProbe、startupProbe を書けない。サイドカーは Probe を使える |
| サイドカーはアプリケーションより先に動く | サイドカーはアプリケーションコンテナより先に起動する。アプリケーションコンテナがまだ待ち受けていないことがあると考えて、補助の処理を書く |
| 終了時のサイドカーの終了コードは気にしない | Pod が終了するとき、ほかのコンテナが猶予の時間を使い切ると、サイドカーは強制的に止められる。0 以外の終了コードはよくあること |
| コンテナの一覧は後から変えられない | init コンテナもアプリケーションコンテナも、Pod を作ったあとで足したり減らしたりはできない |
| 資源の計算は、コンテナを足し合わせるだけではない | init コンテナは順に 1 つずつ動くので、Pod の資源の要求は、おおまかには、init コンテナの中の最大値と、アプリケーションコンテナとサイドカーの合計の大きいほうで計算される(第29回) |
| サイドカーは v1.33 から GA | v1.28 で初めて入った機能。古いバージョンのクラスタや、新しいフィールドに対応していないツールでは、正しく動かないことがある |
参考:Init Containers | Kubernetes、Sidecar Containers | Kubernetes、Pod Lifecycle | Kubernetes、Pod | Kubernetes API Reference、Adopting Sidecar Containers | Kubernetes
関連記事
Pod の数を一定に保つ ReplicaSet と、ReplicaSet が Pod を選ぶラベルセレクタについては、以下の記事で解説しています。
ReplicaSet を動かして理解する同じ Pod を指定した数だけ動かし続ける ReplicaSet の仕組みを図で説明します。ReplicaSet がラベルセレクタで Pod を選んで数える決まり、自分のものにする Pod の条件、数を減らすときに消す Pod の選び方を押さえ、kind のクラスタでラベルを書き換えた Pod が管理から外れる様子を観察する手順を紹介します。Pod のフェーズ、コンテナの状態、restartPolicy による再起動の仕組みについては、以下の記事で解説しています。
この回で使った emptyDir などのボリュームの種類については、以下の記事で解説しています。