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

Kubernetes への頼み方

アプリを動かす設定を書き、Kubernetes に渡します。依頼が記録される仕組みと、実際の状態の読み方を確かめます。

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

Kubernetes で nginx を動かしてみましょう。 使うイメージと名前をファイルに書き、kubectl apply で渡します。 ホストに入って起動コマンドを打つ代わりに、クラスタへ「これを動かしてほしい」と伝える操作です。

この章では、設定ファイルの書き方と、その依頼を受け取る窓口を見ます。 最後に Pod を 1 つ作り、依頼した内容と実際の状態を見比べます。

GOAL この章のゴール

  • アプリを動かす設定ファイルの基本項目を読める
  • 依頼した状態と現在の状態を見分けられる
  • Pod を作成し、状態を確認してから削除できる

頼む相手が 1 台からクラスタになる

頼む相手の違い: docker compose は 1 台のホストの Docker デーモンに頼む。kubectl はクラスタの窓口 (kube-apiserver) に頼み、どのノードで動くかは窓口の先で決まる左は docker compose。compose ファイルを読んだ docker CLI が同じホストの dockerd に直接コンテナの起動を頼む。右は Kubernetes。kubectl が YAML を kube-apiserver に送り、apiserver の先に複数のノードがある。どのノードで動くかは利用者が書かない。docker compose updocker CLIcompose.yaml を読むこのホストEC2 1 台dockerdwebnginxdbpg起動先はこのホストだけAPI (UNIX socket)kubectl apply -f web.yamlkubectlYAML を HTTPS で送るkube-apiserverクラスタの窓口node-1webnginxnode-2dbpgnode-3(空き)どのノードで動くかは YAML に書かない。窓口の先で決まる
図 1頼む相手の違い。docker compose は同じホストの Docker デーモンに頼む。kubectl はクラスタの窓口に頼み、どのノードで動くかは窓口の先で決まる。

コンテナを動かすノードが複数あっても、利用者が 1 台ずつログインして依頼を出す必要はありません。 クラスタには 1 つの窓口があり、kubectl はそこに HTTPS で依頼を送ります。 EC2 でいえば、aws ec2 run-instances を打つと依頼は AWS の API エンドポイントに届き、どの物理ホストに載せるかは AWS が決める、という構造と同じです。 通常は、使うイメージや必要な資源を指定し、具体的な配置先は Kubernetes に任せます。

窓口の名前と中身は、次の節以降で順に見ます。 先に、窓口に渡す依頼書の形から始めます。

依頼書: マニフェスト

マニフェストの 4 つの欄: apiVersion、kind、metadata、spec。どの種類のオブジェクトにもこの 4 つがある左に Pod のマニフェストの YAML。右に 4 つの欄の説明。apiVersion は API のグループとバージョン、kind は種類、metadata は名前などの識別情報、spec は望む内容。web.yamlapiVersion: v1kind: Podmetadata: name: web-solospec: containers: - name: nginx image: nginx:1.29apiVersion: API グループとバージョンkind: 何の種類のオブジェクトかmetadata: 名前や namespace、ラベルspec: こうあってほしい、という内容種類ごとに書ける項目が違う
図 2Pod のマニフェストで使う 4 つの欄。種類と名前を指定し、spec に動かしたい内容を書く。

Kubernetes に頼むときは、やってほしいことを YAML で書きます。 この YAML を マニフェストマニフェストKubernetes に「何をどう動かしたいか」を伝えるための YAML (または JSON) のファイル。オブジェクトの種類 (kind) と名前、望む内容 (spec) を書く。用語集で見る と呼びます。 このファイルを渡すと、クラスタに「Kubernetes オブジェクト」として記録されます。 オブジェクトには、名前や設定、現在の状態が入ります。

Pod を作るときは、次の 4 つの欄を使います。

  • apiVersion:使う API のバージョン。Pod なら v1。
  • kind:作るものの種類。ここでは Pod。
  • metadata:名前やラベルなど、見分けるための情報。
  • spec:使うイメージなど、動かしたい内容。

種類によって欄は変わります。 たとえば ConfigMap は spec の代わりに data などへ値を書きます。 次のマニフェストは、2 章で出てきた Pod を 1 つ、nginx のイメージで動かしてほしい、という依頼書です。 kubectl apply -f - で標準入力から渡します。

Pod を 1 つ作る
kubectl apply -f - <<'EOF'
apiVersion: v1
kind: Pod
metadata:
  name: web-solo
