Ritolabo
  1. Home
  2. Kubernetes
  3. Kubernetes のアーキテクチャを整理する:Control Plane と Node の構成要素

Kubernetes のアーキテクチャを整理する:Control Plane と Node の構成要素

  • 公開日
  • カテゴリKubernetes
  • タグKubernetes,学習メモ
Kubernetes のアーキテクチャを整理する:Control Plane と Node の構成要素

Kubernetes では、人間が「あるべき状態」を宣言し、Kubernetes が現在の状態をそこへ近づけ続ける(この考え方は「Kubernetes(k8s)とは?何を解決する仕組みなのかを整理する」を参照)。

この記事では、その「Kubernetes が」の中身を整理する。クラスタがどんな構成要素でできていて、それぞれが何を担当しているのか、Pod が動き出すまでに誰がどの順で動くのかを見ていく。

この記事で整理すること:

  • クラスタの全体像(Control Plane と Node)
  • Control Plane のコンポーネント(kube-apiserver、etcd、kube-scheduler、kube-controller-manager)
  • Node のコンポーネント(kubelet、container runtime、kube-proxy)と CNI
  • kubectl apply から Pod が動き出すまでの流れ
  • Node が落ちたときの動き

コンポーネント同士の関係や処理の流れを図にしたものは「Kubernetes の仕組みを図で整理する」にまとめている。

contents

  1. クラスタの全体像
  2. Node
  3. Control Plane のコンポーネント
    1. kube-apiserver
    2. etcd
    3. kube-scheduler
    4. kube-controller-manager
    5. cloud-controller-manager
  4. Node のコンポーネント
    1. kubelet
    2. container runtime
    3. kube-proxy
  5. CNI とネットワークモデル
  6. アドオン
  7. kubectl apply から Pod が動くまで
  8. Pod は Node 間を移動しない
  9. Node が落ちたときの流れ
  10. 用語メモ
  11. 参考 URL

クラスタの全体像

Kubernetes のクラスタは、Control Plane(コントロールプレーン)と、Pod を動かす1台以上の Node(ノード)でできている。

部分役割
Control Planeクラスタ全体を管理する。スケジューリングのようなクラスタ全体に関わる判断や、イベントの検知と対応を担う
NodePod(コンテナ)を実際に動かす。Worker Node とも呼ぶ
┌─ Control Plane ────────────────────────────────────────┐
│                                                        │
│   kube-scheduler        kube-controller-manager        │
│         │                         │                    │
│         ▼                         ▼                    │
│   ┌────────────────────────────────────────────┐       │
│   │               kube-apiserver               │◀──────┼──── kubectl
│   └────────────────────────────────────────────┘       │
│         │                 ▲                            │
│         ▼                 │                            │
│       etcd                │                            │
└───────────────────────────┼────────────────────────────┘
                            │
             ┌──────────────┴──────────────┐
             │                             │
┌─ Node 1 ───┴─────────────┐  ┌─ Node 2 ───┴─────────────┐
│ kubelet                  │  │ kubelet                  │
│ kube-proxy               │  │ kube-proxy               │
│ container runtime        │  │ container runtime        │
│ [Pod] [Pod] [Pod]        │  │ [Pod] [Pod]              │
└──────────────────────────┘  └──────────────────────────┘

この図の特徴は、矢印がすべて kube-apiserver に集まっていること。kube-scheduler も kube-controller-manager も、各 Node の kubelet も、人間が使う kubectl も、互いに直接やりとりせず kube-apiserver を経由する。公式ドキュメントでは、この形を「hub-and-spoke」(ハブ・アンド・スポーク)の API パターンと呼んでいる。

Node

Node は、Pod を動かすためのマシン1台。物理サーバーでも仮想マシンでもよい。

  • Node も Kubernetes のリソースの一つ。一覧は kubectl get nodes で確認
  • Node の状態を表す condition の一つが ReadyTrue なら、Node が正常で Pod を受け入れられる状態
  • kubelet は、Node ごとの Lease というオブジェクトを既定で10秒ごとに更新する。これが Node の生存を知らせるハートビート

Control Plane のコンポーネントがどこで動くかは、構築方法によって変わる。

  • kubeadm のような構築ツールでは、Control Plane のコンポーネントを static Pod として動かす方式が一般的。static Pod は、kubelet が Node 上の決まったディレクトリ(kubeadm では /etc/kubernetes/manifests)にある定義ファイルを読んで、直接起動する Pod
  • kubeadm の既定は、セキュリティ上の理由で、Control Plane の Node に通常の Pod を置かない設定
  • EKS / GKE / AKS のようなマネージドサービスでは、Control Plane の管理はクラウド事業者の担当

Control Plane のコンポーネント

kube-apiserver

