コントロールプレーンとノードの構成

v1.37 時点(2026年10月21日 確認)

Kubernetes 基本仕様第4回 / 全45回 ・ 第1部 クラスタの仕組み
目次

この記事ではクラスタを形づくる 2 つの部分である コントロールプレーン と ノード の構成を、図で説明します。それぞれの中でどんなコンポーネントが動き、何を受け持っているのか、互いにどうやり取りしているのかがわかります。後半では kind のマルチノードのクラスタで kube-system の Pod を一覧し、各コンポーネントの役割と対応づける手順を示します。

kubectl と kubeconfig を扱った第3回の内容を前提にしています。まだの方は先にこちらをどうぞ。

基本仕様 第3回 kubectl と kubeconfig の仕組みkubectl がどのクラスタに接続するかを決める kubeconfig の仕組みを図で説明します。clusters・users・contexts の関係と、接続先が決まる順番を押さえ、get・describe・explain の使い分けと、コンテキストを切り替えると接続先が変わることを kind のクラスタで確かめる手順を紹介します。

以下の図は、コントロールプレーンとノードのそれぞれで動くコンポーネントと、そのつながりを表しています。

kube-apiserver を中心にやり取りする、コントロールプレーンとノードのコンポーネント

kube-scheduler や kubelet などのコンポーネントは、互いに直接やり取りせず、kube-apiserver を通してやり取りします。クラスタのデータを etcd に読み書きするのも kube-apiserver です。例外はノードの中の kubelet とコンテナランタイムで、kubelet はコンテナランタイムに直接指示を出します。

図の「監視」の線は、各コンポーネントが kube-apiserver に接続して、自分に関係するリソースの変化を見張っていることを表しています。Kubernetes の API には、最初に状態を取り出し、その後の変化を流れとして受け取り続ける watch という仕組みがあります。公式ドキュメントでは、kube-scheduler はノードが割り当てられていない新しい Pod を見張り、コントローラーは kube-apiserver を通してクラスタの状態を見張ると説明されています。変化に気づいた多くのコンポーネントは、決めたことや結果を kube-apiserver に書き戻します。たとえば kube-scheduler は選んだノードを、kubelet はノードの状態を書き込みます。kube-proxy は見張った Service の内容をノードのネットワークの規則に反映します。

参考:Kubernetes API Concepts | Kubernetes、Cluster Architecture | Kubernetes、kube-controller-manager | Kubernetes、Kubernetes Scheduler | Kubernetes、Node Status | Kubernetes、kube-proxy | Kubernetes

コントロールプレーンとノードとは

Kubernetes のクラスタ は、コントロールプレーンと 1 台以上のノードでできています。どちらもいくつかのプログラムの組み合わせです。公式ドキュメントはこのプログラムを コンポーネント(component) と呼んでいます。このシリーズでもコンポーネントと呼びます。

参考:Kubernetes Components | Kubernetes

コントロールプレーン は クラスタ全体についての判断(たとえば、Pod をどのノードで動かすか)を下し、クラスタで起きた出来事を見つけて対応する部分 です。第1回の表で「クラスタ全体を管理する部分」と説明したものにあたります。

ノード は Pod を動かすマシンで、仮想マシンのことも物理マシンのこともあります。各ノードはコントロールプレーンに管理され、Pod を動かすのに必要なコンポーネントを持っています。ノードで動くコンポーネントは、kubelet、コンテナランタイム、kube-proxy です。

参考:Cluster Architecture | Kubernetes、Nodes | Kubernetes

それぞれで動く主なコンポーネントを、次の表にまとめます。1 つずつの役割は「コントロールプレーンとノードの仕組み」で説明します。

部分主なコンポーネント
コントロールプレーンkube-apiserver、etcd、kube-scheduler、kube-controller-manager、cloud-controller-manager(クラウドで動かす場合だけ)
ノードkubelet、コンテナランタイム、kube-proxy(無い構成もある)
アドオン(クラスタの機能を足すソフトウェア)クラスタ DNS、ネットワークプラグインなど

参考:Kubernetes Components | Kubernetes、Cluster Architecture | Kubernetes

第2回ではコントロールプレーンのコンポーネントを動かすノードを「コントロールプレーンのノード」、利用者の Pod を動かすノードを「ワーカー」と呼ぶことにしました。コントロールプレーンのノードもノードなので、ノードのコンポーネントも動いています。

なぜコントロールプレーンとノードが必要なのか

第3回で見たとおり、Docker では docker コマンドからの指示を Docker デーモン(dockerd)が受け付け、イメージやコンテナなどを管理します。1 台のマシンの上で、受け付けから実行までを dockerd が引き受けています。

参考:What is Docker? | Docker Docs

Kubernetes では、この仕事が複数のコンポーネントに分かれています。第1回で見たように、止まったコンテナを再起動するのはノードの kubelet で、消えた Pod を作り直すのはコントロールプレーンのコントローラーでした。Pod を動かすノードを決めるのは kube-scheduler です。第3回では、kube-apiserver は受け取ったリクエストを自分ですべて実行するわけではない、とも説明しました。

参考:Kubernetes Components | Kubernetes、Controllers | Kubernetes

大きく分けると、コントロールプレーンが 何をどこで動かすかを決め、ノードが 決まったとおりにコンテナを動かします。判断する側と動かす側が分かれているので、ノードが 1 台故障しても、コントロールプレーンがそれに気づきます。Deployment などで管理している Pod なら、代わりの Pod をほかのノードに作れます。第1回で見た自己修復はこの分担の上に成り立っています。

参考:Cluster Architecture | Kubernetes、Nodes | Kubernetes

