Certified Kubernetes Administrator (CKA) #10 Workloads 1: Deployment の深掘り、ReplicaSet、rolling update/rollback

#9 RBAC ではユーザーと ServiceAccount に最小権限を付与する方法を扱いました。今回からは試験の Workloads and Scheduling ドメイン (15%) に入ります。その最初のテーマは、運用者がクラスターで 最も頻繁に手をかけるワークロードである Deployment です。

アプリを立ち上げ、トラフィックが増えれば replica を増やし、新しいイメージで無停止アップデートし、問題が起きれば以前のバージョンに戻す — この 4 つが運用の日常であり、Deployment 1 つがそのすべてを担います。CKAD でマニフェストを書く視点から Deployment を見たなら、今回は 運用者が kubectl で素早く扱う視点 から rollout と rollback まで深く見ていきます。

Deployment → ReplicaSet → Pod の階層 #

Deployment を理解する出発点は、それが 自分だけで Pod を作らない という事実です。Deployment は ReplicaSet を作り、ReplicaSet が Pod を作ります。つまり 3 段階の階層構造です。

Deployment   (望ましい状態を宣言 + rollout 履歴を管理)
   └─ ReplicaSet   (指定された replica 数だけ Pod を維持)
        └─ Pod ... Pod   (実際にコンテナが動く単位)

各層の役割は明確です。

リソース責務
Deployment望ましい状態を宣言し、アップデート時に新しい ReplicaSet へ入れ替え、以前のバージョンを revision として保管
ReplicaSet自身が管理するラベルの Pod 数を常に指定された replica 数に維持
Podコンテナが実際に動く最小単位

アップデートが起きると Deployment は 新しい ReplicaSet をもう 1 つ作り、新しい ReplicaSet の Pod を増やしながら以前の ReplicaSet の Pod を減らします。以前の ReplicaSet は削除されず replica 0 で残り、rollback の足がかりになります。

# 1 つの Deployment が抱える ReplicaSet と Pod を一目で
k get deploy,rs,pod -l app=web

ラベル selector で束ねられる #

階層が互いを見つける方法は ラベル selector です。ReplicaSet は spec.selector.matchLabels で自身が管理する Pod を識別し、その selector は spec.template.metadata.labels と一致しなければなりません。この 2 つが食い違うと API server が Deployment の作成を拒否します。

spec:
  selector:
    matchLabels:
      app: web        # このラベルを持つ Pod を自分のものとして管理
  template:
    metadata:
      labels:
        app: web      # selector と必ず一致

もう 1 つ覚えておく点は、spec.selector作成後に変更不可 (immutable) なフィールドだということです。selector を変えたいなら Deployment を作り直さなければならないので、最初に正確に決めておく方が速いです。

作成とスケール #

運用者はマニフェストを最初から手で書くより、kubectl で骨組みを作ってから整える ケースが多いです。試験でもこちらが圧倒的に速いです。

# Deployment 作成 (replica 3、イメージ指定)
k create deploy web --image=nginx:1.25 --replicas=3

# YAML の骨組みだけ抜き出して編集後に適用 (do = --dry-run=client -o yaml)
k create deploy web --image=nginx:1.25 $do > deploy.yaml
k apply -f deploy.yaml

作成した Deployment の replica 数を変える方法は 2 つあります。

# 1) kubectl scale: 最も速い
k scale deploy web --replicas=5

# 2) マニフェストを修正して再適用: 宣言的で GitOps とかみ合う
k edit deploy web        # spec.replicas を修正

急ぐときは k scale が速いですが、運用の定石はマニフェストの replicas を変えて再適用する 宣言的な方式です。k scale で変えた値は次の k apply がマニフェストの値に上書きするので、2 つを混ぜて使うと replica 数が意図と食い違うことがあります。

# 現在の replica と可用状態を確認
k get deploy web
# NAME   READY   UP-TO-DATE   AVAILABLE   AGE
# web    5/5     5            5           2m

READY は準備できた/望ましい Pod 数、UP-TO-DATE は最新の template を反映した Pod 数、AVAILABLE は可用 Pod 数です。rollout 中はこの 3 つの値が互いに異なるので、アップデート完了の可否を判断するときは一緒に見ます。

ローリングアップデート #