Kubernetes API を公開するサーバー。公式ドキュメントでは、Control Plane のフロントエンド(窓口)と説明されている。

  • kubectl での操作も、実体は kube-apiserver への HTTP リクエスト
  • リクエストの処理順は、認証 → 認可 → Admission 制御
リクエスト → 認証(誰か?)→ 認可(それをしてよいか?)→ Admission 制御(内容の検証・変更)→ 処理
  • etcd へのアクセスは、理想的には kube-apiserver だけに限定(etcd へのアクセスはクラスタの root 権限に等しいため)
  • 変更を効率よく検知するための watch の仕組みも提供。クライアントはリソースの変化を購読でき、kube-scheduler やコントローラーが変化を受け取る手段になっている
  • インスタンスを増やして負荷を分散できる、水平スケールを前提とした設計

etcd

クラスタの全データを保存する、一貫性と高可用性を備えたキーバリューストア。Deployment や Pod など、あらゆるリソースの定義と状態がここに入る。

  • 発音は「エトシーディー」(/ˈɛtsiːdiː/)。名前の意味は "distributed etc directory"(分散された etc ディレクトリ)
  • 合意アルゴリズムは Raft。更新の確定には、過半数(クォーラム)の合意が必要
  • クラスタの全データを持つため、バックアップの計画が必須

etcd は奇数台で組むことが推奨されている。台数と、故障しても動き続けられる台数の関係は次のとおり(etcd の FAQ より)。

台数過半数耐えられる故障台数
321
431
532
642

奇数台が推奨される理由は2つ。

  • 偶数台にしても、耐えられる故障台数は1台少ない奇数台と同じ(壊れうるマシンだけが増える形)
  • ネットワーク分断時に、必ずどちらか一方に過半数が残る構成(4台が2台ずつに分かれると、どちらも過半数に届かない)

kube-scheduler

まだ Node が割り当てられていない Pod を見つけて、動かす Node を選ぶコンポーネント。選び方は2段階になっている。

① フィルタリング: Pod の条件を満たせない Node を候補から外す
   (例: Pod が要求する CPU・メモリの空きがない Node)
        ↓
② スコアリング: 残った Node に点数を付ける
        ↓
最も点数の高い Node を選び、kube-apiserver に通知する(binding)
(同点の Node が複数あれば、その中からランダムに選ぶ)
  • 選ばれた Node は、Pod の spec.nodeName に入る
  • 条件を満たす Node が1台もなければ、Pod はスケジュールされないまま待機
  • kube-scheduler の仕事は Node を選ぶところまで。コンテナを起動するのは、選ばれた Node の kubelet

kube-controller-manager

コントローラーを実行するコンポーネント。コントローラーは、今の状態を観察し、あるべき状態との差分を埋める「調整ループ」を回す担当。論理的にはコントローラーごとに別のプロセスだが、複雑さを減らすために1つのバイナリ・1つのプロセスにまとめられている。

主なコントローラーは次のとおり。

コントローラー担当
Deployment コントローラーDeployment に対応する ReplicaSet を作り、更新時は新旧の ReplicaSet の数を段階的に入れ替える
ReplicaSet コントローラーPod の数を replicas に合わせる
Node コントローラーNode からの報告が途絶えたら、Node の状態を Unknown にして taint(問題のある Node を示す印)を付ける
taint-eviction コントローラーtaint が付いた Node から Pod を退避させる(v1.29 で Node コントローラーから分離)
Job コントローラー一度きりのタスクを表す Job を見て、完了するまで Pod を動かす
EndpointSlice コントローラーEndpointSlice を作成・更新し、Service と Pod を結びつける
  • コントローラーは、自分で Pod やコンテナを動かさない。たとえば Job コントローラーは kube-apiserver に Pod の作成・削除を依頼するだけで、その先はほかのコンポーネントの担当
  • Deployment コントローラーも同様に、ReplicaSet を作るまでが担当。その ReplicaSet を見て Pod を作るのは ReplicaSet コントローラー

cloud-controller-manager

クラウドプロバイダーと連携するためのコンポーネント(オプション)。オンプレミスや、手元の PC の学習環境で動かすクラスタには存在しない。

  • Node コントローラー: 応答しなくなった Node の VM が、クラウド側で削除されていないかの確認
  • Route コントローラー: クラウドのネットワークへの経路設定
  • Service コントローラー: クラウドのロードバランサーの作成・更新・削除

Node のコンポーネント

まず、Node の中にいるものを一覧にすると次のようになる(kubeadm で構築したクラスタの場合)。

Node(マシン1台)
├── OS(Linux カーネル)  … kube-proxy が設定した転送ルール(iptables / nftables など)もここに入る
├── kubelet              … OS のサービス(systemd)として動く。Pod ではない
├── container runtime    … containerd など。Node 上のデーモンとして動く
├── CNI プラグイン       … container runtime から呼ばれ、Pod のネットワークを用意する
├── Pod: kube-proxy      … DaemonSet として、Node ごとに1つずつ置かれる
├── Pod: アプリ          … kube-scheduler がこの Node に割り当てたもの
│   └── コンテナ
└── Pod: etcd / kube-apiserver / kube-scheduler / kube-controller-manager
                         … Control Plane 用の Node の場合だけ(static Pod)

