Pod

Kubernetes の最小デプロイ単位で、1 つ以上のコンテナをまとめて管理するリソース

Kubernetesコンテナ

Pod とは

Pod は Kubernetes の最小デプロイ単位で、1 つ以上のコンテナをまとめて管理するリソースである。共有されるのは network namespace で、Pod にはアドレスファミリーごとに IP アドレスが 1 つ割り当てられ、中のコンテナは IP アドレスとポート空間まで共有する。だから互いを localhost で呼べる代わりに、2 つのコンテナが同じポートを listen することはできない。SystemV セマフォや POSIX 共有メモリといったプロセス間通信も、Pod 内では素通しで使える。ストレージの扱いは少し違い、Volume は Pod 側で宣言するが、使うコンテナが個別に volumeMounts で mount しない限り見えない。

この namespace を保持しているのは、アプリのコンテナより先に作られる基盤コンテナ (pause コンテナ) である。namespace と cgroup は、それを存続させるプロセスがいないと消えてしまうため、何もせず待つだけの pause がその役を担う。アプリのコンテナがクラッシュして入れ替わってもネットワーク設定が失われないのは、器である pause が生き続けているからだ。

Pod は使い捨ての単位でもある。いったんノードに割り当てられた Pod が別のノードへ移ることはなく、実行完了・Pod オブジェクトの削除・リソース不足による退避・ノード障害のいずれかで終わる。ノードが落ちたときに起きるのは Pod の移動ではなく、コントローラーによる別の Pod の作成で、名前も IP も UID も変わる。Pod 名や Pod IP を固定値と見なして設定を書くと、この入れ替わりで壊れる。構成は通常 1 Pod = 1 コンテナで、ログ収集やプロキシを足したいときにサイドカーで複数コンテナにする。

Pod の定義

定義で効いてくるのは resources と 2 種類の probe である。requests はスケジューラーがノードを選ぶときに使う見積もりで、limits は超えたときに絞られる (CPU) か kill される (メモリ) 上限である。probe は名前が似ているが役割が別で、readinessProbe が落ちても Service のエンドポイントから外れて新規トラフィックが止まるだけだが、livenessProbe が落ちるとコンテナが再起動される。起動に時間の掛かるアプリで livenessProbe の閾値だけを厳しくすると、起動しきる前に再起動を掛けられて延々と立ち上がらない。

apiVersion: v1
kind: Pod
metadata:
  name: api-server
  labels:
    app: api-server
spec:
  containers:
    - name: app
      image: myapp:1.0
      ports:
        - containerPort: 3000
      resources:
        requests: { cpu: 100m, memory: 128Mi }
        limits: { cpu: 500m, memory: 512Mi }
      readinessProbe:
        httpGet: { path: /ready, port: 3000 }
      livenessProbe:
        httpGet: { path: /healthz, port: 3000 }

Pod のライフサイクル

Pod の status.phase が取る値は 5 つで、これ以外は存在しない。

Pending ──→ Running ──┬──→ Succeeded (全コンテナが正常終了)
                      └──→ Failed    (少なくとも 1 つが失敗で終了)

Unknown       ノードと通信できず状態が取得できない
フェーズ説明
PendingAPI サーバーに受理されたが、まだ動き出していない (ノードへの割り当て待ち・イメージのダウンロード中)
Runningノードに束縛され全コンテナが作成済みで、少なくとも 1 つが実行中か起動・再起動の途中
Succeeded全コンテナが正常終了し、再起動されない (Job で到達する)
Failed全コンテナが終了し、少なくとも 1 つが非ゼロ終了かシステムによる停止で終わった
Unknown何らかの理由で Pod の状態が取得できない (ノードとの通信障害が典型)

kubectl get pods の STATUS 列に出る CrashLoopBackOffTerminating はフェーズではない。STATUS は人が読んで分かるようにした表示用の欄で、フェーズは Pod API のフィールドである。この 2 つを同じ軸だと思うと、存在しない状態遷移を探すことになる。

CrashLoopBackOff が指しているのは、コンテナの再起動に指数バックオフの待ちが掛かっている状態そのものである。kubelet は終了したコンテナを 10 秒・20 秒・40 秒と倍々の間隔で再起動し、待ち時間は 300 秒 (5 分) で上限に達する。コンテナが 10 分間問題なく動き続けるとこのタイマーはリセットされ、次のクラッシュは 1 回目として扱われる。逆に言えば、直ったように見えて 10 分持たずに再発する不具合では、待ち時間が伸び続けて復旧が遅く見える。

