Kubernetes で nginx を動かしてみましょう。
使うイメージと名前をファイルに書き、kubectl apply で渡します。
ホストに入って起動コマンドを打つ代わりに、クラスタへ「これを動かしてほしい」と伝える操作です。
この章では、設定ファイルの書き方と、その依頼を受け取る窓口を見ます。 最後に Pod を 1 つ作り、依頼した内容と実際の状態を見比べます。
GOAL この章のゴール
- アプリを動かす設定ファイルの基本項目を読める
- 依頼した状態と現在の状態を見分けられる
- Pod を作成し、状態を確認してから削除できる
頼む相手が 1 台からクラスタになる
コンテナを動かすノードが複数あっても、利用者が 1 台ずつログインして依頼を出す必要はありません。
クラスタには 1 つの窓口があり、kubectl はそこに HTTPS で依頼を送ります。
EC2 でいえば、aws ec2 run-instances を打つと依頼は AWS の API エンドポイントに届き、どの物理ホストに載せるかは AWS が決める、という構造と同じです。
通常は、使うイメージや必要な資源を指定し、具体的な配置先は Kubernetes に任せます。
窓口の名前と中身は、次の節以降で順に見ます。 先に、窓口に渡す依頼書の形から始めます。
依頼書: マニフェスト
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 - で標準入力から渡します。
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 createdcreated と返ってきました。
この 1 行の裏で何が起きたかを、次の節で追います。
窓口: apiserver と記録係の 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 に保存した」だけです。
コンテナを起動する作業は、まだ誰も始めていないかもしれません。
この「保存」と「実行」の分離が、この章と次の章の中心になります。
あるべき状態と現在の状態
保存された Pod を、apiserver から取り出して見てみます。
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.521 行目は、先ほどのマニフェストに自分で書いた内容です。
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 は戻らない
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 は「起動しろ」という命令でした。
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 が要ります。
参考
- Kubernetes ドキュメント「Cluster Architecture」: https://kubernetes.io/docs/concepts/architecture/
- Kubernetes ドキュメント「Objects In Kubernetes」(record of intent、spec と status、必須の 4 つの欄): https://kubernetes.io/docs/concepts/overview/working-with-objects/
- Kubernetes ドキュメント「The Kubernetes API」: https://kubernetes.io/docs/concepts/overview/kubernetes-api/
- Kubernetes ドキュメント「Kubernetes API Concepts」(REST、watch): https://kubernetes.io/docs/reference/using-api/api-concepts/
- Kubernetes ドキュメント「Kubernetes Components」(etcd の定義): https://kubernetes.io/docs/concepts/overview/components/
- Kubernetes ドキュメント「Pods」: https://kubernetes.io/docs/concepts/workloads/pods/
- Kubernetes community「API Conventions」(spec と status の規約): https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md