Deployment とローリングアップデートの仕組み

v1.37 時点(2026年10月23日 確認)

Kubernetes 基本仕様第12回 / 全45回 ・ 第2部 ワークロード
目次

この記事では、ReplicaSet を持ち、Pod を宣言的に更新するリソースである Deployment の仕組みを、図で説明します。Pod のひな形を書き換えたときに、Deployment が新しい ReplicaSet を作り、Pod を少しずつ入れ替える流れ(ローリングアップデート)がわかります。前のリビジョン(版)に戻す切り戻しの仕組みと、そのために古い ReplicaSet が残される理由もわかります。後半では kind のクラスタで Deployment のイメージを更新し、kubectl rollout status で進み具合を見て、kubectl rollout undo で切り戻す手順を示します。

ReplicaSet がラベルセレクタで Pod を選んで数える仕組みを扱った第11回の内容を前提にしています。Deployment・ReplicaSet・Pod の持ち主の関係を扱った第6回の内容も使います。まだの方は先にこちらをどうぞ。

基本仕様 ・ 第11回 ReplicaSet を動かして理解する同じ Pod を指定した数だけ動かし続ける ReplicaSet の仕組みを図で説明します。ReplicaSet がラベルセレクタで Pod を選んで数える決まり、自分のものにする Pod の条件、数を減らすときに消す Pod の選び方を押さえ、kind のクラスタでラベルを書き換えた Pod が管理から外れる様子を観察する手順を紹介します。 基本仕様 ・ 第6回 消した Pod が戻ってくる仕組み(調整ループ)コントローラーが実際の状態を望ましい状態に近づけ続ける調整ループの仕組みを図で説明します。Deployment・ReplicaSet・Pod の関係と、Pod を消したときに誰が気づいて代わりを作るのかを押さえ、kind のクラスタで Deployment の Pod を手で削除し、作り直される様子を観察する手順を紹介します。

以下の図は、イメージを nginx:1.14.2 から nginx:1.16.1 に更新し終えたあとの、Deployment と 2 つの ReplicaSet と Pod の関係を表しています。

イメージを更新したあとの Deployment、2 つの ReplicaSet、Pod の関係

Deployment は、Pod のひな形が変わるたびに新しい ReplicaSet を作り、Pod をそちらへ移します。移し終えた古い ReplicaSet は消さずに、数を 0 にして残します。古い ReplicaSet が前のリビジョンのひな形を覚えているので、そこへ戻せば切り戻せます。それぞれの ReplicaSet は pod-template-hash のラベルで自分の Pod だけを選ぶので、2 つの ReplicaSet が同じ Pod を取り合うことはありません。破線の「持つ」と実線の「選ぶ」は、第6回と第11回の図と同じ意味です。

参考:Deployments | Kubernetes

Deployment とは

Deployment は Pod と ReplicaSet を宣言的に更新するためのリソース です。公式ドキュメントは Deployment を、アプリケーションを動かす Pod の集まりを管理するもので、ふつうは状態を持たないアプリケーションに使う、と説明しています。利用者は Deployment に望ましい状態を書き、Deployment のコントローラー(第6回の deployment-controller)が、制御された速さで実際の状態を望ましい状態に変えていきます。

参考:Deployments | Kubernetes、kube-controller-manager | Kubernetes

第6回で見たとおり、Deployment は ReplicaSet を作って持ち、ReplicaSet が Pod を作って持ちます。Pod の数を保つのは ReplicaSet の仕事で、Deployment が受け持つのはその上の段の仕事です。公式ドキュメントが挙げる Deployment の典型的な使い方は、次の 7 つです。この回で扱うかどうかも添えます。

公式ドキュメントが挙げる使い方この回での扱い
Deployment を作り、ReplicaSet を展開する(ReplicaSet が裏で Pod を作る)扱う
Pod のひな形を書き換えて、Pod の新しい状態を宣言する(新しい ReplicaSet へ制御された速さで移す)扱う。入れ替えの速さの調整は第13回
今の状態が安定しないとき、前のリビジョンに切り戻す扱う
負荷が増えたときに、Pod の数を増やす数を変えても Pod の入れ替えが起きないことだけ扱う。自動で変える仕組みは第35回
展開を一時停止し、ひな形を何度か直してから再開する注意点で触れるだけ
Deployment の状態を、展開が止まったことの目印に使う扱う
要らなくなった古い ReplicaSet を片付ける残す数の決まりだけ扱う

参考:Deployments | Kubernetes

表に出てくる言葉を、この回での使い方とあわせて次の表にまとめます。どれも「Deployment の仕組み」で詳しく説明します。

言葉この回での意味
展開(rollout)Deployment が ReplicaSet と Pod を、Deployment に書かれたひな形に合わせていく一連の動き。公式ドキュメントは Deployment を作ることも ReplicaSet の展開として説明している
更新ひな形を書き換えたことで始まる展開
ローリングアップデート既定のやり方(RollingUpdate)で進む更新。古い Pod を新しい Pod に少しずつ入れ替える
リビジョン(revision)Deployment の版の番号。展開が始まるたび(ひな形を変えたときや、切り戻したとき)に増える
切り戻し(rollback)前のリビジョンのひな形に戻すこと

参考:Deployments | Kubernetes

公式ドキュメントは Deployment が持つ ReplicaSet を、利用者が直接管理しないよう 注意しています。数を変えるのも中身を変えるのも、Deployment を通して行います。

参考:Deployments | Kubernetes

なぜ Deployment が必要なのか

第11回の最後で見たとおり、ReplicaSet は Pod の数を保ちますが、Pod の中身を入れ替えることはしないと考えられます。公式ドキュメントの Pods のページによると、Pod のひな形を変えたり、新しいひな形に切り替えたりしても、すでにある Pod には直接の影響はありません。ひな形を変えたら、ワークロードのリソースが新しいひな形を使った置き換えの Pod を作る必要があります。どう置き換えるかの決まりは、ワークロードのリソースごとに違います。

参考:Pods | Kubernetes、ReplicaSet | Kubernetes

では、ReplicaSet だけで Pod を新しいイメージに入れ替えるには、どうすればよいでしょうか。ReplicaSet と同じ役目の古いリソース ReplicationController のページに、ReplicationController で Pod を入れ替えるときに勧められるやり方が書かれています。Pod の数が 1 の新しい ReplicationController を、新しいひな形で作ります。そのあと、新しい方を 1 つ増やして古い方を 1 つ減らすことを繰り返し、古い方が 0 になったら消します。2 つの ReplicationController が作る Pod には、イメージのタグのような、両者を見分けるラベルが少なくとも 1 つ要ります。タグは nginx:1.14.2 の 1.14.2 のように、イメージの名前の : の後ろに付けて、同じ系列のイメージのバージョンを見分けるものです。見分けるラベルが要るのは、セレクタが重ならないようにするためだと考えられます。同じページの Pod Selector の節は、セレクタが重なる ReplicationController ができてしまったら、消すときの扱いを自分で管理する必要があるとしています。Pod Template の節もひな形のラベルをほかのコントローラーと重ねないよう注意しています。また同じページは、理想的なローリングアップデートは、アプリケーションの準備ができたかを考えに入れ、要求を受けられる Pod の数を十分に保つものだ、とも書いています。

