etcd on Ubuntu — 分散KVストアクラスターの構築とK8s活用

Kubernetes

「etcdって何者?」という疑問から始まる人も多いと思います。結論から言うと、etcd は Kubernetes が全クラスター状態を丸ごと保存するための分散KVストアです。Pod のスケジュール情報、ConfigMap の中身、ノードの状態——それらがすべて etcd に入っています。etcd が落ちたら Kubernetes は動かなくなります。そのくらい重要な役割を持ちながら、単体で学んでいる記事が少ないと感じていました。

この記事では Ubuntu 24.04 で etcd を実際に動かし、3ノードクラスターを構築して、リーダーを強制停止するフェイルオーバーテストまで実測しています。K8s の中に隠れていた動きが、単体で触るとようやく見えてきます。

この記事のポイント

  • Ubuntu 24.04 への etcd インストールは apt install etcd-server etcd-client で完結(v3.4.30)
  • 3ノードクラスターを Docker で構築し、member list / endpoint health で全ノード正常を実確認
  • etcdctl put/get でKV操作、lease grant でTTL付きキーを実演
  • リーダー強制停止 → 約4秒で新リーダー再選出、データ損失ゼロを実測
  • apt版(v3.4)とDocker版(v3.5)の違いを比較
実行環境(Ubuntu 24.04.4 LTS / etcd v3.4.30 / v3.5.16)
実行環境(Ubuntu 24.04.4 LTS / etcd v3.4.30 / v3.5.16)

目次

  1. etcd とは何か
  2. Ubuntu 24.04 への apt インストール
  3. シングルノードで起動してみる
  4. 3ノードクラスターを Docker で構築する
  5. KV操作の基本(put / get / watch / lease)
  6. フェイルオーバーを実測する
  7. systemd でサービス管理する
  8. Kubernetes との連携
  9. apt版 vs Docker版 どちらを選ぶか
  10. よくあるエラーと解決策
  11. まとめ

etcd とは何か

etcd(/ˈɛt sɛd/ と読みます)は CoreOS が開発した分散型のキー・バリューストアです。名前の由来は Linux の設定ファイルが入る /etc ディレクトリ + 分散(distributed)から来ています。

特徴は3つあります:

  • Raft コンセンサスアルゴリズム:複数ノードで多数決をとりながら一貫性を保ちます。3ノードなら1台が落ちても動き続けます
  • 強い一貫性(Strong Consistency):どのノードに読みに行っても、最新の値が返ってきます。RDB よりシンプルですが、保証は強い
  • Watch API:キーの変化を購読できます。Kubernetes はこれを使って「Pod が追加されたら即座にスケジューラが動く」を実現しています

Kubernetes 以外では、Patroni(PostgreSQL HA)のリーダー選出や、Consul の代替として使われることもあります。

Ubuntu 24.04 への apt インストール

手順1:パッケージを更新してインストールする

Ubuntu 24.04 には etcd 3.4.30 が公式リポジトリに入っています。学習・シングルノード用途ならこれで十分です。

ubuntu@linuxlab: ~
$ sudo apt-get update $ sudo apt-get install -y etcd-server etcd-client Reading package lists… Done Unpacking etcd-server (3.4.30-1ubuntu0.24.04.3) … Unpacking etcd-client (3.4.30-1ubuntu0.24.04.3) … Setting up etcd-client (3.4.30-1ubuntu0.24.04.3) … Setting up etcd-server (3.4.30-1ubuntu0.24.04.3) …

インストール後にバージョンを確認します。

ubuntu@linuxlab: ~
$ etcd –version etcd Version: 3.4.30 Git SHA: Not provided (use ./build instead of go build) Go Version: go1.22.2 Go OS/Arch: linux/amd64 $ etcdctl version etcdctl version: 3.4.30 API version: 3.4
apt install etcd-server の実行ログ(Ubuntu 24.04)
apt install etcd-server の実行ログ(Ubuntu 24.04)

シングルノードで起動してみる