「誰が何をするのか」を知っておくと、この先の回で仕組みを追うときに迷いにくくなります。Pod が起動しないときに、どのコンポーネントの様子を見ればよいかの見当も付けやすくなると考えられます。

コントロールプレーンとノードの仕組み

コントロールプレーンのコンポーネント

コントロールプレーンで動くコンポーネントの役割を、次の表にまとめます。

コンポーネント役割
kube-apiserverKubernetes の HTTP の API を公開する、コントロールプレーンの窓口(フロントエンド)
etcdクラスタのすべてのデータを保存する、一貫性と可用性の高いキーと値のデータストア
kube-schedulerノードが割り当てられていない Pod を見つけて、動かすノードを選ぶ
kube-controller-managerKubernetes に組み込みのコントローラーをまとめて動かす
cloud-controller-managerクラスタをクラウドプロバイダーの API とつなぐ。クラウドに固有のコントローラーだけを動かす

参考:Kubernetes Components | Kubernetes、Cluster Architecture | Kubernetes

kube-apiserver は API のオブジェクト(Pod や Service など)のデータを検証し、設定します。クラスタの共有の状態への窓口になり、ほかのコンポーネントはすべて kube-apiserver を通してやり取りします。kube-apiserver は複数台を並べて負荷を分けられるように作られています。

参考:kube-apiserver | Kubernetes、Cluster Architecture | Kubernetes

etcd(読み:エトシーディー)は、クラスタのすべてのデータを保存しておく場所です。第3回で kubectl から見たノードや Namespace の情報も、もとは etcd にあります。公式ドキュメントは、etcd にアクセスできることはクラスタの root 権限を持つことに等しいので、理想的には kube-apiserver だけがアクセスできるようにすべきだと説明しています。etcd を使うなら、データのバックアップの計画を持つよう勧めてもいます。

参考:Operating etcd clusters for Kubernetes | Kubernetes、Cluster Architecture | Kubernetes

kube-scheduler は、新しく作られてまだノードが決まっていない Pod を見つけ、動かすノードを選びます。選び方は 2 段階です。まず Pod を置けるノードに絞り込み(Filtering)、残ったノードに点数を付けて(Scoring)、最も点数の高いノードを選びます。決めた結果は binding(バインディング)と呼ばれる処理で kube-apiserver に伝えます。kube-scheduler は置き場所を決めるだけで、Pod のコンテナを起動するわけではありません。置けるノードが 1 つも無ければ、Pod はノードが決まらないまま待ちます。

参考:Kubernetes Scheduler | Kubernetes、kube-scheduler | Kubernetes

kube-controller-manager は、Kubernetes に組み込みのコントローラーを動かすコンポーネントです。コントローラーは kube-apiserver を通してクラスタの状態を見て、実際の状態を望ましい状態に近づけるように変更を加えます。コントローラーは論理的には 1 つずつ別のプロセスですが、複雑さを減らすために 1 つのプログラムにまとめられ、1 つのプロセスとして動きます。公式ドキュメントが挙げる例は次の 4 つです(すべてではありません)。

  • ノードが止まったことに気づいて対応する、ノードのコントローラー
  • 1 回きりの処理を表す Job から Pod を作る、Job のコントローラー
  • Service と Pod を結び付ける情報を用意する、EndpointSlice のコントローラー
  • 新しい Namespace に既定の ServiceAccount を作る、ServiceAccount のコントローラー

参考:Cluster Architecture | Kubernetes、kube-controller-manager | Kubernetes

Job や Service、EndpointSlice、ServiceAccount は後の回で扱うリソースなので、ここでは名前だけで構いません。コントローラーも自分では Pod やコンテナを動かさない点が大事です。公式ドキュメントは Job のコントローラーを例に、Pod を作ったり消したりするよう kube-apiserver に伝えるだけだと説明しています。第1回で見た ReplicaSet のコントローラーも同じく、kube-apiserver を通して Pod を作ります。

参考:Controllers | Kubernetes

cloud-controller-manager は、クラウドの上でクラスタを動かすときに、クラウドプロバイダーの API と連携するコンポーネントです。たとえばクラウドのロードバランサーを作ったり消したりします。公式ドキュメントによると、自社の設備(オンプレミス)や手元の PC の学習用の環境で動かすクラスタには、cloud-controller-manager はありません。このシリーズの kind のクラスタにも無いことになります。

参考:Cluster Architecture | Kubernetes、Cloud Controller Manager | Kubernetes

ノードのコンポーネント

ノードで動くコンポーネントの役割を、次の表にまとめます。

コンポーネント役割
kubelet各ノードで動くエージェント。Pod とその中のコンテナが動いている状態を保つ
コンテナランタイムコンテナを実際に動かすソフトウェア。containerd、CRI-O など
kube-proxyノードのネットワークの規則を保ち、Service を実現する。無い構成もある

参考:Kubernetes Components | Kubernetes、Cluster Architecture | Kubernetes

kubelet は Pod の中身を記述した PodSpec(Pod の spec)を受け取り、そこに書かれたコンテナが動いていて正常であるようにします。PodSpec は主に kube-apiserver から受け取りますが、ノードのファイルから読むこともできます(後の「kind のクラスタのコントロールプレーン」で使います)。kubelet は Kubernetes が作っていないコンテナは管理しません。

参考:kubelet | Kubernetes、Cluster Architecture | Kubernetes

kubelet は既定では、自分のノードを kube-apiserver に登録します。登録したあとも、ノードが生きていることを知らせる信号(ハートビート)を送り続けます。ハートビートには 2 つの形があり、1 つはノードの status の更新です。もう 1 つは kube-node-lease の Namespace にある、ノードごとの Lease というオブジェクトの更新です。Lease はここでは「kubelet が最後にいつ更新したか」の時刻を持つ記録だと考えれば十分です。コントロールプレーンは、この時刻からノードが動いているかを判断します。