参考:ReplicationController | Kubernetes、Images | Kubernetes

このやり方を人が手で進めると、何度もコマンドを打つ必要があり、途中で失敗したときに戻すのも手作業になると考えられます。Deployment は、この入れ替えをクラスタの中の deployment-controller が進める仕組みです。第11回で見たとおり、公式ドキュメントはこれを宣言的な、サーバー側(server-side)のローリングアップデートと呼んでいます。利用者は新しいひな形を書いて渡すだけで、新しい ReplicaSet を作ること、数を少しずつ移すこと、古い ReplicaSet を残しておくことは、deployment-controller が引き受けます。ReplicationController のページも、ローリングアップデートを使いたいときは Deployment を勧めています。

参考:ReplicationController | Kubernetes、ReplicaSet | Kubernetes、Deployments | Kubernetes

Deployment の仕組み

マニフェストに書くフィールド

以下は Pod を 3 つ動かす Deployment のマニフェストです。第1回と第6回で引用した公式ドキュメントの例(controllers/nginx-deployment.yaml)と同じものです。後半の「手元で試す」でも、このマニフェストを使います。

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80

参考:Deployments | Kubernetes

spec の形は第11回の ReplicaSet とほとんど同じで、replicas、selector、template の 3 つが中心です。公式ドキュメントによると、Deployment の spec で必須なのは template と selector の 2 つだけです。この回にかかわるフィールドを、v1.37 時点の公式ドキュメントと API リファレンスに沿って次の表にまとめます。

フィールド内容既定値
spec.replicas望ましい Pod の数1
spec.selectorPod を選ぶラベルセレクタ。必須。ひな形のラベルに合っていなければ API が受け付けない。apps/v1 では作ったあとで変えられないなし
spec.templatePod のひな形。必須。restartPolicy に書けるのは Always だけなし
spec.strategy.type古い Pod を新しい Pod に入れ替えるやり方。RollingUpdate か RecreateRollingUpdate
spec.revisionHistoryLimit切り戻しのために残す、古い ReplicaSet の数10
spec.progressDeadlineSeconds更新が進まないまま、この秒数がたつと、更新が失敗したとみなされる600
spec.minReadySeconds新しい Pod が、コンテナが落ちずに Ready のまま、この秒数がたつと「使える(available)」とみなされる(第6回)0

参考:Deployments | Kubernetes、Deployment | Kubernetes API Reference

公式ドキュメントによると、metadata.name の nginx-deployment は、あとで作られる ReplicaSet と Pod の名前のもとになります。Deployment が作る ReplicaSet の名前は [Deployment の名前]-[ハッシュ] の形で、Pod の名前はさらにその後ろに文字列が付いたものです。公式ドキュメントの例では、ReplicaSet が nginx-deployment-75675f5897、Pod が nginx-deployment-75675f5897-7ci7o でした(第6回)。

参考:Deployments | Kubernetes

pod-template-hash のラベル

ReplicaSet の名前の [ハッシュ] の部分は、pod-template-hash というラベルの値と同じです。第6回で触れたとおり、このラベルは Deployment のコントローラーが付けます。公式ドキュメントの説明を次の表にまとめます。

項目内容
付けるものDeployment のコントローラー
付ける先Deployment が作るか自分のものにした、すべての ReplicaSet
値ReplicaSet の Pod のひな形(PodTemplate)から計算したハッシュ
加わる場所ReplicaSet のセレクタ、ReplicaSet が持つ Pod のひな形のラベル、ReplicaSet がすでに持っている Pod
目的同じ Deployment の ReplicaSet どうしが重ならないようにする

参考:Deployments | Kubernetes

ハッシュはデータから決まった手順で計算する短い値のことです。一般に、もとのデータが同じなら同じ値になり、違えばふつうは違う値になります。公式ドキュメントは計算の手順を説明していません。ただ、ひな形から計算する以上、ひな形を書き換えれば、新しい ReplicaSet には前と違う値が付くと考えられます。値がぶつかった場合に備えて、Deployment の status.collisionCount があります。API リファレンスによると、Deployment のコントローラーは、いちばん新しい ReplicaSet の名前を作るときに、ぶつかるのを避けるためにこのフィールドを使います。

参考:Deployments | Kubernetes、Deployment | Kubernetes API Reference

このラベルが要るのは、更新のあいだ、同じ Deployment の ReplicaSet が 2 つ同時にあるからです。どちらの Pod もひな形に書いた app: nginx のラベルを持っています。もし 2 つの ReplicaSet のセレクタが app: nginx だけだったら、セレクタが重なります。第11回で見たとおり、同じ Namespace で ReplicaSet のセレクタが重なると、コントローラーが Pod をいくつ動かすべきか決められなくなることがあります。pod-template-hash を加えたセレクタなら、それぞれの ReplicaSet は自分のひな形から作った Pod だけを選びます。公式ドキュメントは このラベルを変えないよう 注意しています。

参考:Deployments | Kubernetes、Labels and Selectors | Kubernetes

一方、Deployment 自身のセレクタは app: nginx のままです。Deployment 自身のひな形にこのラベルが加わるとは、公式ドキュメントには書かれていません。公式ドキュメントの kubectl describe deployments の例でも、Deployment の Pod Template: の Labels: は app=nginx だけです。

API リファレンスによると、Deployment の status.replicas は、その Deployment が対象にしている(ラベルがセレクタに合う)終了中でない Pod の合計です。そのため Deployment は新しい ReplicaSet と古い ReplicaSet の Pod をまとめて数えます。公式ドキュメントの、更新が止まった例の kubectl describe deployment の表示にも、次の行があります。

Replicas:       3 desired | 1 updated | 4 total | 3 available | 1 unavailable

このとき古い ReplicaSet に Pod が 3 つ、新しい ReplicaSet に Pod が 1 つあり、4 total はその合計にあたります。この例は「更新が止まったときと切り戻し」で扱います。

参考:Deployment | Kubernetes API Reference、Deployments | Kubernetes

ひな形を書き換えるとローリングアップデートが始まる

公式ドキュメントによると、Deployment の展開(rollout)が始まるのは、Pod のひな形(spec.template)が変わったときだけです。たとえば、ひな形のラベルやコンテナのイメージを書き換えたときです。Pod の数を変えるような、ほかの変更では展開は始まりません。

参考:Deployments | Kubernetes

既定の RollingUpdate の戦略では、Deployment は古い ReplicaSet を少しずつ減らしながら、新しい ReplicaSet を増やします。公式ドキュメントによると、Deployment のコントローラーは、新しく作られた Deployment を見つけると、望ましい Pod を作るための ReplicaSet を作ります。そのあと Deployment を更新すると、Deployment の spec.selector に合う Pod を持っていても、ひな形が Deployment の spec.template と合わない ReplicaSet は減らされていきます。最後には、新しい ReplicaSet が spec.replicas の数になり、古い ReplicaSet はすべて 0 になります。

参考:Deployments | Kubernetes、Deployment | Kubernetes API Reference

