はじめに
TRIAL&RetailAI Advent Calendar 2025の1日目の記事になります。
本日のテーマは"kubernetes Deep Dive (etcd + Raft)"です。私はいつも開発でkubernetes(k8s)を使っています。より深くk8sについて知識を深めたく、再度基本的な事項からetcdとRaftについてまとめたいと思います。
自己紹介
基盤システム部に所属して、主にバックエンドの開発を行っております。
2024年にもAdvent Calendarの記事を書いていますので、よかったら見ていただけると幸いです。
k8sとは
k8sとはコンテナを自動で管理するコンテナオーケストレーションシステム。
アプリケーションを「宣言的(desired state)」に保つことが特徴に挙げらます。
k8sが解決してくれること
- サーバーやコンテナの自動配置
- スケール(増減)の自動化
- 障害時の自己修復
- ロードバランシング
- 設定・機密情報の管理
基本構成
Control Plane の役割
- クラスタ全体の desired state(あるべき状態)を定義して維持する
- Node や Pod の状態を監視して調整する
- Pod をどこで動かすか(スケジューリング)を決める
- etcd にクラスタ全体の設定・状態を保存する
| コンポーネント | 役割 |
|---|---|
| API Server | すべての操作の入口となる REST API。 |
| Scheduler | Pod をどの Node に配置するか決定。 |
| Controller Manager | 各コントローラーによりクラスタ状態を desired state へ調整。 |
| etcd | Kubernetes の唯一のデータストア(全設定・全状態を保存)。 |
Worker Node の役割
- Pod(アプリコンテナ)を実行する
- コンテナのライフサイクルを管理
- Pod のネットワーク通信を制御
- Control Plane に状態を報告する
| コンポーネント | 役割 |
|---|---|
| Kubelet | Node 内の Pod を管理し、API Server と通信。 |
| Kube-Proxy | Service によるロードバランシング・ネットワーク制御。 |
| Container Runtime | 実際にコンテナを実行(containerd / CRI-O 等)。 |
etcd
k8sが内部状態を保存するための分散Key-Valueストア。
Control Plane のすべての設定・状態は etcd に保存される。
そのため etcd が停止すると Kubernetes 全体が機能不全になる。
etcd Demo
# Macを使っているためHome brewでetcdをインストール
brew install etcd
# インストールの確認
which etcd
# etcd起動
export ETCDCTL_API=3
etcd
# 値の書き込み
# 別ターミナルで以下を実行
etcdctl put foo "hello etcd"
> OK
# 値の取得
etcdctl get foo
> OK
> foo
> hello etcd
# 変更監視
# 別ターミナルで以下を実行
etcdctl watch foo
# さらに別ターミナルで以下を実行
etcdctl put foo "new value"
> OK
# etcdctl watch fooを実行したターミナルでリアルタイムで変更確認
> PUT
> foo
> new value
Kubernetes Control Plane のコンポーネント(API Server など)がetcd に対して"PUT / GET / WATCH" をしていることを体験できると思います。
Raft
Raft は etcd・Consul・TiKV・Kubernetes(内部の etcd)が使っている
分散合意アルゴリズム。これにより強整合性が生まれる。
| 要点 | 説明 |
|---|---|
| Leader は常に 1 台 | 書き込みの競合を防ぎ、一貫性を維持するため。 |
| 書き込みはすべて Leader 経由 | 分散システムでデータを線形化(Linearizable)するための基本原則。 |
| 過半数(Majority)がログを保持したらコミット | 複数ノード故障時でも正しく復旧できるようにする仕組み。 |
| Leader が死んだら自動選挙 | フェイルオーバーによる高可用性を実現。クラスタは停止しない。 |
| etcd の強整合性の基盤 | etcd の Read/Write が Linearizable である理由は Raft によるログ複製と選挙。 |
| 役割 | 説明 |
|---|---|
| Leader | すべての書き込みを受け付ける唯一のノード。Follower にログを複製する。 |
| Follower | Leader からログ複製を受け取り、状態機械に適用する。 |
| Candidate | Follower が一定時間 Leader と通信できなかった場合に選挙へ立候補する役割。 |
Raft Demo
# 3つのターミナルで以下を1つずつ実行する
# 1つ目のターミナル
etcd \
--name n1 \
--listen-peer-urls http://127.0.0.1:2380 \
--listen-client-urls http://127.0.0.1:2379 \
--advertise-client-urls http://127.0.0.1:2379 \
--initial-advertise-peer-urls http://127.0.0.1:2380 \
--initial-cluster-token etcd-cluster \
--initial-cluster n1=http://127.0.0.1:2380,n2=http://127.0.0.1:2382,n3=http://127.0.0.1:2384 \
--initial-cluster-state new
# 2つ目のターミナル
etcd \
--name n2 \
--listen-peer-urls http://127.0.0.1:2382 \
--listen-client-urls http://127.0.0.1:2377 \
--advertise-client-urls http://127.0.0.1:2377 \
--initial-advertise-peer-urls http://127.0.0.1:2382 \
--initial-cluster-token etcd-cluster \
--initial-cluster n1=http://127.0.0.1:2380,n2=http://127.0.0.1:2382,n3=http://127.0.0.1:2384 \
--initial-cluster-state new
# 3つ目のターミナル
etcd \
--name n3 \
--listen-peer-urls http://127.0.0.1:2384 \
--listen-client-urls http://127.0.0.1:2378 \
--advertise-client-urls http://127.0.0.1:2378 \
--initial-advertise-peer-urls http://127.0.0.1:2384 \
--initial-cluster-token etcd-cluster \
--initial-cluster n1=http://127.0.0.1:2380,n2=http://127.0.0.1:2382,n3=http://127.0.0.1:2384 \
--initial-cluster-state new
# 3つのうちの一つで以下を確認する
{"level":"info","ts":"2025-11-30T15:14:15.626738+0900","logger":"raft","caller":"v3@v3.6.0/raft.go:970","msg":"b71f75320dc06a6c became leader at term 2"}
# 3つの内のleaderのターミナルを閉じる
# 他の2つでleaderが変化したの確認
"level":"info","ts":"2025-11-30T15:24:09.243345+0900","logger":"raft","caller":"v3@v3.6.0/raft.go:912","msg":"9e85cc091d4d15bb became candidate at term 3"}
{"level":"info","ts":"2025-11-30T15:24:09.243374+0900","logger":"raft","caller":"v3@v3.6.0/raft.go:1064","msg":"9e85cc091d4d15bb [logterm: 2, index: 9] sent MsgVote request to 42b4c95db84f2660 at term 3"}
{"level":"info","ts":"2025-11-30T15:24:09.243410+0900","logger":"raft","caller":"v3@v3.6.0/raft.go:1064","msg":"9e85cc091d4d15bb [logterm: 2, index: 9] sent MsgVote request to b71f75320dc06a6c at term 3"}
{"level":"info","ts":"2025-11-30T15:24:09.257516+0900","logger":"raft","caller":"v3@v3.6.0/raft.go:1077","msg":"9e85cc091d4d15bb received MsgVoteResp from 9e85cc091d4d15bb at term 3"}
{"level":"info","ts":"2025-11-30T15:24:09.257597+0900","logger":"raft","caller":"v3@v3.6.0/raft.go:1693","msg":"9e85cc091d4d15bb has received 1 MsgVoteResp votes and 0 vote rejections"}
{"level":"info","ts":"2025-11-30T15:24:09.262962+0900","logger":"raft","caller":"v3@v3.6.0/raft.go:1077","msg":"9e85cc091d4d15bb received MsgVoteResp from 42b4c95db84f2660 at term 3"}
{"level":"info","ts":"2025-11-30T15:24:09.263028+0900","logger":"raft","caller":"v3@v3.6.0/raft.go:1693","msg":"9e85cc091d4d15bb has received 2 MsgVoteResp votes and 0 vote rejections"}
{"level":"info","ts":"2025-11-30T15:24:09.263059+0900","logger":"raft","caller":"v3@v3.6.0/raft.go:970","msg":"9e85cc091d4d15bb became leader at term 3"}
{"level":"info","ts":"2025-11-30T15:24:09.263086+0900","logger":"raft","caller":"v3@v3.6.0/node.go:370","msg":"raft.node: 9e85cc091d4d15bb elected leader 9e85cc091d4d15bb at term 3"}
k8s の API Server → etcd への更新も
このメカニズムで 100% 安全な状態保存を実現していることを体験できると思います。
まとめ
Control Plane(API Server など)と etcd の関係が Kubernetes の基盤
- API Server は すべての状態変更を etcd に保存
- Worker Node(Kubelet)は etcd の状態を基に動作
Kubernetes の信頼性を支える中枢は etcd
- 全設定・状態を保持する唯一のデータストア
- etcd が止まると Control Plane は全体的に機能不全になる
etcd の強整合性(Linearizable Read/Write)は Raft が実現
- Leader がすべての書き込みを一元管理
- 過半数がログを保持した時点で「コミット」
- Leader 障害時は自動選挙で速やかに復旧
- “分散システムを安全に動かすための ログ複製 + 選挙アルゴリズム”
- 役割は以下の 3 つ:
- Leader
- Follower
- Candidate
- ノード障害やネットワーク分断でもデータを壊さない設計
最後に
次回は@mizoguchi_ryosukeさんによる『遺伝性とマンスキーから学ぶ「○○で決まる」の罠:生成AIで実践する誠実な分析』です。
ぜひお楽しみに!!
RetailAIとTRIALではエンジニアを募集しています。
Kotlin、Flutter、Go、Python、他いろいろな言語を扱っています。
興味ある方は以下から確認してもらうか、メッセージをいただければと思います。
