消した Pod が戻ってくる仕組み(調整ループ)

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

Kubernetes 基本仕様第6回 / 全45回 ・ 第1部 クラスタの仕組み
目次

この記事では、コントローラーが実際の状態を望ましい状態に近づける繰り返しである 調整ループ(Reconciliation Loop) の仕組みを、図で説明します。Deployment で動かしている Pod を手で消したときに、誰がそれに気づき、どうやって代わりの Pod を作るのかがわかります。後半では kind のクラスタで Deployment の Pod を削除し、作り直される様子を観察する手順を示します。

マニフェストの spec と status を扱った第5回の内容を前提にしています。まだの方は先にこちらをどうぞ。

基本仕様 第5回 マニフェストと宣言的 API の仕組みKubernetes に渡すマニフェストの構造と、望ましい状態を書いて任せる宣言的 API の仕組みを図で説明します。apiVersion・kind・metadata・spec・status の役割と、spec と status を誰が書くのかを押さえ、kind のクラスタで kubectl explain を使い、Pod の spec と status を作成直後と数秒後で比べる手順を紹介します。

以下の図は、ReplicaSet のコントローラーが Pod の数を 3 に保つ調整ループを表しています。

replicaset-controller が見る・比べる・変えるを繰り返し、Pod の数を 3 に保つ調整ループ

replicaset-controller は ReplicaSet を受け持つコントローラーです。kube-apiserver を通して ReplicaSet と Pod を監視し(見る)、ReplicaSet の spec.replicas と、実際にある Pod の数を比べます(比べる)。足りなければ、Pod を作るよう kube-apiserver に頼みます(変える)。この 3 つを終わりなく繰り返すのが調整ループです。

第5回では、オブジェクトの status が実際の状態を表すと説明しました。この図で replicaset-controller が実際の状態として見ているのは、kube-apiserver に保存されている Pod のオブジェクトです。数えた結果は ReplicaSet の status に書き込まれます。書き込むのは replicaset-controller だと考えられます(理由は「見る・比べる・変える」で説明します)。

見る・比べる・変えるの 3 段階に分けるのは、このメディアの説明のための整理です。公式ドキュメントでの定義は「調整ループとは」で示します。図の線はどれも kube-apiserver を通ります。第4回で見たとおり、コンポーネントどうしは kube-apiserver を通してやり取りするからです。

参考:Controllers | Kubernetes、ReplicaSet | Kubernetes、kube-controller-manager | Kubernetes、Communication between Nodes and the Control Plane | Kubernetes

調整ループとは

第1回では、公式ドキュメントのサーモスタット(温度調節器)のたとえで、コントローラーを紹介しました。設定温度が望ましい状態、今の室温が実際の状態で、サーモスタットは機器をつけたり止めたりして室温を設定温度に近づけます。

コントローラー は、クラスタの状態を監視し、実際の状態を望ましい状態に近づける変更を加える(または求める)制御ループ です。kube-controller-manager のリファレンスは、制御ループ(control loop)を、システムの状態を調整し続ける終わらないループだと説明しています。このシリーズでは、この繰り返しを調整ループと呼びます。英語の reconcile には「食い違いをなくして一致させる」という意味があります。Kubernetes の API の規約(開発者向けの文書)にも、コントローラーがリソースを reconcile する(調整する)という言い方が出てきます。

参考:Controllers | Kubernetes、kube-controller-manager | Kubernetes、Kubernetes API Conventions(2026年10月時点の内容)

公式ドキュメントによると、コントローラーは少なくとも 1 つのリソースの種類を受け持ちます。その種類のオブジェクトの spec が望ましい状態を表し、コントローラーは実際の状態をそれに近づける役目を負います。コントローラーが自分で作業することもあります。ただし Kubernetes では、コントローラーが kube-apiserver にメッセージを送り、その結果として状態を変えるほうが一般的です。第4回で見たとおり、Job のコントローラーは自分では Pod を動かさず、Pod を作るか消すよう kube-apiserver に伝えるだけです。

公式ドキュメントは、コントローラーが、自分が受け持つオブジェクト(Job のコントローラーなら Job)も更新すると説明しています。Job の例では、作業が終わると、Job のコントローラーが Job のオブジェクトに完了の印を付けます。コントローラーは変更を加えたあと、実際の状態を kube-apiserver に報告します。ほかの制御ループがその報告を見て動くこともあります。

参考:Controllers | Kubernetes

組み込みのコントローラーはコントロールプレーンの kube-controller-manager の中で動きます(第4回)。v1.37 時点の kube-controller-manager のリファレンスには、deployment-controller、replicaset-controller、garbage-collector-controller など、40 を超えるコントローラーの名前が並んでいます。この回で主に扱うのは Deployment を受け持つ deployment-controller と、ReplicaSet を受け持つ replicaset-controller です。

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

なぜ調整ループが必要なのか