spec:
  containers:
    - name: nginx
      image: nginx:1.29.0
EOF

出力例

pod/web-solo created

created と返ってきました。 この 1 行の裏で何が起きたかを、次の節で追います。

窓口: apiserver と記録係の etcd

kubectl apply が返るまでに起きること: apiserver が送り主を確かめ (認証)、操作の可否を確かめ (認可)、内容を検査して (admission)、etcd に保存する。保存が済んだ時点で kubectl に応答が返る左から kubectl、kube-apiserver (中に認証、認可、admission の 3 段)、etcd。kubectl から apiserver へ HTTPS の矢印、apiserver から etcd へ保存の矢印、etcd から apiserver、apiserver から kubectl へ応答の矢印。下に、この時点ではコンテナはまだ 1 つも動いていない、という注記。kubectlapply -f web.yamlPOSTcreatedkube-apiserver認証誰からか認可してよいかadmission内容の検査通らなければここでエラーを返す保存OKetcd記録係apply が返った時点で起きたのは「記録した」だけ。コンテナはまだ 1 つも動いていない。etcd に触るのは apiserver だけ。kubectl もノードも etcd を直接は読まない。
図 3kubectl apply が返るまでに起きること。apiserver が認証、認可、admission を通してから etcd に保存し、保存が済んだ時点で応答を返す。

kubectl apply は、このマニフェストを 1 つの場所に送っています。 その送り先が apiserverエーピーアイサーバーkube-apiserver。Kubernetes の窓口になる HTTP の API サーバー。kubectl も、クラスタ内の全プログラムも、ここを通してしか状態を読み書きしない。用語集で見る です。 利用者もクラスタ内のプログラムも、この窓口を通してオブジェクトを読み書きします。 API は REST の形で、POST、GET、PATCH、DELETE でオブジェクトを作り、読み、変え、消します。 kubectl はこの API を呼ぶコマンドラインツールで、REST を直接呼んでも同じことができます。

apiserver は、届いた依頼をそのまま保存するわけではありません。 送り主を確認し (認証)、その操作をしてよいかを確かめ (認可)、内容を検査して必要なら補います (admission)。 通らなければ、ここでエラーを返します。 通った依頼は、etcd に保存されます。 etcd は、Pod の設定など、クラスタの管理情報を保存するデータベースです。 アプリがボリュームに書いたファイルまで、ここへ保存されるわけではありません。 etcd に読み書きするのは apiserver だけで、kubectl もノードも etcd を直接は触りません。

つまり、kubectl apply が created を返した時点で起きていることは、「apiserver がマニフェストの内容を etcd に保存した」だけです。 コンテナを起動する作業は、まだ誰も始めていないかもしれません。 この「保存」と「実行」の分離が、この章と次の章の中心になります。

あるべき状態と現在の状態

1 つのオブジェクトの中にある 2 つの欄: spec は利用者が書く「こうあってほしい」、status はシステム側が書き戻す「実際はこうなっている」中央に Pod オブジェクト。上半分が spec (image: nginx:1.29)、下半分が status (phase: Running、podIP)。左から利用者 (kubectl apply) が spec に書く矢印。右から kubelet が status に書く矢印。利用者kubectl apply書くPod: web-solo (apiserver の中)spec (あるべき状態)containers: - image: nginx:1.29status (現在の状態)phase: RunningpodIP: 10.0.0.52containerStatuses: […]kubeletノード上の担当書き戻すspec と status が別の欄だから、「望みと現実がずれている」を 1 つのオブジェクトの中で判定できる。
図 41 つのオブジェクトにある 2 つの欄。spec は利用者が書き、status はシステム側 (この例では kubelet) が書き戻す。

保存された Pod を、apiserver から取り出して見てみます。

Pod の spec と status を読む
kubectl wait --for=condition=Ready pod/web-solo --timeout=180s
kubectl get pod web-solo -o jsonpath='{.spec.containers[0].image}{"\n"}{.status.phase}{"\n"}{.status.podIP}{"\n"}'

出力例

nginx:1.29.0
Running
10.0.0.52