ノードのコントローラーは、ノードと連絡が取れなくなる(ハートビートが途絶えるなど)と、ノードの Ready の状態を Unknown にします。そのまま連絡が取れなければ、既定では 5 分待ってから、そのノードの Pod の退去(eviction)を始めます。退去は Pod を決まった手順で終了させて取り除く処理です。詳しくは第36回で扱います。

参考:Nodes | Kubernetes、Leases | Kubernetes、API-initiated Eviction | Kubernetes

コンテナランタイム はコンテナの実行とライフサイクルを受け持つソフトウェアです。kubelet はコンテナランタイムに、CRI(Container Runtime Interface)という決まった手順(gRPC という通信の方式)で指示を出します。CRI があるので、kubelet を作り直さなくても、いろいろなコンテナランタイムを使えます。v1.37 時点では、CRI に対応したコンテナランタイムが必要です。

参考:Container Runtime Interface (CRI) | Kubernetes、Container Runtimes | Kubernetes

kind のノードでは、コンテナランタイムとして containerd が動いています。第2回では、ノードの containerd の設定を書き換える kind の手順を根拠に、こう説明しました。

kube-proxy は各ノードで動くネットワークのプロキシで、Service(後の回で扱う、Pod の集まりに通信を届けるリソース)をノードの上で実現します。公式ドキュメントでは省略できる(optional)コンポーネントとされています。ネットワークプラグインの中には自前でこの働きを実装するものがあり、それを使うノードでは kube-proxy は要りません。Service の仕組みは第21回で扱います。

参考:kube-proxy | Kubernetes、Cluster Architecture | Kubernetes

アドオン

コントロールプレーンとノードのコンポーネントのほかに、クラスタの機能を足す アドオン があります。アドオンは DaemonSet や Deployment などの Kubernetes のリソースを使って動きます。DaemonSet はすべての(または一部の)ノードで Pod を 1 つずつ動かすためのリソースで、詳しくは第15回で扱います。アドオンはクラスタ全体のための機能なので、Namespace に属するアドオンのリソースは kube-system に置かれます。kube-system は、公式ドキュメントでは Kubernetes のシステムが作るオブジェクトのための Namespace と説明されています。

参考:Cluster Architecture | Kubernetes、Namespaces | Kubernetes、DaemonSet | Kubernetes

この回で出てくる主なアドオンを、次の表にまとめます。

アドオン役割
クラスタ DNSService の DNS のレコードを返す DNS サーバー(kubeadm で作ったクラスタでは CoreDNS)。Kubernetes が起動したコンテナは、自動でこの DNS サーバーを使う
ネットワークプラグインCNI(Container Network Interface)という仕様を実装したソフトウェア(kind では kindnetd)。Pod に IP アドレスを割り当て、Pod どうしを通信できるようにする

参考:Cluster Architecture | Kubernetes、Customizing DNS Service | Kubernetes、Implementation details | Kubernetes、Configuration | kind

公式ドキュメントは、DNS 以外のアドオンは必須ではないとしたうえで、多くの例が頼っているので、すべてのクラスタにクラスタ DNS を置くべきだと説明しています。DNS の仕組みは第23回、Pod どうしの通信は第20回で扱います。

すべてのやり取りは kube-apiserver を通る

公式ドキュメントは Kubernetes の API の構成を「ハブ・アンド・スポーク」と説明しています。ハブ(中心)は kube-apiserver です。ノードやノードで動く Pod が API を使うときの接続先は、すべて kube-apiserver です。ノードの kubelet が Pod の情報を受け取るときも、Pod の中のアプリケーションが Kubernetes の API を呼ぶときも同じです(Pod の中から API を呼ぶ方法は第38回で扱います)。kube-apiserver 以外のコントロールプレーンのコンポーネントは、離れた場所(remote)から使われるサービスを提供するようには作られていません。冒頭の図で、ノードの中の線(kubelet → コンテナランタイム → Pod)を除くと、線がすべて kube-apiserver につながっていたのはこのためです。

参考:Communication between Nodes and the Control Plane | Kubernetes

etcd は kube-apiserver からの接続を受けますが、それはコントロールプレーンの中のやり取りです。kubeadm の構成では、etcd は同じノードの localhost:2379 で待ち受け、そのノードの kube-apiserver とだけやり取りします。etcd の説明で見たように、etcd にアクセスするのは理想的には kube-apiserver だけです。ノードや Pod が etcd に直接つなぐ構成にはなっていません。

参考:Implementation details | Kubernetes、Options for Highly Available Topology | Kubernetes、Operating etcd clusters for Kubernetes | Kubernetes

逆向きに kube-apiserver から kubelet へつなぐこともあります。Pod のログを取り出すときや、動いている Pod に接続(attach)するとき、ポートを転送するときです。Docker でいえば docker logs や docker attach にあたる操作で、kubectl では kubectl logs などで行います。この逆向きの接続は冒頭の図には描いていません。

参考:Communication between Nodes and the Control Plane | Kubernetes、kubectl logs | Kubernetes、docker container logs | Docker Docs、docker container attach | Docker Docs

この分担を、第1回の Deployment のマニフェストを渡してから Pod のコンテナが動くまでの流れで見てみます。大まかな流れを次の表にまとめます。

順番担当すること
1kube-apiserverkubectl から受け取った Deployment を検証し、etcd に保存する
2kube-controller-manager の中のコントローラーDeployment から ReplicaSet を、ReplicaSet から Pod を作るよう、kube-apiserver に伝える
3kube-schedulerノードが決まっていない Pod を見つけてノードを選び、binding で kube-apiserver に伝える
4選ばれたノードの kubeletkube-apiserver を監視して受け取った PodSpec をもとに、CRI でコンテナランタイムにコンテナを動かしてもらう
5kubeletノードの状態とハートビートを kube-apiserver に送り続ける

