etcdのスナップショットを取り検証する
目標
稼働中のetcdの状態を確認し、スナップショットを取り、そのスナップショットが実際に使えるものかどうかを機械的に検証する手順を、手に覚えさせます。
なぜ重要なのか
バックアップは、作ることよりも信頼できるものにすることのほうが難しいものです。毎日動いているCronJobが毎日0バイトのファイルを残していても、アラートが1つも鳴らないシステムは、現実には非常に多くあります。そこでこのラボでは、ファイルを作るステップと、そのファイルを判定するステップを分けています。snapshot statusが返すrevision、totalKey、hashの3つの値は、それぞれ「いつの時点か」「どれだけ含まれているか」「壊れていないか」への答えです。この3つをスクリプトが読み取ってOKを出すようにすれば、バックアップの成否が人の目ではなく終了コードで表されます。もう1つ、クォーラムは障害を防いでくれても誤削除は防げないという点を、メンバー数の計算とあわせて心に刻んでおいてください。メンバーが3つでも5つでも、誤った削除は律儀に全メンバーに複製されます。
ステップ
etcdctl --endpoints=127.0.0.1:2379 endpoint healthの出力を/root/etcd/backup/out/health.txtに保存してください。ファイルの中にhealthyとエンドポイントのポート2379が見えていて、unhealthyという単語が含まれていてはいけません。etcdctl member listの出力を/root/etcd/backup/out/members.txtに保存してください(16進数のメンバーIDと2379/2380のアドレスが含まれている必要があります)。続けて/root/etcd/backup/out/quorum.txtにmembers=<멤버 수>とquorum=<과반 수>の2行を書いてください。このラボのetcdは単一メンバーなので、members=1、quorum=1になります。- スナップショットを
/root/etcd/backup/snap-01.dbとして保存してください。ファイルサイズは20000バイト以上である必要があります。 snapshot statusをJSON形式で実行し、結果を/root/etcd/backup/out/snap-01.jsonに保存してください。revisionが0より大きく、totalKeyが20より大きく、hashが0でない必要があります。/registryプレフィックスの下にあるキーの数を数え、数字だけを/root/etcd/backup/out/key-count.txtに入れてください(20以上である必要があります)。そして、リソース種別ごとのキー数の上位一覧を/root/etcd/backup/out/top-prefixes.txtに保存してください。このファイルにはpods、configmaps、secrets、leases、events、namespacesのうち1つ以上の名前が含まれている必要があります。- ネームスペース
etcd-labを作成し、その中にConfigMapetcd-lab-markerをdata.owner=platformで作成してください。そのあとで2つ目のスナップショットを/root/etcd/backup/snap-02.dbとして取り、検証結果を/root/etcd/backup/out/snap-02.jsonに保存してください。2つ目のJSONのrevisionが1つ目より大きい必要があります。 /root/etcd/backup/cronjob.yamlに定期バックアップの仕様を書いてください。kind: CronJob、metadata.namespace: kube-system、spec.schedule: "0 */6 * * *"です。コンテナのcommandはリスト形式で、その中にsnapshot saveと--endpoints、--cacert、--cert、--keyの4つのフラグ、さらにfindと-mtime +7を使った削除コマンドがすべて含まれている必要があります。Podの仕様には、hostPathが/etc/kubernetes/pki/etcdであるボリューム、キーにcontrol-planeが含まれるnodeSelector、そしてtolerationsが必要です。ボリュームはすべてhostPath形式で書いてください。/root/etcd/backup/verify.shを実行権限付きのスクリプトとして作成してください。引数で受け取ったスナップショットが正常なら、1行目にOK <revision> <keys>の形式(空白区切り、数字2つ)で出力し、そうでなければOKで始まらない行を出力する必要があります。このスクリプトでsnap-01.dbの結果を/root/etcd/backup/out/verify-01.txtに、snap-02.dbの結果を/root/etcd/backup/out/verify-02.txtに保存してください。最後に、わざと壊したファイルを1つ作って検証した結果を/root/etcd/backup/out/verify-bad.txtに残してください。
参考
- このラボのetcdはクライアントTLSがないため、
--cacert/--cert/--keyなしで接続できます。本番ではこの3つのフラグが必須で、パスはkubectl describe pod etcd-<노드> -n kube-systemの実行引数にそのまま書かれています。ステップ7でその形を練習します。 snapshot statusは最近etcdutlに移されました。etcdutl snapshot status <파일> -w jsonが推奨の形で、etcdctlでも警告付きで動作します。警告は標準エラー出力に出るので、> 파일のリダイレクトには混ざりません。- キーを数えるとき、
--keys-onlyの出力には空行が混ざります。/registryで始まる行だけを数えると正確です。リソース種別は/registry/<종류>/...の2番目の要素なので、cut -d/ -f3で取り出してsort | uniq -c | sort -rnにかければ求められます。 - 壊れたファイルは、短いゴミファイルを1つ作れば十分です。本物のスナップショットを上書きしないでください。
- よくある間違い1: ステップ6でConfigMapを作る前に2つ目のスナップショットを取ってしまうことです。そうするとrevisionが増えません。
- よくある間違い2: ステップ8のスクリプトが失敗の経路でも
OKで始まる行を出力してしまうことです。そのような検証スクリプトは、検証をしていないのと同じです。 - ラボのPodはラボごとに新しく起動するので、前のラボで作ったクラスターの状態は残っていません。必要なオブジェクトはこのラボの中で自分で作成してください。運用手順を記憶ではなくランブックとマニフェストとして残すべき理由が、まさにここにあります。
バックアップ前にetcdの健全性を確認する
etcdctl --endpoints=127.0.0.1:2379 endpoint healthの出力を/root/etcd/backup/out/health.txtに保存してください。ファイルの中にhealthyとエンドポイントのポート2379が見えていて、unhealthyという単語が含まれていてはいけません。
不健全なetcdをバックアップすると、不健全なスナップショットができます。etcdctlのendpoint系のサブコマンドのうち健全性を見るものを使い、出力をファイルに残してください。このラボはクライアントTLSがないので、証明書のフラグは不要です。
メンバー一覧を確認してクォーラムを計算する
etcdctl member listの出力を/root/etcd/backup/out/members.txtに保存してください(16進数のメンバーIDと2379/2380のアドレスが含まれている必要があります)。続けて/root/etcd/backup/out/quorum.txtにmembers=<멤버 수>とquorum=<과반 수>の2行を書いてください。このラボのetcdは単一メンバーなので、members=1、quorum=1になります。
member listで実際のメンバーを確認します。クォーラムは過半数です。メンバーN個の過半数は、Nを2で割った商に1を足した値であることを思い出してください。
最初のスナップショットを取る
スナップショットを/root/etcd/backup/snap-01.dbとして保存してください。ファイルサイズは20000バイト以上である必要があります。
snapshot saveの後ろに、保存するファイルのパスを付けます。結果のファイルサイズが数十KB以上になっているか確認してください。0バイトのバックアップはよくある事故です。
スナップショットをJSONで検証する
snapshot statusをJSON形式で実行し、結果を/root/etcd/backup/out/snap-01.jsonに保存してください。revisionが0より大きく、totalKeyが20より大きく、hashが0でない必要があります。
取得できたことと、使えることは別です。snapshot statusに-w jsonを付けると、hash、revision、totalKeyがJSON1行で出力されます。警告メッセージは標準エラー出力に出るので、リダイレクトには混ざりません。
etcdの中に何がどれだけあるかを数える
/registryプレフィックスの下にあるキーの数を数え、数字だけを/root/etcd/backup/out/key-count.txtに入れてください(20以上である必要があります)。そして、リソース種別ごとのキー数の上位一覧を/root/etcd/backup/out/top-prefixes.txtに保存してください。このファイルにはpods、configmaps、secrets、leases、events、namespacesのうち1つ以上の名前が含まれている必要があります。
Kubernetesのオブジェクトはすべて/registryプレフィックスの下にあります。getにプレフィックス検索のオプションとキーのみを出力するオプションを付けます。リソース種別はキーパスの3番目の要素です。
変更を加えて2つ目のスナップショットを取る
ネームスペースetcd-labを作成し、その中にConfigMapetcd-lab-markerをdata.owner=platformで作成してください。そのあとで2つ目のスナップショットを/root/etcd/backup/snap-02.dbとして取り、検証結果を/root/etcd/backup/out/snap-02.jsonに保存してください。2つ目のJSONのrevisionが1つ目より大きい必要があります。
オブジェクトを1つ作るとrevisionが上がります。2つのスナップショットのrevisionを比べると、バックアップ時点の意味が実感できます。作成したあとで取るという順序が核心です。
定期バックアップのCronJobの仕様を書く
/root/etcd/backup/cronjob.yamlに定期バックアップの仕様を書いてください。kind: CronJob、metadata.namespace: kube-system、spec.schedule: "0 */6 * * *"です。コンテナのcommandはリスト形式で、その中にsnapshot saveと--endpoints、--cacert、--cert、--keyの4つのフラグ、さらにfindと-mtime +7を使った削除コマンドがすべて含まれている必要があります。Podの仕様には、hostPathが/etc/kubernetes/pki/etcdであるボリューム、キーにcontrol-planeが含まれるnodeSelector、そしてtolerationsが必要です。ボリュームはすべてhostPath形式で書いてください。
etcdはコントロールプレーンにしかなく、そこにはテイントが設定されています。証明書はホストのパスからマウントし、古いファイルを削除する保管ポリシーもコマンドに入れてください。
スナップショット検証スクリプトを作る
/root/etcd/backup/verify.shを実行権限付きのスクリプトとして作成してください。引数で受け取ったスナップショットが正常なら、1行目にOK <revision> <keys>の形式(空白区切り、数字2つ)で出力し、そうでなければOKで始まらない行を出力する必要があります。このスクリプトでsnap-01.dbの結果を/root/etcd/backup/out/verify-01.txtに、snap-02.dbの結果を/root/etcd/backup/out/verify-02.txtに保存してください。最後に、わざと壊したファイルを1つ作って検証した結果を/root/etcd/backup/out/verify-bad.txtに残してください。
正常なファイルには決まった形式でOKを出し、壊れたファイルにはOKを出してはいけません。検証スクリプトが常に成功するなら、何も検証していないのと同じです。