第5回で見たとおり、kubectl apply が成功したことは、望ましい状態を受け付けたという意味でした。保存された spec を読み、実際の状態をそれに近づけるのはクラスタのコンポーネントです。第5回では、Pod の実際の状態を kubelet が status に書き込む様子を見ました。Pod の数を保つのは、この回で扱う replicaset-controller です。

一度近づけて終わりにできないのは、クラスタが変わり続けるからです。公式ドキュメントは、作業が進み、制御ループが故障を自動で直すので、クラスタはいつでも変わっていて、安定した状態に至らないこともあり得ると説明しています。コントローラーが動いて役に立つ変更を加えられている限り、全体が安定しているかどうかは問題ではない、とも書かれています。たとえば、消えた Pod の代わりを作っているあいだに、別の Pod が消えたり、利用者が望ましい数を変えたりすることも考えられます。そのたびに一から指示し直さなくても、コントローラーが今の差を埋め続ければよいということです。

参考:Controllers | Kubernetes

たとえば Pod は操作の誤りで消されることも、ノードの故障で失われることもあります。公式ドキュメントによると、ReplicaSet は、利用者が直接作った Pod の場合とは違い、どんな理由で消えたり終了したりした Pod も置き換えます。理由の例として、ノードの故障や、カーネルの更新のようなノードの保守が挙げられています。そのため公式ドキュメントは Pod を 1 つだけ動かす場合でも ReplicaSet を使うよう勧めています。ただし同じページは、ReplicaSet を使いたいときは、ReplicaSet を直接作らずに Deployment を使うよう勧めています。直接作った Pod を消すとどうなるかは第8回で扱います。

参考:ReplicaSet | Kubernetes

調整ループには途中の変化を見落としても困らないという性質もあります。第5回で見たとおり、Kubernetes の API の規約は、システムはそれまでの spec がどうだったかにかかわらず、最新の spec に向かって動くとしています。規約はこれを、システムの振る舞いが edge-based(変化の瞬間に反応する)ではなく level-based(今の値を見て動く)だと表現しています。そうすることで、途中の状態の変化を見落としても頑健に動けるという説明です。

参考:Kubernetes API Conventions(2026年10月時点の内容)

この考え方を Pod の数に当てはめると、調整ループは「Pod が 1 つ消された」という出来事ではなく、「望ましい数は 3 で、今ある数は 2」という状態を見て動くと考えられます。消えたのが 1 つでも 2 つでも、比べた結果の差を埋めればよいからです。公式ドキュメントも、ReplicaSet は望ましい数に届くよう、必要に応じて Pod を作ったり消したりすると説明しています。replicaset-controller の作りの細部までは、このメディアで確かめた範囲の公式ドキュメントには書かれていません。

参考:ReplicaSet | Kubernetes

公式ドキュメントは多くの制御ループをつなげた大きな 1 つの仕組みより、単純なコントローラーをたくさん持つほうが役に立つと説明しています。コントローラーは故障することがあるので、Kubernetes はそれを許容するように作られています。

参考:Controllers | Kubernetes

調整ループの仕組み

見る・比べる・変える

冒頭の図の 3 つの段階を、replicaset-controller を例に次の表にまとめます。

段階することreplicaset-controller の場合
見るkube-apiserver から対象のオブジェクトを取り出し、そのあとの変化を watch で受け取り続けるReplicaSet と、その ReplicaSet の Pod を監視する
比べる望ましい状態と実際の状態の差を見つけるspec.replicas と、終了中でない Pod の数を比べる
変える差を埋める変更を kube-apiserver に求め、観測した結果を報告する足りなければ Pod を作り、多ければ消す。観測した数を ReplicaSet の status に入れる(入れるのは replicaset-controller だと考えられる。後述)

参考:Controllers | Kubernetes、ReplicaSet | Kubernetes、ReplicaSet | Kubernetes API Reference

まず「見る」です。第4回で、Kubernetes の API には watch という仕組みがあると説明しました。公式ドキュメントによると、クライアントは最初に list(一覧)や get(取得)でオブジェクトを取り出し、続けて watch を送ると、そのあとの作成・削除・更新を流れとして受け取れます。こうして今の状態を取り出したうえで、そのあとの変化を見落とさずに受け取れます。

参考:Kubernetes API Concepts | Kubernetes

次に「比べる」です。ReplicaSet はどの Pod を自分のものとして扱うかをラベルセレクタで決め、何個の Pod を保つかを replicas で決めます(ラベルセレクタは後の「Deployment・ReplicaSet・Pod の関係」で説明します)。数えるのは終了中でない Pod です。公式ドキュメントによると、Pod を削除して終了の処理が始まると、ReplicaSet などのワークロードのリソース(ReplicaSet や Deployment のように、Pod を作って管理するリソース)は、その Pod を有効な数に入れなくなります。API リファレンスでも、ReplicaSet の status.replicas は「最後に観測した、終了中でない Pod の数」と説明されています。

参考:ReplicaSet | Kubernetes、Pod Lifecycle | Kubernetes、ReplicaSet | Kubernetes API Reference