入れ替えのあいだ、Pod の数には上と下の限りがあります。公式ドキュメントの説明は次の 2 つです。

  • 更新中に使えない(unavailable)Pod の数を限る。既定では、望ましい数の少なくとも 75% の Pod が動いているようにする(maxUnavailable が 25%)
  • 望ましい数を超えて作る Pod の数を限る。既定では、動いている Pod が望ましい数の 125% までになるようにする(maxSurge が 25%)

割合から出した数は整数に丸められます。そのため望ましい数が 3 のときは、次の例のとおり、使える Pod は少なくとも 3 つ、Pod の合計は多くても 4 つになります。

参考:Deployments | Kubernetes、Deployment | Kubernetes API Reference

公式ドキュメントの例(replicas: 3)では、Deployment はまず新しい Pod を 1 つ作り、次に古い Pod を 1 つ消し、また新しい Pod を作ります。十分な数の新しい Pod が動き出すまでは古い Pod を消さず、十分な数の古い Pod を消すまでは新しい Pod を作りません。そうして、使える Pod が少なくとも 3 つ、Pod の合計が多くても 4 つになるよう保ちます。replicas が 4 なら、Pod の数は 3 から 5 のあいだになる、とも書かれています。25% という割合から 3 や 4 という数を出すときの丸め方と、値を変えると入れ替え方がどう変わるのかは、第13回で扱います。

参考:Deployments | Kubernetes

以下の図は、イメージを nginx:1.14.2 から nginx:1.16.1 に書き換えたあとに、Pod が入れ替わる様子を、コマ送りで表しています。公式ドキュメントの例のイベント(後で引用します)の順番に沿っています。図の A〜F は Pod の名前の略です。図とキャプションの「動いている」は、第6回の「使える(available)」の状態にあたる Pod を指します。図の「作られた」は、作られたばかりで、まだ使える状態になっていない Pod を指します。kube-apiserver、kube-scheduler、kubelet は省き、deployment-controller が ReplicaSet の数を変える操作だけを描いています。Pod を実際に作ったり消したりするのは、第11回と同じく、ReplicaSet を受け持つ replicaset-controller です。

(初期)ひな形のイメージを nginx:1.16.1 に書き換えた直後。古い ReplicaSet(nginx:1.14.2)の Pod A・B・C の 3 つが動いている
(1/4)deployment-controller が新しい ReplicaSet(nginx:1.16.1)を作り、数を 1 にする。新しい Pod D が作られ、Pod は合わせて 4 つになる。古い Pod はまだ減らさない
(2/4)Pod D が動き出したあと、古い ReplicaSet を 2 に減らし(Pod A が終了する)、新しい ReplicaSet を 2 に増やす(Pod E が作られる)
(3/4)同じように、古い ReplicaSet を 1 に減らし(Pod B が終了する)、新しい ReplicaSet を 3 に増やす(Pod F が作られる)
(4/4)古い ReplicaSet を 0 に減らす(Pod C が終了する)。新しい ReplicaSet の Pod D・E・F の 3 つが動く。Pod C が終了し終わると、古い ReplicaSet は Pod を持たないまま残る

どのコマでも、使える Pod は少なくとも 3 つで、終了中のものを除いた Pod の合計は多くても 4 つです。公式ドキュメントによると、終了中の Pod は使える Pod の数に入りません。そのため実際には、終了中の Pod も含めると、一時的に 4 つより多く見えることがあります(「注意点」で説明します)。どの古い Pod から先に終了させられるかは、第11回で見た、ReplicaSet が数を減らすときに消す Pod を選ぶ決まりで決まります。図の A→B→C の順は説明のためのものです。

参考:Deployments | Kubernetes、ReplicaSet | Kubernetes

公式ドキュメントの例で、更新を終えたあとに kubectl describe deployments を実行したときの表示の Events: の欄を引用します。上の部分は省いています。

(省略)
  Events:
    Type    Reason             Age   From                   Message
    ----    ------             ----  ----                   -------
    Normal  ScalingReplicaSet  2m    deployment-controller  Scaled up replica set nginx-deployment-2035384211 to 3
    Normal  ScalingReplicaSet  24s   deployment-controller  Scaled up replica set nginx-deployment-1564180365 to 1
    Normal  ScalingReplicaSet  22s   deployment-controller  Scaled down replica set nginx-deployment-2035384211 to 2
    Normal  ScalingReplicaSet  22s   deployment-controller  Scaled up replica set nginx-deployment-1564180365 to 2
    Normal  ScalingReplicaSet  19s   deployment-controller  Scaled down replica set nginx-deployment-2035384211 to 1
    Normal  ScalingReplicaSet  19s   deployment-controller  Scaled up replica set nginx-deployment-1564180365 to 3
    Normal  ScalingReplicaSet  14s   deployment-controller  Scaled down replica set nginx-deployment-2035384211 to 0

参考:Deployments | Kubernetes

nginx-deployment-2035384211 が最初からあった ReplicaSet、nginx-deployment-1564180365 が更新で作られた ReplicaSet です。公式ドキュメントによると、Deployment を作ったとき、Deployment は 1 つ目の ReplicaSet を作り、すぐに 3 まで増やしました。更新したときは、新しい ReplicaSet を作って 1 まで増やし、それが動き出すのを待ちました。続いて古い方を 2 に減らし、新しい方を 2 に増やしました。こうして、使える Pod が少なくとも 3 つ、作られた Pod が多くても 4 つになるよう保っています。そのあとも同じやり方で増減を続け、最後は新しい ReplicaSet に使える Pod が 3 つ、古い ReplicaSet は 0 になっています。From 列がどの行も deployment-controller なので、ReplicaSet の数を変えたのが Deployment のコントローラーだとわかります。

参考:Deployments | Kubernetes

公式のチュートリアルはこうして Pod を少しずつ置き換えることで、アプリケーションを止めずに更新できると説明しています。ただし同じチュートリアルは、そのためには Pod を複数動かしておく必要があるとしています。Deployment を Service(第21回)で公開していれば、Service は要求を受けられる Pod にだけ通信を送る、とも説明しています。止めずに更新できるのは、こうした仕組みとの組み合わせによると考えられます。この回では Service を作らないので、通信が止まらないかどうかは確かめません。Pod が要求を受けられるかを確かめる仕組み(readiness Probe)は第34回で扱います。

参考:Performing a Rolling Update | Kubernetes

2 つの戦略(RollingUpdate と Recreate)

spec.strategy.type に書ける戦略は 2 つです。v1.37 時点の公式ドキュメントと API リファレンスに沿って、次の表にまとめます。

戦略入れ替え方
RollingUpdate(既定)古い ReplicaSet を少しずつ減らしながら、新しい ReplicaSet を増やす。望ましい数を超えて作れる Pod の数(maxSurge)と、使えなくてもよい Pod の数(maxUnavailable)の上限を決められる
Recreate新しい Pod を作る前に、今ある Pod をすべて終了させる

参考:Deployments | Kubernetes、Deployment | Kubernetes API Reference

