2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Kubernetes の Control Plane と etcd、何台にすべきか: 「増やせば速くなる」という誤解をボトルネックから解く

2
Last updated at Posted at 2026-09-25

はじめに

Control Plane はステートレスなアーキテクチャなので、ノード数を増やせばスケールすると思っている方が結構いるように感じています。

ただ、実際のところスケールするかどうかは、ボトルネックに対してその構成変更が有効かどうかが大事です。実際のクラスタでは、期待した効果を得られていないことがあります。アーキテクチャ全体を見て、どこがボトルネックになっているのかを特定し、その構成変更がボトルネックに対して実際に効くのかを確認することが先に必要です。

Kubernetes において心臓部にあたるのは etcd です。etcd が壊れるとクラスタ全体が状態を変更できなくなり、最悪の場合バックアップからのリストアが必要になります。一方で、Control Plane (kube-apiserver・kube-scheduler・kube-controller-manager) はそれぞれ性質が大きく異なり、「etcd と同じルールでノード数を決める」と、意図した効果が得られない構成になってしまいます。

この記事では、

  1. etcd は何台にすべきか
  2. Control Plane は何台にすべきか、スケールする/しない部分はどこか
  3. etcd と Control Plane を同居させてよい場合と、避けたほうがよい場合

の3章に分けて、ソースコードと公式ドキュメントを根拠に整理します。

対象コンポーネントとバージョン

コンポーネント バージョン リポジトリ 本記事での役割
Kubernetes v1.37.0 https://github.com/kubernetes/kubernetes kube-apiserver の etcd への読み書き、watch cache、--etcd-servers-overrides、kube-scheduler/kube-controller-manager の leader election
etcd v3.7.0 (v1.37.0 が go.mod で参照するバージョン) https://github.com/etcd-io/etcd EtcdServer の書き込み処理 (Propose)、linearizable/serializable read の分岐 (read パッケージ)
kubernetes/website 2026-09-15 時点の main https://github.com/kubernetes/website etcd 運用、kubeadm HA トポロジ、API のセマンティクスに関する公式ドキュメント

Raft のコンセンサスアルゴリズム自体の実装 (go.etcd.io/raft) は別リポジトリ (etcd-io/raft) に切り出されており、本記事ではそこまでは追っていません。ここで確認したのは etcd-io/etcd 側、つまり EtcdServer が書き込みを常に raft.Node.Propose に委譲していること、および読み込みが Serializable フラグの有無で ReadIndex 経由の linearizable read と分岐する実装です。Raft 自身のリーダー選出・ログレプリケーションのアルゴリズムそのものの説明は、etcd 公式ドキュメント の記述と一般的な Raft の知識に基づいています。

TL;DR

  • etcd はプロダクションでは 5 台構成を推奨します。 3 台構成はメンテナンスで 1 台停止した瞬間に「もう1台も落ちたら終わり」の状態になりますが、5 台構成ならメンテナンス中でも 1 台分の余裕が残ります
  • etcd への書き込みは、台数を増やしても速くなりません。 Raft はリーダー1台が書き込みを確定させる仕組みのため、Event のような書き込みが多いリソースではリーダーがボトルネックになります
  • etcd への読み込みも、既定では linearizable (quorum) 読み込みに委譲され、リーダーに依存します。 kube-apiserver の watch cache が吸収できる範囲 (resourceVersion=0 など) は etcd に触れませんが、強整合性が要求される Get/List は今も etcd 側の quorum read に流れます
  • 書き込みが多いリソースは、--etcd-servers-overrides で別の etcd クラスタに切り出せます。 まずは Event、さらに必要なら他の高頻度リソースも同様に分離できます
  • kube-scheduler・kube-controller-manager は leader election で動くため、レプリカを増やしても水平スケールしません。 常に 1 台だけが処理を行い、残りは待機系です
  • kube-apiserver のレプリカを増やすと、クライアント接続の処理能力は増えますが、etcd への負荷は増えます。 レプリカごとに独立した watch cache を持つため、watch 接続数や起動時の relist 負荷はレプリカ数に比例して増加します
  • etcd と Control Plane の同居は、PoC・検証環境では合理的ですが、本番で 5 台同居させる構成は計測してから判断すべきです。 長期運用やスケールが見込まれるなら、最初から分離しておくほうが後の変更コストが小さくなります

