Kubernetes 解体新書Part 1 Kubernetes の全体像

ハンズオン: 最初のクラスタ

アプリの起動、台数の変更、更新、前のバージョンへの復元を、自分専用のクラスタで試します。

  • L1 Web ターミナル
  • 更新 2026年10月6日

Web ターミナルを開いて、nginx を動かしてみましょう。 起動できたら数を増やし、そのうち 1 つの Pod を削除して、代わりが作られる様子を見ます。

続いて、アプリを更新し、前のバージョンへ戻します。 自分専用の実習環境で、ここまで学んだ「希望する状態を書き、Kubernetes がそれに近づける」という動きを確かめる章です。

GOAL この章のゴール

  • アプリを起動し、Pod の数を増減できる
  • Pod を削除して、代わりが作られることを確認できる
  • アプリを更新し、前のバージョンへ戻せる
環境の状態を確認しています…
この章のハンズオンの 7 歩: 眺める、作る、増やす、壊す、調べる、更新する、戻す7 つの箱が左から右へ矢印で繋がる。1. 眺めるget nodes / pods -A2. 作るapply Deployment3. 増やすscale --replicas4. 壊すdelete pod → 復活5. 調べるdescribe / events6. 更新するset image / rollout7. 戻すrollout undo4 の「壊す」が本題。ReplicaSet controller が差分を埋めるところを実機で見る。全部 Web ターミナル (L1) で済む。ノードの中には入らない。
図 1この章の 7 歩。4 の「壊す」が本題で、ReplicaSet controller が差分を埋めるところを実機で見る。

1. 眺める

まず、何も作っていない状態のクラスタを見ます。

ノードと、動いている全 Pod を見る
kubectl get nodes -o wide
kubectl get pods -A

出力例

NAME                         STATUS   ROLES           AGE   VERSION   INTERNAL-IP   OS-IMAGE
tenant-control-plane-7k2qd   Ready    control-plane   1h    v1.37.1   10.0.1.10     Ubuntu 26.04.1 LTS
NAMESPACE     NAME                                               READY   STATUS    RESTARTS   AGE
kube-system   cilium-xxxxx                                       1/1     Running   0          1h
kube-system   cilium-operator-xxxxxxxxxx-xxxxx                   1/1     Running   0          1h
kube-system   coredns-xxxxxxxxxx-xxxxx                           1/1     Running   0          1h
kube-system   etcd-tenant-control-plane-7k2qd                    1/1     Running   0          1h
kube-system   kube-apiserver-tenant-control-plane-7k2qd          1/1     Running   0          1h
kube-system   kube-controller-manager-tenant-control-plane-7k2qd 1/1     Running   0          1h
kube-system   kube-scheduler-tenant-control-plane-7k2qd          1/1     Running   0          1h

kube-system に、5 章で並べた登場人物が Pod として居ます。 etcd、kube-apiserver、kube-controller-manager、kube-scheduler は kubeadm が static Pod として置いたものです。 Kubernetes のドキュメントによると、static Pod は「API サーバーが関知しないまま、特定のノードの kubelet が直接管理する Pod」で、kubelet は「static Pod ごとに API サーバー上にミラー Pod を作るので、API サーバーからは見えるが、API サーバーから操作はできない」ものです。 Cilium は Pod をネットワークにつなぐプログラムで、CoreDNS はクラスタ内の名前解決を行う DNS サーバーです。 kube-proxy の Pod は無く、cloud-controller-manager も管理クラスタ側に居るのでここには出ません。

2. 作る

nginx の Deployment を作ります。 YAML は heredoc でそのまま流します。

nginx の Deployment を作る
kubectl apply -f - <<'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
  labels: { app: web }
spec:
  replicas: 2
  selector:
    matchLabels: { app: web }
  template:
    metadata:
      labels: { app: web }
    spec:
      containers:
        - name: nginx
          image: nginx:1.29.0
          ports:
            - containerPort: 80
EOF
kubectl rollout status deployment/web --timeout=180s
kubectl get deployment,replicaset,pods -l app=web

出力例