参考:kube-apiserver | Kubernetes、Controllers | Kubernetes、Deployments | Kubernetes、Kubernetes Scheduler | Kubernetes、kubelet | Kubernetes、Container Runtime Interface (CRI) | Kubernetes、Node Status | Kubernetes

ノードの中の kubelet とコンテナランタイムのやり取りを除けば、どの段階でもコンポーネントどうしは直接やり取りせず、kube-apiserver を間に挟んでいます。この流れのうち、コントローラーが望ましい状態に近づける繰り返しは第6回、kube-apiserver がリクエストを受け取ってから保存するまでは第40回で詳しく扱います。

コントロールプレーンはどうやって動いているか

コントロールプレーンのコンポーネントも、どこかのマシンで動くプログラムです。公式ドキュメントはコントロールプレーンの動かし方として次の 4 つを挙げています。

動かし方内容
従来の方法専用のマシンや仮想マシンの上で直接動かす。systemd(Linux でプログラムを起動・管理する仕組み)のサービスとして管理することが多い
static Pod特定のノードの kubelet が管理する static Pod として動かす。kubeadm などのツールでよく使われる方法
セルフホストクラスタの中の Pod として動かし、Deployment などのリソースで管理する
マネージドサービスクラウドプロバイダーが、サービスの一部としてコントロールプレーンを管理する。利用者からは見えないことが多い

参考:Cluster Architecture | Kubernetes

ここまでは、どのクラスタにも共通する話でした。ここからは、このシリーズで使う kind のクラスタの場合を見ていきます。

kind のクラスタのコントロールプレーン

kind は kubeadm でノードを設定するので、第2回で見たとおり、コントロールプレーンは static Pod の方法で動きます。static Pod は kube-apiserver を通さずに、特定のノードの kubelet が直接管理する Pod です。kubeadm はコントロールプレーンのコンポーネントのマニフェストを、ノードの /etc/kubernetes/manifests に書き出します。ファイルの名前は etcd.yaml、kube-apiserver.yaml、kube-controller-manager.yaml、kube-scheduler.yaml です。kubelet はこのディレクトリを見て書かれた Pod を起動し、ファイルが変わっていないかを定期的に(既定では 20 秒ごとに)確かめます。このあとの kind のクラスタの図で、kubelet から /etc/kubernetes/manifests への「監視」の線が表すのはこのことです。冒頭の図の kube-apiserver の監視(watch)とは別の仕組みです。

参考:Configuration | kind、Static Pods | Kubernetes、Implementation details | Kubernetes、kubelet | Kubernetes

次の一覧に出てくるラベルは、リソースに付けるキーと値の組です。kubectl で絞り込むときに使えます。ラベルの仕組みは第42回で扱います。kubeadm が書き出す static Pod の特徴は次の 4 つです。

  • すべて kube-system の Namespace に置かれる
  • tier: control-plane と component: <コンポーネントの名前> のラベルが付く
  • ノードのネットワークをそのまま使う(hostNetwork: true)。ネットワークの準備より先に起動するため
  • 同じノードの kube-apiserver に 127.0.0.1 で接続する(kube-controller-manager と kube-scheduler)

参考:Implementation details | Kubernetes

hostNetwork: true が要るのは、起動の順番のためです。Pod に IP アドレスを割り当てるのはネットワークプラグインです。kubeadm の手順では、ネットワークプラグインは kubeadm init のあとに kubectl apply で入れます。つまり kube-apiserver が動いてからでないと入れられません。そこでコントロールプレーンの Pod は、自分の IP アドレスを持たずに、ノードのネットワークをそのまま使って先に起動します。Docker の host ネットワークのモード(コンテナが自分の IP アドレスを持たず、ホストのネットワークを共有する)に近い考え方です。

参考:Implementation details | Kubernetes、Creating a cluster with kubeadm | Kubernetes、Host network driver | Docker Docs

kube-controller-manager と kube-scheduler は、kube-apiserver と同じノードで動いていても kube-apiserver を通してやり取りします。ここにもハブ・アンド・スポークの形が表れています。

第3回で触れたとおり、kubelet は static Pod ごとに、kube-apiserver から見える写しの mirror Pod を作ります。そのため kubectl からも見えますが、kubectl から static Pod を操作することはできません。kubectl で mirror Pod を消しても、kubelet は static Pod を止めず、mirror Pod を作り直します。mirror Pod の名前は static Pod の名前の後ろに - とノードのホスト名を付けたものです。mirror Pod には kubernetes.io/config.mirror というアノテーション(リソースに付ける付加情報。第42回で扱います)が付くので、見分けられます。

参考:Static Pods | Kubernetes

一方、kubelet そのものは Pod ではありません。kubelet は各ノードで常に裏で動き続けるデーモンです。そのため、kubelet を動かすのは OS の初期化の仕組みや、サービスを管理する仕組みです。DEB や RPM のパッケージで kubelet を入れた場合は、systemd が管理するように設定されます。kind のノードのコンテナの中で kubelet をどう動かしているかは、kind の公式ドキュメントには書かれていません。ただし kubelet は static Pod を起動する側なので、kubelet が kube-system の Pod の一覧に現れることはないと考えられます。

参考:Configuring each kubelet in your cluster using kubeadm | Kubernetes、Node Image | kind

kind のクラスタの kube-system の Pod