最後に「変える」です。ReplicaSet は望ましい数になるよう、必要に応じて Pod を作ったり消したりします。Pod を作るときは、ReplicaSet に書かれた Pod のひな形(spec.template)を使います。多すぎるときに消す Pod は、Pending の Pod(ノードが決まらない Pod を含む)や、新しく作られた Pod などから順に選ばれます。選び方の詳しい決まりは第11回で扱います。

参考:ReplicaSet | Kubernetes

ReplicaSet の status には、観測した Pod の数が入ります。主なフィールドを次の表にまとめます。値は v1.37 時点の API リファレンスに基づきます。

フィールド内容
replicas最後に観測した、終了中でない Pod の数
readyReplicas終了中でない Pod のうち、Ready の状態の Pod の数
availableReplicas終了中でない Pod のうち、使える(available)Pod の数。使える Pod とは、Ready になってから minReadySeconds(既定は 0 秒)以上たった Pod
terminatingReplicas終了中の Pod の数。Deployment の status にも同じ名前のフィールドがある。v1.37 時点では beta の機能だが既定で有効なので、利用者が有効にする操作は要らない
observedGeneration最後に観測した ReplicaSet の世代(metadata.generation)

参考:ReplicaSet | Kubernetes API Reference、ObjectMeta | Kubernetes API Reference

ObjectMeta の API リファレンスでは、metadata.generation は望ましい状態の、ある世代を表す連番で、システムが値を入れます。API の規約は observedGeneration を、望ましい状態の変化に対応するコンポーネントが最後に観測した generation と説明しています。報告された status が最新の望ましい状態を反映しているかを確かめるのに使える、とも書かれています。status.observedGeneration と metadata.generation を比べれば、status が最新の spec を読んだうえでの報告かどうかがわかります。

ReplicaSet の API リファレンスは、status をシステムが入れる読み取り専用のフィールドとするだけで、どのコンポーネントが書き込むかは明記していません。API の規約は、システムのコンポーネントが自分の管理するリソースの status を更新できるよう、/status サブリソースを用意する決まりとしています(第5回)。権限の例としても、関係するコントローラーには spec の読み取りと status の書き込みを許す形を挙げています。公式ドキュメントも Job の例で、コントローラーが自分の受け持つオブジェクトを更新すると説明しています。これらから、ReplicaSet の status も replicaset-controller が書き込むと考えられます。

参考:ObjectMeta | Kubernetes API Reference、Kubernetes API Conventions(2026年10月時点の内容)、Controllers | Kubernetes

Deployment・ReplicaSet・Pod の関係

第1回で見たとおり、Deployment で Pod を動かすと、Deployment は ReplicaSet を作り、ReplicaSet が Pod を作ります。調整ループを観察するのに必要な範囲で、2 つのリソースを次の表にまとめます。詳しくは ReplicaSet を第11回、Deployment を第12回で扱います。

リソース役割
Deploymentアプリケーションを動かす Pod の集まりを管理する。ReplicaSet を作って持ち、Pod のひな形を変えたときの Pod の入れ替え(たとえば、動かすプログラムを新しい版にするとき)を受け持つ
ReplicaSet同じ Pod を、指定した数だけ動かし続ける

参考:Deployments | Kubernetes、ReplicaSet | Kubernetes

公式ドキュメントによると、Kubernetes のコントローラーの多くは、1 つの種類のリソースを望ましい状態として読み、別の種類のリソースを作って管理します。Job のコントローラーなら、Job を読んで Pod を作ります。Deployment と ReplicaSet のコントローラーも、この形でつながっています。kubelet はコントローラーではありませんが、公式ドキュメントは、ノードが決まった Pod は kubelet にとっての望ましい状態の一部になると説明しています。それぞれが何を読み、何を管理するかを次の表にまとめます。

担当望ましい状態として読むもの作って管理するもの
deployment-controllerDeploymentReplicaSet
replicaset-controllerReplicaSetPod
ノードの kubelet自分のノードに割り当てられた PodPod のコンテナ

参考:Controllers | Kubernetes、Deployments | Kubernetes、ReplicaSet | Kubernetes、kubelet | Kubernetes

以下の図は、Deployment・ReplicaSet・Pod の持ち主の関係と、ReplicaSet がラベルで Pod を選ぶ関係を表しています。

Deployment・ReplicaSet・Pod の持ち主の関係と、ラベルセレクタで Pod を選ぶ関係

破線の「持つ」は持ち主の関係で、矢印は持ち主から持たれるものへ向いています。実線はラベルで選ぶ関係です。ReplicaSet と Pod の間には、この 2 種類の結び付きがあります。

まず、持ち主の関係です。公式ドキュメントでは、ほかのオブジェクトを持つオブジェクトを持ち主(owner)、持たれるオブジェクトを従属物(dependent)と呼びます。ReplicaSet は Pod の持ち主です。持ち主の関係は従属物の metadata.ownerReferences というフィールドに記録されます。ownerReferences は持たれる側(Deployment が持つ ReplicaSet や、ReplicaSet が持つ Pod)に入り、持ち主を指します。ReplicaSet や Deployment の従属物には、Kubernetes がこの値を自動で入れます。以下は公式ドキュメントの ReplicaSet の例(frontend という ReplicaSet)で、Pod の ownerReferences の部分を引用したものです。

