TT Lab
はじめる
学ぶ 学習パス コース

Kubernetes運用実務

etcd — スナップショットがなければクラスタもない

TT Labで続きを見る

一言でいうと

kubectl getで見えるものはすべて、etcdから読み出した値です。etcdを失えば、クラスターを失います。

なぜ必要なのか

コントロールプレーンを複数台に増やすと安心できます。筆者がホームラボを3ノードから7ノードに増やしたとき、コントロールプレーンを3台にしてetcdのメンバーを3つに揃えた記録があります。メンバーが3なら過半数は2なので、1台が落ちてもクラスターは生き残ります。ところがその記事の最後の行に、こんなメモが残っています。クォーラムは障害への備えであって、操作ミスへの備えではありません。

この一文が、このモジュールの出発点です。ノードが落ちる事故はレプリケーションが防いでくれます。しかしkubectl delete ns paymentsを誤って実行した事故は、レプリケーションでは防げません。削除は正常な書き込みであり、etcdメンバー3台がその削除を律儀に3つ複製するからです。メンバーが100個あっても結果は同じです。時間を巻き戻せる唯一の手段は、その時点より前のスナップショットです。

同じ記事には、こんな事例もあります。コントロールプレーン3台がすべて正常なのに、最初のノードが落ちた途端、kubectlもkubeletもすべて接続が切れました。controlPlaneEndpointがVIPではなく最初のノードの物理IPで固定されていたためです。etcdのクォーラムとAPIの可用性は別の問題でした。このように「冗長化したから大丈夫」という感覚は、よく外れます。復旧手順を自分の手で一度やってみた人だけが、実際に何が守られているのかを知っています。

どう動くのか

etcdのバックアップはファイルのコピーではなく、一貫した時点を1つ丸ごと取り出すことです。

etcdctl snapshot save /backup/snap.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

本番のetcdは、クライアント認証まで求めるTLSでしか通信を受け付けません。そのため、証明書が3つ常に付いて回ります。パスを暗記する必要はありません。etcdはkubeadmクラスターでは静的Podとして起動するので、kubectl describe pod etcd-<노드> -n kube-systemの実行引数に正解がそのまま書かれています。このラボ環境のetcdはクライアントTLSなしで127.0.0.1:2379から平文で待ち受けているため、3つのフラグを省略します。その代わり、定期バックアップのCronJobを書くステップで、本番と同じ形をそのまま書くことになります。

取得したあとは、必ず検証します。

etcdutl snapshot status /backup/snap.db -w json
# {"hash":3106878859,"revision":12450,"totalKey":1287,"totalSize":5779456}

3つの数字はすべて意味を持っています。revisionはこのスナップショットが保持している時点、totalKeyは含まれるオブジェクトの規模、hashはファイルの整合性です。0バイトのファイルが毎日正常に積み上がっているバックアップシステムは、思ったより多くあります。検証されていないバックアップはバックアップではなく、バックアップに対する願望です。

復旧はその逆方向の作業ですが、決定的に違う点が1つあります。snapshot restoreはデータを巻き戻すコマンドではなく、新しいetcdクラスターを1つ作るコマンドです。そのため、--name、--initial-cluster、--initial-advertise-peer-urls、--initial-cluster-tokenのようなアイデンティティ用のフラグを受け取ります。そして--data-dirは必ず空の新しいパスでなければなりません。既存の/var/lib/etcdをそのまま指定すると、失敗するか、さらに悪い場合は稼働中のデータに触れてしまいます。

復旧の順序も決まっています。

順序 やること 理由
1 スナップショットの検証 使えないファイルでクラスターを立ち上げると二度死にます
2 kube-apiserverの停止 復旧中に書き込みが続いてはいけません
3 snapshot restore 新しいデータディレクトリを作成します
4 etcdマニフェストのhostPathを新しいパスに置き換える これを忘れると古いデータで再び起動します
5 apiserver起動後にkubectl getで確認 復旧できたかどうかはAPIで確認します

現場での姿

1つ目は、バックアップの間隔がそのまま最大損失量になることです。6時間ごとにバックアップすると、最悪の場合6時間分の変更が失われます。復旧目標時点(RPO)を決め、それに合わせて間隔を決めるのであって、その逆ではありません。

2つ目は、バックアップファイルを同じディスクに置いてしまうミスです。ノードのディスクが壊れる事故では、スナップショットも一緒に失われます。筆者のホームラボでも、NASへ退避する経路を別に作りました。保管ポリシーも必要です。find /backup -mtime +7 -deleteの1行がないと、数か月後にディスクがいっぱいになります。

3つ目は、復旧後の証明書と時間です。スナップショットに含まれるのはオブジェクトだけです。ノードのkubelet証明書やトークン、そしてその間に作られた実際のコンテナは、スナップショットの外の世界です。復旧直後にクラスターが落ち着かないのは正常で、しばらくするとコントローラーが宣言された状態へ収束させます。

次のラボですること

1つ目のラボでは、稼働中のetcdの健全性とメンバーを確認し、スナップショットを2回取ってrevisionが増えることを確かめ、定期バックアップのCronJobとスナップショット検証スクリプトを作ります。2つ目のラボでは、オブジェクトを実際に削除したあと、スナップショットを新しいデータディレクトリへ復旧し、そのデータで2つ目のetcdプロセスを自分で起動して、削除した値がよみがえることを目で確かめます。