kubeadm はアドオンとして kube-proxy と CoreDNS を、kube-apiserver を通してリソースとして作ります。kube-proxy は DaemonSet として、CoreDNS は Deployment として、どちらも kube-system に作られます。v1.37 時点の kubeadm のソースコードでは、名前はそれぞれ kube-proxy と coredns です。CoreDNS の Service は昔の DNS のアドオンとの互換性のために kube-dns という名前です。kubelet とコンテナランタイムはリソースではないので、kubeadm が作るものには入りません。ネットワークプラグインの kindnetd を入れるのは kind です。kind v0.33.0 のソースコードでは、kindnetd は kindnet という名前の DaemonSet として kube-system に作られます。kindnetd がどのリソースで動くかは kind の公式ドキュメントには書かれていないため、kind のソースコードで確かめました。

参考:Implementation details | Kubernetes、Customizing DNS Service | Kubernetes、kubeadm のソースコード(kube-proxy の DaemonSet)、kubeadm のソースコード(定数)、kind のソースコード(kindnet)

以下の図は、kind のマルチノードのクラスタ(コントロールプレーン 1、ワーカー 2)で、kube-system の Pod がどのノードに置かれ、static Pod と DaemonSet の Pod のどちらとして動くかを表しています。

kind のマルチノードのクラスタで、各ノードの kubelet が起動する static Pod と DaemonSet の Pod

static Pod はコントロールプレーンのノードだけにあり、DaemonSet の Pod はすべてのノードにあります。kubelet は Pod ではなく、どのノードでもノードの上で直接動いていると考えられます(kubeadm の場合、kubelet を動かすのはサービスを管理する仕組みです)。この図ではコンテナランタイムを省いています。図の「起動」の線のとおり、static Pod も DaemonSet の Pod も、コンテナを動かすのは各ノードの kubelet です。違うのは PodSpec の受け取り方で、static Pod はノードのファイルから、DaemonSet の Pod は kube-apiserver から受け取ります。

参考:kubelet | Kubernetes、Static Pods | Kubernetes、Configuring each kubelet in your cluster using kubeadm | Kubernetes

v1.37 時点の kubeadm と kind v0.33.0 のソースコードでは、kube-proxy と kindnet の DaemonSet は、どの Taint も許容する設定になっています。Taint はノードが Pod を寄せ付けないようにする印です。kubeadm はコントロールプレーンのノードに、新しい Pod を置かせない Taint(node-role.kubernetes.io/control-plane:NoSchedule)を付けます。kube-proxy と kindnet はこれを許容するので、コントロールプレーンのノードでも動くと考えられます。Taint の仕組みは第32回で扱います。

参考:Implementation details | Kubernetes、Taints and Tolerations | Kubernetes、kubeadm のソースコード(kube-proxy の DaemonSet)、kind のソースコード(kindnet)

CoreDNS の Pod は図に描いていません。DaemonSet の Pod は各ノードに 1 つずつと置き先が決まっていますが、Deployment の Pod の置き先は kube-scheduler が選ぶからです。なお DaemonSet の Pod も、ノードへの割り当て(binding)は通常、既定のスケジューラーが行います。v1.37 時点の kubeadm のソースコードでは、CoreDNS の Pod の数の既定値は 2 です。

参考:DaemonSet | Kubernetes、kubeadm のソースコード(CoreDNS)、Kubernetes Scheduler | Kubernetes

ここまでをまとめると、kind のクラスタの kube-system には次の Pod が並ぶと考えられます。

Pod の名前コンポーネントPod のもと動くノード
etcd-<ノード名>etcdノードのマニフェストのファイル(static Pod)コントロールプレーンのノード
kube-apiserver-<ノード名>kube-apiserverノードのマニフェストのファイル(static Pod)コントロールプレーンのノード
kube-controller-manager-<ノード名>kube-controller-managerノードのマニフェストのファイル(static Pod)コントロールプレーンのノード
kube-scheduler-<ノード名>kube-schedulerノードのマニフェストのファイル(static Pod)コントロールプレーンのノード
kube-proxy- で始まる名前kube-proxyDaemonSet kube-proxyすべてのノードに 1 つずつ
kindnet- で始まる名前kindnetd(ネットワークプラグイン)DaemonSet kindnetすべてのノードに 1 つずつ
coredns- で始まる名前CoreDNS(クラスタ DNS)Deployment coredns(ReplicaSet を通して)kube-scheduler が選んだノード

「Pod のもと」は、Pod の内容がどこに書かれているかを表します。static Pod は kubelet がファイルを読んで直接起動します。DaemonSet の Pod は DaemonSet のコントローラーが、Deployment の Pod は Deployment が作った ReplicaSet のコントローラーが、kube-apiserver を通して作ります。どの Pod でも、コンテナを動かすのはそのノードの kubelet です。

static Pod の名前の <ノード名> は、正しくはノードのホスト名です。kubelet は既定ではホスト名でノードを登録するので、通常はノードの名前と同じです。DaemonSet と Deployment の Pod の名前は、リソースの名前の後ろに文字列が付いたものになります。付け方は第12回と第15回で扱います。

参考:Static Pods | Kubernetes、kubelet | Kubernetes、Controllers | Kubernetes、DaemonSet | Kubernetes、Deployments | Kubernetes、ReplicaSet | Kubernetes

手元で試す

ここまでの内容を、手元の kind のクラスタで試す手順を示します。マルチノードのクラスタの kube-system の Pod を一覧し、各コンポーネントの役割と対応づけます。試す環境は次のとおりです。

項目内容
kindv0.33.0
Kubernetesv1.37.0(kind v0.33.0 の既定のノードイメージ)
ノード構成マルチノード(クラスタ名 multi-node):コントロールプレーン 1、ワーカー 2
必要なものDocker、kubectl(v1.36〜v1.38)

参考:Release v0.33.0 · kubernetes-sigs/kind、Version Skew Policy | Kubernetes

