Initコンテナ
このページでは、Initコンテナの概要について説明します。 Initコンテナとは、Pod内でアプリケーションコンテナの前に実行される特別なコンテナです。 Initコンテナにはユーティリティやアプリケーションコンテナのイメージに存在しないセットアップスクリプトを含めることができます。
Podの仕様では、アプリケーションコンテナを記述するcontainers配列と並んで、Initコンテナを指定できます。
Kubernetesでは、サイドカーコンテナは、メインのアプリケーションコンテナよりも前に起動し、実行し続ける コンテナです。 このドキュメントでは、Podの初期化中に実行が完了するコンテナであるInitコンテナについて説明します。
Initコンテナを理解する
Podは、内部で実行される複数のアプリケーションコンテナを持つことができますが、アプリケーションコンテナが起動する前に実行される1つ以上のInitコンテナを持つこともできます。
Initコンテナは下記の項目をのぞいて、通常のコンテナと全く同じです:
- Initコンテナは常に完了するまで稼働します。
- 各Initコンテナは、次のInitコンテナが稼働する前に正常に完了しなくてはなりません。
Pod内のInitコンテナが失敗した場合、kubeletは成功するまで、Initコンテナの再起動を繰り返します。
しかし、PodのrestartPolicyがNeverに設定されていて、Podの起動時にInitコンテナが失敗した場合、KubernetesはPod全体を失敗として扱います。
PodにInitコンテナを指定するためには、Podの仕様にinitContainersフィールドをcontainerアイテムの配列として追加してください(アプリケーションのcontainersフィールドとそのコンテンツに似ています)。
詳細については、APIリファレンスのコンテナを参照してください。
Initコンテナのステータスは、.status.initContainerStatusesフィールドにコンテナのステータスの配列として返されます(.status.containerStatusesと同様)。
通常のコンテナとの違い
Initコンテナは、リソース制限、ボリューム、セキュリティ設定などのアプリケーションコンテナの全てのフィールドと機能をサポートしています。 ただし、Initコンテナのリソース要求と制限は、コンテナ間のリソース共有に記載されているように、異なる方法で処理されます。
通常のInitコンテナ(つまり、サイドカーコンテナを除く)は、lifecycle、livenessProbe、readinessProbe、startupProbeフィールドをサポートしていません。
InitコンテナはPodの準備が完了する前に実行を完了する必要があります。
一方、サイドカーコンテナはPodのライフタイム中は常に実行され続け、一部のProbeを サポートしています。
サイドカーコンテナの詳細については、サイドカーコンテナを参照してください。
単一のPodに対して複数のInitコンテナを指定した場合、KubeletはそれらのInitコンテナを順次実行します。 各Initコンテナは、次のInitコンテナが実行される前に正常に終了する必要があります。 全てのInitコンテナの実行が完了すると、KubeletはPodのアプリケーションコンテナを初期化し、通常通り実行します。
サイドカーコンテナとの違い
Initコンテナは、メインのアプリケーションコンテナが起動する前にタスクを実行して完了します。 サイドカーコンテナとは異なり、Initコンテナはメインコンテナと並行して継続的に実行されることはありません。
Initコンテナは順次実行され完了します。 すべてのInitコンテナが正常に完了するまで、メインコンテナは起動しません。
Initコンテナはlifecycle、livenessProbe、readinessProbe、startupProbeをサポートしていませんが、サイドカーコンテナはこれらすべてのProbeをサポートしてライフサイクルを制御します。
Initコンテナは、メインのアプリケーションコンテナとリソース(CPU、メモリ、ネットワーク)を共有しますが、直接やり取りすることはありません。 ただし、共有ボリュームを使用してデータの交換を行うことは可能です。
Initコンテナを使用する
Initコンテナはアプリケーションコンテナのイメージとは分離されているため、コンテナの起動に関連したコードにおいていくつかの利点があります:
- Initコンテナには、アプリケーションイメージに含まれないセットアップ用のユーティリティやカスタムコードを含めることができます。たとえば、セットアップ時に
sed、awk、python、digなどのツールを使用するためだけに、別のイメージをFROMしてイメージを作成する必要はありません。 - アプリケーションイメージをビルドする役割とデプロイする役割は、共同で単一のアプリケーションイメージをビルドする必要がないため、それぞれ独立して実施することができます。
- Initコンテナは、同じPod内のアプリケーションコンテナとは異なる方法でファイルシステムにアクセスできます。その結果、アプリケーションコンテナがアクセスできないSecretに対するアクセス権限を得ることができます。
- Initコンテナはアプリケーションコンテナが開始する前に完了するまで実行されるため、Initコンテナを使用することで、特定の前提条件が満たされるまでアプリケーションコンテナの起動をブロックしたり遅らせることができます。前提条件が満たされると、Pod内の全てのアプリケーションコンテナを並行して起動することができます。
- Initコンテナは、アプリケーションコンテナイメージのセキュリティを低下させる可能性のあるユーティリティやカスタムコードを安全に実行できます。不要なツールを分離することで、アプリケーションコンテナイメージの攻撃対象領域を制限できます。
例
Initコンテナを活用する方法について、いくつかのアイデアを次に示します:
-
シェルのワンライナーコマンドを使ってServiceが作成されるのを待機する:
for i in {1..100}; do sleep 1; if nslookup myservice; then exit 0; fi; done; exit 1 -
Downward APIを使用して、このPodをリモートサーバーに登録する。 以下のようなコマンドを実行します:
curl -X POST http://$MANAGEMENT_SERVICE_HOST:$MANAGEMENT_SERVICE_PORT/register -d 'instance=$(<POD_NAME>)&ip=$(<POD_IP>)' -
以下のようなコマンドを使ってアプリケーションコンテナの起動を待機する:
sleep 60 -
Gitリポジトリをボリュームにクローンする。
-
いくつかの値を設定ファイルに配置し、メインのアプリケーションコンテナのための設定ファイルを動的に生成するためのテンプレートツールを実行する。 例えば、そのPodの
POD_IPの値を設定ファイルに配置し、Jinjaを使ってメインのアプリケーションコンテナの設定ファイルを生成する。
Initコンテナの具体的な使用方法
下記の例は、2つのInitコンテナを含むシンプルなPodを定義しています。
1つ目のInitコンテナはmyserviesの起動を、2つ目のInitコンテナはmydbの起動をそれぞれ待ちます。
両方のInitコンテナの実行が完了すると、Podはspecセクションにあるアプリケーションコンテナを実行します。
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
labels:
app.kubernetes.io/name: MyApp
spec:
containers:
- name: myapp-container
image: busybox:1.28
command: ['sh', '-c', 'echo The app is running! && sleep 3600']
initContainers:
- name: init-myservice
image: busybox:1.28
command: ['sh', '-c', "until nslookup myservice.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for myservice; sleep 2; done"]
- name: init-mydb
image: busybox:1.28
command: ['sh', '-c', "until nslookup mydb.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for mydb; sleep 2; done"]
次のコマンドを実行して、このPodを開始します:
kubectl apply -f myapp.yaml
実行結果は下記のようになります:
pod/myapp-pod created
そして次のコマンドでステータスを確認します:
kubectl get -f myapp.yaml
実行結果は下記のようになります:
NAME READY STATUS RESTARTS AGE
myapp-pod 0/1 Init:0/2 0 6m
より詳細な情報は次のコマンドで確認します:
kubectl describe -f myapp.yaml
実行結果は下記のようになります:
Name: myapp-pod
Namespace: default
[...]
Labels: app.kubernetes.io/name=MyApp
Status: Pending
[...]
Init Containers:
init-myservice:
[...]
State: Running
[...]
init-mydb:
[...]
State: Waiting
Reason: PodInitializing
Ready: False
[...]
Containers:
myapp-container:
[...]
State: Waiting
Reason: PodInitializing
Ready: False
[...]
Events:
FirstSeen LastSeen Count From SubObjectPath Type Reason Message
--------- -------- ----- ---- ------------- -------- ------ -------
16s 16s 1 {default-scheduler } Normal Scheduled Successfully assigned myapp-pod to 172.17.4.201
16s 16s 1 {kubelet 172.17.4.201} spec.initContainers{init-myservice} Normal Pulling pulling image "busybox"
13s 13s 1 {kubelet 172.17.4.201} spec.initContainers{init-myservice} Normal Pulled Successfully pulled image "busybox"
13s 13s 1 {kubelet 172.17.4.201} spec.initContainers{init-myservice} Normal Created Created container init-myservice
13s 13s 1 {kubelet 172.17.4.201} spec.initContainers{init-myservice} Normal Started Started container init-myservice
このPod内のInitコンテナのログを確認するためには、次のコマンドを実行します:
kubectl logs myapp-pod -c init-myservice # 1つ目のInitコンテナを調査する
kubectl logs myapp-pod -c init-mydb # 2つ目のInitコンテナを調査する
この時点で、これらのInitコンテナはmydbとmyserviceという名前のServiceの検出を待機しています。
これらのServiceを検出するための設定は以下の通りです:
---
apiVersion: v1
kind: Service
metadata:
name: myservice
spec:
ports:
- protocol: TCP
port: 80
targetPort: 9376
---
apiVersion: v1
kind: Service
metadata:
name: mydb
spec:
ports:
- protocol: TCP
port: 80
targetPort: 9377
mydbおよびmyserviceというServiceを作成するために、以下のコマンドを実行します:
kubectl apply -f services.yaml
実行結果は下記のようになります:
service/myservice created
service/mydb created
Initコンテナが完了し、myapp-podというPodが実行状態に移行したことを確認できます:
kubectl get -f myapp.yaml
実行結果は下記のようになります:
NAME READY STATUS RESTARTS AGE
myapp-pod 1/1 Running 0 9m
この簡単な例は、独自のinitコンテナを作成する際のヒントになるはずです。 次の項目には、さらに詳細な使用例に関するリンクがあります。
Initコンテナのふるまいに関する詳細
Podの起動時に、kubeletはネットワークおよびストレージの準備が整うまで、Initコンテナを実行可能な状態にしません。 また、kubeletはPodのspecに定義された順番に従って、PodのInitコンテナを起動します。
各Initコンテナは次のInitコンテナが起動する前に正常に終了しなくてはなりません。
もし、あるInitコンテナがランタイムにより起動失敗した場合、もしくはエラーで終了した場合、そのPodのrestartPolicyの値に従ってリトライされます。
しかし、PodのrestartPolicyがAlwaysに設定されていた場合は、InitコンテナのrestartPolicyはOnFailureとして適用されます。
すべてのInitコンテナが成功するまで、PodはReadyになりません。
InitコンテナのポートはService配下に集約されません。
初期化中のPodはPending状態ですが、条件Initializedはfalseに設定されているはずです。
Podを再起動するとき、またはPodが再起動されたとき、全てのInitコンテナは必ず再度実行されます。
Initコンテナのspecに対する変更は、コンテナイメージフィールドに制限されています。
Initコンテナのimageフィールドを直接変更しても、Podの再起動や再作成はトリガー されません。
ただし、Podがまだ起動していない場合、その変更はPodの起動方法に影響を与える可能性があります。
Podテンプレートの場合、通常はInitコンテナの任意のフィールドを変更できます。 その変更の影響は、Podテンプレートがどこで使用されているかによって異なります。
Initコンテナは何度も再起動、リトライおよび再実行可能であるため、べき等(Idempotent)である必要があります。
特に、emptyDirにファイルを書き込むコードは、書き込み先のファイルがすでに存在している可能性を考慮に入れなければいけません。
Initコンテナは、アプリケーションコンテナが持つすべてのフィールドを持っています。
ただし、KubernetesではreadinessProbeの使用が禁止されています。
これは、Initコンテナでは完了とは別にreadiness状態を定義することができないためです。
この制約は、バリデーション時に強制されます。
Initコンテナが永久に失敗し続けることを防ぐために、Pod上でactiveDeadlineSecondsを使用してください。
activeDeadlineSecondsの設定はInitコンテナが実行中の時間にも適用されます。
ただし、activeDeadlineSecondsはInitコンテナが完了した後にも影響が及ぶため、アプリケーションをJobとしてデプロイする場合にのみ使用することを推奨します。
すでに正しく動作しているPodは、activeDeadlineSecondsを設定すると強制終了されます。
Pod内の各アプリケーションコンテナとInitコンテナの名前はユニークである必要があります。 他のコンテナと同じ名前を共有していた場合、バリデーションエラーが返されます。
コンテナ間のリソース共有
Initコンテナ、サイドカーコンテナ、アプリケーションコンテナの実行順序を考慮すると、リソース使用に関して以下のルールが適用されます:
- すべてのInitコンテナで定義された特定のリソースの要求または制限のうち、最も高い値が実効Init要求/制限となります。 リソース制限が指定されていない場合、これが最も高い制限であるとみなされます。
- リソースに対するPodの実効要求/制限は、以下のうち高い方になります。
- すべてのアプリケーションコンテナのリソース要求/制限の合計
- リソースに対する実効Init要求/制限
- スケジューリングは実効要求/制限に基づいて行われます。 つまり、Initコンテナは初期化のためにリソースを予約できますが、これらはPodのライフタイム中は使用されません。
- Podの実効QoS tierは、Initコンテナとアプリケーションコンテナの両方に適用されるQoS(サービス品質) tierです。
クォータと制限は、実効的なPodの要求と制限に基づいて適用されます。
InitコンテナとLinux cgroups
Linuxでは、Podレベルのコントロールグループ(cgroups)に対するリソース割り当ては、スケジューラーと同様に、実効的なPodの要求と制限に基づいています。
Podの再起動の理由
以下の理由によりPodは再起動し、Initコンテナの再実行を引き起こす可能性があります:
- Podインフラストラクチャコンテナが再起動された場合。 これは稀なケースであり、ノードへのルートアクセス権を持つ人が実行する必要があります。
restartPolicyがAlwaysに設定されている状態でPod内のすべてのコンテナが終了し、再起動が強制され、かつInitコンテナの完了記録がガベージコレクションにより失われた場合。
Initコンテナイメージが変更された場合、またはガベージコレクションによりInitコンテナの完了記録が失われた場合、Podは再起動されません。 これはKubernetes v1.20以降に適用されます。 それ以前のバージョンのKubernetesを使用している場合は、使用しているバージョンのドキュメントを参照してください。
次の項目
詳しく学ぶには、以下を参照してください:
- Initコンテナを持つPodの作成
- Initコンテナのデバッグ
- kubeletとkubectlの概要
- Probeの種類: Liveness、Readiness、Startup Probe
- サイドカーコンテナ