公式ドキュメントによると、Recreate で Deployment を更新すると、古いリビジョンの Pod はすべてすぐに終了させられ、それらが消えきるのを待ってから新しいリビジョンの Pod が作られます。そのため、動いている Pod が 1 つも無い時間ができます。この回の後半では既定の RollingUpdate だけを使います。maxSurge と maxUnavailable の値を変えて入れ替え方を比べるのは、第13回です。

参考:Deployments | Kubernetes

リビジョンと切り戻し

公式ドキュメントによると、Deployment のリビジョンは展開が始まるときに作られます。つまり、新しいリビジョンが作られるのは、Pod のひな形(spec.template)が変わったときだけです。Pod の数を変えるような、ほかの更新ではリビジョンは作られません。そのため、前のリビジョンに切り戻すときに戻るのは、Deployment のうち Pod のひな形の部分だけ です。replicas は切り戻しでは戻りません。

参考:Deployments | Kubernetes

リビジョンの中身は古い ReplicaSet に残っています。公式ドキュメントによると、Deployment のリビジョンの履歴は、その Deployment が管理する ReplicaSet に保存されています。それぞれのリビジョンのひな形は、そのリビジョンの ReplicaSet が持っています。冒頭の図で、数が 0 になった古い ReplicaSet を消さずに残していたのはこのためです。残す古い ReplicaSet の数は spec.revisionHistoryLimit で決まり、既定は 10 です。古い ReplicaSet が消されると、そのリビジョンには切り戻せなくなります。0 にすると古い履歴がすべて消え、切り戻しができなくなります。古い ReplicaSet を残しすぎると、etcd の容量を使い、kubectl get rs の一覧も見にくくなります。

参考:Deployments | Kubernetes、Deployment | Kubernetes API Reference

リビジョンを扱う kubectl のコマンドは kubectl rollout にまとまっています。v1.37 時点のリファレンスには kubectl rollout で扱えるリソースとして Deployment、DaemonSet(第15回)、StatefulSet(第14回)が挙がっています。この回で使うサブコマンドを次の表にまとめます。

コマンドすること
kubectl rollout status展開の進み具合を表示する。既定では、いちばん新しい展開が終わるまで見続ける
kubectl rollout history前のリビジョンの一覧と設定を表示する。--revision で 1 つのリビジョンのひな形を詳しく見る
kubectl rollout undo前の展開に切り戻す。--to-revision で戻す先のリビジョンを指定できる。付けなければ(既定は 0)、直前のリビジョンに戻る

参考:kubectl rollout | Kubernetes、kubectl rollout status | Kubernetes、kubectl rollout history | Kubernetes、kubectl rollout undo | Kubernetes

kubectl rollout status は、見ている途中で新しい展開が始まると、いちばん新しいリビジョンを見続けます。公式ドキュメントによると、展開が無事に終わると、kubectl rollout status は終了コード 0 で終わります。終了コードは、コマンドが成功したかどうかをシェルに伝える数で、一般に 0 が成功を表します。更新が進まないまま spec.progressDeadlineSeconds の時間を過ぎると、0 以外の終了コードで終わります。

参考:kubectl rollout status | Kubernetes、Deployments | Kubernetes

kubectl rollout history の一覧の CHANGE-CAUSE 列には、リビジョンを作ったときの Deployment の kubernetes.io/change-cause というアノテーションの値が写されます。アノテーションは第11回で触れた、ラベルとは別のキーと値の組です。公式ドキュメントによると、この値を入れる方法は、kubectl annotate で Deployment に付ける、マニフェストを手で書き換える、値を自動で付けるツールを使う、の 3 つです。この回の手順では付けないので、CHANGE-CAUSE 列は公式ドキュメントの例と同じく <none> になるはずです。

参考:Deployments | Kubernetes

更新が止まったときと切り戻し

更新しても、新しい Pod がいつまでも使える状態にならないことがあります。公式ドキュメントは、Deployment がいちばん新しい ReplicaSet を展開しようとして、終わらないまま止まる原因として、次の 6 つを挙げています。

  • 割り当て量(quota)が足りない(第30回)
  • readiness Probe が失敗する(第34回)
  • イメージを取得できない
  • 権限が足りない
  • LimitRange の制限にかかる(第30回)
  • アプリケーションの設定が誤っている

参考:Deployments | Kubernetes

公式ドキュメントの切り戻しの例では、イメージのタグを nginx:1.16.1 ではなく nginx:1.161 と打ち間違えて更新します。新しい Pod はイメージを取得できず、ImagePullBackOff(第9回)のまま止まります。公式ドキュメントによると、Deployment のコントローラーはこのような悪い展開を自動で止め、新しい ReplicaSet を増やすのをやめます。どこで止まるかは maxUnavailable の値によります。既定の設定を使った公式ドキュメントの例では、古い ReplicaSet の Pod 3 つが動き続け、新しい ReplicaSet の Pod は 1 つで止まっています。

参考:Deployments | Kubernetes、Images | Kubernetes

止まったままにしておくと、spec.progressDeadlineSeconds(既定は 600 秒)を過ぎたところで、Deployment の状態(status.conditions)の Progressing が False になり、理由(Reason)が ProgressDeadlineExceeded になります。ただし公式ドキュメントによると、Kubernetes は止まった Deployment に対して、状態を報告する以外に何もしません。自動で切り戻されることはありません。切り戻すのは利用者か、その状態を見て動く上位の仕組みです。

参考:Deployments | Kubernetes、Deployment | Kubernetes API Reference

以下の図は、打ち間違えたイメージで止まった更新を、kubectl rollout undo で切り戻す様子を、コマ送りで表しています。公式ドキュメントの切り戻しの例の表示をもとにしています。図の D〜G は Pod の名前の略で、D・E・F はコマ送りの 1 つ目の図の最後に動いていた Pod です。数が 0 のリビジョン 1(nginx:1.14.2)の ReplicaSet は省いています。

(初期)ひな形のイメージを nginx:1.161(打ち間違い)に書き換えたあと。リビジョン 3 の ReplicaSet の Pod G はイメージを取得できず ImagePullBackOff のまま。更新は止まり、リビジョン 2 の ReplicaSet の Pod D・E・F が動き続ける
(1/2)利用者が kubectl rollout undo を実行する。Deployment のひな形が直前のリビジョン 2 のもの(nginx:1.16.1)に戻り、Deployment のリビジョンの番号は 4 になる
(2/2)deployment-controller がリビジョン 3 の ReplicaSet を 0 に減らし、Pod G が終了する。リビジョン 2 だった ReplicaSet が、Pod D・E・F を持ったまま、いちばん新しい ReplicaSet になる

公式ドキュメントの例では、切り戻したあとの kubectl describe deployment の表示で、Deployment のアノテーション deployment.kubernetes.io/revision が 4 になっています。公式ドキュメントも、切り戻すたびに Deployment のリビジョンが更新されると説明しています。同じ表示の OldReplicaSets: と NewReplicaSet: の欄と、Events: の欄を引用します。