kind でクラスタを作る手順は、以下の記事で解説しています。この回ではマルチノードのクラスタを使います。

基本仕様 第2回 kind で検証用のクラスタを作るDocker のコンテナをノードにして、手元のマシンに Kubernetes のクラスタを作るツール kind の仕組みを図で説明します。kind create cluster で何が起きるかを押さえ、シングルノードとマルチノードのクラスタを作って、ノードの正体が Docker のコンテナであることを確かめる手順を紹介します。

マルチノードのクラスタを作る

第2回と同じ次の設定ファイルを使います。公式ドキュメントの例をそのまま引用しています。kind-multi-node.yaml という名前で、コマンドを実行するディレクトリに保存します。

yaml
# three node (two workers) cluster config
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
- role: worker

参考:Quick Start | kind

まず、第2回や第3回のクラスタが残っていないかを確かめます。

console
$ kind get clusters

multi-node が表示されたら、作り直さずにそのまま使えるので、クラスタを作る次のコマンドは飛ばします。表示されなければ、クラスタを作ってから、もう一度一覧します。

console
$ kind create cluster --name multi-node --config kind-multi-node.yaml
$ kind get clusters

kind get clusters に multi-node が表示されれば、クラスタができています。第3回で決めたとおり、ここからのコマンドには --context kind-multi-node を付けて、操作するクラスタをはっきりさせます。

参考:Quick Start | kind

続けて、ノードを一覧します。

console
$ kubectl get nodes --context kind-multi-node

ノードが 3 つ表示され、STATUS 列が Ready になっていれば準備ができています。Ready になっていなければ、少し待ってからもう一度実行します。NAME 列に表示されたノードの名前は、このあとの手順で使います。

参考:kubectl get | Kubernetes、Node Status | Kubernetes

3 つのうちどれがコントロールプレーンのノードかは、ラベルで見分けられます。kubeadm はコントロールプレーンのノードに node-role.kubernetes.io/control-plane というラベルを付けます。-l にラベルのキーだけを書くと、そのラベルを持つノードだけが表示されます(-l は後の「static Pod を見分ける」でも使います)。

console
$ kubectl get nodes -l node-role.kubernetes.io/control-plane --context kind-multi-node

ノードが 1 つだけ表示されれば、それがコントロールプレーンのノードです。kubectl get nodes の表示には ROLES 列もあり、公式ドキュメントの出力例では、どのノードも ROLES 列が <none> です。ノードの役割はラベルのキーの / より後ろの部分で表すので、コントロールプレーンのノードでは、この列に control-plane と表示されると考えられます。

参考:Implementation details | Kubernetes、Well-Known Labels, Annotations and Taints | Kubernetes、Labels and Selectors | Kubernetes、kubectl get | Kubernetes、Troubleshooting Clusters | Kubernetes

kube-system の Pod を一覧する

kube-system の Pod を、動いているノードの名前と一緒に一覧します。

console
$ kubectl get pods -n kube-system -o wide --context kind-multi-node

-n kube-system で Namespace を指定し、-o wide でノードの名前などの列を加えています。NAME 列の Pod の名前と NODE 列のノードの名前を、「kind のクラスタの kube-system の Pod」の最後の表と見比べてください。

参考:kubectl get | Kubernetes

確かめるところは次の 4 つです。

  • etcd・kube-apiserver・kube-controller-manager・kube-scheduler で始まる Pod が 1 つずつあり、どれもコントロールプレーンのノードで動いている
  • 4 つの Pod の名前の後ろに、コントロールプレーンのノードの名前が付いている
  • kube-proxy と kindnet で始まる Pod が 3 つのノードに 1 つずつある
  • kubelet とコンテナランタイムの Pod は無い

表にない Pod が並んでいたり、Pod の数が表と違ったりしても、慌てる必要はありません。この回の表は kubeadm と kind の公式ドキュメントとソースコードから組み立てたもので、kind のバージョンによって変わることがあります。

参考:Static Pods | Kubernetes、Implementation details | Kubernetes、Node Image | kind

ほかの Namespace の Pod も見たいときは、-n の代わりに -A(--all-namespaces)を付けると、すべての Namespace の Pod を一覧できます。kind のクラスタでは local-path-storage という Namespace にも Pod が見えるはずです。これは kind が入れる local-path-provisioner で、ストレージ(データを保存する場所)を用意するためのものです。kind の公式ドキュメントには書かれていないため、kind v0.33.0 のソースコードで確かめました。ストレージは第27回で扱います。

console
$ kubectl get pods -A --context kind-multi-node

参考:kubectl get | Kubernetes、kind のソースコード(local-path-provisioner)

static Pod を見分ける

kubeadm が static Pod に付けるラベル tier=control-plane で、Pod を絞り込みます。

console
$ kubectl get pods -n kube-system -l tier=control-plane -o wide --context kind-multi-node

-l(--selector)はノードを見分けるときにも使った、指定したラベルを持つものだけを表示するフラグです。ここでは キー=値 の形で、キーと値の両方を指定しています。kubelet は static Pod のラベルを mirror Pod にもそのまま付けるので、mirror Pod をラベルで絞り込めます。etcd・kube-apiserver・kube-controller-manager・kube-scheduler の 4 つだけが、コントロールプレーンのノードに表示されるはずです。

参考:kubectl get | Kubernetes、Static Pods | Kubernetes、Implementation details | Kubernetes

次に、kube-apiserver の Pod を詳しく表示します。<コントロールプレーンのノード名> には、kubectl get nodes で表示されたコントロールプレーンのノードの名前を入れます。上の一覧の kube-apiserver- で始まる名前を、そのまま使っても構いません。