yaml
  ownerReferences:
  - apiVersion: apps/v1
    blockOwnerDeletion: true
    controller: true
    kind: ReplicaSet
    name: frontend
    uid: e129deca-f864-481b-bb16-b27abfd92292

参考:Owners and Dependents | Kubernetes、ReplicaSet | Kubernetes

持ち主は種類(kind)、名前(name)、UID(uid)で指します。controller: true は、その持ち主がこの Pod を管理するコントローラーであることを表します。API リファレンスによると、管理するコントローラーは 1 つまでです。公式ドキュメントによると、ReplicaSet はこのつながりを通して、自分が保っている Pod の状態を知ります。blockOwnerDeletion は持ち主を消すときの順番に関わるフィールドです。この回では、Kubernetes が自動で付けるものと押さえれば十分です。

参考:OwnerReference | Kubernetes API Reference、ObjectMeta | Kubernetes API Reference、ReplicaSet | Kubernetes、Owners and Dependents | Kubernetes

Deployment が作った ReplicaSet にも、同じように Deployment を指す ownerReferences が入ります。持ち主の関係はオブジェクトを消すときにも使われます。公式ドキュメントによると、持ち主を消すと、Kubernetes は既定で従属物も消します(カスケード削除)。Deployment を消すと、その ReplicaSet と Pod も消えるのはこのためです。後半の「片付け」でこの動きを使います。

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

次に、ラベルで選ぶ関係です。第4回と第5回で触れたとおり、ラベルはオブジェクトに付けるキーと値の組です。ラベルセレクタはラベルを条件にしてオブジェクトの集まりを選ぶ仕組みで、公式ドキュメントは Kubernetes でまとまりを作る中心の仕組みだと説明しています。Deployment のマニフェストでは、Pod のひな形に app: nginx のラベルを書き、spec.selector.matchLabels に同じ app: nginx を書きます。Deployment のコントローラーは、Deployment が作る ReplicaSet に pod-template-hash というラベルを付け、同じラベルを ReplicaSet のセレクタ、Pod のひな形のラベル、ReplicaSet がすでに持っている Pod にも加えます。値は Pod のひな形から計算したハッシュです。そのため ReplicaSet は app: nginx に pod-template-hash を加えたセレクタで自分の Pod を見つけます。公式ドキュメントによると、このラベルは、同じ Deployment の ReplicaSet どうしが重ならないようにするためのもので、変えないよう注意されています。詳しくは第12回で扱います。

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

公式ドキュメントによると、ReplicaSet は、持ち主のコントローラーがいない Pod でも、セレクタに合えばすぐに自分のものにします。持ち主のコントローラーがいない Pod とは、たとえば第5回のように Pod を直接作り、そこに ReplicaSet のセレクタに合うラベルを付けていた場合です。Deployment のドキュメントは、ラベルやセレクタを、ほかのコントローラー(ほかの Deployment なども含む)と重ねないでください と注意しています。重なっても Kubernetes は止めず、コントローラーどうしがぶつかって思わぬ動きをすることがあるからです。セレクタの仕組みは第11回、ラベルの設計は第42回で扱います。

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

Pod を消したときに起きること

Deployment で Pod を 3 つ動かしているときに、そのうち 1 つを kubectl delete pod で消したとします。以下の図は、そのあとに起きることを、コマ送りで表しています。図では Pod の名前を A〜D と略しています。実際の名前は、ReplicaSet の名前の後ろに文字列が付いたものです。図ではノードが 1 つの場合を描き、kube-apiserver と kube-scheduler は省いて、線を対象の Pod に直接引いています。利用者と replicaset-controller の操作は、実際にはどちらも kube-apiserver を通ります。replicaset-controller はコントロールプレーンの kube-controller-manager の中で動いています。kind のシングルノードのクラスタでは同じノードの中にありますが、図ではノードの外に分けて描いています。

(初期)ReplicaSet の望ましい数は 3 で、ノードでは Pod A・B・C の 3 つが動いている
(1/4)利用者が Pod A を削除すると、Pod A は終了中(Terminating)になり、ReplicaSet の数に入らなくなる
(2/4)replicaset-controller が望ましい数 3 と今ある数 2 の差に気づき、ひな形から Pod D を作るよう kube-apiserver に頼む(図の Pod D はまだ無い)。kubelet は、Pod A が終了中になったときから Pod A のコンテナの停止を始めている
(3/4)kube-scheduler が Pod D のノードを選び、kubelet が Pod D のコンテナを起動する。同じころ、kubelet は Pod A のコンテナの停止を進める
(4/4)Pod A は API から消え、Pod B・C・D の 3 つが動いている。Pod D は Pod A とは UID が別の Pod で、名前もふつうは別

