Kubernetes特集
夜の港でクレーンがコンテナを1つだけ吊り上げている

Kubernetes特集 | 第1章(Day 2 / 6)

Kubernetesの基礎
コンテナを「望ましい状態」で動かす

Podを1つ動かして、わざと壊して、状態を読んで直す。
この章を終えると、Kubernetesに「何を頼んでいるのか」が自分の言葉で言えます。

📅 Day 2 | 10/20(火)公開

特集トップを読んでくれた方、おかえりなさい。今日から本編です。今日のテーマは、特集トップで「この特集の主題」として掲げた一文、「こうなっていてほしい」という状態を宣言すると、裏側の部品たちがその状態へ近づけ続けてくれるを、実際に手を動かして確かめることです。

まだ特集トップを読んでいない方は、先に「Kubernetesを1枚の図で」と「先に覚える用語」だけ目を通しておくと、この章がぐっと読みやすくなります。

この章の結論

💡 Kubernetesは、コンテナを一度起動して終わるツールではありません。「このアプリを3個、このイメージで、このポートで動かしたい」という望ましい状態をAPIに登録し、実際の状態との差を埋め続けるプラットフォームです。そして最初に覚える実行単位は、コンテナ単体ではなくPodです。

この章で大事にするのは、YAMLを上手に書くことではありません。kubectl get で状態を読み、イベントを調べ、「頼んだこと」と「いま起きていること」の差を説明できるようになることです。

✅ この章を読み終えるとできること

  • Cluster・Control Plane・Node・Podの関係を、特集トップの図1を指さしながら説明できる
  • 「命令的」と「宣言的」の違いを、docker run と kubectl apply で説明できる
  • Podを1つ動かし、get・describe・logs で状態を読める
  • ImagePullBackOff・CrashLoopBackOff・Endpointが空のServiceを、自分で起こして原因を見つけられる
  • Kubernetesに任せられることと、自分で責任を持つことの線を引ける

docker run と何が違うのか

コンテナそのものは、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で比べてみます。

命令的(Docker) docker run web:1.0 を3回実行 コンテナが3個起動して、終わり 1個落ちても、誰も数を数えていない 「手順」を頼む。結果を見張るのは人 宣言的(Kubernetes) kubectl apply「web:1.0を3個」 望ましい状態として保存(etcd) 「3個あるべき」がずっと残る 1個落ちたら、差に気づいて1個作る (Controllerが見張り続ける) 「状態」を頼む。見張るのはKubernetes

図1-1: 命令的な操作は「やること」を頼み、宣言的な操作は「あるべき姿」を頼む。

宣言的に書いたManifest(YAML)は、Gitで管理してレビューし、差分を確認して、何度でも同じ状態に再適用できます。これが、Kubernetesの運用がGitと相性のよい理由です。

⚠️ 手で直した変更は消えることがあります。 kubectl edit でクラスター上のリソースを直接書き換えると、次にGitのManifestを apply したときに上書きされます。学習中は気軽に使ってかまいませんが、本番では必ず元のファイルにも反映してください。

今日の話は、図1のどこか

特集トップの図1を思い出してください。この章で出てくる用語は、図1の中で次の位置にいます。

用語図1での位置役割
Cluster点線で囲まれた全体KubernetesのAPIで管理される範囲
Control Plane上段の青い枠(司令塔)API Server(唯一の窓口)、etcd(状態の保存庫)、Scheduler(置き場所を決める)、Controller Manager(差を埋める)
Node下段の2つの枠(作業者のマシン)kubeletが、自分に割り当てられたPodをコンテナランタイムに起動させる
PodNodeの中の青い箱コンテナをまとめた最小の実行単位

部品どうしがどう連携して、YAMLが実際のコンテナになるのかは、明日の第2章で順番に追います。今日は「あなたはAPI Serverにしか話さない」「動かす場所はNode」「動く単位はPod」の3つが言えれば十分です。