サイドカーパターン

従来のサイドカーは containers にアプリと並べて書く。

spec:
  containers:
    - name: app
      image: myapp:1.0
    - name: log-agent       # サイドカー: ログ収集
      image: fluent/fluent-bit:5.1.1
      volumeMounts:
        - name: logs
          mountPath: /var/log/app
  volumes:
    - name: logs
      emptyDir: {}

メインコンテナとサイドカーが同じ Volume を共有し、ログ収集・プロキシ・モニタリングといった横断的関心事を本体から分離する。イメージのタグを固定しているのは意図的で、latest のまま置くとノードによって違う版が動いたり、再起動しただけで版が変わったりする。

ただし containers に並べる書き方には起動順・終了順の保証がない。ログ収集より先にアプリが立ち上がって初期のログを取り逃す、Job でアプリが終わってもサイドカーが動き続けて Pod が完了しない、といった噛み合わせの悪さが出る。これに対して initContainersrestartPolicy: Always を付ける書き方があり、こちらはアプリのコンテナより先に起動して後に終わる。Kubernetes v1.29 以降は既定で使える。

spec:
  initContainers:
    - name: log-agent
      image: fluent/fluent-bit:5.1.1
      restartPolicy: Always   # これがサイドカーの指定 (init のまま終わらない)
      volumeMounts:
        - name: logs
          mountPath: /var/log/app
  containers:
    - name: app
      image: myapp:1.0

kubectl での操作

調査には順番がある。コンテナが一度も起動していない障害ではログが空なので、まず kubectl describe pod の Events を見る。ノードのリソース不足や taint でスケジューリングできない理由、イメージの取得失敗はログではなく Events に出る。起動した後で落ちているなら kubectl logs、既に再起動されて現世代のログが空なら --previous で 1 つ前のコンテナの出力を取る。

# Pod の一覧
kubectl get pods

# Pod の詳細 (イベント、状態)
kubectl describe pod api-server

# Pod のログ
kubectl logs api-server
kubectl logs api-server -c log-agent  # サイドカーのログ
kubectl logs api-server --previous    # 再起動前のコンテナのログ

# Pod に入る
kubectl exec -it api-server -- /bin/sh

# Pod の削除
kubectl delete pod api-server

Pod を直接作らない

本番環境では Pod を直接作成せず、Deployment 経由で管理する。Deployment が Pod の数を維持し、異常終了時に自動再作成する。

✅ Deployment → ReplicaSet → Pod (自動管理)
❌ Pod を直接作成 (異常終了しても再作成されない)

ECS タスクとの比較

複数コンテナの共同スケジューリング単位という位置づけで、ECS タスクは Pod に対応する。仕組みも近く、ECS の awsvpc モードはタスクに ENI を 1 枚割り当て、先に起動した pause コンテナのネットワークスタックを他のコンテナが共有する。結果としてタスク内のコンテナは ENI の IP で addressable になり、localhost で通信できる。違いが出るのは作り方で、ECS はタスク定義をリビジョンとして登録してから起動するのに対し、Pod は manifest をそのまま作成できる。手軽さの裏返しとして、前節の「直接作らない」規律が要る。

観点Kubernetes PodECS タスク
最小単位Pod (1+ コンテナ)タスク (1+ コンテナ)
サイドカーネイティブ対応サポートあり
ネットワークPod 内は localhostタスク内は localhost
ストレージ共有VolumeBind mount

踏みやすい落とし穴は 2 つに集約される。1 つは Pod IP や Pod 名を固定と見なす設計で、Pod は入れ替わる前提のものなので参照は Service 名へ寄せる。もう 1 つは resources の未設定で、requestslimits も書かない Pod は QoS クラス BestEffort になり、ノードのメモリが枯渇したときの退避で最初に選ばれる側に回る (kubelet は使用量が requests を超えているか、Pod の優先度、requests に対する使用量の比で順位を付ける)。平時は差が出ないため、ノードが混み始めた日に落ちるのは resources を書いていない Pod からになる。

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

関連用語

関連する記事