(省略)
OldReplicaSets:  <none>
NewReplicaSet:   nginx-deployment-c4747d96c (3/3 replicas created)
Events:
  Type    Reason              Age   From                   Message
  ----    ------              ----  ----                   -------
  Normal  ScalingReplicaSet   12m   deployment-controller  Scaled up replica set nginx-deployment-75675f5897 to 3
  Normal  ScalingReplicaSet   11m   deployment-controller  Scaled up replica set nginx-deployment-c4747d96c to 1
  Normal  ScalingReplicaSet   11m   deployment-controller  Scaled down replica set nginx-deployment-75675f5897 to 2
  Normal  ScalingReplicaSet   11m   deployment-controller  Scaled up replica set nginx-deployment-c4747d96c to 2
  Normal  ScalingReplicaSet   11m   deployment-controller  Scaled down replica set nginx-deployment-75675f5897 to 1
  Normal  ScalingReplicaSet   11m   deployment-controller  Scaled up replica set nginx-deployment-c4747d96c to 3
  Normal  ScalingReplicaSet   11m   deployment-controller  Scaled down replica set nginx-deployment-75675f5897 to 0
  Normal  ScalingReplicaSet   11m   deployment-controller  Scaled up replica set nginx-deployment-595696685f to 1
  Normal  DeploymentRollback  15s   deployment-controller  Rolled back deployment "nginx-deployment" to revision 2
  Normal  ScalingReplicaSet   15s   deployment-controller  Scaled down replica set nginx-deployment-595696685f to 0

参考:Deployments | Kubernetes

この例の ReplicaSet の名前は、これまでに引用した例の名前と違います。公式ドキュメントの例は、場所ごとに名前やハッシュの値がそろっていません(別々に実行した例を載せているためだと考えられます)。ただし、1 つの ReplicaSet の名前の末尾と、その ReplicaSet の pod-template-hash のラベルの値が同じであることは、公式ドキュメントに書かれています。手元では 1 つのクラスタで順に試すので、後半の kubectl rollout history --revision=2 に出る pod-template-hash の値も、リビジョン 2 の ReplicaSet の名前の末尾と同じになると考えられます。

参考:Deployments | Kubernetes

この例の ReplicaSet は、nginx-deployment-75675f5897 がリビジョン 1、nginx-deployment-c4747d96c がリビジョン 2、nginx-deployment-595696685f が打ち間違えたリビジョン 3 にあたると読み取れます。切り戻しのあとのイベントは DeploymentRollback と、リビジョン 3 の ReplicaSet を 0 に減らした行の 2 つだけです。NewReplicaSet: の欄は nginx-deployment-c4747d96c です。ここから、切り戻しでは新しい ReplicaSet を作らずに、リビジョン 2 の ReplicaSet をもう一度いちばん新しい ReplicaSet として使っていると読み取れます。リビジョン 2 の ReplicaSet は更新が止まっていたあいだも Pod を 3 つ持っていたので、増やす必要がなかったと考えられます。

参考:Deployments | Kubernetes

リビジョン 2 の ReplicaSet がもう一度使われる理由は、公式ドキュメントには書かれていません。pod-template-hash はひな形から計算されるので、ひな形がリビジョン 2 と同じに戻れば、同じ値になり、同じ ReplicaSet にあたるからだと考えられます。

切り戻したあと、Deployment のリビジョンの番号は 4 です。もう一度使われる ReplicaSet が、履歴の上で 2 と 4 のどちらの番号で扱われるかは、公式ドキュメントには書かれていません。図の「リビジョン 2 だった ReplicaSet」は、リビジョン 2 のひな形を持つ ReplicaSet という意味です。Deployment のリビジョンの番号 4 は、この ReplicaSet のひな形を指していると考えられます。

手元で試す

ここまでの内容を手元の kind のクラスタで試す手順を示します。Deployment を作ったあと、マニフェストのイメージを書き換えて更新し、kubectl rollout status で進み具合を見ます。続けて、わざと打ち間違えたイメージで更新して止まる様子を見たあと、kubectl rollout undo で切り戻します。コマンドは macOS や Linux のシェル(bash や zsh)で実行する前提で書いています。Windows では WSL(Windows の中で Linux を動かす仕組み)などを使ってください。試す環境は次のとおりです。

項目内容
kindv0.33.0
Kubernetesv1.37.0(kind v0.33.0 の既定のノードイメージ)
ノード構成シングルノード(クラスタ名 kind):コントロールプレーン 1
必要なものDocker、kubectl(v1.36〜v1.38)、テキストエディタ

参考:Release v0.33.0 · kubernetes-sigs/kind、Version Skew Policy | Kubernetes

kind でクラスタを作る手順は、以下の記事で解説しています。この回ではシングルノードのクラスタを使います。この回で見る ReplicaSet と Pod の入れ替わりは、ノードが 1 つでも確かめられるからです。

基本仕様 ・ 第2回 kind で検証用のクラスタを作るDocker のコンテナをノードにして、手元のマシンに Kubernetes のクラスタを作るツール kind の仕組みを図で説明します。kind create cluster で何が起きるかを押さえ、シングルノードとマルチノードのクラスタを作って、ノードの正体が Docker のコンテナであることを確かめる手順を紹介します。

シングルノードのクラスタを用意する

まず、前の回のクラスタが残っていないかを確かめます。

console
$ kind get clusters

kind が表示されたら、作り直さずにそのまま使えるので、クラスタを作る次のコマンドは飛ばします。表示されなければ、クラスタを作ってから、もう一度一覧します。

console
$ kind create cluster
$ kind get clusters

kind get clusters に kind が表示されれば、クラスタができています。第3回で決めたとおり、ここからのコマンドには --context kind-kind を付けて、操作するクラスタをはっきりさせます。

参考:Quick Start | kind

続けて、ノードを一覧します。

console
$ kubectl get nodes --context kind-kind

ノードが 1 つ表示され、STATUS 列が Ready になっていれば準備ができています。Ready になっていなければ、少し待ってからもう一度実行します。

参考:kubectl get | Kubernetes、Node Status | Kubernetes

Deployment を作る

「マニフェストに書くフィールド」のマニフェストを、nginx-deployment.yaml という名前で、コマンドを実行するディレクトリに保存します。第6回や第7回で保存した同じ名前のファイルが残っていたら、下の内容で上書きします(第7回では replicas を書き換えていました)。このファイルはあとでエディタで書き換えます。

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80

参考:Deployments | Kubernetes

前の回のクラスタを使い続けている場合は、先に nginx-deployment が無いことを確かめます。

console
$ kubectl get deployments --context kind-kind

nginx-deployment が表示されたら、kubectl delete deployment nginx-deployment --context kind-kind で消してから進みます。Deployment が 1 つも無ければ、見つからないことを知らせる 1 行が出るはずです。

Deployment を作ります。

console
$ kubectl apply -f nginx-deployment.yaml --context kind-kind

続けて、展開が終わるまで見ます。deployment/nginx-deployment は「種類/名前」の形で、nginx-deployment という Deployment を指します。

console
$ kubectl rollout status deployment/nginx-deployment --context kind-kind

参考:kubectl rollout status | Kubernetes

公式ドキュメントの例では、出力は次のとおりです。

Waiting for rollout to finish: 2 out of 3 new replicas have been updated...
deployment "nginx-deployment" successfully rolled out

参考:Deployments | Kubernetes