deployment.apps/web created
NAME                  READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/web   2/2     2            2           5s
NAME                             DESIRED   CURRENT   READY   AGE
replicaset.apps/web-7c9d8f6b5    2         2         2       5s
NAME                       READY   STATUS    RESTARTS   AGE
pod/web-7c9d8f6b5-abcde    1/1     Running   0          5s
pod/web-7c9d8f6b5-fghij    1/1     Running   0          5s

Deployment を 1 つ書くと、ReplicaSet が 1 つ、その配下に Pod が 2 つできています。 6 章の入れ子がそのまま出ています。 Pod 名の 7c9d8f6b5 は ReplicaSet 名の末尾と同じで、template のハッシュです。

3. 増やす

replicas を 4 にします。 YAML を書き換えて apply し直してもよいですが、ここでは scale を使います。

4 つに増やす
kubectl scale deployment web --replicas=4
kubectl get pods -l app=web

出力例

deployment.apps/web scaled
NAME                   READY   STATUS              RESTARTS   AGE
web-7c9d8f6b5-abcde    1/1     Running             0          1m
web-7c9d8f6b5-fghij    1/1     Running             0          1m
web-7c9d8f6b5-klmno    0/1     ContainerCreating   0          1s
web-7c9d8f6b5-pqrst    0/1     ContainerCreating   0          1s

scale がやったのは Deployment の spec.replicas を 4 に書き換えることだけです。 Deployment controller がそれを ReplicaSet に伝え、ReplicaSet controller が「2 足りない」を見て 2 つ作りました。 kubectl get pods -o wide で、それぞれが配置されたノードも確認できます。

4. 壊す

本題です。 Pod を 1 つ消して、戻ってくるのを見ます。

Pod を消したときに起きること: kubectl delete で Pod が Terminating になり、ReplicaSet controller が差分を見て新しい Pod を作り、scheduler と kubelet が起動する上段に時間軸。t0: Running 3。t1: delete で 1 つ Terminating。t2: ReplicaSet controller が新 Pod を作成 (Pending)。t3: scheduler が配置、kubelet が起動して Running 3 に戻る。t0: 3 つ Runningabcdefghijklmnodeletet1: 1 つ TerminatingabcdefghijklmnoReplicaSet controller: replicas 3 に対して (Terminating を除いて) 2 → 1 足りない → Pod を POST削除した本人が誰かは関係ない。差分だけを見る。watcht2: 新 Pod が Pending → ContainerCreatingfghijklmnoxyz12scheduler → kubelet → containerdt3: 3 つ Running (名前は 1 つ変わった)fghijklmnoxyz12数秒の出来事。Deployment も Service も、何も変更していない。
図 2Pod を消したときに起きること。delete で Terminating になり、ReplicaSet controller が差分を見て新しい Pod を作る。

先にシミュレータでもう一度動きを確認しておきます。 「Pod を kill」を押すと、少し遅れて controller が作り直します。

desired 3observed 3一致
  • web-00000Running
  • web-00001Running
  • web-00002Running
  1. controller: 監視を開始しました

controller を止めると、kill した Pod は戻ってきません。これが「宣言した状態を誰かが見張り続ける」ことの意味です。

実機では kubectl get pods -w で変化を追います。 -w は変化があるたびに行を出し、Ctrl-C まで止まりません。 先に delete を投げてから -w を始めると、Terminating と新しい Pod の作成が順に流れます。

Pod を 1 つ消して、戻ってくるのを見る (Ctrl-C で抜ける)
kubectl delete pod $(kubectl get pod -l app=web -o jsonpath='{.items[0].metadata.name}') --wait=false
kubectl get pods -l app=web -w

出力例

pod "web-7c9d8f6b5-abcde" deleted
NAME                   READY   STATUS              RESTARTS   AGE
web-7c9d8f6b5-abcde    1/1     Terminating         0          3m
web-7c9d8f6b5-fghij    1/1     Running             0          3m
web-7c9d8f6b5-klmno    1/1     Running             0          2m
web-7c9d8f6b5-pqrst    1/1     Running             0          2m
web-7c9d8f6b5-uvwxy    0/1     Pending             0          0s
web-7c9d8f6b5-uvwxy    0/1     ContainerCreating   0          0s
web-7c9d8f6b5-uvwxy    1/1     Running             0          2s
web-7c9d8f6b5-abcde    0/1     Terminating         0          3m