console
$ kubectl describe pod kube-apiserver-<コントロールプレーンのノード名> -n kube-system --context kind-multi-node

kubectl describe pod の表示の先頭には、Labels: や Annotations: の行が並びます。公式ドキュメントの出力例の先頭は次のとおりです(Deployment で作った nginx の Pod の例。以降は省略)。

Name:         nginx-deployment-67d4bdd6f5-w6kd7
Namespace:    default
Priority:     0
Node:         kube-worker-1/192.168.0.113
Start Time:   Thu, 17 Feb 2022 16:51:01 -0500
Labels:       app=nginx
              pod-template-hash=67d4bdd6f5
Annotations:  <none>
(省略)

参考:Debug Running Pods | Kubernetes

この例の nginx の Pod にはアノテーションが付いていないので、Annotations: は <none> です。kube-apiserver の Pod なら、Labels: に component=kube-apiserver と tier=control-plane が、Annotations: に kubernetes.io/config.mirror があるはずです。このアノテーションが mirror Pod の印です。Node: の行がコントロールプレーンのノードになっていることも確かめてください。

参考:Static Pods | Kubernetes、Implementation details | Kubernetes、kubectl describe | Kubernetes

最後に、static Pod のもとになっているマニフェストを、ノードのコンテナの中で確かめます。まず、ノードのコンテナの名前を確かめます。

console
$ docker ps

NAMES 列で、コントロールプレーンのノードのコンテナの名前を探します。公式ドキュメントの例では、クラスタ名 kind のコントロールプレーンのノードのコンテナは kind-control-plane という名前です。この例に倣うと、クラスタ名 multi-node では multi-node-control-plane になると考えられます。<コンテナの名前> には、NAMES 列で確かめた名前を入れます。

console
$ docker exec <コンテナの名前> ls /etc/kubernetes/manifests

etcd.yaml、kube-apiserver.yaml、kube-controller-manager.yaml、kube-scheduler.yaml の 4 つが表示されるはずです。kubelet はこのディレクトリのマニフェストから、さきほど見た 4 つの Pod を起動しています。ワーカーのノードのコンテナでも同じコマンドを実行すると、この 4 つのファイルは無いはずです。第2回で見たとおり、ワーカーでは kubeadm join が実行され、コントロールプレーンのマニフェストは作られないからです。ワーカーでディレクトリそのものがあるかどうかは公式ドキュメントでは確かめられなかったため、何も表示されないことも、ディレクトリが無いというエラーになることもあり得ます。どちらでも、4 つのファイルが無ければ問題ありません。

参考:Quick Start | kind、docker container exec | Docker Docs、Implementation details | Kubernetes、kubeadm join | Kubernetes

DaemonSet と Deployment で動くコンポーネントを確かめる

static Pod ではない Pod がどのリソースから作られているかを確かめます。kubectl get には、種類を , でつないで複数指定できます。

console
$ kubectl get daemonsets,deployments -n kube-system --context kind-multi-node

DaemonSet として kube-proxy と kindnet、Deployment として coredns が表示されるはずです。DaemonSet はノードごとに Pod を 1 つずつ動かすので、ノードが 3 つのこのクラスタでは、どちらの DaemonSet も Pod の数が 3 になると考えられます。coredns の Pod の数は、kubeadm の既定のままなら 2 です。

Deployment の行の READY 列は Pod の数を「使える数/望ましい数」の形で表します。coredns なら 2/2 のように表示されるはずです。DaemonSet の行にも Pod の数を表す列(DESIRED や READY など)が並ぶと考えられますが、各列の意味は公式ドキュメントでは確かめられませんでした。DaemonSet の Pod の数は上の Pod の一覧でも数えられます。

参考:kubectl get | Kubernetes、Deployments | Kubernetes、Implementation details | Kubernetes、kubeadm のソースコード(定数)、kubeadm のソースコード(CoreDNS)、kind のソースコード(kindnet)

CoreDNS の Service も確かめます。

console
$ kubectl get services -n kube-system --context kind-multi-node

CoreDNS の Service は kube-dns という名前で表示されるはずです。名前は kube-dns ですが、中で動いているのは CoreDNS です。

参考:Customizing DNS Service | Kubernetes、Implementation details | Kubernetes

Pod にならないコンポーネントを確かめる

kubelet とコンテナランタイムは Pod の一覧には出てこないはずです。代わりにノードの情報から確かめます。<ノード名> には kubectl get nodes で表示された名前を 1 つ入れます。

console
$ kubectl describe node <ノード名> --context kind-multi-node

公式ドキュメントによると、ノードの情報(Info)には、カーネルのバージョン、kubelet のバージョン、コンテナランタイムの詳細、OS などが含まれます。この情報は kubelet がノードから集めて、kube-apiserver に送ったものです。公式ドキュメントの kubectl describe node の出力例では、System Info: の中に次の行があります(v1.23 のクラスタの例。前後は省略)。

      Container Runtime Version:  containerd://1.5.9
      Kubelet Version:            v1.23.3

参考:Node Status | Kubernetes、Troubleshooting Clusters | Kubernetes、kubectl describe | Kubernetes

手元の表示でも、Container Runtime Version: と Kubelet Version: の行を探してみてください。第2回で見たように kind のノードでは containerd が動いているので、Container Runtime Version: には containerd の名前が表示されるはずです。Kubelet Version: は、この回の環境なら v1.37.0 になると考えられます。

続けて、kubelet が送っているハートビートの Lease を一覧します。

console
$ kubectl get leases -n kube-node-lease --context kind-multi-node

公式ドキュメントによると、ノードごとに、名前の対応する Lease が kube-node-lease にあります。ノードの名前と対応する Lease が 3 つ並ぶはずです。kubelet はハートビートのたびに、Lease の spec.renewTime(更新した時刻)を書き換えます。コントロールプレーンは、この時刻からノードが動いているかを判断します。