Waiting for rollout to finish で始まる行は、まだ途中であることを表します。作っただけなのに「new replicas have been updated」と出るのは、公式ドキュメントが Deployment を作ることも ReplicaSet の展開として扱っていて、最初の展開で作られる Pod も「新しい」Pod として数えられるからだと考えられます。API リファレンスによると、Deployment の status.updatedReplicas は今のひな形を持つ Pod の数なので、作ったばかりの Pod もこの数に入ります。手元では、この行の数や数字が例と違うことがあります。クラスタを作り直したばかりなら、nginx:1.14.2 のイメージの取得に時間がかかるので、しばらくこの行のまま止まって見えることもあります。最後に successfully rolled out で終わる行が出て、コマンドが終われば、展開は終わっています。

参考:Deployments | Kubernetes、Deployment | Kubernetes API Reference

続けて、今のシェルで直前に実行したコマンドの終了コードを表示します。

console
$ echo $?

公式ドキュメントによると、展開が無事に終わったときの kubectl rollout status の終了コードは 0 なので、0 が表示されるはずです。

参考:Deployments | Kubernetes

最後に、ReplicaSet をラベルの列つきで一覧します。--show-labels はラベルを最後の列に表示する指定です(第6回)。

console
$ kubectl get rs --show-labels --context kind-kind

参考:kubectl get | Kubernetes

nginx-deployment- で始まる名前の ReplicaSet が 1 つ表示され、DESIRED、CURRENT、READY の列はどれも 3 のはずです。公式ドキュメントによると、ReplicaSet の名前の末尾のハッシュは、ReplicaSet の pod-template-hash のラベルの値と同じです。LABELS 列にも pod-template-hash= で始まるラベルがあり、その値が名前の末尾と同じになっているはずです。あとで比べられるように、ReplicaSet の名前を控えておきます。

参考:Deployments | Kubernetes

イメージを更新する

nginx-deployment.yaml をテキストエディタで開き、image: の行を nginx:1.16.1 に書き換えて保存します。書き換えたあとの containers: の部分は次のとおりです。ほかの行は変えません。

yaml
      containers:
      - name: nginx
        image: nginx:1.16.1
        ports:
        - containerPort: 80

公式ドキュメントの例では、イメージを kubectl set image deployment/nginx-deployment nginx=nginx:1.16.1 のコマンドで書き換えています。この回では例から変えて、マニフェストを書き換えて kubectl apply します。第7回で見たとおり、公式ドキュメントは 1 つのオブジェクトを 1 つの管理の方法だけで扱うよう警告しているからです。Deployment は kubectl apply で作ったので、更新も kubectl apply で行います。どちらのやり方でも Pod のひな形のイメージが変わるので、展開が始まることは同じです。

参考:Deployments | Kubernetes、Kubernetes Object Management | Kubernetes、kubectl set image | Kubernetes

書き換えたマニフェストを渡します。

console
$ kubectl apply -f nginx-deployment.yaml --context kind-kind

すぐに、展開の進み具合を見ます。

console
$ kubectl rollout status deployment/nginx-deployment --context kind-kind

公式ドキュメントの例では、更新の途中の出力は次の 1 行です。

Waiting for rollout to finish: 2 out of 3 new replicas have been updated...

終わったときの出力は次のとおりです。

deployment "nginx-deployment" successfully rolled out

参考:Deployments | Kubernetes

手元でも、Waiting for rollout to finish で始まる行が出たあと、最後に successfully rolled out で終わる行が出るはずです。途中の行の数や数字は、例と違うことがあります。nginx:1.16.1 のイメージを取得するあいだは、しばらく止まって見えることがあります。展開がすでに終わってから実行した場合は、successfully rolled out の行だけが出ると考えられます。

更新のあとの ReplicaSet と Pod を確かめる

ReplicaSet と Pod を一覧します。

console
$ kubectl get rs --context kind-kind
$ kubectl get pods --context kind-kind

公式ドキュメントの例では、更新を終えたあとの kubectl get rs の出力は次のとおりです。

NAME                          DESIRED   CURRENT   READY   AGE
nginx-deployment-1564180365   3         3         3       6s
nginx-deployment-2035384211   0         0         0       36s

kubectl get pods の出力は次のとおりです。

NAME                                READY     STATUS    RESTARTS   AGE
nginx-deployment-1564180365-khku8   1/1       Running   0          14s
nginx-deployment-1564180365-nacti   1/1       Running   0          14s
nginx-deployment-1564180365-z9gth   1/1       Running   0          14s

参考:Deployments | Kubernetes

公式ドキュメントによると、Deployment は新しい ReplicaSet を作って 3 まで増やし、古い ReplicaSet を 0 まで減らしました。手元でも ReplicaSet が 2 つ並び、一方は DESIRED 列が 3、もう一方は 0 になるはずです。0 の方の名前は「Deployment を作る」で控えた名前と同じはずです。Pod は 3 つとも、DESIRED 列が 3 の ReplicaSet の名前で始まる、新しい名前になっているはずです。名前の末尾の文字列は手元では例と違う値になります。

続けて、Deployment を詳しく表示します。

console
$ kubectl describe deployment nginx-deployment --context kind-kind

表示の末尾の Events: の欄に、「ひな形を書き換えるとローリングアップデートが始まる」で引用した例と同じ形の行が並ぶはずです。新しい ReplicaSet の Scaled up と古い ReplicaSet の Scaled down が交互に並び、最後は古い ReplicaSet の to 0 で終わることを確かめてください。第6回で見たとおり、イベントを残しておく時間の既定は 1 時間なので、時間を空けると古い行は表示されないことがあります。

参考:Deployments | Kubernetes、kube-apiserver | Kubernetes

間違ったイメージで更新して止まる様子を見る

公式ドキュメントの例と同じく、イメージのタグ(「なぜ Deployment が必要なのか」で説明)を打ち間違えて更新します。nginx-deployment.yaml の image: の行を nginx:1.161 に書き換えて保存します。1.16.1 の 2 つ目の . が抜けたタグです。公式ドキュメントの例ではこのイメージを取得できないので、手元でも取得できないと考えられます。書き換えたあとの containers: の部分は次のとおりです。

yaml
      containers:
      - name: nginx
        image: nginx:1.161
        ports:
        - containerPort: 80

書き換えたマニフェストを渡します。

console
$ kubectl apply -f nginx-deployment.yaml --context kind-kind

参考:Deployments | Kubernetes、Images | Kubernetes

展開の進み具合を見ます。

console
$ kubectl rollout status deployment/nginx-deployment --context kind-kind

公式ドキュメントの例では、出力は次の 1 行で止まります。

Waiting for rollout to finish: 1 out of 3 new replicas have been updated...

参考:Deployments | Kubernetes

手元でもこの行のまま先に進まなくなるはずです。公式ドキュメントのとおり、Ctrl+C を押して kubectl rollout status の監視を止めます。止めるのは監視だけで、Deployment の中身は変わりません。止めずに待つと、更新が進まないまま spec.progressDeadlineSeconds の 600 秒を過ぎたところで、コマンドは 0 以外の終了コードで終わります。公式ドキュメントの Failed Deployment の節の例では、kubectl rollout status deployment/nginx-deployment の出力は次のとおりです(例の中では、2 行目の名前が nginx と表示されています)。