Pod A の終了と Pod D の作成は順番に起きるのではなく並行して進みます。そのため、一時的に Pod A・B・C・D の 4 つが同時に見えることがあります。

流れを担当ごとに次の表にまとめます。

順番担当すること
1利用者(kubectl)kubectl delete pod で、Pod A の削除を kube-apiserver に頼む
2kube-apiserverPod A に、削除する時刻(metadata.deletionTimestamp)と猶予の時間を書き込む。Pod A は終了中になる
3replicaset-controller終了中の Pod A を数に入れず、足りない 1 つを Pod のひな形から作るよう kube-apiserver に頼む
4kube-schedulerノードが決まっていない Pod D を見つけ、ノードを選ぶ
5Pod D のノードの kubeletPod D のコンテナを起動し、status を書き込む
2 のあと(3〜5 と並行)Pod A のノードの kubeletPod A のコンテナを止め、止め終わったら Pod A を API から消すよう求める

参考:Pod Lifecycle | Kubernetes、ObjectMeta | Kubernetes API Reference、ReplicaSet | Kubernetes、Kubernetes Scheduler | Kubernetes

公式ドキュメントによると、kubectl delete で Pod を消すと、kube-apiserver の Pod に、削除する時刻(この時刻を過ぎると Pod は止まったとみなされる)と猶予の時間が書き込まれます。猶予の時間の既定は 30 秒で、第5回で見た Pod の spec の既定値 terminationGracePeriodSeconds: 30 がこれにあたります。このとき kubectl describe で Pod を見ると、Terminating と表示されます。kubelet は Pod が終了中になったことに気づくと、コンテナを止める処理を始めます。コンテナのプロセスに TERM シグナル(終了を求める合図)を送り、猶予の時間が過ぎても動いているプロセスは強制的に止めます。最後に kubelet が Pod を API からすぐに消すよう求め、kube-apiserver が Pod のオブジェクトを消します。

参考:Pod Lifecycle | Kubernetes

同じページによると、kubelet が Pod を止め始めるのと同じころ、ReplicaSet などのワークロードのリソースは、その Pod を有効な数に入れなくなります。Deployment のドキュメント(Recreate 戦略の節の注意書き)にも、Pod を手で削除した場合は ReplicaSet が Pod のライフサイクルを受け持ち、古い Pod がまだ Terminating でも代わりの Pod がすぐに作られる、と書かれています。replicaset-controller は、Pod A が消えきるのを待たずに Pod D を作るということです。Deployment のドキュメントはさらに、削除や数の削減で終了中になった Pod は終わるまでに時間がかかることがあり、そのあいだ Pod の合計が spec.replicas を一時的に超えることがあると説明しています。v1.37 時点では、終了中の Pod の数は Deployment と ReplicaSet のどちらの status.terminatingReplicas でも追えます。beta の機能ですが既定で有効なので、利用者が有効にする操作は要りません。

参考:Pod Lifecycle | Kubernetes、Deployments | Kubernetes、ReplicaSet | Kubernetes API Reference

Pod の数が 3 に戻るので、消した Pod が戻ってきたように見えます。しかし Pod D は Pod A が戻ってきたものではなく、新しく作られた別の Pod です。公式ドキュメントによると、ある Pod(UID で見分けられる Pod)が別のノードに移されることはなく、代わりにほとんど同じ新しい Pod に置き換えられます。置き換えの Pod は前と同じ名前にすることもできますが、UID は必ずもとの Pod と違います。クラスタの全期間を通じて、作られたオブジェクトごとに UID が違うからです。新しい Pod が前と同じノードに置かれるとも限りません。

名前は、ふつうは違うものになります。公式ドキュメントの ReplicaSet の例では、ReplicaSet が作った Pod の metadata.generateName が frontend- で、名前は frontend-gbgfx です。generateName は、名前を指定しないときに、kube-apiserver が指定された頭の部分に一意な文字列を付けて名前を作る仕組みです。

参考:Pod Lifecycle | Kubernetes、Object Names and IDs | Kubernetes、ReplicaSet | Kubernetes、ObjectMeta | Kubernetes API Reference

この流れに Deployment と deployment-controller は出てきません。Pod の数を保つのは ReplicaSet と replicaset-controller の仕事だからです。Deployment は ReplicaSet を持ち、Pod の更新のときに働きます。

手元で試す

ここまでの内容を、手元の kind のクラスタで試す手順を示します。Deployment を作って Pod の持ち主を確かめたあと、Pod を手で削除し、作り直される様子を観察します。コマンドは 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 でクラスタを作る手順は、以下の記事で解説しています。この回ではシングルノードのクラスタを使います。ノードの数に関係しない仕組みを試すからです。

基本仕様 第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 を作る