Node の中身は、Pod を動かすための土台(OS・kubelet・container runtime)と、その上に置かれた Pod の2層になっている。kube-proxy のように、Node のコンポーネントでありながら Pod として動くものもある。DaemonSet は、各 Node に1つずつ Pod を置くためのリソース。

kubelet

各 Node で動くエージェント。Pod の定義(PodSpec)に書かれたコンテナが、動いていて正常な状態であることを保証する。

  • Kubernetes が作っていないコンテナは管理対象外
  • container runtime とのやりとりは CRI 経由
  • kubeadm で構築したクラスタでは、kubelet 自体は Pod ではなく systemd のサービスとして動作

container runtime

コンテナの実行とライフサイクルを管理するソフトウェア。containerd や CRI-O など、CRI(Container Runtime Interface)を実装したものが使える。

  • CRI は、kubelet と container runtime の間の通信を定めた gRPC のプロトコル
  • kubelet は、CRI のクライアントとして container runtime に接続

kube-proxy

Service の仕組みの一部を実装する、各 Node 上のネットワークプロキシ。Node 上のネットワークルールを管理し、クラスタの内外から Pod へ通信が届くようにする。

  • Service の仮想 IP 宛ての通信を捕まえ、Service の裏にいる Pod へ転送するように Node を設定
  • OS のパケットフィルタリング層が使える場合はそれを使い、使えない場合は kube-proxy 自身が転送
  • Linux で選べるモードは iptables / ipvs / nftables。既定は iptables で、将来のバージョンで nftables に変わる予定
  • ipvs モードは v1.35 で非推奨になり、v1.40 から既定で無効になる予定
  • kube-proxy はオプション扱い。Service の転送を自前で実装するネットワークプラグイン(Cilium など)を使う場合は不要

CNI とネットワークモデル

Kubernetes のネットワークモデルでは、次のことが決められている。

  • 各 Pod が、クラスタ内で一意の IP アドレスを持つこと
  • すべての Pod が、同じ Node にいても別の Node にいても、プロキシや NAT(アドレス変換)なしで直接通信できること

ただし、Kubernetes 本体が実装しているのはこのモデルの一部だけで、Pod のネットワークそのものは外部のコンポーネントに任されている。Linux ではほとんどの container runtime が CNI(Container Network Interface)を使ってネットワークの実装とやりとりするため、こうした実装は CNI プラグインと呼ばれる。

  • CNI プラグインの役割は、Pod への IP アドレスの割り当てと、Pod 同士の通信の実現
  • CNI プラグインを読み込んで使うのは container runtime
  • 代表例は Calico、Cilium、Flannel など

CNI プラグインと kube-proxy の役割分担は次のとおり。

担当
CNI プラグインPod に IP を割り当て、Pod と Pod を直接つなぐ
kube-proxyService 宛ての通信を、Service の裏にいる Pod へ届ける

コンテナの実行(CRI)やネットワーク(CNI)のように、Kubernetes が API や約束事だけを定め、実装を外部のコンポーネントに任せている部分もある。

アドオン

Control Plane と Node のコンポーネントに加えて、クラスタの機能を拡張するアドオンがある。アドオンは DaemonSet や Deployment などの Kubernetes のリソースとして動き、kube-system という名前空間に置かれる。

アドオン役割
DNSクラスタ内の名前解決。厳密には必須ではないが、すべてのクラスタが持つべきとされている
Web UI(Dashboard)Web 画面からのクラスタ管理
Container Resource Monitoringコンテナのメトリクスの収集と保存
Cluster-level Loggingコンテナのログを、検索できる中央のストアに保存

kubectl apply から Pod が動くまで

replicas: 3 の Deployment を kubectl apply したときの流れは次のとおり。

kubectl apply -f deployment.yaml
  │
  │ ① kube-apiserver が認証・認可・Admission 制御を通し、Deployment を etcd に保存
  │ ② Deployment コントローラーが新しい Deployment を検知し、ReplicaSet を作る
  │ ③ ReplicaSet コントローラーが「Pod が 0/3」を検知し、Pod のオブジェクトを3つ作る
  │    (etcd に定義が保存されただけで、まだコンテナは動いていない。Node も未定)
  │ ④ kube-scheduler が Node 未定の Pod を検知し、Node を選んで割り当てる
  │ ⑤ 割り当てられた Node の kubelet が、自分の Node に来た Pod を検知する
  ▼