Waiting for rollout to finish: 2 out of 3 new replicas have been updated...
error: deployment "nginx" exceeded its progress deadline

手元では、error: で始まる行の名前は nginx-deployment と表示されると考えられます。

公式ドキュメントによると、こうして失敗とみなされた Deployment にも、完了した Deployment と同じ操作ができます。この先の手順に進むのが 10 分より遅れても、切り戻しはできます。

参考:Deployments | Kubernetes

もし新しい Pod が Running になり、kubectl rollout status が successfully rolled out で終わったら、このタグのイメージを取得できたということです。その場合は止まる様子は見られませんが、このあとの履歴の確認と切り戻しの手順は、そのまま試せます。

ReplicaSet と Pod を一覧します。

console
$ kubectl get rs --context kind-kind
$ kubectl get pods --context kind-kind

公式ドキュメントの例では、kubectl get rs の出力は次のとおりです。

NAME                          DESIRED   CURRENT   READY   AGE
nginx-deployment-1564180365   3         3         3       25s
nginx-deployment-2035384211   0         0         0       36s
nginx-deployment-3066724191   1         1         0       6s

kubectl get pods の出力は次のとおりです。

NAME                                READY     STATUS             RESTARTS   AGE
nginx-deployment-1564180365-70iae   1/1       Running            0          25s
nginx-deployment-1564180365-jbqqo   1/1       Running            0          25s
nginx-deployment-1564180365-hysrc   1/1       Running            0          25s
nginx-deployment-3066724191-08mng   0/1       ImagePullBackOff   0          6s

参考:Deployments | Kubernetes

手元でも ReplicaSet が 3 つになり、新しくできた ReplicaSet は DESIRED 列が 1、READY 列が 0 になるはずです。前の更新で作られた ReplicaSet は 3 のままです。Pod は、前の更新で作られた 3 つが Running のまま残り、新しい Pod が 1 つ加わるはずです。新しい Pod の STATUS 列は、公式ドキュメントの例では ImagePullBackOff です。手元では、少し待つまで別の値が表示されることもあります。第9回で見たとおり、ImagePullBackOff はイメージを取得できずコンテナを始められないことを表し、Kubernetes は待ち時間を延ばしながら取得を試し続けます。

参考:Deployments | Kubernetes、Images | Kubernetes

続けて、Deployment の状態(Conditions: の欄)を見ます。

console
$ kubectl describe deployment nginx-deployment --context kind-kind

公式ドキュメントの、更新が止まった例では、表示の Replicas: の行と Conditions: の欄は次のとおりです。

(省略)
Replicas:       3 desired | 1 updated | 4 total | 3 available | 1 unavailable
(省略)
Conditions:
  Type           Status  Reason
  ----           ------  ------
  Available      True    MinimumReplicasAvailable
  Progressing    True    ReplicaSetUpdated
(省略)

参考:Deployments | Kubernetes

止まってから 600 秒たつまでは、手元でも Progressing の行の Status は True のままだと考えられます。公式ドキュメントによると、spec.progressDeadlineSeconds を過ぎると、Progressing は Status が False、Reason が ProgressDeadlineExceeded になります。10 分ほど待ってからもう一度実行すると、この変化を確かめられます。待たずに先へ進んでもかまいません。

参考:Deployments | Kubernetes

更新の履歴を確かめる

Deployment のリビジョンの一覧を表示します。

console
$ kubectl rollout history deployment/nginx-deployment --context kind-kind

公式ドキュメントの例では、出力は次のとおりです。

deployments "nginx-deployment"
REVISION    CHANGE-CAUSE
1           <none>
2           <none>
3           <none>

参考:Deployments | Kubernetes

手元でも、リビジョン 1(nginx:1.14.2)、2(nginx:1.16.1)、3(nginx:1.161)の 3 行が並ぶはずです。CHANGE-CAUSE 列は、kubernetes.io/change-cause のアノテーションを付けていないので <none> になるはずです。

リビジョン 2 の中身を詳しく表示します。

console
$ kubectl rollout history deployment/nginx-deployment --revision=2 --context kind-kind

公式ドキュメントの例では、出力は次のとおりです。

deployments "nginx-deployment" revision 2
  Labels:       app=nginx
          pod-template-hash=1159050644
  Containers:
   nginx:
    Image:      nginx:1.16.1
    Port:       80/TCP
     QoS Tier:
        cpu:      BestEffort
        memory:   BestEffort
    Environment Variables:      <none>
  No volumes.

参考:Deployments | Kubernetes

この表示はリビジョン 2 の Pod のひな形です。QoS Tier: と BestEffort は、Pod に割り当てる資源(CPU とメモリ)の設定にかかわる項目で、第29回で扱います。ここでは気にしなくてかまいません。手元では、表示の形が例と違うところがあるかもしれません。Image: が nginx:1.16.1 で、ラベルに pod-template-hash= の行があることを確かめてください。

「更新が止まったときと切り戻し」で書いたとおり、公式ドキュメントの例は場所ごとに値がそろっていないので、この例の pod-template-hash の値は、上の kubectl get rs の例の名前の末尾とは違います。手元では、この値がリビジョン 2 の ReplicaSet(前の更新で作られた ReplicaSet)の名前の末尾と同じになると考えられます。

rollout undo で切り戻す

直前のリビジョン(この手順ではリビジョン 2)に切り戻します。

console
$ kubectl rollout undo deployment/nginx-deployment --context kind-kind

公式ドキュメントの例では、出力は次のとおりです。

deployment.apps/nginx-deployment rolled back

参考:Deployments | Kubernetes、kubectl rollout undo | Kubernetes

戻す先を番号で決めたいときは、--to-revision=2 のように付けます。この手順では直前のリビジョンに戻すので付けません。

続けて、展開の進み具合を見ます。

console
$ kubectl rollout status deployment/nginx-deployment --context kind-kind

リビジョン 2 の ReplicaSet は Pod を 3 つ持ったままなので、あまり待たずに successfully rolled out で終わる行が出ると考えられます。その前に Waiting for rollout to finish で始まる行が出ることもあります。

ReplicaSet と Pod を一覧します。

console
$ kubectl get rs --context kind-kind
$ kubectl get pods --context kind-kind

「更新が止まったときと切り戻し」で見た公式ドキュメントの例のとおりなら、nginx:1.161 の ReplicaSet は DESIRED 列が 0 になり、前の更新で作られた ReplicaSet は 3 のままになると考えられます。ReplicaSet は 3 つのまま並ぶはずです。Pod は、ImagePullBackOff だった Pod が消え、切り戻す前から Running だった 3 つが同じ名前のまま残ると考えられます。ImagePullBackOff だった Pod が Terminating で残っていたら、少し待ってからもう一度実行します。

参考:Deployments | Kubernetes

Deployment を詳しく表示します。

console
$ kubectl describe deployment nginx-deployment --context kind-kind