第1回で引用した、nginx の Pod を 3 つ動かす Deployment のマニフェストを使います。公式ドキュメントの例をそのまま引用しています。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.replicas が望ましい Pod の数、spec.selector が Pod を見つけるために ReplicaSet が使うラベルセレクタ、spec.template が Pod のひな形です。前の回のクラスタを使い続けている場合は、先に kubectl get deployments --context kind-kind を実行し、nginx-deployment が無いことを確かめてください。あった場合は、この回の「片付け」の kubectl delete -f nginx-deployment.yaml --context kind-kind で消し、kubectl get deployments,rs,pods --context kind-kind で消えたことを確かめてから進みます。

Deployment を作り、できたことを確かめます。

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

公式ドキュメントの例では、作っている途中の kubectl get deployments の出力は次のとおりです。

NAME               READY   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   0/3     0            0           1s

数秒後にもう一度実行したときの出力は次のとおりです。

NAME               READY   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   3/3     3            3           18s

参考:Deployments | Kubernetes

READY 列は「使える数/望ましい数」の形です。3/3 になるまで、少し待ってから何度か実行します。

続けて、Deployment が作った ReplicaSet と、Pod のラベルを確かめます。rs は replicasets の短い名前です。--show-labels はラベルを最後の列に表示する指定です。

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

参考:Deployments | Kubernetes、Command line tool (kubectl) | Kubernetes、kubectl get | Kubernetes

ReplicaSet は nginx-deployment- の後ろに文字列が付いた名前で 1 つ表示され、DESIRED 列は 3 のはずです。公式ドキュメントによると、DESIRED 列は望ましい数、CURRENT 列は今動いている数、READY 列は利用者が使える数です。同じページによると、ReplicaSet の名前は必ず [Deployment の名前]-[ハッシュ] の形で、ハッシュの部分は ReplicaSet の pod-template-hash のラベルの値と同じです。公式ドキュメントの例では、kubectl get pods --show-labels の出力は次のとおりです。

NAME                                READY     STATUS    RESTARTS   AGE       LABELS
nginx-deployment-75675f5897-7ci7o   1/1       Running   0          18s       app=nginx,pod-template-hash=75675f5897
nginx-deployment-75675f5897-kzszj   1/1       Running   0          18s       app=nginx,pod-template-hash=75675f5897
nginx-deployment-75675f5897-qqcnn   1/1       Running   0          18s       app=nginx,pod-template-hash=75675f5897

参考:Deployments | Kubernetes

手元でも、Pod の名前が ReplicaSet の名前で始まり、LABELS 列に app=nginx と pod-template-hash=<ハッシュ> が並ぶはずです。ハッシュや名前の末尾の文字列は、手元では例と違う値になります。

Pod の持ち主を確かめる

Pod の ownerReferences から、持ち主の種類と名前を取り出します。あわせて、Pod の UID も表示します。-o custom-columns は 列の名前:フィールド の形で列の名前とフィールドを指定し、表の形で表示する出力の形式です。-l app=nginx で、app: nginx のラベルを持つ Pod だけに絞り込みます(第4回)。

console
$ kubectl get pods -l app=nginx -o 'custom-columns=NAME:.metadata.name,OWNER_KIND:.metadata.ownerReferences[0].kind,OWNER:.metadata.ownerReferences[0].name,UID:.metadata.uid' --context kind-kind

参考:Command line tool (kubectl) | Kubernetes、kubectl get | Kubernetes

