Kubernetes特集 | 第2章(Day 3 / 6)
API Server・etcd・Scheduler・Controller・kubelet。5つの部品が何を頼み合っているかがわかると、
「どの層で止まっているか」を自分で切り分けられるようになります。
3日目です。昨日の第1章では、Podを直接作ると誰も見張らず、Deploymentという見張り役を付けると消しても戻ってくることを確かめました。今日は、その「見張り役」と「裏側の部品たち」を分解します。kubectl apply と打ってからコンテナが動き出すまでに、誰が何をしているのか。それが見えると、症状を見た瞬間に「止まっているのはどの層か」を言えるようになります。
昨日の演習の続きになっているので、第1章を読んでいない方は、先に「最初のPodを動かす」と「kubectlで状態を読む」だけ目を通しておいてください。
💡 Kubernetesは「YAMLを実行する」のではなく、APIに保存された望ましい状態(spec)と、いま観測されている状態(status)を、複数の制御ループが比べて差を埋め続けている仕組みです。API Serverは唯一の窓口、etcdは保存庫、SchedulerはNodeを選ぶ係、Controllerは差を埋める係、kubeletはNodeの上で実際に動かす係。障害調査は、この5つのどこまで進んだかを切り分けることから始まります。
この章では部品の名前を暗記してもらうつもりはありません。代わりに、1本のDeploymentを作って、イベントの1行1行が「誰の仕事か」を読めるようにします。読めるようになると、Pending と ImagePullBackOff が別の層の話だと、見た瞬間にわかります。
kubectl apply からコンテナ起動までの流れを、図2-1を指さしながら6ステップで説明できるkubectl get events の1行ずつが、Controller・Scheduler・kubeletのどの仕事か読めるownerReferences で確かめられるPending・イメージ取得失敗・Forbidden を「止まっている層」で切り分けられる特集トップの図1で「司令塔」とまとめたControl Planeの中身と、Node側の部品を、仕事で分けると次の表になります。
| 部品 | いる場所 | 仕事 | かなり雑に言うと |
|---|---|---|---|
| API Server | Control Plane | すべての読み書きを受け付ける。認証・認可・検証をして、etcdに保存する | 港の受付窓口。誰もここを通らずには何も頼めない |
| etcd | Control Plane | 望ましい状態と観測した状態を保存する分散データベース | 台帳。ここが消えると、何を頼まれていたか誰もわからなくなる |
| Scheduler | Control Plane | まだNodeが決まっていないPodを見つけて、置き場所を1つ選ぶ | 配置係。「どの岸壁に下ろすか」を決めるだけで、自分では運ばない |
| Controller Manager | Control Plane | Deployment、ReplicaSet、Node、Jobなど、種類ごとの見張り役をまとめて動かす | 見張り役の詰め所。台帳と現実を見比べて、足りなければ作る |
| kubelet | 各Node | 自分に割り当てられたPodを、コンテナランタイムに起動させ、結果を報告する | 現場の作業者。実際にコンテナを動かすのはここだけ |
ほかにも、Serviceの通信を各Nodeで振り分けるkube-proxyや、クラウドと連携するCloud Controller Managerがいますが、第2章ではこの5つに絞ります。kube-proxyは第4章で扱います。
第1章でDeploymentを作ったとき、裏では次の順番で仕事が回っていました。図2-1の番号を追ってください。
図2-1: kubectl applyからコンテナ起動まで。1〜4はControl Plane、5はNode、6は報告。
apply が「成功」と返すのはこの時点で、まだコンテナは1つも動いていません。nodeName に書き込みます。運ぶのではなく、台帳に「置き場所」を書くだけです。kubectl get pod で見ている STATUS は、この報告です。📌 部品どうしは直接話しません。 ControllerもSchedulerもkubeletも、API Serverの台帳を読んで、自分の担当の差を見つけて、結果を台帳に書き戻すだけです。この「全員が台帳を見て動く」形のおかげで、どれか1つが止まっても、ほかの部品は台帳を読み続けられます。
ここから手を動かします。昨日と同じく、まずContextを確かめて lab を作ってください。
kubectl config current-context # kind-xxx / minikube / docker-desktop などが出ればOK
kubectl create namespace lab
次のDeploymentを deploy.yaml として保存します。第1章の create deployment と同じ内容を、今回はYAMLで書きます。
# deploy.yaml ― 「nginxのPodを2個、いつもあるように」という望ましい状態
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
labels:
app.kubernetes.io/name: api
spec:
replicas: 2
selector:
matchLabels:
app.kubernetes.io/name: api # このラベルのPodを「自分の子」とみなす
template:
metadata:
labels:
app.kubernetes.io/name: api # selectorと一致していないと適用できない
spec:
containers:
- name: api
image: nginx:stable
ports:
- containerPort: 80
resources:
requests: # Schedulerが置き場所を選ぶときの「必要量」
cpu: 100m
memory: 64Mi
このYAMLを適用すると、裏で起きること
selector と template のラベルが一致しているかなどを検証し、etcdに保存するapi-<ハッシュ> を1つ作るapi-<ハッシュ>-<ランダム> を2個作るrequests の分だけ空きがあるNodeを選んで2個のPodに書き込むnginx:stable を取得して起動し、Readyになったと報告するkubectl -n lab apply -f deploy.yaml
# 親子3世代を一度に見る
kubectl -n lab get deploy,rs,pod -o wide
# 出来事を古い順に並べる(新しいものが下に来る)
kubectl -n lab get events --sort-by=.lastTimestamp
イベントは、次のような順番で並ぶはずです(名前やNodeは環境によって違います)。
# 出力の例(列を一部省略)
REASON OBJECT MESSAGE
ScalingReplicaSet deployment/api Scaled up replica set api-7d9c6b5f4 to 2
SuccessfulCreate replicaset/api-7d9c6b5f4 Created pod: api-7d9c6b5f4-x8k2q
SuccessfulCreate replicaset/api-7d9c6b5f4 Created pod: api-7d9c6b5f4-m4n7z
Scheduled pod/api-7d9c6b5f4-x8k2q Successfully assigned lab/api-7d9c6b5f4-x8k2q to kind-worker
Pulling pod/api-7d9c6b5f4-x8k2q Pulling image "nginx:stable"
Pulled pod/api-7d9c6b5f4-x8k2q Successfully pulled image "nginx:stable"
Created pod/api-7d9c6b5f4-x8k2q Created container api
Started pod/api-7d9c6b5f4-x8k2q Started container api
この1行1行に、担当者の名前を付けてみます。
| REASON | 誰の仕事か | 図2-1のステップ |
|---|---|---|
ScalingReplicaSet | Deployment Controller(Controller Managerの中) | 3 |
SuccessfulCreate | ReplicaSet Controller(同上) | 3 |
Scheduled | Scheduler | 4 |
Pulling / Pulled | kubelet(コンテナランタイムに取得を頼んだ) | 5 |
Created / Started | kubelet(コンテナランタイムに起動を頼んだ) | 5 |
障害のときは、この表のどこで行が途切れているかを見ます。Scheduled が出ていなければSchedulerの手前、Scheduled はあるのに Pulled がなければkubeletかレジストリ、という読み方です。第1章の演習1で「どこまでは成功したか」と書いたのは、このことでした。
kubectlも、Controllerも、kubeletも、etcdに直接書きには行きません。全員がAPI Serverを通ります。そのおかげで、認証、権限、検証、監査ログを1か所でそろえられます。API Serverの中では、1つのリクエストが次の関所を順番に通ります。
図2-2: API Serverの関所。どこで止まったかは、kubectlのエラー文でほぼ特定できる。
関所の順番を知っていると、エラー文を見ただけで切り分けが進みます。Unauthorized ならkubeconfigや証明書の期限、Forbidden なら権限、error validating ならYAMLの項目名です。Admissionは、クラスター管理者が「このラベルがないPodは拒否する」「このレジストリ以外のイメージは拒否する」といったルールを差し込む場所で、ここで止まると、YAMLは正しいのに適用できない、という症状になります。
# 自分がその操作をしてよいか、適用する前に聞ける(②認可の確認)
kubectl auth can-i create deployments -n lab
kubectl auth can-i delete nodes
# YAMLの項目名が正しいか、クラスターに何も作らずに確かめる(④検証の確認)
kubectl -n lab apply --dry-run=server -f deploy.yaml
--dry-run=server は、昨日使った --dry-run=client と違って、API Serverまでリクエストを送り、関所をすべて通してから「保存だけしない」で返してくれます。Admissionで拒否されるかどうかも、これで事前にわかります。
⚠️ 環境依存: Admission Webhookは、外部のプログラムに判断を委ねる仕組みです。そのプログラムが落ちているときに「拒否する(Fail)」か「通す(Ignore)」かは、Webhookごとに failurePolicy で決まっています。本番で「Webhookが落ちたせいで、全Namespaceで何も適用できなくなった」という障害は珍しくありません。導入するときは、落ちたときの挙動を先に決めてください。
etcdは、Kubernetesのすべての状態を保存する分散キーバリューストアです。DeploymentやServiceの定義だけでなく、Secretの値、Podにどのnodeが割り当てられたか、Controllerが管理している途中経過まで、全部ここにあります。
第1章で「Kubernetesは状態を頼む仕組み」と書きましたが、その状態が置かれている場所がetcdです。Nodeの上でコンテナが動いていても、etcdが失われると、「何個あるべきだったか」「どのServiceがどのPodを指していたか」を誰も思い出せなくなり、クラスターとして組み直せなくなります。
| 環境 | etcdを守るのは誰か | あなたが確認すること |
|---|---|---|
| kind / minikube | 誰も守らない(学習用) | 消えても作り直せばよい。だから壊す練習ができる |
| 自前で構築したクラスター | あなた | etcdctl snapshot save の定期実行、復元テスト、暗号化、保管期間 |
| EKS / GKE / AKS などマネージド | クラウド事業者 | 事業者が守るのはControl Planeの中だけ。アプリのデータ(PVCの中身)は別 |
⚠️ etcdのバックアップと、データのバックアップは別物です。 マネージドKubernetesで「バックアップは事業者がやってくれる」と聞いたとき、それはetcd、つまり「何を頼んでいたか」の台帳の話です。データベースのPodが使っているディスク(PVC)の中身は含まれません。第4章で、PVCのスナップショットを別に設計します。
Podが作られた直後は、まだどのNodeにも置かれていません。Schedulerは、この「Node未定」のPodを見つけると、全Nodeを2段階でふるいにかけます。
図2-3: Schedulerはまず「置けないNode」を除外し、残りを採点して1つ選ぶ。
フィルタで見るのは、requests に対するCPUとメモリの空き、NodeのTaint(「特別な許可がないPodは乗せない」という印)とPodのToleration(その許可)、Nodeを指名するAffinity、ホストのポート競合、ボリュームの制約などです。ここで注意してほしいのは、Schedulerが見るのは「実際の使用量」ではなく「requestsの合計」だということです。Nodeのメモリが実際には空いていても、すでに置かれたPodのrequestsの合計で埋まっていれば、置けません。
だから Pending のときに「Nodeを増やす」の前に、まず requests の値が妥当かを見ます。開発者が「念のため」と大きめに書いたrequestsが、クラスター全体の空きを食いつぶしていることはよくあります。
# 各NodeのCPU・メモリの「requestsの合計」と「上限」を見る(Allocated resources の欄)
kubectl describe node <Node名>
# NodeにTaintが付いているか
kubectl get nodes -o custom-columns=NAME:.metadata.name,TAINTS:.spec.taints
⚠️ Tolerationを安易に足さないでください。 Taintの付いたNodeに乗せたいとき、Tolerationを書けば乗ります。でもTaintは「ここは特別なNodeだ」という管理者の意思表示です。意味を確かめずにTolerationを付けると、重要なアプリがControl Planeと同じNodeに載ってしまったり、GPU専用Nodeを普通のアプリが埋めてしまったりします。
第1章で「見張り役」と呼んでいたのがControllerです。Deployment、ReplicaSet、Node、Jobなど、リソースの種類ごとに専用の見張り役がいて、Controller Managerという1つのプロセスの中でまとめて動いています。それぞれの仕事は同じ形です。台帳の「望ましい状態」と「観測した状態」を比べて、差があれば1つ埋める。これをずっと繰り返す。
図2-4: 親子関係と制御ループ。Podが1個消えると、差の1個だけを作り直す。
親子関係は、台帳の上で確かめられます。子のリソースには metadata.ownerReferences に親の名前が書かれていて、ReplicaSetはこれと selector を使って「自分の子」を数えます。
# ReplicaSetの親はDeployment、Podの親はReplicaSet
kubectl -n lab get rs,pod -o custom-columns=KIND:.kind,NAME:.metadata.name,OWNER:.metadata.ownerReferences[0].kind,OWNER_NAME:.metadata.ownerReferences[0].name
# 出力の例
KIND NAME OWNER OWNER_NAME
ReplicaSet api-7d9c6b5f4 Deployment api
Pod api-7d9c6b5f4-x8k2q ReplicaSet api-7d9c6b5f4
Pod api-7d9c6b5f4-m4n7z ReplicaSet api-7d9c6b5f4
Deploymentを更新すると、Deployment Controllerは既存のReplicaSetを書き換えるのではなく、新しいReplicaSetをもう1つ作り、古い方の数を減らしながら新しい方の数を増やします。これが「ローリングアップデート」で、古いReplicaSetは数が0になっても残るので、戻せます。使い方は明日の第3章で扱います。
📌 同じYAMLを何度applyしても、結果は同じになります。 Controllerは「差があれば埋める」だけなので、差が0なら何もしません。これが宣言的な運用の安心感です。逆に言うと、台帳の外で現実を変えると、Controllerはそれを「差」とみなして元に戻そうとします。Podを手で書き換えると数が合わなくなるのは、このためです(演習3で試します)。
kubeletは各Nodeで動くエージェントです。API Serverの台帳を見て、自分のNode名が書かれたPodを見つけると、CRI(Container Runtime Interface)という共通の取り決めを通して、containerdなどのコンテナランタイムに頼みます。イメージを取得して、コンテナを作って、起動して、止めて、ログを管理するのはランタイムの仕事です。
切り分けの観点では、ここが大事です。Podに Scheduled のイベントがあるのに起動しないなら、API ServerやSchedulerの話はもう終わっていて、Node側の話に入っています。 見る場所は、kubelet、コンテナランタイム、レジストリへの通信、Nodeのディスク容量、CNI(ネットワーク)、CSI(ストレージ)です。
| Node側の症状 | 疑う場所 | 見るコマンド |
|---|---|---|
ImagePullBackOff | レジストリの認証、Nodeからの外向き通信、イメージ名、CPUアーキテクチャ | describe pod のEvents |
ContainerCreating が長い | ボリュームのマウント(CSI)、ネットワークの用意(CNI) | describe pod のEvents、get pvc |
CrashLoopBackOff | アプリ自身(設定が読めない、依存先に届かない) | logs --previous、Last State の Exit Code |
Nodeが NotReady | kubeletの停止、ディスク枯渇、ランタイムの異常 | get nodes、describe node のConditions |
| 止めるのに30秒かかる | アプリがPID 1でSIGTERMを受け取っていない | DockerfileのENTRYPOINT、アプリの終了処理 |
最後の行は、Kubernetesの問題に見えて実際はコンテナの中身の問題、という典型です。kubeletはコンテナを止めるときにまずSIGTERMを送り、既定で30秒待ってから強制終了します。アプリがシェル経由で起動されていてシグナルを受け取れないと、毎回30秒待たされ、ローリングアップデートがやたら遅くなります。Dockerfileの書き方を直せば解決する話で、Kubernetesの設定をいじっても直りません。
ここまでの部品がやり取りしているのは、全部APIオブジェクトです。YAMLを「設定ファイル」ではなく、台帳の1ページとして読み直してみます。どのリソースも、次の4〜5つの区画でできています。
| 区画 | 誰が書くか | 何が入るか |
|---|---|---|
apiVersion / kind | あなた | どの種類のリソースか。apps/v1 の Deployment など |
metadata | あなた(+Kubernetes) | name(識別子)、namespace(範囲)、labels(選び出す用)、annotations(メモ用)。uid や ownerReferences はKubernetesが足す |
spec | あなた | 望ましい状態。Controllerが実現しようとする目標 |
status | Controllerとkubelet | 観測した状態。何個Readyか、どのNodeにいるか、条件(Conditions) |
spec はあなたの注文、status は現場からの報告です。status をYAMLに手で書いて apply しても、Controllerが報告で上書きするので意味がありません。逆に、kubectl get -o yaml で取り出したものには status も、Kubernetesが補った既定値も入っているので、それをそのままGitに戻すとファイルが膨らみ、差分が読めなくなります。
# 台帳の実体を読む(statusと既定値が足されている。手元のYAMLとは別物)
kubectl -n lab get deployment api -o yaml
# 手元のYAMLと台帳の差だけを見る(Gitのdiffと同じ感覚)
kubectl -n lab diff -f deploy.yaml
# どの項目を誰が書いたかを見る(通常は省略されている)
kubectl -n lab get deployment api -o yaml --show-managed-fields | less
status の中でも conditions は読む価値があります。Deploymentなら Available(必要数がReadyか)と Progressing(更新が進んでいるか)の2つがあり、「applyは成功したのに使えない」ときの最初の手がかりになります。
kubectl -n lab get deployment api -o jsonpath='{range .status.conditions[*]}{.type}={.status} ({.reason}){"\n"}{end}'
⚠️ 「applyが成功した」と「アプリが使える」は別です。 applyの成功は、図2-2の⑤「台帳に書けた」という意味しかありません。コンテナが動き、Readyになり、ServiceのEndpointに入り、ヘルスチェックに応えて、初めて「使える」です。確認の順番は第1章と同じで、存在 → Ready → エンドポイント → イベントです。
| 項目 | 何のため | 気をつけること |
|---|---|---|
| Namespace | 名前の衝突を避け、権限や上限(ResourceQuota)の範囲を区切る | Node、StorageClass、ClusterRoleはNamespaceの外にある。Namespaceを分けただけでは通信も権限も隔離されない(第4章・第5章) |
| Label | 選び出すためのタグ。ServiceやReplicaSetのselectorが見る | 後から変えると「誰の子か」が変わる。命名規則を先に決める(app.kubernetes.io/name など公式の推奨ラベルがある) |
| Annotation | 選ぶ条件ではない情報。ツールの設定、所有者、チケット番号など | 巨大な値や秘密情報を入れない。get -o yaml で誰でも読める |
ここからは、さっきの deploy.yaml を使って、層ごとに1つずつ壊します。壊すたびに「どの部品の仕事で止まったか」を言ってから直してください。
CPUのrequestsを、どのNodeにもないほど大きくします。
# requests を cpu 64コア に変える(学習環境にそんなNodeはない)
kubectl -n lab set resources deployment/api --requests=cpu=64
kubectl -n lab get pods -w # 新しいPodが Pending のまま止まる(Ctrl+Cで終了)
注目してほしいのは、古い2個のPodはRunningのまま残っていることです。Deploymentは「古いのを消してから新しいのを作る」のではなく、「新しいのが使えるようになってから古いのを減らす」ので、置けない変更を入れても利用者には影響が出ていません。
kubectl -n lab describe pod <Pending の Pod名> # 一番下の Events
# Events の例
Warning FailedScheduling default-scheduler 0/1 nodes are available: 1 Insufficient cpu.
preemption: 0/1 nodes are available: 1 No preemption victims found for incoming pod.
発行者が default-scheduler、理由が Insufficient cpu。図2-1でいう4番目のステップで止まっていて、kubeletもレジストリも、まだ何もしていません。ここでNodeを増やすのは1つの手ですが、今回はrequestsの方が間違っています。前の版に戻します。
kubectl -n lab rollout undo deployment/api
kubectl -n lab get pods # Pending だったPodが消え、2個に戻る
今度は、置き場所は決まるのに、Nodeの上で起動できない変更を入れます。
kubectl -n lab set image deployment/api api=nginx:this-tag-does-not-exist
kubectl -n lab rollout status deployment/api --timeout=60s
rollout status は「1 out of 2 new replicas have been updated...」のまま進まず、60秒でタイムアウトします。イベントを読みましょう。
kubectl -n lab get rs # ReplicaSetが2つになっている
kubectl -n lab get events --sort-by=.lastTimestamp | tail -8
新しいReplicaSetが作られ(Controllerの仕事は成功)、Podに Scheduled が付き(Schedulerの仕事も成功)、そのあと Failed / BackOff の行が、kubeletの名前で並んでいるはずです。止まっているのは図2-1の5番目、Node側です。そして演習1と同じく、古いReplicaSetの2個はRunningのままです。
この状態を放っておくと、Deploymentは既定の10分(progressDeadlineSeconds: 600)後に、Progressing の条件を ProgressDeadlineExceeded という理由でFalseにします。ただし、それは「あきらめた」という意味ではなく、Controllerは引き続き新しいPodの起動を試み続けます。原因を直すまで、この状態は終わりません。
kubectl -n lab rollout history deployment/api # 戻る前に、履歴を見ておく
kubectl -n lab rollout undo deployment/api
kubectl -n lab rollout status deployment/api # successfully rolled out
戻した理由をメモに残してください。「イメージのタグが存在しなかった。Schedulerではなくkubeletの段階で失敗。古いPodが残っていたので利用者への影響はなし」。この3点が書ければ、今日の章は身についています。
最後に、Controllerの「差を埋める」動きを逆方向から見ます。ReplicaSetの子であるPodのラベルを、selectorに合わないものに書き換えます。
POD=$(kubectl -n lab get pod -l app.kubernetes.io/name=api -o jsonpath='{.items[0].metadata.name}')
kubectl -n lab label pod "$POD" app.kubernetes.io/name=rogue --overwrite
kubectl -n lab get pods --show-labels
Podが3個になります。ラベルを変えたPodは消えずにRunningのまま残り、新しいPodが1個増えています。ReplicaSet Controllerから見ると、「selectorに合うPodが1個になった」ので差の1個を作っただけです。書き換えたPodは、もう誰の子でもありません。
kubectl -n lab get pod -o custom-columns=NAME:.metadata.name,OWNER:.metadata.ownerReferences[0].kind,LABEL:.metadata.labels.app\.kubernetes\.io/name
OWNER が <none> の行が、さっき書き換えたPodです。ServiceもラベルでPodを探すので、このPodには通信も届かなくなっています(第1章の演習3と同じ症状です)。本番でこれが起きると、「Podは動いているのに数が合わない」「削除しても別のが増える」という混乱になります。Podを直接いじる前に、そのPodの所有者が誰かを見る癖をつけてください。
kubectl -n lab delete pod "$POD" # 親のいないPodは、消しても誰も作り直さない
kubectl -n lab get pods # 2個に戻る
第1章と同じ6項目です。今日は「原因」の欄に、止まった層(部品の名前)を必ず入れてください。
# 演習メモの例
日時/Context : 2026-10-21 21:30 / kind-kind(Namespace: lab)
症状 : set image 後、rollout status が60秒で timed out
最初に見たもの: get events → 新Podに Scheduled あり、その後 Failed(kubelet)
原因 : イメージタグが存在しない。止まった層は kubelet/レジストリ
修正 : rollout undo で前の ReplicaSet に戻した
再発防止 : タグは --dry-run=server では検出できない。CIでイメージの存在を確認する
今日の内容を、障害のときにすぐ引ける形にまとめます。上から順に見ていけば、層を飛ばして「とりあえず再起動」をせずに済みます。
| 症状 | 止まっている層 | 最初に見る場所 |
|---|---|---|
connection refused、Unable to connect to the server | API Serverに届いていない | kubectl config current-context、VPN、エンドポイントの到達性 |
Unauthorized | 認証(図2-2の①) | kubeconfigの証明書・トークンの期限、クラウドのログイン状態 |
Forbidden | 認可(②) | kubectl auth can-i、RoleBinding |
admission webhook ... denied | Admission(③) | エラー文に書かれたルール名。管理者に確認 |
error validating、unknown field | 検証(④) | kubectl explain、インデント、apiVersion |
| applyは成功したのにPodが出ない | Controller(図2-1の3) | get rs、describe deployment のConditionsとEvents |
Pending | Scheduler(4) | describe pod の FailedScheduling の理由 |
ContainerCreating、ImagePullBackOff、CrashLoopBackOff | Node側(5) | describe pod のEvents、logs --previous、get nodes |
| Runningなのに届かない | Pod以外(Service、ラベル、ネットワーク) | get endpointslice、--show-labels(第1章・第4章) |
「API Serverが落ちたらサービスが全部止まる」と思っている人が多いのですが、正確ではありません。図2-1で、kubeletはAPI Serverの台帳を読んで動いていました。台帳が読めなくなっても、kubeletは最後に読んだPodを維持し続けます。
| 続くこと | 止まること |
|---|---|
| すでに動いているPodは動き続ける | 新しいPodの作成、Deploymentの更新 |
| Serviceの振り分けは、各Nodeに配られた設定で続く(kube-proxy) | Pod入れ替わりに伴うServiceの宛先更新 |
Nodeの上での再起動(restartPolicy)はkubeletが行う | 別のNodeへの作り直し、Pendingの解消、オートスケール |
| 利用者から見た画面は、しばらく正常に見える | kubectl のすべて。状態を読むことも、直すこともできない |
だから「画面が見えているから正常」とは判断できません。逆に、Control Planeの障害中でも、慌ててNodeを再起動するとPodが戻ってこなくなります。マネージドKubernetesでは、Control PlaneのログやetcdにはあなたからはOSごと触れないので、クラウドのヘルスイベント、監査ログ、サポート窓口を使う前提で、障害対応の手順書を作ります。
# Control Planeの各部品の健康状態(権限がないと Forbidden になる環境もある)
kubectl get --raw='/readyz?verbose'
# Nodeの条件(Ready / MemoryPressure / DiskPressure など)
kubectl get nodes
kubectl describe node <Node名> | sed -n '/Conditions:/,/Addresses:/p'
⚠️ 環境依存: kubectl get --raw がマネージド環境で Forbidden になっても、それはクラスター障害ではなく権限の話です。「自分が実行できる確認」と「事業者に問い合わせる項目」を、障害が起きる前に分けておいてください。責任分界を知らないまま障害に入ると、見られないログを探して時間が溶けます。
kubectl delete namespace lab
kubectl get namespaces # lab が消えたことを確認
今日もロードバランサーやディスクは作っていないので、これで終わりです。演習2のイメージタグ違いは、CIでイメージの存在を確認すれば防げる類のものです。明日の第3章では、更新とロールバックをDeploymentの機能として、もう少し丁寧に扱います。
Pending はSchedulerの層、ImagePullBackOff はNodeの層。古いPodが残っていれば利用者への影響はない🔖 明日10月22日(木)、第3章「ワークロード」を公開します。 今日作ったDeploymentに、Service、ConfigMapとSecret、Probe、requests/limitsを足して、Webアプリを複数台で安定して動かします。ローリングアップデートの「同時に止めてよい数」や、Jobで一度だけ動かす処理も扱います。
この特集は10月24日まで、毎日1ページずつ増えていきます。新しい章へのリンクは、すべて特集トップに追加されます。章ごとにブックマークしなくても、特集トップを1つ保存しておけば迷いません。
毎日の公開は、X(@BusinessHub413)でもお知らせします。
※本章の記述は2026年10月時点の公式ドキュメントに基づきます。イベントの文言、Schedulerのメッセージ、kubectl の出力形式は、Kubernetesのバージョンや学習環境によって少し異なることがあります。
「うちはKubernetesが要るのか」「ECSやCloud Runで足りるのか」から、
クラスター設計・運用体制づくりまで、30分の無料相談で承ります。