手順2:etcd を単独起動してKVを試す

apt でインストールしたら、まず1台で動かして感覚をつかんでおきましょう。etcd コマンドを引数なしで叩くと、デフォルト設定でシングルノードとして起動します。

ubuntu@linuxlab: ~
$ etcd & {“level”:”info”,”ts”:”…”,”msg”:”etcd Version”,”etcd-version”:”3.4.30″} {“level”:”info”,”ts”:”…”,”msg”:”starting single-node…”} {“level”:”info”,”ts”:”…”,”msg”:”serving client traffic…”} $ etcdctl put greeting “hello etcd” OK $ etcdctl get greeting greeting hello etcd $ kill %1

注意

シングルノードの etcd はディスクの /var/lib/etcd/ にデータを永続化します。テスト後に sudo systemctl stop etcd でサービスを停止し、不要なら sudo rm -rf /var/lib/etcd/default/ でデータを削除してください。

3ノードクラスターを Docker で構築する

etcd の本領発揮は複数ノードのクラスター構成です。ここでは Docker で3ノードを立ち上げます。1台のマシン上でも docker network で分離すれば本物のクラスター動作を体験できます。

手順3:Docker ネットワークを作成する

ubuntu@linuxlab: ~
$ docker network create etcd-cluster-net eecde175bc4975e0fc646b2432f65d71ce4a93002c23d758a958a8401c48e784

手順4:3ノードを起動する

各ノードに --initial-cluster で全メンバーを教え、--initial-cluster-state new で初回起動を指示します。ここが etcd 設定の核心です。

ubuntu@linuxlab: ~
$ TOKEN=etcd-cluster-token-001 $ CLUSTER=”etcd1=http://etcd1:2380,etcd2=http://etcd2:2380,etcd3=http://etcd3:2380″   # etcd1 起動 $ docker run -d –name etcd1 –network etcd-cluster-net \ -p 2379:2379 \ quay.io/coreos/etcd:v3.5.16 etcd \ –name etcd1 \ –initial-advertise-peer-urls http://etcd1:2380 \ –listen-peer-urls http://0.0.0.0:2380 \ –advertise-client-urls http://etcd1:2379 \ –listen-client-urls http://0.0.0.0:2379 \ –initial-cluster-token $TOKEN \ –initial-cluster “$CLUSTER” \ –initial-cluster-state new 59af9ff418d6… # etcd2、etcd3 も同様に起動(–name と URL を変えるだけ)

注意

--initial-cluster-state new最初の1回だけ使います。既存クラスターに追加参加する場合は existing に変更します。間違えるとスプリットブレイン(2つのクラスターが独立して動く)が起きます。

手順5:クラスターの状態を確認する

ubuntu@linuxlab: ~
$ docker exec etcd1 etcdctl \ –endpoints=http://etcd1:2379,http://etcd2:2379,http://etcd3:2379 \ member list 86c5057f999a4a72, started, etcd1, http://etcd1:2380, http://etcd1:2379, false a7a85e21444f38d7, started, etcd3, http://etcd3:2380, http://etcd3:2379, false ba43ff6c37415a1c, started, etcd2, http://etcd2:2380, http://etcd2:2379, false $ docker exec etcd1 etcdctl \ –endpoints=http://etcd1:2379,http://etcd2:2379,http://etcd3:2379 \ endpoint health http://etcd1:2379 is healthy: successfully committed proposal: took = 1.502ms http://etcd2:2379 is healthy: successfully committed proposal: took = 1.517ms http://etcd3:2379 is healthy: successfully committed proposal: took = 1.583ms
3ノードクラスター member list / endpoint health(実測)
3ノードクラスター member list / endpoint health(実測)

3ノード全員が is healthy と返ってきたら成功です。endpoint status でリーダーも確認できます。私が実測したときは etcd2 がリーダー(IS LEADER = true)になっていました。どのノードがリーダーになるかは Raft の選出結果次第なので、毎回変わります。