1 行目は、先ほどのマニフェストに自分で書いた内容です。 2 行目と 3 行目は、自分では書いていません。 Pod が実際に動き出したあと、システムの側が書き込んだ内容です。 Pod では、この 2 種類の情報が spec と status に分かれています。 自分が書いた「こうあってほしい」の欄を あるべき状態あるべきじょうたいオブジェクトの spec 欄。利用者が書く「こうあってほしい」という望み。用語集で見る (spec) と呼び、システムが書き込む「実際はこうなっている」の欄を 現在の状態げんざいのじょうたいオブジェクトの status 欄。実際に今どうなっているかを、システム側が書き込む。用語集で見る (status) と呼びます。 spec を更新しても、すぐにその状態になるとは限りません。 システムが処理を進め、その結果を status に反映します。 この Pod で status.phase と status.podIP を書いたのは、ノードで動いているプログラム (次の章で kubelet と名付けます) です。

spec と status が別の欄に分かれていると、「望みと現実がずれている」ことを、1 つのオブジェクトの中で判定できます。 ドキュメントの例を借りると、Deployment の spec に 3 つの replica を書けば「Kubernetes は spec を読んで 3 つのインスタンスを起動し、status を spec に合わせて更新する。インスタンスが 1 つ落ちたら (status の変化)、Kubernetes は spec と status の差に反応して、代わりを起動する」という流れになります。 ただし、この「差に反応する」担当が居るかどうかは、オブジェクトの種類によって違います。 まず、担当が居ない場合に何が起きるかを見ます。

消した Pod は戻らない

Pod を消す
kubectl delete pod web-solo
kubectl get pods

出力例

pod "web-solo" deleted
No resources found in default namespace.

Pod は戻ってきません。 マニフェストに書いたのは「この Pod が居てほしい」でしたが、delete によって、その記録自体が etcd から消えました。 消えたあとに、それを思い出して作り直す担当が居なかったからです。 コンテナ内のプロセスが終了する場合とは区別してください。 Pod 自体が残っていれば、kubelet は restartPolicy に従ってコンテナを再起動できます。 Kubernetes のドキュメントが「Pod は比較的短命で使い捨ての存在として設計されていて、個々の Pod を直接作ることは滅多に無い」と書いているのは、この性質のためです。 Pod を作るのは、上位のオブジェクトの仕事で、次の章で見ます。

命令と宣言

命令と宣言の違い: docker run は「起動しろ」で終わる。宣言は「3 つあれ」を預け、以後は誰かが見張り続ける左は命令型。docker run を 3 回打ち、1 つ落ちると落ちたまま。右は宣言型。replicas: 3 を預け、見張り役が見張り、落ちたら作り直す。命令: docker run ×3$ docker run web$ docker run web$ docker run webwebweb(落ちた)コマンドは返った時点で仕事が終わる落ちたことに気付くのも直すのも人宣言: replicas: 3宣言replicas: 3預ける見張り役webwebweb (新)作り直すapply が返っても仕事は終わらない差分が出るたびに見張り役が埋める
図 5命令と宣言。「起動する」という操作と、「3 つ動く状態を保つ」という依頼の違い。

docker run は「起動しろ」という命令でした。 Docker はその後もコンテナを管理し、再起動ポリシーを設定すれば再起動もできます。 ただし、このコマンドだけで複数ノードにまたがる Pod の数を保つわけではありません。 それに対して、Kubernetes のマニフェストは「こうあってほしい」という宣言です。 Kubernetes のドキュメントはこれを「宣言的な設定」と呼び、「オブジェクトを作ったら、Kubernetes のシステムはそのオブジェクトが存在し続けるように絶えず働く」と書いています。

ただし、この章で見た通り、apiserver と etcd がしているのは「宣言を記録し、読み出せるようにする」ことだけです。 宣言が預けたあとも効き続けるには、預けた宣言と現実の差を、誰かが見張り続ける必要があります。 その見張り役を、Kubernetes では controller と呼びます。 次の章で、controller と、その動作を導入し、「web が 3 つ動いていてほしい」を消えても戻る形で実現します。

ふりかえり

Qkubectl apply が「created」を返した時点で、確実に起きていることは?

apply が返るのは、apiserver が認証、認可、admission を通して etcd に保存した時点です。コンテナの起動も、ノードの決定も、その後に別の担当が行います。

QPod の status.podIP を書くのは誰?

spec は利用者が書き、status はシステム側が書き戻します。Pod の場合、ノードで動くプログラム (次の章の kubelet) が書きます。

Q直接作った Pod を kubectl delete で消すと、どうなる?

delete で記録そのものが消え、作り直す担当も居ないので戻りません。Pod を数を保って動かすには、上位のオブジェクトと、それを見張る controller が要ります。

参考