kubelet ──CRI──▶ container runtime: イメージを取得して、コンテナを起動
                   └──CNI──▶ CNI プラグイン: Pod に IP を割り当てる
  │
  │ ⑥ kubelet が Pod の状態(Running など)を kube-apiserver に報告し、etcd に保存される
  ▼
kubectl get pods で Running が見える

この流れから、次の点が読み取れる。

  • どのコンポーネントも、相手に直接命令しない。kube-apiserver を通じて変化を受け取り、自分の担当範囲の差分を埋める形
  • 「Pod のオブジェクトを作る」ことと「コンテナを起動する」ことは別の仕事。前者は ReplicaSet コントローラー(Control Plane)、後者は kubelet(Node)の担当
  • 例外として、kubectl logs でのログ取得、実行中の Pod への接続(attach)、ポートフォワードでは、kube-apiserver から kubelet に接続

同じ流れをシーケンス図にしたものは「Kubernetes の仕組みを図で整理する」にある。

Pod は Node 間を移動しない

Node が落ちると、そこにいた Pod の代わりが別の Node で動き出す。ただし、これは Pod の移動ではない。Pod がスケジュールされるのは一生に一度だけで、同じ Pod が別の Node に移ることはない

公式ドキュメントでは、同じ Pod(UID で識別される)が別の Node に「再スケジュール」されることはなく、ほぼ同じ内容の新しい Pod に置き換えられる、と説明されている。

Node 1 が落ちる前:
  Node 1: [api-abc(IP 10.244.1.5)]
  Node 2: [api-def(IP 10.244.2.7)]

Node 1 が落ちた後:
  Node 1: 停止。api-abc は削除される
  Node 2: [api-def(IP 10.244.2.7)] [api-xyz(IP 10.244.2.9)] ← 新しく作られた別の Pod

(Pod の名前と IP は説明用に簡略化したもの)
  • 新しい Pod は UID が別物で、IP アドレスも新しく割り当てられる
  • Deployment などを使わずに Pod を単体で作ると、Node が落ちても代わりを作る担当がいない。そのため、Pod はコントローラー経由で管理するのが一般的
  • 作り直されるたびに Pod の IP アドレスは変わる。Pod の IP に頼らない安定した宛先を用意する仕組みが Service

Node が落ちたときの流れ

コンテナがクラッシュしただけなら、Pod は削除されず、Kubernetes がそのコンテナの再起動を試みる。一方、Node そのものが落ちた場合は、次の流れで Pod が別の Node に作り直される(時間はいずれも既定値)。

kubelet のハートビート(Lease の更新)が途絶える
      ↓ 50 秒
Node コントローラーが Node の Ready を Unknown にし、unreachable の taint を付ける
      ↓ 5 分
taint-eviction コントローラーが、その Node の Pod を退避(削除)する
      ↓
ReplicaSet コントローラーが Pod の不足を検知し、新しい Pod を作る
      ↓
kube-scheduler が、正常な Node に新しい Pod を割り当てる
  • 50 秒は、kube-controller-manager の node-monitor-grace-period の既定値
  • 5 分は、Pod に自動で付く tolerationSeconds=300(taint を許容する時間)によるもの。Pod ごとに変更も可能
  • 通信が途絶えただけで Node 自体が動いている場合は、削除の決定が kubelet に届くまで、Pod がその Node で動き続けることもある

用語メモ

用語読み方意味
Control Planeコントロールプレーンクラスタ全体を管理する部分
kube-apiserverキューブ・エーピーアイサーバーKubernetes API を公開するサーバー
etcdエトシーディークラスタの全データを保存するキーバリューストア
kube-schedulerキューブ・スケジューラーPod を動かす Node を選ぶ
kube-controller-managerキューブ・コントローラー・マネージャーコントローラーを実行する
kubeletキューブレット各 Node で動くエージェント
kube-proxyキューブ・プロキシService の転送ルールを Node に設定する
CNIシーエヌアイContainer Network Interface
taintテイントNode に付ける、Pod の配置や退避に関わる印
Quorumクォーラム更新の確定に必要な過半数

まとめ

  • Kubernetes のクラスタは、クラスタ全体を管理する Control Plane と、Pod を動かす Node で構成
  • コンポーネント同士のやりとりはすべて kube-apiserver 経由で、クラスタの全データは etcd に保存
  • Pod を置く Node の選択は kube-scheduler、調整ループは kube-controller-manager のコントローラーの担当
  • Node では kubelet が container runtime を通じてコンテナを動かし、kube-proxy が Service の転送、CNI プラグインが Pod のネットワークを担当
  • Pod は Node 間を移動せず、Node が落ちると別の Node に新しい Pod が作られる仕組み

参考 URL

Author

rito

rito

  • Backend Engineer
  • Tokyo, Japan
  • PHP 5 技術者認定上級試験 認定者
  • 統計検定 3 級