名前が 1 つ変わって、数は 4 のまま。 Deployment にも ReplicaSet にも触っていません。 ReplicaSet controller が replicas: 4 と「Terminating を除いた Pod の数 3」の差分を見て、1 つ作っただけです。 誰が消したかは関係ありません。 手で Pod を消した場合や、ノード障害で Pod が削除された場合に、この仕組みで数を補います。 Pod が残ったままコンテナだけが OOM kill された場合は、kubelet がコンテナを再起動します。

Ctrl-C で watch を抜けてから、次に進んでください。

5. 調べる

describe は、1 つのオブジェクトについて spec、status、関連する Events をまとめて出します。 何かがおかしいとき、最初に見るのはここです。

Pod を describe して Events を読む
POD=$(kubectl get pod -l app=web -o jsonpath='{.items[0].metadata.name}')
kubectl describe pod "$POD"
kubectl events --for "pod/$POD"

出力例

Events:
  Type    Reason     Age   From               Message
  ----    ------     ----  ----               -------
  Normal  Scheduled  2m    default-scheduler  Successfully assigned default/web-7c9d8f6b5-uvwxy to tenant-control-plane-7k2qd
  Normal  Pulled     2m    kubelet            Container image "nginx:1.29.0" already present on machine
  Normal  Created    2m    kubelet            Created container: nginx
  Normal  Started    2m    kubelet            Started container nginx

Events の From 列に注目してください。 default-scheduler がノードを決め、kubelet がイメージを確認してコンテナを作り、起動しています。 5 章のシーケンス図の後半が、そのままログになっています。 Pulled が “already present” なのは、先に作った Pod で pull 済みだからです。

Deployment 側も見ておきます。

Deployment を describe する
kubectl describe deployment web | grep -A3 -E '^Replicas|^StrategyType|^RollingUpdateStrategy|^NewReplicaSet'

出力例

Replicas:               4 desired | 4 updated | 4 total | 4 available | 0 unavailable
StrategyType:           RollingUpdate
RollingUpdateStrategy:  25% max unavailable, 25% max surge
NewReplicaSet:          web-7c9d8f6b5 (4/4 replicas created)

6. 更新する

イメージを変えて、ローリングアップデートを走らせます。 6 章のシミュレータで見た maxSurge / maxUnavailable が既定の 25% で効きます。

イメージを変えて rollout を見る
kubectl set image deployment/web nginx=nginx:1.29.1
kubectl rollout status deployment/web
kubectl get replicasets -l app=web

出力例

deployment.apps/web image updated
Waiting for deployment "web" rollout to finish: 1 out of 4 new replicas have been updated...
Waiting for deployment "web" rollout to finish: 2 out of 4 new replicas have been updated...
Waiting for deployment "web" rollout to finish: 1 old replicas are pending termination...
deployment "web" successfully rolled out
NAME             DESIRED   CURRENT   READY   AGE
web-5d4c7b8f9    4         4         4       30s
web-7c9d8f6b5    0         0         0       8m

新しい ReplicaSet が 4、古い ReplicaSet が 0 で残っています。 Pod 名のハッシュも変わっているはずです。

7. 戻す

rollout history と undo: Deployment は revision ごとに ReplicaSet を残し、undo は古い ReplicaSet を再び伸ばす上段に revision 1 (nginx:1.29) と revision 2 (nginx:1.29.1) の ReplicaSet。set image で 1 → 2、undo で 2 → 3 (中身は 1 と同じ ReplicaSet) に進む。revision 1rs: web-7c9d8f6b5image: nginx:1.29replicas: 4 → 0set imagerevision 2rs: web-5d4c7b8f9image: nginx:1.29.1replicas: 0 → 4rollout undorevision 3 (= 1 の再利用)rs: web-7c9d8f6b5image: nginx:1.29replicas: 0 → 4kubectl rollout history deployment/webREVISION CHANGE-CAUSE (1 は 3 に昇格して消え、2 と 3 が残る)undo は新しい ReplicaSet を作らない。同じ template の ReplicaSet が残っていればそれを伸ばす。
図 3rollout history と undo。Deployment は revision ごとに ReplicaSet を残し、undo は古い ReplicaSet を再び伸ばす。