⚠️ 環境依存: kindやDocker Desktopのような学習環境では、Control PlaneとPodが同じマシンに同居していることがあります。本番では役割を分け、Control Planeのバックアップや、Nodeが落ちたときの再配置まで考えます。「Nodeを増やせば自動で高可用になる」わけではなく、アプリを複数に分けて、異なる障害ドメインへ散らす設定が要ります(第5章)。

Podは「使い捨ての箱」

Podは、1つ以上のコンテナをまとめた単位です。同じPodの中のコンテナは、IPアドレス、ポート、ボリュームを共有します。

Pod「web-demo」 IP 10.244.1.7(例) メインのコンテナ nginx:stable ポート 80 通常はこれ1つだけ サイドカー(任意) ログ転送・プロキシなど 同じ寿命・同じネットワークが 必要なときだけ同居させる 共有するもの IPアドレス ・ ポート空間(localhostで話せる) ・ ボリューム 落ちる 古いPodは修理されない 削除されて終わり IP 10.244.1.7 は消える 新しいPodが作られる 別のNodeかもしれない IPは別の値(例 10.244.2.4)

図1-2: Podは1つのIPを持つ箱。落ちたら直すのではなく、新しい箱に置き換わる。

Webアプリなら、Pod1つにメインのコンテナ1つが基本です。ここで大切なのは、Podは「ずっと同じ名前で居続けるサーバー」ではないということです。作り直されればIPも変わります。だから、アプリはPodのIPを覚えてはいけません。代わりにServiceの名前を使います。特集トップの図1で、利用者がServiceから入っていたのはこのためです。

最初のPodを動かす

