基準: Kubernetes v1.35 (kind
kindest/node:v1.35.8でコマンド・YAML を確認)
目次
- Pod の中の container の種類
- init container
- sidecar
- ambassador と adapter
- ephemeral container
- 例
- 紛らわしいものの比較
- 試験のポイント
- 参考ドキュメント
Pod の中の container の種類
| 種類 | 定義する場所 | いつ実行されるか | 再起動 | probe |
|---|---|---|---|---|
| app container | spec.containers | init 段階が終わった後、並列 | Pod restartPolicy | 対応 |
| init container | spec.initContainers | app より先に、順番に、完了まで | 失敗時に再試行 (後述) | 不可 |
| sidecar (native) | spec.initContainers + restartPolicy: Always | 定義順に起動し、その後ずっと | 常に | 対応 |
| ephemeral container | ephemeralcontainers subresource | ユーザーが追加したとき | しない | 不可 |
共有するもの
- network: Pod IP を一つ共有する。container 同士は
localhost:<port>で通信でき、その代わりポートが重複してはいけない - storage:
spec.volumesに定義した volume を各 container がvolumeMountsで mount する。受け渡しするファイルには普通emptyDirを使う - IPC: SystemV semaphore、POSIX shared memory も使える
- process namespace はデフォルトで分離。必要なら process namespace sharing を有効にする
init container
Pod の起動前に準備作業をする container。ドキュメントが挙げる用途は、Service ができるまで待つ、Git repository を volume に clone する、設定ファイルをテンプレートからレンダリングする、など。
- app とは別のイメージなので、
sedやdigのようなツールを app のイメージに入れなくて済む - app container からは見えない Secret にアクセスさせられる
- 条件が揃うまで app の起動を止めておく用途に使う
動作の流れ
- kubelet は network と storage の準備ができてから、spec に書かれた順に init container を実行する
- 各 init container が成功 (exit 0) しないと次のものは始まらない
- すべて終わると app container を起動する
失敗したとき:
- Pod
restartPolicyがNever: Pod 全体が失敗扱い OnFailure: その init container を再試行Always: init container にはOnFailureとして適用される
その他のルール:
- init が終わるまで Pod phase は
Pending、conditionInitializedは false。kubectl getの STATUS にはInit:0/2のように表示される - 通常の init container には
lifecycle、livenessProbe、readinessProbe、startupProbeを書けない。書くと API server がForbidden: may not be set for init containers without restartPolicy=Alwaysで拒否する - 再実行されることがあるので idempotent に書く。
emptyDirに書き込むファイルがすでにあるかもしれない - 実行中の Pod では init container の
imageだけ変更でき、変えても Pod は再起動しない - container 名は init・app 全体で一意でなければならない
- init container のポートは Service に紐付かない
リソースの計算
- init container の中で最も大きい request/limit = effective init request/limit
- Pod の effective request/limit = max(app・sidecar container の合計, effective init の値) + pod overhead
- スケジューリングはこの effective な値で行われるので、init が大きいと初期化のときにしか使わないリソースも予約される
sidecar
メインのアプリの隣で、ログ収集、モニタリング、データ同期のような補助機能を担う container。アプリのコードを変えずに機能を足すのが目的です。
native sidecar
SidecarContainers feature gate (v1.29 からデフォルト有効) で入った方式。initContainers の項目に restartPolicy: Always を付けると sidecar になります。
- init container と同じく順序が保証される。sidecar が
startedになると次の init container が始まる。startupProbeがあれば、それが成功して初めてstarted - 起動後は Pod が終わるまで動き続ける。終了すると Pod
restartPolicyに関係なく再起動 - probe に対応。
readinessProbeの結果は Pod の ready 判定に含まれる - Pod 終了時: メイン container が完全に止まってから sidecar に TERM が送られ、定義順の逆順に止まる
- Job ではメイン container が終われば、sidecar が残っていても Job は完了する
- image を変えると Pod ではなくその container だけが再起動
- 終了処理でメイン container が grace period を使い切ると、sidecar はすぐに SIGKILL を受けることがある。その場合の 0 以外の exit code は正常とみなしてよい
-
grace period: Pod の削除リクエストから強制終了までの猶予時間。
spec.terminationGracePeriodSecondsで指定し、デフォルトは 30 秒。この間に終わらなかった container は SIGKILL で終了する -
container ごとではなく、Pod 全体で 1 つのタイマーを共有する。メインが SIGTERM を受けて後処理をしている間も時間は進むので、メインが長引くほど sidecar の持ち時間が減る
-
例: grace period 30 秒、メインの後処理に 28 秒、ログ収集 sidecar は残りのログを送るのに 5 秒かかる場合
t=0s Pod 削除リクエスト。メインに SIGTERM t=28s メイン終了 (exit 0)。ここでやっと sidecar に SIGTERM t=30s 期限到達。後処理中の sidecar に SIGKILL -> exit 137 (128+9)メインが 30 秒を使い切ると、sidecar は SIGTERM すら受けずにメインと一緒に SIGKILL を受ける
-
sidecar はメインを補助する container なので、メインが正常に終了していれば Pod の終了は役目を果たしている。公式ドキュメントも、この場合の sidecar の 0 以外の exit code は外部ツールで無視するよう案内している。モニタリングが sidecar の 137 を障害として検知しないようにしておく
-
従来型の sidecar
containers に app container をもう一つ入れる方式。container 単位の restartPolicy がない古いバージョンでも動き、今でも有効です。ただし普通の app container なので:
- アプリより先に起動する保証がない
- Job では問題になる。Pod が
Succeededになるにはすべての container が終了する必要があり、動き続ける sidecar がいると終わらない
起動・終了の順序が問題にならず、すべての container が Pod の動作に必要なら、この方式で十分だと公式ドキュメントも説明しています。
ambassador と adapter
Kubernetes ブログの 2015 年の記事 “Patterns for Composite Containers” で sidecar と一緒に紹介されたパターン。現在の concepts ドキュメントには独立した項目がなく、実装も専用フィールドなしで container を追加するだけです。
| パターン | 役割 | 例 (ブログより) |
|---|---|---|
| sidecar | メイン container の機能拡張 | nginx + Git リポジトリを同期する container、ファイルシステムを共有 |
| ambassador | 外部接続を代わりに受け持つローカル proxy | アプリは localhost の Redis につなぎ、ambassador が読み書きを振り分けて実際のサーバーに転送 |
| adapter | 出力を標準形式に変換 | アプリごとに違うモニタリングデータの形式を共通の形式にして出す |
ambassador は network namespace の共有、adapter は volume か localhost の共有に頼る構造。三つとも実装は containers か native sidecar で行います。
ephemeral container
実行中の Pod に一時的に付けるデバッグ用 container。v1.25 から stable。
- Pod には作成後に container を追加できないが、ephemeral container は
ephemeralcontainerssubresource で追加する。だからkubectl editでは入れられない - 追加した後は変更・削除できない
- 自動再起動なし、リソース保証なし
ports、livenessProbe、readinessProbe、resourcesは使えない- static Pod では使えない
- 使う場面: container がクラッシュした、または distroless のように shell がなくて
kubectl execができないとき
kubectl debug -it <pod> --image=busybox:1.28 --target=<container>
--target は指定した container の process namespace に入り、そのプロセスを見られるようにする。container runtime が対応している必要があり、対応していなければ process namespace が分かれたまま起動することがある。
例
init container で設定ファイルを作り、native sidecar がアプリのログを読む Pod。
apiVersion: v1
kind: Pod
metadata:
name: web
spec:
initContainers:
- name: init-config
image: busybox:1.36
command: ["sh", "-c", 'echo "server_name=web" > /config/app.conf']
volumeMounts:
- name: config
mountPath: /config
- name: log-shipper
image: busybox:1.36
restartPolicy: Always
command: ["sh", "-c", "touch /logs/app.log; tail -F /logs/app.log"]
volumeMounts:
- name: logs
mountPath: /logs
containers:
- name: app
image: busybox:1.36
command:
[
"sh",
"-c",
"while true; do cat /config/app.conf >> /logs/app.log; sleep 5; done",
]
volumeMounts:
- name: config
mountPath: /config
- name: logs
mountPath: /logs
volumes:
- name: config
emptyDir: {}
- name: logs
emptyDir: {}
実行順: init-config 完了 → log-shipper 起動 (started) → app 起動。log-shipper は app と一緒に動き続ける。
同じ sidecar を従来型で書くなら、containers の下に移して restartPolicy を消せばよい。この場合 log-shipper と app は順序なしで一緒に起動します。
kubectl apply -f web.yaml
kubectl get pod web # STATUS: Init:0/2 → Running
kubectl logs web -c init-config # init container のログ
kubectl logs web -c log-shipper -f # sidecar のログ
kubectl logs web --all-containers
kubectl exec -it web -c app -- sh
kubectl describe pod web # Init Containers セクションの State/Reason
紛らわしいものの比較
init container、native sidecar、従来型の sidecar
| init container | native sidecar | 従来型の sidecar | |
|---|---|---|---|
| 場所 | initContainers | initContainers | containers |
container restartPolicy | なし | Always | なし |
| 実行期間 | 終わるまで | Pod の生存期間全体 | Pod の生存期間全体 |
| app より先に起動 | はい (完了まで) | はい (started まで) | 保証なし |
| probe | 不可 | 対応 | 対応 |
| app とのデータ受け渡し | 一方向 (volume に書いておく) | 双方向 | 双方向 |
| Job の完了を妨げる | いいえ | いいえ | はい |
| 終了順 | 該当なし | メインの後、逆順 | 順序なし |
kubectl exec と kubectl debug
kubectl exec | kubectl debug (ephemeral) | |
|---|---|---|
| 使うイメージ | 既存の container のイメージ | 新しく指定したイメージ |
| shell のないイメージ | 不可 | 可能 |
| クラッシュした container | 不可 | 可能 |
| Pod spec の変化 | なし | ephemeral container が残る |
試験のポイント
コマンド
マルチ container の Pod を一発で作る imperative コマンドはありません。骨組みを作ってから YAML を直す流れです。
kubectl run web --image=busybox:1.36 --dry-run=client -o yaml \
--command -- sh -c 'sleep 3600' > web.yaml
# initContainers、追加の container、volumes を手で書く
kubectl logs <pod> -c <container>
kubectl exec -it <pod> -c <container> -- sh
kubectl debug -it <pod> --image=busybox:1.28 --target=<container>
kubectl explain
kubectl explain pod.spec.initContainers
kubectl explain pod.spec.initContainers.restartPolicy # Always = native sidecar
kubectl explain pod.spec.shareProcessNamespace # container 間のプロセス共有
kubectl explain pod.spec.volumes.emptyDir # container 間のファイル共有
kubectl explain pod.spec.containers.volumeMounts
参考ドキュメント
- Init Containers | Kubernetes
- Sidecar Containers | Kubernetes
- Ephemeral Containers | Kubernetes
- Pods: Pods with multiple containers | Kubernetes
- Pod Lifecycle: Pod phase, Restart policy, Pod shutdown and sidecar containers | Kubernetes
- Debug Running Pods: ephemeral container | Kubernetes
- Share Process Namespace between Containers in a Pod | Kubernetes
- The Distributed System ToolKit: Patterns for Composite Containers | Kubernetes Blog (2015)
- kubectl debug
- CNCF CKAD curriculum