第1章: etcd の台数設計

1.1 なぜ 3 台ではなく 5 台か

etcd はプロダクションでは 奇数台のクラスタ として運用することが公式に推奨されています。

You should run etcd as a cluster with an odd number of members.

(参考: Operating etcd clusters for Kubernetes)

さらに信頼性の観点からは、3 台ではなく 5 台 が推奨されています。

For durability and high availability, run etcd as a multi-node cluster in production and back it up periodically. A five-member cluster is recommended in production.

(参考: Operating etcd clusters for Kubernetes)

理由は、Deployment が管理する Pod のようにメンバーを動的に増減できないことにあります。etcd のメンバーはあらかじめクラスタ構成に登録されたノードの集合であり、メンテナンスのたびに「1台追加してから1台減らす」ような運用は簡単ではありません。つまりメンテナンスでは、一定時間クラスタのメンバーが 1 台減った状態で運用することになります。

このとき重要なのは、etcd が生き続けるために必要なのは 過半数 (quorum) であって「1台減っても大丈夫」という話ではない点です。

メンバー数 quorum (過半数) 通常時に許容できる同時故障数 メンテナンスで1台停止中の残り台数 その状態でさらに許容できる故障数
3 2 1 2 0
5 3 2 4 1

3 台構成は、通常時は 1 台の故障まで許容できますが、メンテナンスで 1 台を停止した瞬間に残り 2 台 = quorum ぎりぎりの状態になります。この状態でもう 1 台に何か起きれば、クラスタは書き込みを受け付けられなくなります。

5 台構成であれば、メンテナンスで 1 台停止しても残り 4 台で quorum (3) を上回っており、さらに 1 台分の故障を許容できます。メンテナンスという「計画済みの1台減」を、無停止のリスクなしに行える最小の台数が 5 台、というのがこの推奨の実質的な意味です (故障許容数の一般式や具体的な数値の出典は etcd 公式 FAQ を参照してください)。

過半数を割り込んだ場合の挙動も明記されています。

If the majority of etcd members have permanently failed, the etcd cluster is considered failed. In this scenario, Kubernetes cannot make any changes to its current state.

(参考: Operating etcd clusters for Kubernetes)

1.2 書き込みはリーダー1台がボトルネック

etcd はリーダーベースの分散システムです。

etcd is a leader-based distributed system. Ensure that the leader periodically send heartbeats on time to all followers to keep the cluster stable.

(参考: Operating etcd clusters for Kubernetes)

リーダーが不安定だとクラスタは状態変更ができなくなり、その影響は Kubernetes 側にも直結します。

An unstable etcd indicates that no leader is elected. Under such circumstances, a cluster cannot make any changes to its current state, which implies no new pods can be scheduled.

(参考: Operating etcd clusters for Kubernetes)

これは Raft の制約です。書き込み (Raft のログエントリの追加) は必ずリーダーが受け取り、フォロワーへレプリケートしてコミットする必要があります。リーダーは1クラスタに1台だけなので、メンバー数を 5 台に増やしても、書き込みスループットの上限はリーダー1台の処理能力に縛られます。むしろメンバーを増やすほど、リーダーが確認を取るべきフォロワーが増える分だけレプリケーションのオーバーヘッドは増えます。

実際に etcd server 側のコードを見ても、書き込みリクエストはローカルノードが leader かどうかに関わらず、常に raft の Propose へ委譲されています。

// server/etcdserver/v3_server.go
err = s.r.Propose(cctx, data)

