Kubernetes特集 | 第1章(Day 2 / 6)
Podを1つ動かして、わざと壊して、状態を読んで直す。
この章を終えると、Kubernetesに「何を頼んでいるのか」が自分の言葉で言えます。
特集トップを読んでくれた方、おかえりなさい。今日から本編です。今日のテーマは、特集トップで「この特集の主題」として掲げた一文、「こうなっていてほしい」という状態を宣言すると、裏側の部品たちがその状態へ近づけ続けてくれるを、実際に手を動かして確かめることです。
まだ特集トップを読んでいない方は、先に「Kubernetesを1枚の図で」と「先に覚える用語」だけ目を通しておくと、この章がぐっと読みやすくなります。
💡 Kubernetesは、コンテナを一度起動して終わるツールではありません。「このアプリを3個、このイメージで、このポートで動かしたい」という望ましい状態をAPIに登録し、実際の状態との差を埋め続けるプラットフォームです。そして最初に覚える実行単位は、コンテナ単体ではなくPodです。
この章で大事にするのは、YAMLを上手に書くことではありません。kubectl get で状態を読み、イベントを調べ、「頼んだこと」と「いま起きていること」の差を説明できるようになることです。
docker run と kubectl apply で説明できるget・describe・logs で状態を読めるImagePullBackOff・CrashLoopBackOff・Endpointが空のServiceを、自分で起こして原因を見つけられるコンテナそのものは、Dockerで使ってきたものと同じです。アプリと依存ファイルをイメージにまとめ、ホスト上でプロセスとして動かします。1台のマシンで開発したり、単純なバッチを動かしたりするなら、docker run と再起動設定で十分なこともあります。
困るのは、同じアプリを複数台で動かし始めたときです。
| 困りごと | Docker(1台)なら | Kubernetesでは |
|---|---|---|
| どのマシンに何個あるか | 1台なので docker ps で見える | API Serverに聞けば、全Nodeの分が一覧で返る |
| 落ちたらどこで再起動するか | restart: always で同じマシンに再起動 | Controllerが、空いている別のNodeにも作り直す |
| 更新中に何個止めてよいか | 手順書と人の判断 | Deploymentに「同時に止めてよい数」を書いておく(第3章) |
| 相手のコンテナをどう見つけるか | composeのサービス名 | Serviceという固定の名前とIP(第3章・第4章) |
ここで1つ覚えておいてほしいのは、Kubernetes自身がコンテナを直接起動しているわけではないことです。各Nodeにいるkubeletが、containerdなどのコンテナランタイムに「このコンテナを動かして」と頼んでいます。Kubernetesは、誰に何を頼むかを決めて、頼んだとおりになっているかを見張る係です。
Kubernetesがわかりにくい最大の理由は、頼み方がDockerと逆向きだからです。図1-1で比べてみます。
図1-1: 命令的な操作は「やること」を頼み、宣言的な操作は「あるべき姿」を頼む。
宣言的に書いたManifest(YAML)は、Gitで管理してレビューし、差分を確認して、何度でも同じ状態に再適用できます。これが、Kubernetesの運用がGitと相性のよい理由です。
⚠️ 手で直した変更は消えることがあります。 kubectl edit でクラスター上のリソースを直接書き換えると、次にGitのManifestを apply したときに上書きされます。学習中は気軽に使ってかまいませんが、本番では必ず元のファイルにも反映してください。
特集トップの図1を思い出してください。この章で出てくる用語は、図1の中で次の位置にいます。
| 用語 | 図1での位置 | 役割 |
|---|---|---|
| Cluster | 点線で囲まれた全体 | KubernetesのAPIで管理される範囲 |
| Control Plane | 上段の青い枠(司令塔) | API Server(唯一の窓口)、etcd(状態の保存庫)、Scheduler(置き場所を決める)、Controller Manager(差を埋める) |
| Node | 下段の2つの枠(作業者のマシン) | kubeletが、自分に割り当てられたPodをコンテナランタイムに起動させる |
| Pod | Nodeの中の青い箱 | コンテナをまとめた最小の実行単位 |
部品どうしがどう連携して、YAMLが実際のコンテナになるのかは、明日の第2章で順番に追います。今日は「あなたはAPI Serverにしか話さない」「動かす場所はNode」「動く単位はPod」の3つが言えれば十分です。
⚠️ 環境依存: kindやDocker Desktopのような学習環境では、Control PlaneとPodが同じマシンに同居していることがあります。本番では役割を分け、Control Planeのバックアップや、Nodeが落ちたときの再配置まで考えます。「Nodeを増やせば自動で高可用になる」わけではなく、アプリを複数に分けて、異なる障害ドメインへ散らす設定が要ります(第5章)。
Podは、1つ以上のコンテナをまとめた単位です。同じPodの中のコンテナは、IPアドレス、ポート、ボリュームを共有します。
図1-2: Podは1つのIPを持つ箱。落ちたら直すのではなく、新しい箱に置き換わる。
Webアプリなら、Pod1つにメインのコンテナ1つが基本です。ここで大切なのは、Podは「ずっと同じ名前で居続けるサーバー」ではないということです。作り直されればIPも変わります。だから、アプリはPodのIPを覚えてはいけません。代わりにServiceの名前を使います。特集トップの図1で、利用者がServiceから入っていたのはこのためです。
ここから手を動かします。特集トップの「読み始める前の準備」で用意した学習環境(kind、minikube、Docker Desktopのどれか)を使ってください。最初の1行は、いつも「いまどのクラスターに向いているか」の確認です。
# 本番に向いていないことを必ず確認(kind-xxx / minikube / docker-desktop などが出ればOK)
kubectl config current-context
# この特集の演習用に、壊してよい作業場所(Namespace)を作る
kubectl create namespace lab
次に、Podを1つ書きます。pod.yaml という名前で保存してください。
# pod.yaml ― 「nginxのコンテナを1つ、ポート80で動かしたい」という望ましい状態
apiVersion: v1
kind: Pod
metadata:
name: web-demo
labels:
app: web-demo # 後でServiceがこのラベルでPodを探す
spec:
containers:
- name: web
image: nginx:stable
ports:
- name: http
containerPort: 80
このYAMLを適用すると、裏で起きること
nginx:stable の取得と起動を頼む# まず「書き方が正しいか」だけを確認(クラスターには何も作らない)
kubectl -n lab apply --dry-run=client -f pod.yaml
# 本当に適用する
kubectl -n lab apply -f pod.yaml
# 状態を見る。-o wide で「どのNodeに置かれたか」「PodのIP」も出る
kubectl -n lab get pod web-demo -o wide
数秒〜数十秒で、次のような表示になります(名前やIPは環境によって違います)。
# 出力の例
NAME READY STATUS RESTARTS AGE IP NODE
web-demo 1/1 Running 0 20s 10.244.1.7 kind-control-plane
READY 1/1 は「コンテナ1つのうち1つが準備完了」、STATUS Running は「コンテナが動いている」という意味です。本当にnginxが応答するかも確かめましょう。
# 手元の8080番をPodの80番につなぐ(Ctrl+Cで終了)
kubectl -n lab port-forward pod/web-demo 8080:80
# 別のターミナルで
curl -s http://localhost:8080 | head -5
nginxのHTMLが返ってくれば成功です。port-forward は学習や調査のための一時的な抜け道で、利用者に公開する方法ではありません。公開の方法は第3章と第4章で扱います。
ここで1つ実験をします。いま作ったPodを消してみてください。
kubectl -n lab delete pod web-demo
kubectl -n lab get pods
Podは消えたままで、戻ってきません。「Kubernetesは落ちたら作り直してくれるのでは?」と思った方、それは正しい疑問です。今回のYAMLは「Podを1つ作って」という頼み方で、「いつも1個あるように見張って」とは頼んでいないからです。見張り役を付けるとどうなるか、比べてみましょう。
# 「nginxを3個、いつもあるように」と頼む(Deploymentという見張り役付き)
kubectl -n lab create deployment web --image=nginx:stable --replicas=3
kubectl -n lab get pods
# 3個のうち1個を消してみる(NAMEは自分の画面の値に置き換える)
kubectl -n lab delete pod <表示されたPod名の1つ>
# すぐに新しいPodが作られ、また3個になる
kubectl -n lab get pods
新しい名前のPodが現れて、また3個に戻ります。これが特集トップで書いた「状態を宣言すると、裏側の部品がその状態へ近づけ続ける」の正体です。Deploymentの細かい使い方は第3章で扱うので、今日は「見張り役がいると戻ってくる」だけ覚えておいてください。
📌 ここがこの章のいちばん大事なところです。 Podを直接作ると、誰も見張りません。本番のアプリは、必ずDeploymentのような見張り役(Controller)を通して動かします。この「見張り役が差を埋める仕組み」を、明日の第2章で部品ごとに分解します。
kubectl はAPI Serverに話しかけるクライアントです。Kubernetesの学習は、ほとんどが「状態を読む」練習だと思ってください。特集を通して、読む順番はいつも同じです。
kubectl getkubectl get の READY と STATUSkubectl get endpointslicekubectl describe、kubectl get events| コマンド | 何がわかるか | いつ使うか |
|---|---|---|
kubectl get | 一覧と、ひとことの状態 | まず最初に。全体を眺める |
kubectl describe | 詳細と、関係するイベント | 状態がおかしいとき、理由を探す |
kubectl logs | コンテナの標準出力 | アプリ自身が何を言っているか知りたいとき |
kubectl get events | Namespace内の出来事の時系列 | 「さっき何が起きたか」を追うとき |
kubectl explain | YAMLの項目の意味 | 書き方を調べるとき(ネットより正確) |
# よく使う形
kubectl get pods -A # 全Namespaceを眺める
kubectl -n lab get events --sort-by=.lastTimestamp # 新しい出来事が下に来る
kubectl explain pod.spec.containers # containers に書ける項目の説明
⚠️ 表示しすぎに注意: kubectl get ... -o yaml はAPI上の実体を丸ごと読めて便利ですが、Secretの値なども表示できてしまいます。画面共有中や、ログが残る端末では気をつけてください。
| STATUS | 何が起きているか | 次に見る場所 |
|---|---|---|
Pending | まだどのNodeにも置かれていない、またはイメージ取得前 | describe のEvents(リソース不足、置けるNodeがない) |
ContainerCreating | Nodeには置かれたが、準備中 | describe のEvents(ボリューム、ネットワーク) |
ImagePullBackOff | イメージが取れず、間隔をあけて再試行中 | describe のEvents(タグ、認証、通信) |
CrashLoopBackOff | 起動してはすぐ終了し、再起動をくり返している | logs --previous(前回の終了直前の出力) |
Running なのに届かない | Pod以外(Serviceやラベル)の問題かもしれない | get endpointslice、--show-labels |
特集トップで約束したとおり、ここからは安全に小さく壊します。本番で出会う障害の多くは、この3つの組み合わせです。演習はすべて lab Namespaceで行います。
kubectl -n lab run broken --image=example.invalid/not-found:0
kubectl -n lab get pod broken -w # 状態の移り変わりを見る(Ctrl+Cで終了)
ErrImagePull から ImagePullBackOff に変わるはずです。次にイベントを読みます。
kubectl -n lab describe pod broken # 一番下の Events を読む
Eventsを上から読むと、Scheduled(Nodeへの割り当ては成功)のあとに、イメージの取得で失敗していることがわかります。「どこまでは成功したか」を区別するのがポイントです。
ImagePullBackOff という名前だけを見て「タグの打ち間違いだ」と決めつけないでください。プライベートなレジストリの認証、Nodeから外への通信、CPUアーキテクチャの違い(ARMのMacで作ったイメージなど)でも、同じ症状になります。
kubectl -n lab delete pod broken # 片付け
# "starting" と表示して、すぐ終了コード1で終わるコンテナ
kubectl -n lab run crash --image=busybox:1.36 -- sh -c "echo starting; exit 1"
kubectl -n lab get pod crash -w
RESTARTSの数が増えながら、CrashLoopBackOff になります。再起動の間隔は少しずつ長くなります。
kubectl -n lab logs crash --previous # 前回のコンテナが最後に出した出力
kubectl -n lab describe pod crash # Last State の Exit Code を見る
Last State: Terminated、Exit Code: 1 と出ていれば、Kubernetesではなくアプリ(コンテナの中身)が自分で終わったということです。設定ファイルが読めずに終了しているアプリは、何度再起動しても直りません。原因を直して、Manifestに反映してから適用し直します。
kubectl -n lab delete pod crash
最初の pod.yaml をもう一度適用し、ラベルをわざと間違えたServiceを作ります。svc.yaml として保存してください。
kubectl -n lab apply -f pod.yaml
# svc.yaml ― selectorを1文字間違えている
apiVersion: v1
kind: Service
metadata:
name: web-svc
spec:
selector:
app: web-dem0 # 正しくは web-demo(最後が数字のゼロ)
ports:
- port: 80
targetPort: http
kubectl -n lab apply -f svc.yaml
kubectl -n lab get pod web-demo --show-labels
kubectl -n lab describe svc web-svc # Endpoints: <none>
kubectl -n lab get endpointslice -l kubernetes.io/service-name=web-svc
PodはRunningなのに、ServiceのEndpointsは空です。ServiceはラベルでPodを探すので、1文字違うだけで「届け先が0個」になります。Podが元気でも、利用者には届きません。
selectorを web-demo に直して適用し直すと、EndpointsにPodのIPが入ります。名前解決ができたことだけで「つながる」と判断しないのが、この演習の教訓です。
演習のたびに、次の6項目を1行ずつ書いてください。この特集の残りの章でも同じ形式を使います。チームで共有すれば、そのまま障害対応のRunbookの下書きになります。
# 演習メモの例
日時/Context : 2026-10-20 21:10 / kind-kind(Namespace: lab)
症状 : web-demo は Running だが web-svc に届かない
最初に見たもの: kubectl describe svc web-svc → Endpoints: <none>
原因 : Serviceの selector が app=web-dem0(Podは app=web-demo)
修正 : svc.yaml の selector を直して apply
再発防止 : apply 後に get endpointslice まで確認する
ローカルで動いたからといって、本番で同じように動くとは限りません。今日の演習の範囲でも、次の差があります。
| 項目 | 学習環境(kind / minikube / Docker Desktop) | 本番のクラウド |
|---|---|---|
| Control Plane | 同じマシンにPodと同居することがある | 分離され、マネージドサービスでは事業者が管理 |
| Nodeの台数 | 1台のことが多い | 複数台。複数のゾーンに分散できる |
| type: LoadBalancer | 外部IPが付かない(または追加設定が要る) | クラウドのロードバランサーが作られ、料金がかかる |
| StorageClass | 1種類だけのことが多い | ディスクの種類ごとに複数あり、性能と料金が違う |
| 失敗したときの影響 | 作り直せばよい | 利用者と請求額に響く |
演習メモに「使った環境」「Kubernetesの配布形態」「追加したアドオン」を書いておくのは、この差をあとで確かめるためです。学習環境の便利な前提を、本番の設計に持ち込まないこと。それが最初の安全策です。
特集トップで「Kubernetesを使わない判断も成果」と書きました。その判断の土台になるのが、この境界線です。
| Kubernetesがやってくれること | それでも自分が責任を持つこと |
|---|---|
| 落ちたプロセスを再起動する | アプリのバグを直す |
| Podの数を増やす | データベースへの同時更新を安全にする |
| Secretというオブジェクトを保存する | 保存時の暗号化やアクセス権を設定する(初期設定のままで強く守られるとは限らない) |
| クラウドのロードバランサーを作る | DNS、TLS証明書、WAF、そして請求額 |
| 望ましい状態を保ち続ける | その「望ましい状態」が正しいかどうか |
Manifestをレビューするときは、「これはKubernetesの機能か」「アプリ・クラウド・運用の責任か」を分けて読む習慣をつけてください。この線引きは、第5章の本番運用でもう一度、表にして整理します。
演習を終えたら、仕事で新しいクラスターを触るときの最初の手順として、次の表を手元に置いておいてください。いきなりアプリを適用するより、この順番の方がアプリの問題とクラスターの準備不足を混同しにくくなります。
| 順番 | 確認すること | コマンドの例 |
|---|---|---|
| 1 | 向いているクラスター(Context) | kubectl config current-context |
| 2 | Namespaceの一覧と、自分の作業場所 | kubectl get namespaces |
| 3 | Nodeの台数と状態 | kubectl get nodes |
| 4 | 使えるストレージの種類 | kubectl get storageclass |
| 5 | 外からの入口の仕組み(IngressやGateway) | kubectl get ingressclass |
| 6 | テスト用Podを1つ起動して、ログとイベントを読む | この章の pod.yaml |
誰がどのリソースを変更できるか、Namespaceを誰が作るか、Secretをどう配るかといったチーム内の取り決めも、早めに決めておくと後で困りません。権限の考え方は第5章で扱います。
最後に、演習で作ったものをまとめて消します。Namespaceを消すと、中のPod、Deployment、Serviceも一緒に消えます。
kubectl delete namespace lab
kubectl get namespaces # lab が消えたことを確認
今日の演習では外部のロードバランサーやディスクを作っていないので、これで終わりです。クラウドで練習した場合は、ロードバランサーやディスクが残っていないかを、クラウドの管理画面でも確認してください。「作って、確かめて、片付ける」までを1つの手順にする習慣は、本番で不要なリソースを残す事故を防ぎます。明日の第2章でも、また lab を作るところから始めます。
🔖 明日10月21日(水)、第2章「アーキテクチャ」を公開します。 今日「見張り役が差を埋める」と書いた部分を、API Server、etcd、Scheduler、Controller Manager、kubeletに分けて、YAMLが実際のコンテナになるまでを図で追います。
この特集は10月24日まで、毎日1ページずつ増えていきます。新しい章へのリンクは、すべて特集トップに追加されます。章ごとにブックマークしなくても、特集トップを1つ保存しておけば迷いません。
毎日の公開は、X(@BusinessHub413)でもお知らせします。
※本章の記述は2026年10月時点の公式ドキュメントに基づきます。出力の表示形式やイベントの文言は、Kubernetesのバージョンや学習環境によって少し異なることがあります。
「うちはKubernetesが要るのか」「ECSやCloud Runで足りるのか」から、
クラスター設計・運用体制づくりまで、30分の無料相談で承ります。