公式ドキュメントの例のとおりなら、Annotations: の欄の deployment.kubernetes.io/revision が 4 になり、Events: の欄に DeploymentRollback の行と、nginx:1.161 の ReplicaSet を to 0 に減らした行が加わるはずです。Image: が nginx:1.16.1 に戻っていることも確かめてください。NewReplicaSet: の欄は前の更新で作られた ReplicaSet(DESIRED 列が 3 の ReplicaSet)の名前になると考えられます。

この手順では Deployment を kubectl apply で作ったので、Annotations: の欄には、第7回で見た kubectl.kubernetes.io/last-applied-configuration も並ぶと考えられます。公式ドキュメントの例の表示には、この行はありません。

参考:Deployments | Kubernetes

最後に、リビジョンの一覧をもう一度表示します。

console
$ kubectl rollout history deployment/nginx-deployment --context kind-kind

切り戻したあとの一覧の例は公式ドキュメントにはありません。Deployment のリビジョンが 4 になったので、いちばん下の行の番号は 4 になると考えられます。「更新の履歴を確かめる」の一覧(1、2、3 の 3 行)と比べて、リビジョン 2 の行が残っているかどうかを見てください。リビジョン 2 のひな形を持つ ReplicaSet がリビジョン 4 として使われているなら、2 の行は無くなっているかもしれません。どちらになっても、この先の手順には影響しません。

マニフェストのファイルを合わせる

kubectl rollout undo が書き換えたのは、クラスタにある Deployment だけです。手元の nginx-deployment.yaml は nginx:1.161 のままです。第7回で見たとおり、マニフェストに書いてあるフィールドは、次の kubectl apply でマニフェストの値に戻ります。このファイルのまま kubectl apply すると、ひな形が nginx:1.161 に戻ります。そうなると、また止まる更新が始まると考えられます。

参考:Declarative Management of Kubernetes Objects Using Configuration Files | Kubernetes、Deployments | Kubernetes

そこで、nginx-deployment.yaml の image: の行を nginx:1.16.1 に戻して保存し、マニフェストを渡します。

console
$ kubectl apply -f nginx-deployment.yaml --context kind-kind
$ kubectl rollout history deployment/nginx-deployment --context kind-kind

ファイルのひな形が今の Deployment のひな形と同じになったので、ひな形は変わらず、新しい展開は始まりません。リビジョンの一覧が 1 つ前の kubectl rollout history のときと変わっていなければ、新しいリビジョンは作られていません。

参考:Deployments | Kubernetes

片付け

作った Deployment を、マニフェストを指定して削除します。

console
$ kubectl delete -f nginx-deployment.yaml --context kind-kind

第6回で見たとおり、Deployment を消すと、既定ではガベージコレクター(不要になった従属物を片付けるコントローラー)が従属物を消すので、Deployment の ReplicaSet と Pod も消えます。数が 0 の古い ReplicaSet も Deployment が持っているので、一緒に消えます。少し待ってから、残っていないかを確かめます。

console
$ kubectl get deployments,rs,pods --context kind-kind

参考:Garbage Collection | Kubernetes、kubectl delete | Kubernetes、Owners and Dependents | Kubernetes

Pod が Terminating で表示されていたら、少し待ってからもう一度実行します。nginx-deployment の Deployment、ReplicaSet、Pod がどれも表示されなければ、削除できています。

最後に、クラスタを削除し、消えたことを確かめます。

console
$ kind delete cluster
$ kind get clusters

kind delete cluster は存在しないクラスタを指定してもエラーを返しません。kind get clusters に kind が表示されなくなったことを確かめます。kubeconfig に kind-kind のコンテキストが残っていたら、第3回の片付けと同じく kubectl config delete-context kind-kind で消します。

参考:Quick Start | kind、kubectl config | Kubernetes

注意点

Deployment の更新と切り戻しでつまずきやすい点と、後から変えられない設定を、次の表にまとめます。

項目内容
展開が始まるのはひな形を変えたときだけspec.template のラベルやイメージを変えると始まる。replicas を変えても始まらず、リビジョンも作られない
切り戻しで戻るのはひな形だけreplicas などのひな形の外の値は戻らない
止まった更新は自動では戻らないprogressDeadlineSeconds(既定 600 秒)を過ぎると ProgressDeadlineExceeded が報告されるだけ。切り戻すのは利用者
revisionHistoryLimit を 0 にしない古い ReplicaSet が残らず、切り戻せなくなる。既定は 10
Deployment が持つ ReplicaSet を直接いじらない数も中身も Deployment を通して変える。pod-template-hash のラベルも変えない
セレクタは作ったあとで変えられないapps/v1 の Deployment の spec.selector は作ったあとで変えられない。変えるには Deployment を作り直す
rollout undo のあとはファイルを直すkubectl rollout undo はクラスタの Deployment だけを書き換える。kubectl apply で管理しているなら、マニフェストも合わせる
更新中は Pod が多く見えることがある終了中の Pod は使える Pod の数に入らない。そのため更新中は Pod が思ったより多く見えたり、replicas と maxSurge の合計より多くの資源を使ったりすることがある
更新の途中で更新すると、すぐ次へ移る展開の途中でひな形を変えると、新しい ReplicaSet を作って増やし始め、途中だった ReplicaSet は古い側に回して減らす

参考:Deployments | Kubernetes、Deployment | Kubernetes API Reference、Declarative Management of Kubernetes Objects Using Configuration Files | Kubernetes

セレクタは作ったあとで変えられないので、最初に決めておきます。公式ドキュメントによると、Deployment のラベルセレクタは作ったあとでは変えられず、kubectl apply や kubectl edit でも変えられません。変えるには、Deployment を消して作り直します。消すと、--cascade=orphan を付けない限り、その Pod も消えます。

参考:Deployments | Kubernetes

更新中に Pod が多く見えることについて、v1.37 時点では、終了中の Pod の数を Deployment の status.terminatingReplicas で追えます。第6回で見たとおり、beta の機能ですが既定で有効です。

参考:Deployment | Kubernetes API Reference

関連記事

ローリングアップデートの速さと安全性を、maxSurge と maxUnavailable で調整する仕組みについては、以下の記事で解説しています。

基本仕様 ・ 第13回 ・ 準備中 maxSurge と maxUnavailable の違い 公開したら、ここから読めるようになります。

マニフェストを渡す kubectl apply と、オブジェクトを直接書き換える kubectl edit の違いと、1 つのオブジェクトを 1 つの方法で管理する理由については、以下の記事で解説しています。

基本仕様 ・ 第7回 kubectl apply と kubectl edit の違いマニフェストを渡して更新する kubectl apply と、クラスタのオブジェクトをエディタで直接書き換える kubectl edit の違いを図で説明します。更新の流れ、last-applied-configuration、resourceVersion の役割を押さえ、kind のクラスタで 2 つのコマンドで Deployment を更新して、値の変化を比べる手順を紹介します。

Pod が要求を受けられるかを確かめる readiness Probe を含む、3 種類の Probe の違いについては、以下の記事で解説しています。

基本仕様 ・ 第34回 ・ 準備中 liveness・readiness・startup Probe の違い 公開したら、ここから読めるようになります。

Kubernetes 基本仕様の目次へ