(参考: v3_server.go#L1113)

follower がこの Propose を受け取った場合は raft 層の中で leader へ転送されるだけで、follower がローカルにログを確定させることはありません。つまり 書き込みは実装上も必ず leader 一台を経由するため、メンバー数を増やしても書き込みスループットの上限は上がりません。

実務でこの制約に最初に当たりやすいのが Event リソース です。Event は Pod のスケジューリング・イメージ Pull・ヘルスチェック失敗などのたびに生成され、TTL で自動削除されるため、他のリソースに比べて書き込み (Create/Update/Delete) の頻度が圧倒的に高くなります。特にローリングアップデートのように多数の Pod が一度に再作成されるタイミングでは、これらの Event が短時間に集中して発生し、書き込みが急増します。同じ etcd クラスタに他のリソースと混在させていると、この Event の書き込み負荷がリーダーを圧迫し、Pod や Deployment など本来優先したいリソースの書き込みレイテンシにも影響します。

1.3 読み込みはスケールするのか

書き込みと違い、読み込みは複数メンバーに分散できる余地があります。etcd の read には大きく2種類があります。

  • linearizable read (quorum read): 常に最新の値を保証する。読み取り時にリーダーとの整合性確認が必要
  • serializable read: 古い値を返す可能性があるが (stale read)、任意のメンバーが単独で応答できる

kube-apiserver は linearizable read を使っています。実際にソースコードを確認すると、kube-apiserver が etcd を直接読むコードでは WithSerializable() が一度も使われていません。

// vendor/go.etcd.io/etcd/client/v3/kubernetes/client.go
func (k *client) Get(ctx context.Context, key string, opts ...GetOption) (*clientv3.GetResponse, error) {
	return k.KV.Get(ctx, key, clientv3.WithRev(...), clientv3.WithLimit(...))
}

(参考: kubernetes/client.go#L47-L81、etcd3/store.go#L235-L281)

WithSerializable() を付けない限り、etcd クライアントのデフォルトは linearizable (quorum) read です (op.go#L455-L464)。

etcd server 側の実装でも、この Serializable フラグの有無で処理が分岐しています。

// server/etcdserver/v3_server.go (Range)
if !r.Serializable {
	err = s.read.LinearizableReadNotify(ctx)
	...
}

(参考: v3_server.go#L138-L144)

LinearizableReadNotify の実体は raft の ReadIndex プロトコルです。ログエントリを追加するわけではないため書き込みより軽量ですが、リクエストごとに raft 層へ ReadIndex を投げてリーダー側で「現在確定しているインデックス」を確認し、ローカルの適用済みインデックスがそこに追いつくまで待つ、という手順を踏みます。

// server/etcdserver/read/read.go
err := r.sendReadIndex(requestID)
...
case rs := <-r.raft.ReadState():
	...
	return rs.Index, nil

(参考: read.go#L148-L189)

つまり linearizable read は リクエストごとに leader との確認を要するという点では書き込みと同じ制約を持ちます (ログレプリケーションが不要な分、書き込みより軽いだけです)。kube-apiserver が etcd に直接投げる読み込みは、常にリーダーとの整合性確認を伴う quorum read であり、メンバーを増やしても線形にスケールするわけではありません。

ただし実際には、多くの Get/List は etcd まで届かず、kube-apiserver 内の watch cache から応答されます。この振り分けは resourceVersion の指定によって決まります。

resourceVersion の指定 セマンティクス 応答元
未指定 Most Recent (最新であることを保証) etcd への quorum read に委譲
"0" Any (古くてもよい) 常に watch cache から応答 (etcd 負荷なし)
特定の値 Not older than (その版以降であればよい) watch cache が追いついていれば watch cache、そうでなければ etcd

(参考: API Concepts、実装は delegator.go#L110-L183 と delegator/interface.go#L38-L62)

多くの Controller は informer 経由で watch し、ローカルキャッシュを見るため、ホットパスの読み込みは watch cache で吸収されます。しかし 強整合性 (Most Recent) を要求する Get/List は今も etcd の quorum read に流れるため、「etcd のメンバーを増やせば読み込みがスケールする」というのは正確ではありません。読み込みのスケーラビリティを支えているのは、etcd 自体のメンバー分散ではなく kube-apiserver 側の watch cache です。

1.4 スケールさせる: リソース単位で etcd クラスタを分割する

書き込みがリーダー1台に縛られる以上、単一の etcd クラスタのままでは Event のような高頻度書き込みリソースがボトルネックになり続けます。ここで使えるのが kube-apiserver の --etcd-servers-overrides フラグです。

--etcd-servers-overrides string
    Per-resource etcd servers overrides, comma separated. The individual
    override format: group/resource#servers, where servers are URLs,
    semicolon separated. Note that this applies only to resources compiled
    into this server binary.
    e.g. "/pods#http://etcd4:2379;http://etcd5:2379,/events#http://etcd6:2379"

(参考: etcd.go#L130-L134)

指定の単位は group/resource (GroupResource) で、フィールド単位や namespace 単位の分割はできません。

type EtcdServerOverride struct {
	GroupResource schema.GroupResource
	Servers       []string
}

(参考: etcd.go#L623-L653)

最も典型的な使い方は、/events#http://<event用etcdクラスタ> のように Event だけを別の etcd クラスタへ切り出すことです。Event は他のリソースとの間にトランザクション的な整合性要件がなく (単一オブジェクトの読み書きが基本)、削除されても Kubernetes の状態そのものには影響しないため、切り出しやすいリソースです。1.2節で見た通り、書き込み頻度が最も高くリーダーを圧迫しやすいのも Event なので、切り出すことで Pod や Deployment など本来優先したいリソースへの圧迫を防げます。

1.5 さらにスケールする場合

Event を切り出してもなお etcd がボトルネックになる規模であれば、フラグの例にもある /pods#... のように 他の高頻度リソースを別クラスタへ分離する選択肢もあります。ただし、これは非常に大規模なクラスタで初めて検討する高度な構成です。

etcd クラスタを分割するたびに、バックアップ・リストア対象のクラスタが増え、障害調査で確認すべき箇所も増えます。「Event だけ切り出す」までは定石として広く行われていますが、それ以上の分割は、実際に書き込み負荷のプロファイルを取り、どのリソースがリーダーを圧迫しているかを確認した上で判断すべきです。

1.6 参考: etcd の API 互換を保ったまま、裏側をスケールする DB に置き換えるパターン

ここまでの1.4節・1.5節は、あくまで「vanilla の etcd を使い続けたまま、リソース単位でクラスタを分割する」という延長線上のスケール方法でした。しかし etcd の書き込みがリーダー1台に縛られる根本原因は Raft というコンセンサスアルゴリズムの性質そのものにあるため、リソースを分割し続けても、いずれ「1リソースあたり1リーダー」の壁には当たります。

これとは別のアプローチとして、kube-apiserver から見た etcd の gRPC API 互換性だけを保ちつつ、実際のストレージエンジンを水平スケールする別の分散データベースに置き換えるというパターンがあります。kube-apiserver の storage/etcd3 実装が依存しているのは etcd の Raft 実装そのものではなく Get/Put/Range/Watch/Txn などの etcd v3 API なので、この API さえ実装していれば、裏側のストレージエンジンは何であっても Kubernetes 本体に変更を入れずに差し替えられます。

  • kine (k3s-io/kine): etcd v3 API を SQLite・PostgreSQL・MySQL・NATS JetStream 等に翻訳する OSS です。k3s/RKE2 がこれを使い、エッジやシングルバイナリ向けの軽量な Kubernetes ディストリビューションで etcd 運用そのものを省略しています。ただし通常の RDBMS もシングルプライマリである点は変わらないため、ここでの主な狙いは書き込みスケールというより etcd 運用の複雑さと footprint の削減です

  • GKE: Google Cloud は GKE のストレージを、etcd から Spanner ベースのキーバリューストアへ移行しています。公式ブログでは次のように説明されています

    For one, we are transitioning GKE from the open-source etcd, distributed key-value store, to a new, more robust, key-value store based on Spanner, Google's distributed database that delivers virtually unlimited scale.

    By implementing the etcd API for our Spanner-based storage, we help ensure backward compatibility and avoid having to make changes in core Kubernetes to adopt the new technology.

    (参考: Google Kubernetes Engine supports 65,000-node clusters)

    Spanner はもともと Paxos ベースで水平にシャーディングできる分散データベースであり、「1リソースあたり1リーダー」という vanilla etcd の制約を持ちません。GKE はこの上に etcd 互換の API 層を被せることで、kube-apiserver 側を一切変更せずにストレージ層のスケーラビリティ上限を根本から引き上げています。この移行を経て GKE は 65,000 ノードクラスタを正式にサポートしており、2025年末には 130,000 ノード・数百万オブジェクト規模のクラスタが「これまでに公開された中で最大の Kubernetes クラスタ」として報告されています (InfoQ)

自前で構築・運用するクラスタで、etcd を Spanner のような別のストレージエンジンに置き換えるのは現実的ではありません。まずは Event リソースだけを別の etcd クラスタに切り出す (1.4節) のが現実的な出発点です。それ以上のスケールが必要になった場合も、単一の etcd クラスタを大きくすることだけを考えるのではなく、複数の k8s クラスタを利用するマルチクラスタ化のような構成全体の見直しも含めて検討してください。

第2章: Control Plane ノードの台数設計

2.1 このノードグループで何を動かすか

Control Plane ノードグループで動くのは主に次の3プロセスです。性質がまったく異なるため、まとめて「Control Plane を何台にするか」と一括りに考えると誤解が生まれます。

プロセス 役割 複数レプリカの意味
kube-apiserver クライアントからの REST/Watch リクエストを受け、認証・認可・admission を経て etcd に読み書きする、ステートレスな窓口 各レプリカが独立してリクエストを処理できる (水平スケール可能)
kube-scheduler Pod の配置先ノードを決定する leader election で常に1台のみが動作 (水平スケール不可)
kube-controller-manager 各種コントローラーの reconcile loop を実行する leader election で常に1台のみが動作 (水平スケール不可)

2.2 スケジューラー・コントローラーマネージャは水平スケールしない

kube-scheduler と kube-controller-manager は client-go の leader election を使っています。leader に選ばれたレプリカだけが実処理を開始し、それ以外は待機し続けます。

// cmd/kube-scheduler/app/server.go
OnStartedLeading: func(ctx context.Context) {
	close(waitingForLeader)
	...
	sched.Run(ctx) // leader になって初めてスケジューリングループが動く
},
...
leaderElector.Run(ctx)
return fmt.Errorf("lost lease") // leader でない間はここでブロックし続ける

(参考: server.go#L314-L349、kube-controller-manager 側も同様に controllermanager.go#L840-L867 で leaderelection.RunOrDie を呼んでいます)

leader election の実体は、client-go の leaderelection.go#L211-L236 にある通り、acquire に成功した1台だけが OnStartedLeading を呼ばれる、という単純なロックの取り合いです。レプリカを2台にしても5台にしても、実際に処理をするのは常に1台であり、これは可用性 (障害時の切り替え) のための冗長化であって、スループットを増やすための水平スケールではありません。

2.3 API Server をスケールさせるメリット・デメリット

kube-apiserver はステートレスなので、レプリカを増やせばクライアント接続を分散できます。しかし、その裏で etcd への負荷も増えることを見落としがちです。

観点 レプリカを増やすメリット レプリカを増やすデメリット
クライアント接続 (kubectl、controller の watch 確立、admission webhook 呼び出し、TLS 終端) 1台あたりの接続数・CPU 負荷を分散できる —
起動時の relist (初期 List) — レプリカごとに全登録リソースの初期 List を実行するため、レプリカ数に比例して etcd への一時的な quorum read 負荷が増える
watch 接続 — レプリカごとに独立した watch cache を持つため、etcd への watch 接続数はレプリカ数 × リソース種別数で増える
強整合性 Get/List (1.3節の Most Recent) — 結局 etcd の quorum read (リーダー) に委譲されるため、レプリカを増やしてもこのスループットは etcd 側の性能に制約される
メモリ — レプリカごとに全リソースの watch cache をフルコピーで保持するため、メモリ使用量がレプリカ数に比例して増える

watch cache がレプリカごとに独立していることは、NewCacherFromConfig が各リソース型・各プロセスごとに reflector.ListAndWatch を起動する実装から裏付けられます。

(参考: cacher.go#L351-L457、cacher.go#L489-L509、呼び出し元 storage_factory.go#L36-L85)

つまり kube-apiserver をスケールさせても、etcd 自体の負荷だけが増えて性能に寄与しないケースが大半です。クライアント接続数の飽和 (CPU、TLS ハンドシェイク、admission webhook のレイテンシなど) が明確に確認できている場合を除き、まず疑うべきボトルネックは etcd 側です。

2.4 なぜ etcd と違って奇数台ルールがないのか

ここが本記事の核心です。Control Plane には、etcd のような「奇数台にする」「過半数を維持する」といった quorum のルールはありません。

  • kube-apiserver はステートレスです。レプリカ同士が合意形成をする必要がなく、ロードバランサの背後に何台あっても構いません
  • kube-scheduler/kube-controller-manager の leader election は、etcd 上の Lease オブジェクトという 単一のロック を取り合う仕組みです (leaderelection.go)。これは「過半数の合意」ではなく「早い者勝ちのロック取得」なので、候補が2台でも5台でも動作原理は変わりません

一方で、kubeadm の公式ドキュメントには次の記述があります。

Three or more machines that meet [...] for the control-plane nodes. Having an odd number of control plane nodes can help with leader selection in the case of machine or zone failure.

(参考: Creating Highly Available clusters with kubeadm)

これは矛盾しているように見えますが、実際は kubeadm の既定のトポロジが stacked etcd (Control Plane ノードに etcd を同居させる構成) だからです (第3章で詳述)。stacked 構成では Control Plane ノード数 = etcd メンバー数になるため、etcd 側の奇数台ルールがそのまま Control Plane の推奨台数として持ち込まれています。この「奇数台であるべき」という要件の出どころは etcd であって、kube-apiserver/kube-scheduler/kube-controller-manager 自体の要件ではありません。 etcd を分離すれば (external etcd トポロジ)、Control Plane ノードの台数はこのルールから解放されます。

2.5 何台から始めるべきか

etcd 側の制約から解放されたとすると、Control Plane は 2台から始めて、必要になったらスケールアップするのが良いと思います。何台が適切かは、「ローリングアップデートの方式」をどうするかから検討していくと良いと思います。

  • ノードを1台追加してから古いノードを消す方式 (surge upgrade): 常に最低2台は生きた状態を維持できるため、ベースは2台で十分です (更新中は一時的に3台になります)
  • 1台を停止してからバージョンを上げ、それを繰り返す方式 (in place upgrade): 更新中は稼働台数が1台減ります。常に2台は稼働させたいのであれば、ベースは3台にしておく必要があります

これは etcd の「メンテナンス中でも quorum を割らないために5台にする」(1.1節) と同じ考え方です。「常時いくつ動いていてほしいか」から逆算して、メンテナンスで何台減るアップデート方式を採用しているかを足す、というのがどちらの章にも共通する台数の決め方です。

第3章: etcd と Control Plane を同居させる場合

etcd と Control Plane (kube-apiserver 等) を同じノードに同居させる構成は、kubeadm では stacked etcd トポロジと呼ばれ、既定の構成です。

However, a stacked cluster runs the risk of failed coupling. If one node goes down, both an etcd member and a control plane instance are lost, and redundancy is compromised. You can mitigate this risk by adding more control plane nodes. You should therefore run a minimum of three stacked control plane nodes for an HA cluster.

(参考: Options for Highly Available topology)

Stacked etcd topology
Stacked etcd topology (参考: kubeadm の High Availability Considerations)

対して分離する external etcd トポロジは、Control Plane と etcd を別ノードに分ける分ノード数は増えますが、ノード故障時に Control Plane と etcd の両方を同時に失うリスクを避けられます。

However, this topology requires twice the number of hosts as the stacked HA topology. A minimum of three hosts for control plane nodes and three hosts for etcd nodes are required for an HA cluster with this topology.

(参考: Options for Highly Available topology)

公式ドキュメントは「stacked の2倍のホストが必要」としていますが、これは両トポロジを最小構成 (Control Plane 3台・etcd 3台の合計6台) で比較した場合の話です。本記事の結論 (etcd は5台、Control Plane は2台から) を当てはめると、external では etcd 5台 + Control Plane 2台で合計7台となり、単純な2倍にはなりません。増える台数は、実際に選ぶ etcd・Control Plane それぞれの台数次第です。

External etcd topology
External etcd topology (参考: kubeadm の High Availability Considerations)

3.1 PoC・検証環境: 3台同居はミニマム構成として合理的

PoC や検証目的で、まず3台のノードに etcd と Control Plane を同居させるのは経済的にも理解できる選択です。kubeadm の既定トポロジそのものであり、最小構成でクラスタを検証したい場合には適しています。

3.2 本番で5台同居させる場合は計測してから判断する

一方で、1.1節の理由で etcd を5台にした上で、その5台すべてに Control Plane も同居させる構成には注意が必要です。

etcd を5台にする理由は、あくまで「メンテナンス中でも quorum を落とさない」という etcd 自身の信頼性要件です。Control Plane 側は2.4節で見たとおり奇数台ルールも quorum ルールも持たないため、5台すべてに Control Plane が必要かどうかは、etcd の台数とは独立に決めるべき問題です。kube-apiserver のクライアント接続数や kube-scheduler/kube-controller-manager のリーダー処理量を測らずに「etcd が5台だから Control Plane も5台」と決めてしまうと、実際には etcd の負荷だけを増やしていて、Control Plane 側には特に利点がない、という状態になりかねません。

  • kube-apiserver は2.3節で見た通り、レプリカが増えるほど watch/relist で etcd への負荷を増やします
  • 同居させれば、Control Plane プロセスと etcd プロセスが同じノードの CPU・メモリ・ディスク I/O を奪い合います。特に etcd はディスク書き込みレイテンシに敏感なため、同居する Control Plane プロセスの負荷が etcd のコミットレイテンシに影響することもあります

5台同居させるかどうかは、実際にクライアント接続数や kube-apiserver の CPU 使用率を測定した上で判断するのが安全です。

3.3 長期運用・スケールの予定があるなら分離しておく

プロダクションで長期に使う予定がある、または今後利用者が増えてスケールしていく予定があるなら、最初から etcd と Control Plane を分離しておくほうが運用は楽になります。理由は次の2点です。

  1. ライフサイクルが異なる: etcd のメンバー追加・削除など ectd クラスタ構成の変更は、 Control Plane ノードのスケールアップ・ダウンより慎重な対応が必要です。同居していると、Control Plane 側の都合での変更により etcd クラスタが影響を受ける可能性があります
  2. ボトルネック調査・対応の手間が増える: 性能問題が出たとき、分離している構成の方が調査は楽です。また、原因がどちらの場合でも「まず etcd と Control Plane のノードを分離する」ことが第一の選択肢になるケースが多いです。これは、 etcd と Control Plane でスケールさせる方法が異なるからです。最初から分離しておけば、この切り分け作業自体が不要になります

おわりに

マネージドサービスの事例が多く、オンプレミスでクラスタを運用している話をあまり聞かないので、こういう話にも需要があるかなと思って記事にしました。改めて確認してみると色々と学びがあり、面白かったです。

Control Plane が Stateless に設計されていても、接続先の etcd クラスタの制約を考えると、単純に水平スケールできるアーキテクチャではないことに気付いていただけたと思います。これは、Kubernetes 上で Deployment を使ってアプリケーションを運用している際に、接続先の DB がボトルネックになって性能が出ないケースとよく似た事象です。

スケールについては、全体のアーキテクチャを把握し、ボトルネックが何になっているか、そのボトルネックに対してどういう構成変更が有効なのかを把握しないと機能しません。Stateless だからといって、単純に数を増やせば性能が良くなるわけではないという点には注意が必要です。

参考

参考リンク

本文で登場する順に並べています。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?