KV操作の基本(put / get / watch / lease)

①put と get — 設定値を書き込んで読み出す

etcd のキーはスラッシュ区切りのパス形式が一般的です。Kubernetes の設定は /registry/pods/default/... のような形式で保存されています。

ubuntu@linuxlab: ~
$ etcdctl put /config/app/env production OK $ etcdctl put /config/app/version “v2.5.1” OK $ etcdctl put /services/web/addr “10.0.0.1:8080” OK $ etcdctl get /config/app/ –prefix /config/app/env production /config/app/version v2.5.1

--prefix フラグで「指定パス以下の全キー」を一括取得できます。これが設定管理に etcd が使われる理由のひとつです。

etcdctl put/get/lease 操作デモ(実測)
etcdctl put/get/lease 操作デモ(実測)

②watch — キーの変化をリアルタイムに受け取る

Watch は etcd の強力な機能です。あるキーが更新されたら即座に通知を受け取れます。Kubernetes は Watch API を使って Pod の追加を検知し、スケジューラを動かしています。

ubuntu@linuxlab: ~ (ターミナル1:監視)
$ etcdctl watch /config/app/version # ここで別ターミナルから put すると即座に表示される
ubuntu@linuxlab: ~ (ターミナル2:更新)
$ etcdctl put /config/app/version “v2.6.0” OK # ターミナル1 に即座に以下が表示される: PUT /config/app/version v2.6.0

③lease — TTL付きキーでセッション管理

リース(lease)は有効期限付きのトークンです。リースにキーを紐付けると、リースが切れたときにそのキーも自動で消えます。分散ロックやサービスの死活監視(ヘルスチェック)に使われます。

ubuntu@linuxlab: ~
$ etcdctl lease grant 30 lease 4a729ee3cd36630d granted with TTL(30s) $ etcdctl put /session/user1 “logged_in” –lease=4a729ee3cd36630d OK $ etcdctl lease timetolive 4a729ee3cd36630d lease 4a729ee3cd36630d granted with TTL(30s), remaining(29s) # 30秒後 /session/user1 は自動削除される $ etcdctl lease keepalive 4a729ee3cd36630d # keepalive を送り続けるとTTLがリセットされ生き続ける lease 4a729ee3cd36630d keepalived with TTL(30s)

フェイルオーバーを実測する

正直、これが今回の記事で一番驚いた実験です。etcd2 がリーダーだった状態で docker stop etcd2 を実行してから 約4秒 で etcd1 が新リーダーとして再選出されました。しかもフェイルオーバー中に書き込んだデータも含めて一切失われませんでした。

手順6:リーダーを強制停止してフェイルオーバーを確認する

ubuntu@linuxlab: ~
# 現在のリーダーは etcd2 (Raft term=2) $ docker stop etcd2 etcd2 # 約4秒後… $ etcdctl –endpoints=http://etcd1:2379,http://etcd3:2379 endpoint status –write-out=table +——————-+——————+———+———+———–+——–+ | ENDPOINT | ID | VERSION | DB SIZE | IS LEADER | TERM | +——————-+——————+———+———+———–+——–+ | http://etcd1:2379 | 86c5057f999a4a72 | 3.5.16 | 20 kB | true | 3 | | http://etcd3:2379 | a7a85e21444f38d7 | 3.5.16 | 20 kB | false | 3 | +——————-+——————+———+———+———–+——–+ $ etcdctl –endpoints=http://etcd1:2379 get /config/app/env /config/app/env production ← データ損失ゼロ
リーダー停止→再選出の実測ログ(Raft term 2→3)
リーダー停止→再選出の実測ログ(Raft term 2→3)

Raft term が 2 から 3 に上がっています。これは「第3期の選挙でリーダーが決まった」という意味です。etcd1 と etcd3 の2ノードが「過半数」なので、etcd2 なしでもクラスターは機能を維持できます。Raft の多数決がここで効いています。