更新の様子を見るときは、ノードの名前と同じ名前の Lease を -o yaml で表示します。<ノード名> には kubectl get nodes で表示された名前を 1 つ入れます。

console
$ kubectl get lease <ノード名> -n kube-node-lease -o yaml --context kind-multi-node

少し時間を置いてもう一度実行し、spec.renewTime の時刻が進んでいれば、kubelet がハートビートを送り続けています。Lease の名前がノードの名前と完全に同じかどうかについて、公式ドキュメントには「対応する名前(matching name)」としか書かれていません。名前が違って表示に失敗したら、上の一覧に表示された Lease の名前を使ってください。

参考:Leases | Kubernetes、Nodes | Kubernetes、Namespaces | Kubernetes

ここまでで見たものを、コンポーネントごとにまとめると次のとおりです。

コンポーネントどこで確かめたか
kube-apiserver、etcd、kube-scheduler、kube-controller-managerkube-system の mirror Pod と、/etc/kubernetes/manifests のマニフェスト
kube-proxy、kindnetdkube-system の DaemonSet と、その Pod
CoreDNSkube-system の Deployment coredns と、Service kube-dns
kubelet、コンテナランタイムkubectl describe node のノードの情報と、kube-node-lease の Lease
cloud-controller-managerどこにも無い(手元の PC の学習用の環境なので)

片付け

作ったクラスタを削除し、消えたことを確かめます。

console
$ kind delete cluster --name multi-node
$ kind get clusters

kind delete cluster は、存在しないクラスタを指定してもエラーを返しません。kind get clusters に multi-node が表示されなくなったことを確かめます。kubeconfig に kind-multi-node のコンテキストが残っていたら、第3回の片付けと同じく kubectl config delete-context kind-multi-node で消します。

参考:Quick Start | kind、kubectl config | Kubernetes

注意点

コントロールプレーンとノードの構成でつまずきやすい点と、クラスタの作り方で変わる点を、次の表にまとめます。

項目内容
mirror Pod を消しても止まらないkubectl で mirror Pod を消しても、kubelet は static Pod を止めず、mirror Pod を作り直す
kube-system はシステムのための NamespaceKubernetes のシステムが作るオブジェクトのための Namespace。kube- で始まる名前の Namespace は作らないよう勧められている
マルチノードでは利用者の Pod はコントロールプレーンのノードに置かれないkubeadm がコントロールプレーンのノードに NoSchedule の Taint を付けるため。第2回で見たように、kind のシングルノードのクラスタでは、この制限を外していると考えられる
この回の kind のクラスタの etcd は 1 つだけコントロールプレーンのノードが 1 つなので、etcd も 1 つ。公式ドキュメントは etcd を奇数個のメンバーで動かすよう勧め、kubeadm の高可用性の構成ではコントロールプレーンのノードを 3 つ以上にするよう勧めている
cloud-controller-manager が無い手元の PC の学習用の環境のクラスタには無い。クラウドのロードバランサーと連携する機能は、そのままでは使えない
kube-proxy が無いクラスタもあるkube-proxy は省略できる。ネットワークプラグインが代わりを務めるクラスタでは動いていない
Docker Engine は直接は使われないv1.24 で dockershim が取り除かれ、v1.37 時点では、ノードには CRI に対応したコンテナランタイムが要る。kind の Docker はノードのコンテナを動かすためのもので、ノードの中で Pod のコンテナを動かすのは containerd(第2回)
構成はクラスタの作り方で変わるstatic Pod で動かすのは kubeadm などのやり方。マネージドサービスでは、コントロールプレーンが利用者から見えないことが多い

参考:Static Pods | Kubernetes、Namespaces | Kubernetes、Implementation details | Kubernetes、Taints and Tolerations | Kubernetes、Operating etcd clusters for Kubernetes | Kubernetes、Options for Highly Available Topology | Kubernetes、Cluster Architecture | Kubernetes、Container Runtimes | Kubernetes、kind

kube-system に自分のアプリケーションを置かないことは、公式ドキュメントの決まりではなくこのシリーズの進め方です。システムのコンポーネントと混ざると、どれが自分で作ったものかわかりにくくなるためです。

関連記事

コンポーネントどうしがやり取りするマニフェストの構造(apiVersion・kind・metadata・spec・status)については、以下の記事で解説しています。

基本仕様 第5回 マニフェストと宣言的 API の仕組みKubernetes に渡すマニフェストの構造と、望ましい状態を書いて任せる宣言的 API の仕組みを図で説明します。apiVersion・kind・metadata・spec・status の役割と、spec と status を誰が書くのかを押さえ、kind のクラスタで kubectl explain を使い、Pod の spec と status を作成直後と数秒後で比べる手順を紹介します。

kube-controller-manager の中のコントローラーが、実際の状態を望ましい状態に近づける調整ループについては、以下の記事で解説しています。

基本仕様 第6回 消した Pod が戻ってくる仕組み(調整ループ)コントローラーが実際の状態を望ましい状態に近づけ続ける調整ループの仕組みを図で説明します。Deployment・ReplicaSet・Pod の関係と、Pod を消したときに誰が気づいて代わりを作るのかを押さえ、kind のクラスタで Deployment の Pod を手で削除し、作り直される様子を観察する手順を紹介します。

kube-apiserver がリクエストを受け取ってから etcd に保存するまでの流れについては、以下の記事で解説しています。

基本仕様 第40回 ・ 準備中 kube-apiserver がリクエストを処理する流れ 公開したら、ここから読めるようになります。

Kubernetes 基本仕様の目次へ