[0] は、ownerReferences の 1 つ目の項目を指します。zsh などのシェルは [ と ] を特別な文字として扱うことがあるので、指定全体を単一引用符(')で囲んでいます。

3 つの Pod のどれも、OWNER_KIND 列が ReplicaSet、OWNER 列が kubectl get rs で表示された ReplicaSet の名前になるはずです。UID 列の値はあとで比べるので、画面に残しておくか、ファイルに書き出しておきます。書き出すときは、同じコマンドの末尾に > pods-before.txt を付けて実行します。書き出したファイルは「片付け」で消します。

参考:ReplicaSet | Kubernetes、Owners and Dependents | Kubernetes

同じように、ReplicaSet の持ち主も確かめます。

console
$ kubectl get rs -o 'custom-columns=NAME:.metadata.name,OWNER_KIND:.metadata.ownerReferences[0].kind,OWNER:.metadata.ownerReferences[0].name' --context kind-kind

OWNER_KIND 列が Deployment、OWNER 列が nginx-deployment になるはずです。Deployment → ReplicaSet → Pod の持ち主の関係が、ownerReferences に表れていることがわかります。ownerReferences のすべての項目を見たいときは、kubectl get pod <Pod の名前> -o yaml --context kind-kind で表示し、metadata の中の ownerReferences を探します。「Deployment・ReplicaSet・Pod の関係」で引用した例と同じ形の項目があるはずです。

参考:Owners and Dependents | Kubernetes、Deployments | Kubernetes

Pod を 1 つ削除して観察する

ここでは端末(ターミナル)を 2 つ使います。1 つ目の端末で Pod の変化を追い続け、2 つ目の端末で Pod を削除します。

1 つ目の端末で、app: nginx の Pod を監視します。第3回で見たとおり、-w(--watch)を付けると、一覧を表示したあとも変化を追い続けます。--output-watch-events は、公式ドキュメントでは「--watch か --watch-only を使うときに、watch のイベントのオブジェクトを出力する」と説明されている指定です。

console
$ kubectl get pods -l app=nginx --watch --output-watch-events --context kind-kind

参考:kubectl get | Kubernetes

公式ドキュメントによると、--output-watch-events を付けると、すでにある Pod は最初に ADDED の変化として表示されます。まず 3 つの Pod が ADDED の行で表示され、そのあとは変化が起きるまで止まったように見えるはずです。

参考:kubectl get | Kubernetes

2 つ目の端末で、3 つの Pod のうち 1 つを削除します。<Pod の名前> には 1 つ目の端末に表示された名前を 1 つ入れます。

console
$ kubectl delete pod <Pod の名前> --context kind-kind

参考:kubectl delete | Kubernetes

kubectl delete は既定(--wait=true)では、Pod が消えるまで待ってから終わります。Pod には終了までの猶予の時間(既定では 30 秒)があるので、終わるまで少しかかることがあります。

参考:kubectl delete | Kubernetes、Pod Lifecycle | Kubernetes

1 つ目の端末を見ると、行が次々に増えていくはずです。各行の NAME 列と STATUS 列を見て、消した Pod の名前の行と、それ以外の名前の行を見分けます。見るところを次の表にまとめます。

見るところ起きていること
消した Pod の行の STATUS 列が Terminating になるkube-apiserver が Pod に削除の時刻を書き込み、Pod が終了中になった
新しい Pod の行が加わる(名前はふつう、それまでに見たことのないもの)replicaset-controller が代わりの Pod を作った
新しい Pod の行が何度か表示され、STATUS 列が Running になるkube-scheduler がノードを選び、kubelet がコンテナを起動した

参考:Pod Lifecycle | Kubernetes、ReplicaSet | Kubernetes

行の順番や、新しい Pod の STATUS 列が Running になるまでに表示される値は、タイミングで変わります。新しい Pod の行は、消した Pod が Terminating のあいだに現れることもあるはずです。「Pod を消したときに起きること」で見たとおり、ReplicaSet は終了中の Pod を数に入れないからです。新しい Pod の STATUS 列には、Running になる前に別の値が表示されることがありますが、少し待てば Running になるはずです。1 分ほど待っても Running にならなければ、第5回と同じく kubectl describe pod <新しい Pod の名前> --context kind-kind を実行し、Events: の欄で原因を探します。

参考:Deployments | Kubernetes、Debug Pods | Kubernetes

--output-watch-events を付けたので、各行には watch のイベントの種類も表示されると考えられます。公式ドキュメントの変更の検出(Efficient detection of changes)にある watch の例では、イベントの種類として ADDED と MODIFIED が出てきます。種類がどの列に、どう表示されるかは、このメディアで確かめた範囲の公式ドキュメントには書かれていません。

参考:kubectl get | Kubernetes、Kubernetes API Concepts | Kubernetes

2 つ目の端末の kubectl delete が終わり、1 つ目の端末の表示が落ち着いたら、1 つ目の端末で Ctrl+C を押して監視を止めます。そのあと、「Pod の持ち主を確かめる」と同じコマンドで、もう一度 Pod を一覧します。

console
$ kubectl get pods -l app=nginx -o 'custom-columns=NAME:.metadata.name,OWNER_KIND:.metadata.ownerReferences[0].kind,OWNER:.metadata.ownerReferences[0].name,UID:.metadata.uid' --context kind-kind

Pod は再び 3 つになっているはずです。消した Pod の代わりに、新しい Pod が 1 つあります。名前はふつう、消した Pod とは違います。新しい Pod の UID 列の値は、前に控えた 3 つのどれとも違うはずです。pods-before.txt に書き出した場合は、cat pods-before.txt で表示して見比べます。OWNER 列は前と同じ ReplicaSet です。同じ ReplicaSet が同じひな形から新しい Pod を作ったことがわかります。

参考:Pod Lifecycle | Kubernetes、Object Names and IDs | Kubernetes

ReplicaSet のイベントで誰が作ったかを確かめる

代わりの Pod を作ったのが replicaset-controller であることを、ReplicaSet のイベントで確かめます。第3回で見たとおり、kubectl describe は関係するイベントも表示します。<ReplicaSet の名前> には kubectl get rs で表示された名前を入れます。

console
$ kubectl describe rs <ReplicaSet の名前> --context kind-kind

公式ドキュメントの ReplicaSet の例(frontend という ReplicaSet)では、表示の末尾の Events: の欄は次のとおりです。

Events:
  Type    Reason            Age   From                   Message
  ----    ------            ----  ----                   -------
  Normal  SuccessfulCreate  13s   replicaset-controller  Created pod: frontend-gbgfx
  Normal  SuccessfulCreate  13s   replicaset-controller  Created pod: frontend-rwz57
  Normal  SuccessfulCreate  13s   replicaset-controller  Created pod: frontend-wkl7w

参考:ReplicaSet | Kubernetes、kubectl describe | Kubernetes

From 列がイベントを記録したもの、Message 列が作った Pod の名前です。手元では、最初に作った 3 つの Pod の SuccessfulCreate に加えて、さきほどの代わりの Pod の SuccessfulCreate も並ぶと考えられます。Age 列の値が小さく、Message 列に代わりの Pod の名前がある行です。From 列が replicaset-controller なら、代わりの Pod を作ったのが ReplicaSet のコントローラーだと確かめられます。

イベントはいつまでも残るわけではありません。v1.37 時点の kube-apiserver のリファレンスでは、イベントを残しておく時間(--event-ttl)の既定は 1 時間です。Deployment を作ってから時間を空けると、最初の 3 つの SuccessfulCreate が表示されないことがあります。

参考:kube-apiserver | Kubernetes

Pod をまとめて削除する

最後に、ラベルセレクタで 3 つの Pod をまとめて削除します。kubectl delete の -l は指定したラベルを持つものだけを削除する指定です。

console
$ kubectl delete pods -l app=nginx --context kind-kind
$ kubectl get pods -l app=nginx --context kind-kind

参考:kubectl delete | Kubernetes

kubectl get pods には、3 つとも新しく作られた Pod が並び、AGE 列の値が小さいはずです。名前もふつうは、消す前の Pod とは違います。消えたのが 1 つでも 3 つでも、replicaset-controller は望ましい数と今ある数を比べて、差の分だけ Pod を作ります。STATUS 列が Running になっていなければ、少し待ってからもう一度実行します。

参考:ReplicaSet | Kubernetes

片付け

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

console
$ kubectl delete -f nginx-deployment.yaml --context kind-kind
$ kubectl get deployments,rs,pods --context kind-kind

kubectl delete の --cascade の既定は background です。公式ドキュメントによると、background のカスケード削除では、kube-apiserver が持ち主をすぐに消し、ガベージコレクター(不要になった従属物を片付けるコントローラー)が従属物を裏で消します。Deployment を消すと、その ReplicaSet と Pod も消えます。消えきるまでは、Pod が Terminating で表示されることがあります。少し待ってから kubectl get をもう一度実行し、nginx-deployment の Deployment、ReplicaSet、Pod がどれも表示されなければ、削除できています。

参考:kubectl delete | Kubernetes、Garbage Collection | Kubernetes、Pod Lifecycle | Kubernetes

UID を pods-before.txt に書き出した場合は、ファイルを消します。

console
$ rm pods-before.txt

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

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

注意点

調整ループの動きでつまずきやすい点と、バージョンで変わる点を、次の表にまとめます。

項目内容
Pod を消しても数は減らないReplicaSet は、どんな理由で消えた Pod も置き換える。数を減らすときは、Pod を消すのではなく望ましい数(spec.replicas)を変える
作り直された Pod は別の PodUID は必ずもとの Pod と違う。名前も、ReplicaSet が generateName で付けるので、ふつうは違う。前と同じノードに置かれるとも限らない
Pod の数が一時的に replicas を超える終了中の Pod は数に入らないので、代わりの Pod と同時に見えることがある。v1.37 時点では、終了中の数を ReplicaSet と Deployment の status.terminatingReplicas で追える(beta の機能だが既定で有効)
Deployment が作った ReplicaSet は直接いじらない公式ドキュメントは、Deployment が持つ ReplicaSet を管理しないよう注意している。数や中身は Deployment で変える
ラベルやセレクタをほかと重ねないReplicaSet は、持ち主のコントローラーがいない Pod でもセレクタに合えば自分のものにする。重なるとコントローラーどうしがぶつかることがある
kubectl delete pod は終わるまで待つ既定の --wait=true では Pod が消えるまで待つ。Pod の猶予の時間の既定は 30 秒
mirror Pod が戻るのは別の仕組み第4回の mirror Pod を消しても戻るのは、static Pod を受け持つ kubelet が作り直すから。ReplicaSet の調整ループとは別

参考:ReplicaSet | Kubernetes、Pod Lifecycle | Kubernetes、Deployments | Kubernetes、kubectl delete | Kubernetes、Static Pods | Kubernetes

Deployment の Pod の数を変える方法と、Pod の数を負荷に応じて自動で変える仕組みは、後の回で扱います。

関連記事

kubectl apply と kubectl edit でオブジェクトを更新したときに、resourceVersion や last-applied-configuration がどう変わるかについては、以下の記事で解説しています。

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

Deployment などを使わずに直接作った Pod を消すと、もう戻らないことについては、以下の記事で解説しています。

基本仕様 第8回 ・ 準備中 Pod を動かして理解する 公開したら、ここから読めるようになります。

ReplicaSet がラベルセレクタで Pod を選ぶ仕組みと、ラベルを書き換えた Pod が管理から外れる様子については、以下の記事で解説しています。

基本仕様 第11回 ・ 準備中 ReplicaSet を動かして理解する 公開したら、ここから読めるようになります。

Kubernetes 基本仕様の目次へ