ここから手を動かします。特集トップの「読み始める前の準備」で用意した学習環境(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を適用すると、裏で起きること

  1. API Serverが内容をチェックし、etcdに「web-demoというPodがあるべき」と保存する
  2. Schedulerが、空きのあるNodeを1つ選んで割り当てる
  3. そのNodeのkubeletが、コンテナランタイムに nginx:stable の取得と起動を頼む
  4. kubeletが、起動できたかどうかをAPI Serverに報告し続ける
# まず「書き方が正しいか」だけを確認(クラスターには何も作らない)
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章で扱います。

Podを消すと、どうなるか

ここで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で状態を読む

kubectl はAPI Serverに話しかけるクライアントです。Kubernetesの学習は、ほとんどが「状態を読む」練習だと思ってください。特集を通して、読む順番はいつも同じです。

  1. リソースが存在するか ― kubectl get
  2. PodがReadyか ― kubectl get の READY と STATUS
  3. Serviceにエンドポイントがあるか ― kubectl get endpointslice
  4. イベントに警告が出ていないか ― kubectl describe、kubectl get events
コマンド何がわかるかいつ使うか
kubectl get一覧と、ひとことの状態まず最初に。全体を眺める
kubectl describe詳細と、関係するイベント状態がおかしいとき、理由を探す
kubectl logsコンテナの標準出力アプリ自身が何を言っているか知りたいとき
kubectl get eventsNamespace内の出来事の時系列「さっき何が起きたか」を追うとき
kubectl explainYAMLの項目の意味書き方を調べるとき(ネットより正確)
# よく使う形
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を見たら、次に見る場所

STATUS何が起きているか次に見る場所
PendingまだどのNodeにも置かれていない、またはイメージ取得前describe のEvents(リソース不足、置けるNodeがない)
ContainerCreatingNodeには置かれたが、準備中describe のEvents(ボリューム、ネットワーク)
ImagePullBackOffイメージが取れず、間隔をあけて再試行中describe のEvents(タグ、認証、通信)
CrashLoopBackOff起動してはすぐ終了し、再起動をくり返しているlogs --previous(前回の終了直前の出力)
Running なのに届かないPod以外(Serviceやラベル)の問題かもしれないget endpointslice、--show-labels

わざと壊す演習

特集トップで約束したとおり、ここからは安全に小さく壊します。本番で出会う障害の多くは、この3つの組み合わせです。演習はすべて lab Namespaceで行います。

演習1

存在しないイメージを指定する

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      # 片付け
演習2

起動してすぐ終わるコンテナ

# "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
演習3

ラベルが1文字違うService

最初の 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が付かない(または追加設定が要る)クラウドのロードバランサーが作られ、料金がかかる
StorageClass1種類だけのことが多いディスクの種類ごとに複数あり、性能と料金が違う
失敗したときの影響作り直せばよい利用者と請求額に響く

演習メモに「使った環境」「Kubernetesの配布形態」「追加したアドオン」を書いておくのは、この差をあとで確かめるためです。学習環境の便利な前提を、本番の設計に持ち込まないこと。それが最初の安全策です。

Kubernetesに任せないこと

特集トップで「Kubernetesを使わない判断も成果」と書きました。その判断の土台になるのが、この境界線です。

Kubernetesがやってくれることそれでも自分が責任を持つこと
落ちたプロセスを再起動するアプリのバグを直す
Podの数を増やすデータベースへの同時更新を安全にする
Secretというオブジェクトを保存する保存時の暗号化やアクセス権を設定する(初期設定のままで強く守られるとは限らない)
クラウドのロードバランサーを作るDNS、TLS証明書、WAF、そして請求額
望ましい状態を保ち続けるその「望ましい状態」が正しいかどうか

Manifestをレビューするときは、「これはKubernetesの機能か」「アプリ・クラウド・運用の責任か」を分けて読む習慣をつけてください。この線引きは、第5章の本番運用でもう一度、表にして整理します。

新しいクラスターに接続したら、最初に確認すること

演習を終えたら、仕事で新しいクラスターを触るときの最初の手順として、次の表を手元に置いておいてください。いきなりアプリを適用するより、この順番の方がアプリの問題とクラスターの準備不足を混同しにくくなります。

順番確認することコマンドの例
1向いているクラスター(Context)kubectl config current-context
2Namespaceの一覧と、自分の作業場所kubectl get namespaces
3Nodeの台数と状態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 を作るところから始めます。

まとめ

  • Kubernetesは、望ましい状態を受け取り、現実との差を埋め続ける仕組み
  • Control Planeは司令塔、Nodeは作業者のマシン、Podは使い捨ての実行単位
  • Podを直接作っても誰も見張らない。見張り役(Controller)が付くと、消しても戻ってくる
  • 状態は「存在 → Ready → エンドポイント → イベント」の順で読む
  • 症状の名前だけで原因を決めない。Eventsで「どこまで成功したか」を確かめる

🔖 明日10月21日(水)、第2章「アーキテクチャ」を公開します。 今日「見張り役が差を埋める」と書いた部分を、API Server、etcd、Scheduler、Controller Manager、kubeletに分けて、YAMLが実際のコンテナになるまでを図で追います。

🔖 特集トップをブックマークしておいてください

この特集は10月24日まで、毎日1ページずつ増えていきます。新しい章へのリンクは、すべて特集トップに追加されます。章ごとにブックマークしなくても、特集トップを1つ保存しておけば迷いません。

Windows Ctrl+DMac ⌘+Dスマホ 共有 → ブックマーク
𝕏 更新のお知らせを受け取る

毎日の公開は、X(@BusinessHub413)でもお知らせします。

公式資料

※本章の記述は2026年10月時点の公式ドキュメントに基づきます。出力の表示形式やイベントの文言は、Kubernetesのバージョンや学習環境によって少し異なることがあります。

Kubernetesの導入判断や設計についてご相談ください

「うちはKubernetesが要るのか」「ECSやCloud Runで足りるのか」から、
クラスター設計・運用体制づくりまで、30分の無料相談で承ります。

無料相談を予約する →