Deployment の真価は アップデート戦略 にあります。デフォルト戦略の rollingUpdate は以前の Pod を一気に下ろさず、新しい Pod を少しずつ立ち上げながら以前の Pod を少しずつ下ろして サービスを止めずにバージョンを入れ替えます。

戦略パラメータ: maxSurge と maxUnavailable #

rollingUpdate の速度と安全性は 2 つのパラメータが決めます。

パラメータ意味デフォルト値
maxSurge望ましい replica 数を 超えて 一時的に追加で立ち上げられる Pod 数 (または %)25%
maxUnavailablerollout 中に 可用でなくてもよい 最大 Pod 数 (または %)25%

maxSurge を大きくすると新しい Pod を多く先に立ち上げるので速いですが、一時的にリソースをより使います。maxUnavailable を 0 にすると常に望ましい数だけ可用 Pod が維持され 無停止に最も安全 ですが、新しい Pod が Ready になるまで待つので rollout が遅くなります。無停止が絶対条件なら maxUnavailable: 0 の組み合わせが定石です。

spec:
  replicas: 4
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1          # 最大 5 個まで一時的に許容
      maxUnavailable: 0    # 可用 Pod は常に 4 個保証

イメージ入れ替えで rollout をトリガー #

アップデートはたいてい イメージタグの変更 で起きます。template の何かが変わると Deployment が新しい ReplicaSet を作って rollout を開始します。

# コンテナイメージを新しいタグに (最も多い rollout トリガー)
k set image deploy/web nginx=nginx:1.26

# またはマニフェストを直接編集
k edit deploy web        # spec.template.spec.containers[].image を修正

ここで nginx=コンテナ名 です。イメージではなくコンテナ名を書かなければならないので、コンテナ名が紛らわしいなら k get deploy web -o jsonpath='{.spec.template.spec.containers[*].name}' で先に確認します。

rollout 状態と履歴の追跡 #

アップデートが進行している間と終わった後を kubectl rollout で追跡します。

# rollout が終わるまで進行状況を見守る (終わると 0 で終了)
k rollout status deploy/web

# revision 履歴 (各 revision は 1 つの ReplicaSet に対応)
k rollout history deploy/web

# 特定 revision の template 詳細
k rollout history deploy/web --revision=2

k rollout status は rollout が完了すると終了コード 0 で終わるので、アップデート完了を待つシグナル として使えます。k rollout history の各 revision は先ほど見た ReplicaSet 1 つに対応します。

CHANGE-CAUSE の記録: –record の代わりに annotation #

k rollout historyCHANGE-CAUSE 欄は各 revision がなぜ作られたかを示します。以前はコマンドの末尾に --record を付けて埋めましたが、このフラグは deprecated です。今は kubernetes.io/change-cause annotation を直接付ける方式が推奨されます。

# 推奨: annotation で変更理由を記録
k annotate deploy/web kubernetes.io/change-cause="update nginx to 1.26"

# 確認
k rollout history deploy/web
# REVISION   CHANGE-CAUSE
# 1          <none>
# 2          update nginx to 1.26

CHANGE-CAUSE を埋めておくと rollback 対象の revision を選ぶときに何が何だったか一目で分かるので、運用の障害対応が速くなります。

ロールバック #

新しいバージョンに問題が起きたとき、運用者が真っ先に手をかけるのが rollback です。Deployment は以前の ReplicaSet を revision として保管するので、1 行で戻せます。

# すぐ直前の revision に rollback
k rollout undo deploy/web

# 特定 revision に rollback
k rollout undo deploy/web --to-revision=2

rollback もまた rollingUpdate 戦略に従って無停止で進みます。rollback が終わった後に k rollout history を見ると、戻した内容が 新しい revision として追加 されます。つまり revision 番号は減らずに上がり続けます。

pause と resume: 変更をまとめて一度に #

複数のフィールドを連続して変えるとき、変更のたびに rollout が始まると ReplicaSet がどんどん増えます。このとき pause で rollout を止め、変更をすべて適用してから resume で一度に rollout します。

# rollout 一時停止
k rollout pause deploy/web

# この間の変更は即座に rollout されず溜まる
k set image deploy/web nginx=nginx:1.26
k set resources deploy/web -c=nginx --limits=cpu=200m,memory=256Mi