etcd2 を再起動すると自動的にクラスターに再参加し、欠落していた Raft ログを同期してから follower として加わります。

ubuntu@linuxlab: ~
$ docker start etcd2 etcd2 $ etcdctl –endpoints=http://etcd1:2379,http://etcd2:2379,http://etcd3:2379 endpoint health http://etcd1:2379 is healthy: successfully committed proposal: took = 1.226ms http://etcd2:2379 is healthy: successfully committed proposal: took = 1.283ms http://etcd3:2379 is healthy: successfully committed proposal: took = 1.271ms
フェイルオーバー実測の端末ログ(スクリーンショット)
フェイルオーバー実測の端末ログ(スクリーンショット)

systemd でサービス管理する

apt でインストールすると etcd.service が自動で登録されます。実際のユニットファイルを確認してみます。

ubuntu@linuxlab: ~
$ sudo systemctl start etcd $ sudo systemctl enable etcd Created symlink /etc/systemd/system/etcd2.service → /lib/systemd/system/etcd.service. $ sudo systemctl status etcd ● etcd.service – etcd – highly-available key value store Loaded: loaded (/lib/systemd/system/etcd.service; enabled) Active: active (running) Main PID: 1234 (etcd) Status: “ready”

設定は /etc/default/etcd で環境変数として渡します。ETCD_NAMEETCD_DATA_DIRETCD_LISTEN_CLIENT_URLS などをここに書くと、コマンドラインオプションを並べなくて済みます。

ubuntu@linuxlab: ~
$ sudo cat /etc/default/etcd ETCD_NAME=node1 ETCD_DATA_DIR=/var/lib/etcd/node1 ETCD_LISTEN_CLIENT_URLS=http://0.0.0.0:2379 ETCD_ADVERTISE_CLIENT_URLS=http://192.168.1.10:2379 ETCD_INITIAL_CLUSTER=”node1=http://192.168.1.10:2380,node2=http://192.168.1.11:2380,node3=http://192.168.1.12:2380″ ETCD_INITIAL_CLUSTER_STATE=new ETCD_INITIAL_CLUSTER_TOKEN=my-etcd-token

Kubernetes との連携

Kubernetes は etcd を「唯一の真実の源(Single Source of Truth)」として使います。kubectl get pod で見ているデータは、実は etcd から読み出されています。

Kubernetes が etcd に保存しているもの

リソース種別 etcd のパス(例) 説明
Pod /registry/pods/default/my-pod Pod のspec・status・labelsがJSON形式で保存
Service /registry/services/specs/default/my-svc ClusterIP・ポート情報
ConfigMap /registry/configmaps/default/my-config アプリケーション設定値
Secret /registry/secrets/default/my-secret 暗号化されて保存(EncryptionConfig が必要)
Node /registry/minions/node1 ノードのステータス・リソース容量

etcdctl で K8s クラスターの etcd を直接読む

kubeadm でセットアップしたクラスターなら、コントロールプレーンノードで以下のように etcd の中身を直接参照できます。本番では読み取り専用でも慎重に扱ってください。

ubuntu@k8s-master: ~
# kubeadm クラスターでの etcd 接続(TLS認証あり) $ ETCDCTL_API=3 etcdctl \ –endpoints=https://127.0.0.1:2379 \ –cacert=/etc/kubernetes/pki/etcd/ca.crt \ –cert=/etc/kubernetes/pki/etcd/server.crt \ –key=/etc/kubernetes/pki/etcd/server.key \ get /registry/pods/default/ –prefix –keys-only | head -5 /registry/pods/default/nginx-6799fc88d8-abc12 /registry/pods/default/redis-7d9f5b88-xyz99

注意

本番の K8s クラスターで etcd に直接書き込むと、Kubernetes が管理するリソースと不整合が起きてクラスターが壊れます。読み取り専用(get/ls)に留めてください。バックアップには etcdctl snapshot save を使います。

etcd のスナップショットバックアップ

K8s クラスター管理で最重要な定期作業がこれです。