履歴を見て、1 つ前に戻します。

履歴を見て、1 つ前に戻す
kubectl rollout history deployment/web
kubectl rollout undo deployment/web
kubectl rollout status deployment/web
kubectl get replicasets -l app=web
kubectl rollout history deployment/web

出力例

deployment.apps/web
REVISION  CHANGE-CAUSE
1         <none>
2         <none>

deployment.apps/web rolled back
deployment "web" successfully rolled out
NAME             DESIRED   CURRENT   READY   AGE
web-5d4c7b8f9    0         0         0       2m
web-7c9d8f6b5    4         4         4       10m
deployment.apps/web
REVISION  CHANGE-CAUSE
2         <none>
3         <none>

undo は、nginx:1.29.0 の template を持つ古い ReplicaSet (7c9d8f6b5) を 4 に伸ばし、新しい方を 0 に縮めました。 ReplicaSet の一覧が 2 行のまま変わっていないことから、新しい ReplicaSet は作られていないと分かります。 履歴では revision 1 が 3 に昇格しています。 rollout undo --to-revision=N で任意の世代にも戻せます。

後片付け

全部消す
kubectl delete deployment web
kubectl wait --for=delete pod -l app=web --timeout=180s
kubectl get pods -l app=web

出力例

deployment.apps "web" deleted
No resources found in default namespace.

Deployment を消すと、ReplicaSet と Pod も消えます。 子は親の ownerReferences を持っていて、Kubernetes のドキュメントの言葉では「既定ではバックグラウンドのカスケード削除が使われ、API サーバーは親オブジェクトを即座に消し、garbage collector controller が依存するオブジェクトをバックグラウンドで片付ける」からです。 これも controller の 1 つです。

Part 1 のまとめ

  • コンテナは、層になったイメージ、namespace、pivot_root、cgroups を揃えて起動した、ただのプロセス (1 章)。
  • Docker は作って配って動かすを 1 本にしたが、頼む相手は 1 台のホストの Docker だけ (2 章)。
  • 部品は 2008 年に揃い、Docker は 2013 年、Kubernetes は 2014 年に出て、1.0 と同時に CNCF に移った (3 章)。
  • マニフェストを apiserver に預けると etcd に記録され、spec と status の 2 つの欄で望みと現実を分けて持つ (4 章)。
  • controller が spec と現在の状態の差分を埋め続け、scheduler が置き場所を決め、kubelet がノードでコンテナを起動する。dockershim は 2022 年に消えた (5 章)。
  • Deployment → ReplicaSet → Pod。Service が名前を与え、ConfigMap と Secret が可変部分を差す (6 章)。
  • 3 つの顔があり、SIG が作り、CRD で拡張できる (7 章)。
  • そして今、Pod を消しても戻ることを自分の目で見た (この章)。

Part 2 からは、この Pod の「中」と「間」に入っていきます。

ふりかえり

Qkubectl scale deployment web --replicas=4 が直接書き換えるのは?

scale は Deployment の spec.replicas を変えるだけです。ReplicaSet への反映は Deployment controller、Pod の作成は ReplicaSet controller が行います。

QPod を消したあと、戻ってこないものは?

戻るのは数です。新しい Pod は新しい名前、新しい IP、空の書き込み層で起動します。

Qrollout undo が行うことは?

undo は古い ReplicaSet を再利用します。履歴として残っている ReplicaSet があるから戻せます。revisionHistoryLimit を 0 にすると戻せなくなります。

参考

読み切ると称号を獲得

「あるべき状態」の執行者

この Part を読み切りました。

称号一覧