# 溜めた変更を 1 回の rollout で適用
k rollout resume deploy/web

rollout を途中で止めて一部だけ検証してから続行するかどうかを判断するカナリア確認にも pause/resume が役立ちます。

recreate vs rollingUpdate #

戦略には rollingUpdate のほかに Recreate もあります。2 つの違いを明確に知っておくと、無停止が必要な場所とそうでない場所を分けられます。

項目RollingUpdate (デフォルト)Recreate
入れ替え方式新しい Pod を立ち上げながら以前の Pod を段階的に終了以前の Pod を すべて終了した後 に新しい Pod を作成
サービス停止なし (無停止)あり (切り替えの瞬間にダウンタイム発生)
リソースrollout 中に一時的により使用 (maxSurge)追加使用なし
2 バージョンの共存一時的に共存共存しない
使う場所ほとんどのステートレス web/API サービス2 バージョンが同時に立ってはいけないアプリ (スキーマ衝突など)

Recreate は旧バージョンと新バージョンが同じデータベーススキーマを共有できないときのように、2 バージョンの共存自体が問題になる場合 に使います。それ以外の一般的なステートレスサービスは rollingUpdate がデフォルトであり正解です。

運用視点: 無停止アップデートが保証される条件 #

rollingUpdate 戦略だけで無停止が自動保証されるわけではありません。新しい Pod が本当にトラフィックを受ける準備ができたか を Kubernetes が知る必要があるからです。このシグナルが readinessProbe です。

readinessProbe がないとコンテナが起動した途端に Ready とみなされ、アプリがまだ初期化中なのに Service がトラフィックを送ります。rollingUpdate がこの Pod を可用とカウントして以前の Pod を下ろすと、その瞬間にリクエストが失敗します。したがって無停止の実際の条件は rollingUpdate 戦略 + 正確な readinessProbe + maxUnavailable: 0 の組み合わせです。probe は #11 以降とトラブルシューティング編でさらに扱います。

試験ポイント #

  • 階層を覚えます。 Deployment が ReplicaSet を作り、ReplicaSet が Pod を作ります。k get deploy,rs,pod で一度に確認します。
  • selector は immutable であり、template.labels と一致しなければなりません。最初に正確に決めます。
  • 作成は k create deploy --image= --replicas=、スケールは k scale deploy --replicas= で素早く処理します。
  • イメージ入れ替えは k set image deploy/<name> <コンテナ名>=<イメージ> です。イメージではなく コンテナ名 を書きます。
  • rollout 追跡は k rollout status/history、rollback は k rollout undo [--to-revision=N] です。revision 番号は減らず上がります。
  • --record は deprecated です。変更理由は kubernetes.io/change-cause annotation で記録します。
  • 無停止が条件なら strategy.rollingUpdate.maxUnavailable: 0 の組み合わせを思い出します。

まとめ #

この記事で押さえたこと:

  • Deployment → ReplicaSet → Pod の階層 とそれを束ねるラベル selector
  • k create deploy/k scale での作成とスケール、宣言的な方式と命令型の方式の違い
  • rollingUpdate 戦略 (maxSurge/maxUnavailable)、k set image/k edit での rollout トリガー
  • k rollout status/history/undo でバージョンを追跡して戻す rollback、pause/resume で変更をまとめて適用
  • recreate vs rollingUpdate の違いと無停止アップデートが保証される実際の条件 (readinessProbe との連携)

Deployment の動作原理は Kubernetes ワークロードの基本です。コントローラーがどのように望ましい状態を維持するかは、#3 Pod ネットワーキングモデル の前に基礎を固めた k8s 基礎 #4 でも補強できます。

次へ — Workloads 2 #

Deployment はステートレスサービスのためのワークロードでした。しかしクラスターには、その型に収まらないワークロードがまだあります。

#11 Workloads 2: DaemonSet、StatefulSet、Job、CronJob では、すべてのノードに 1 つずつ Pod を立てる DaemonSet、安定した識別子と順序が必要な StatefulSet、一度実行して終わる Job、そして周期的に動く CronJob を扱います。各ワークロードがどんな状況のために設計されたか、kubectl でどう作り運用するかを順に整理します。

X