Init コンテナ

Kubernetes でメインコンテナの起動前に初期化処理を実行する特殊なコンテナ

Kubernetesコンテナ

Init コンテナとは

Init コンテナは、Pod のメインコンテナが起動する前に実行される特殊なコンテナである。初期化処理 (依存サービスの待機、DB マイグレーション、設定ファイルの生成) をメインコンテナから分離し、関心の分離を実現する。

Init コンテナは定義順に直列実行され、すべてが正常終了 (exit 0) するまでメインコンテナは起動しない。失敗したときに作り直されるのは Pod 全体ではなく、失敗したその Init コンテナである。Pod の restartPolicyAlways であっても Init コンテナだけは OnFailure として扱われ、kubelet が成功するまで同じ Init コンテナを起動し直す。例外は restartPolicy: Never の Pod で、この場合は起動中の Init コンテナが失敗した時点で Pod 全体が失敗扱いになる。

基本的な設定

apiVersion: v1
kind: Pod
metadata:
  name: api-server
spec:
  initContainers:
    - name: wait-for-db
      image: busybox:1.36
      command: ['sh', '-c', 'until nc -z postgres-svc 5432; do echo "waiting..."; sleep 2; done']
    - name: migrate
      image: myapp:1.0
      command: ['npm', 'run', 'migrate']
      env:
        - name: DATABASE_URL
          valueFrom:
            secretKeyRef:
              name: db-credentials
              key: url
  containers:
    - name: app
      image: myapp:1.0
      ports:
        - containerPort: 3000

この例では、まず wait-for-dbPostgreSQL の起動を待ち、次に migrate が DB マイグレーションを実行し、両方が成功してからメインの app コンテナが起動する。

メインコンテナとの違い

三者の違いは「いつ動き始めるか」と「いつ終わるか」で整理できる。

観点Init コンテナメインコンテナサイドカー
実行タイミングメインの前Init 完了後メインの前に起動し、Pod の間動き続ける
実行順序定義順に直列同時に起動定義順に起動 (完了は待たれない)
完了条件exit 0 が必須常時稼働常時稼働
失敗時そのコンテナが成功するまで起動し直されるrestartPolicy に従う単独で再起動され、他のコンテナは止めない
リソースメインとは別に指定するが、確保は初期化中だけで終わらない合計値が Pod の要求量の基準になるメインとは別に指定し、稼働中の合計に加わる

表のサイドカー列が Init コンテナの隣にあるのは偶然ではない。Kubernetes のサイドカーコンテナは、initContainers に並べたコンテナへ restartPolicy: Always を付けたもの、つまり Init コンテナの特殊形として定義されている (この指定は v1.29 で既定有効、v1.33 で安定版)。メインより先に起動する点は Init コンテナと同じで、完了を待たれずに Pod の生存期間中動き続ける点だけが違う。

Init コンテナはメインコンテナと異なるイメージを使える。マイグレーションツールやネットワークユーティリティなど、メインアプリには不要なツールを Init コンテナだけに含めることで、メインイメージをクリーンに保てる。

実務での活用パターン

依存サービスの待機

これが最も多い用途である。DB、キャッシュ、他のマイクロサービスが起動するまで待つ。

- name: wait-for-redis
  image: busybox:1.36
  command: ['sh', '-c', 'until nc -z redis-svc 6379; do sleep 1; done']

DB マイグレーション

メインアプリの起動前にスキーマを更新する。複数 Pod がある場合でも、Init コンテナは各 Pod で実行されるため、マイグレーションのべき等性を確保する必要がある。同じ Pod でも安全とは言えない。Pod が作り直されると成功済みの Init コンテナまで含めて全て再実行されるので、二度目の実行で失敗しない書き方 (共有ボリュームに出力ファイルが既にある場合の考慮も含む) が前提になる。

シークレットの取得

AWS Secrets Manager や HashiCorp Vault からシークレットを取得し、共有ボリュームにファイルとして書き出す。メインコンテナはファイルを読むだけでよい。

設定ファイルの動的生成

テンプレートから環境固有の設定ファイルを生成し、共有ボリュームに配置する。

Init コンテナが失敗した場合

Init コンテナが失敗すると、Pod は Init:Error または Init:CrashLoopBackOff 状態になる。restartPolicy: Always (デフォルト) の Pod では、kubelet が失敗した Init コンテナを成功するまで起動し直す。やり直されるのは失敗したコンテナだけで、成功済みの Init コンテナが再び走るのは Pod そのものが作り直されたときである。restartPolicy: Never にしていると再試行はなく、Init コンテナの失敗がそのまま Pod の失敗になる。

# Init コンテナの状態を確認
kubectl describe pod api-server
# Init Containers:
#   wait-for-db:
#     State: Terminated
#     Reason: Error
#     Exit Code: 1

# Init コンテナのログを確認
kubectl logs api-server -c wait-for-db

よくある落とし穴

Init コンテナのタイムアウト

依存サービスが起動しない場合、Init コンテナが永遠に待ち続ける。タイムアウトを設定し、一定時間後に失敗させる。

command: ['sh', '-c', 'timeout 60 sh -c "until nc -z postgres-svc 5432; do sleep 2; done"']

Pod 側の期限で止める手もあり、activeDeadlineSeconds は Init コンテナの時間も含めて数える。ただしこれは Pod 全体の寿命を切る設定なので、初期化を終えて正常に動いている Pod も期限が来れば終了させられる。使いどころは Job のように終わりのある処理で、常時稼働の Deployment には向かない。

リソース制限の未設定

Init コンテナにもリソース要求と制限 (resources) を設定する。マイグレーションが大量のメモリを消費すると、ノード全体に影響する。

逆に大きく書きすぎるのも損である。スケジューリングに使われる Pod の実効値は、「Init コンテナの中で最大の値」と「メインコンテナの合計値」を比べた大きい方で決まる。前者が勝つ設定にすると、初期化が終わって使われなくなった分まで Pod の取り分として押さえ続けることになる。

通常の Init コンテナにプローブは付けられない

livenessProbereadinessProbestartupProbelifecycle は通常の Init コンテナでは働かない。特に readinessProbe は Pod の検証段階で弾かれる (完了とは別の準備状態を定義できないため)。稼働し続けるものの死活を見たいなら、それは Init コンテナではなくサイドカー (restartPolicy: Always) として書く場面である。

基礎から学ぶなら関連書籍が手がかりになる。

この記事は役に立ちましたか?

関連用語

関連する記事