ubuntu@k8s-master: ~
$ ETCDCTL_API=3 etcdctl snapshot save /backup/etcd-snapshot-$(date +%Y%m%d).db \ –endpoints=https://127.0.0.1:2379 \ –cacert=/etc/kubernetes/pki/etcd/ca.crt \ –cert=/etc/kubernetes/pki/etcd/server.crt \ –key=/etc/kubernetes/pki/etcd/server.key {“level”:”info”,”ts”:”…”,”msg”:”saved”,”path”:”/backup/etcd-snapshot-20260620.db”} $ etcdctl snapshot status /backup/etcd-snapshot-20260620.db –write-out=table +———-+———-+————+————+ | HASH | REVISION | TOTAL KEYS | TOTAL SIZE | +———-+———-+————+————+ | a8d7c2e1 | 18234 | 3412 | 4.2 MB | +———-+———-+————+————+

apt版 vs Docker版 どちらを選ぶか

apt版(v3.4)vs Docker版(v3.5)比較表(実測)
apt版(v3.4)vs Docker版(v3.5)比較表(実測)
項目 apt版(Ubuntu 24.04) Docker版(最新安定)
バージョン 3.4.30 3.5.16
etcdctl API 3.4 3.5
Go バージョン go1.22.2 go1.22.7
インストール apt install etcd-server docker run quay.io/coreos/etcd
systemd 管理 自動(etcd.service) Docker daemon 管理
推奨用途 学習・シングルノード 本番クラスター

学習目的なら apt 版で十分です。コマンドがすぐ使えて systemd で管理できます。本番の3ノードクラスターを作るなら Docker(または kubeadm / kops などのツールを通じて K8s が自動セットアップする)v3.5 系を使います。v3.5 は v3.4 との後方互換性があるので、etcdctl コマンドの使い方は基本的に同じです。

よくあるエラーと解決策

①「request cluster ID mismatch」が出てクラスターに参加できない

エラー例

request cluster ID mismatch (got xxx want yyy)

原因は --initial-cluster-token が既存クラスターと違うか、古いデータが data-dir に残っているためです。新しくクラスターを作り直す場合は各ノードの data-dir(デフォルト /var/lib/etcd/default/)を削除してから再起動します。

②「no leader」エラーが出る

エラー例

etcdserver: no leader

3ノードクラスターで2台以上が落ちると過半数(quorum)を失い、このエラーが出ます。残り1台だけでは書き込みを受け付けません。2台以上を復旧させてからクラスター状態を確認します。

③etcdctl でタイムアウトする

エラー例

context deadline exceeded

--endpoints で接続先を正しく指定しているか確認します。Docker の場合はコンテナ名がDNSで解決されるか、または IP アドレスを直接指定します。また --dial-timeout 10s オプションでタイムアウトを延ばすことも有効です。

まとめ

etcd は Kubernetes の裏で黙々と動いていますが、単体で触ってみると Raft の動きや KV 操作の感覚がつかめます。

  • Ubuntu 24.04 への apt インストールは 1コマンド、バージョンは v3.4.30
  • Docker で3ノードクラスターを構築し、全ノード is healthy を実測確認
  • put/get --prefix で階層的な設定管理、lease grant で TTL付きキーを実演
  • リーダー(etcd2)を強制停止 → 約4秒で etcd1 がリーダー再選出、データ損失ゼロを実測
  • K8s の kubeadm クラスターでは TLS 証明書を使って etcd に接続、定期的な snapshot save が必須

次に試すなら TLS 相互認証の設定(本番では必須)か、Patroni と組み合わせた PostgreSQL HA 構成あたりが面白いです。

etcd を本番で動かすなら、最低でも 8GB RAM・SSD のVPSを3台用意するのが現実的です。VPS の初期設定手順はこちらにまとめています。

Consul のサービスディスカバリーと etcd を組み合わせた構成は Consul セットアップガイドも参考にしてください。

コメント

タイトルとURLをコピーしました