Kubernetes での etcd について知りたいと思っていたことすべて(聞けなかったこと) #mirantis #docker #kubernetes #k8s
この記事は1年以上前に投稿されました。情報が古い可能性がありますので、ご注意ください。
本ブログは Mirantis社のblog記事「Everything you ever wanted to know about using etcd with Kubernetes v1.6 (but were afraid to ask)」の翻訳記事です。
クラウドネイティブアプリケーションの世界で、etcd は Kubernetes コントロールプレーンの唯一のステートフルなコンポーネントです。これは管理者にとっての問題をよりシンプルにしますが、Kubernetes 1.6のリリースでは、99.99%の信頼性を維持するためのプロセスが変更されています。しかし、恐れないでください。この記事であなたの疑問を解消できるはずです。
etcdのデータストアフォーマットはv2かv3か?
etcd バージョン 3.0.0.0 以降では、v2 と v3 の 2 つの異なるデータストアをサポートしていますが、情報をバックアップする能力に影響するため、使用しているバージョンを知ることが重要となります。
Kubernetes 1.5 では、デフォルトのデータストアのフォーマットは v2 でしたが、明示的に設定すれば v3 でも利用可能でした。一方、Kubernetes v1.6 では、デフォルトのデータストアのフォーマットは etcd v3 となっていますが、利用する様々なコンポーネントを考慮して、どちらのフォーマットが最適か考える必要があります。
例えば、Calico、Canal、Flannel は etcd v2 のデータストアにしかデータを書き込めません。これらのetcd データを Kubernetes の etcd データストアに単純に組み合わせると、Kubernetes が v3 を使っている場合には、etcd のメンテナンスが複雑になります。
Kubernetes v1.5 から v1.6 に盲目的にアップグレードしているユーザーは驚くかもしれません。(リリースノートを常に読むことが重要です!) Kubernetes v1.6 はデフォルトの etcd バックエンドを v2 から v3 に変更しているので、起動する前に手動で etcd を v3 に移行するようにしてください。 そうすることでデータの一貫性を確保することができますが、そのためにはすべての kube-apiservers をシャットダウンする必要があります。
まだ移行したくない場合は、以下のオプションで kube-apiserver を v2 etcd に戻すことができます。
--storage-backend=etcd2
etcd のバックアップ
Kubernetes のすべての設定は etcd の中に保存されているので、復旧不可能な災害が発生した場合、etcd のバックアップを使用してすべてのデータを復旧できます。etcd は自身で定期的にスナップショットを作成しますが、日次バックアップを別のホストに保存する事は、Kubernetes のディザスタリカバリという意味では良い戦略です。
バックアップ方法
etcd には v2 と v3 の異なるバックアップ方法があり、それぞれに長所と短所があります。 v3 バックアップはよりクリーンで、単一のコンパクトなファイルで構成されていますが、1つの大きな欠点があります。それは、 v2 データのバックアップやリカバリに対応していないことです。
つまり、etcd の v3 データしか持っていない場合(例えば、ネットワークプラグインが etcd を使用しない場合)は v3 バックアップを使用できますが、v2 データがある場合(たとえば v3 データと混在している場合など)は、v2 バックアップの方法を使用しなければなりません。
それぞれの方法を見ていきましょう。
Etcd v2 のバックアップ
etcd v2 のバックアップ方法は、1つの WAL ファイルでディレクトリ構造を作成します。etcd クラスタの操作を中断することなく、オンラインでバックアップを実行できます。etcd v2 + v3 データストアをバックアップするには、次のコマンドを使用します。
etcdctl backup --data-dir /var/lib/etcd/ --backup-dir /backupdir
etcd v2 のリストアの公式手順はこちらにありますが、基本的な手順の概要をご紹介します。難しいのは、クラスタを1ノードずつ再構築する必要があることです。
- すべてのホストで etcd を停止
- すべてのホストの /var/lib/etcd/member をパージします。
- 最初の etcd ホストの /var/lib/etcd/member にバックアップをコピーします。
- force-new-cluster で最初の etcd ホストで etcd を起動します。
- 最初の etcd ホストの正しい PeerURL を 127.0.0.0.1 ではなく、ノードのIPに設定します。
- 次のホストをクラスタに追加します。
- 既存の etcd ホストに -initial-cluster を設定して、次のホストで etcd を起動します。
- etcd ノードがすべて結合されるまで、5 と 6 を繰り返します。
- etcd を正常に再起動する(既存の設定を使用する)
これらの手順は、以下のスクリプトで確認できます。
#!/bin/bash -e # Change as necessary RESTORE_PATH=${RESTORE_PATH:-/tmp/member} #Extract node data from etcd config source /etc/etcd.env || source /etc/default/etcd function with_retries { local retries=3 set -o pipefail for try in $(seq 1 $retries); do ${@} [ $? -eq 0 ] && break if [[ "$try" == "$retries" ]]; then exit 1 fi sleep 3 done set +o pipefail } this_node=$ETCD_NAME node_names=($(echo $ETCD_INITIAL_CLUSTER | \ awk -F'[=,]' '{for (i=1;i<=NF;i+=2) { print $i }}')) node_endpoints=($(echo $ETCD_INITIAL_CLUSTER | \ awk -F'[=,]' '{for (i=2;i<=NF;i+=2) { print $i }}')) node_ips=($(echo $ETCD_INITIAL_CLUSTER | \ awk -F'://|:[0-9]' '{for (i=2;i<=NF;i+=2) { print $i }}')) num_nodes=${#node_names[@]} # Stop and purge etcd data for i in `seq 0 $((num_nodes - 1))`; do ssh ${node_ips[$i]} sudo service etcd stop ssh ${node_ips[$i]} sudo docker rm -f ${node_names[$i]} \ || : # Kargo specific ssh ${node_ips[$i]} sudo rm -rf /var/lib/etcd/member done # Restore on first node if [[ "$this_node" == ${node_names[0]} ]]; then sudo cp -R $RESTORE_PATH /var/lib/etcd/ else rsync -vaz -e "ssh" --rsync-path="sudo rsync" \ "$RESTORE_PATH" ${node_ips[0]}:/var/lib/etcd/ fi ssh ${node_ips[0]} "sudo etcd --force-new-cluster 2> \ /tmp/etcd-restore.log" & echo "Sleeping 5s to wait for etcd up" sleep 5 # Fix member endpoint on first node member_id=$(with_retries ssh ${node_ips[0]} \ ETCDCTL_ENDPOINTS=https://localhost:2379 \ etcdctl member list | cut -d':' -f1) ssh ${node_ips[0]} ETCDCTL_ENDPOINTS=https://localhost:2379 \ etcdctl member update $member_id ${node_endpoints[0]} echo "Waiting for etcd to reconfigure peer URL" sleep 4 # Add other nodes initial_cluster="${node_names[0]}=${node_endpoints[0]}" for i in `seq 1 $((num_nodes -1))`; do echo "Adding node ${node_names[$i]} to ETCD cluster..." initial_cluster=\ "$initial_cluster,${node_names[$i]}=${node_endpoints[$i]}" with_retries ssh ${node_ips[0]} \ ETCDCTL_ENDPOINTS=https://localhost:2379 \ etcdctl member add ${node_names[$i]} ${node_endpoints[$i]} ssh ${node_ips[$i]} \ "sudo etcd --initial-cluster="$initial_cluster" &>/dev/null" & sleep 5 with_retries ssh ${node_ips[0]} \ ETCDCTL_ENDPOINTS=https://localhost:2379 etcdctl member list done echo "Restarting etcd on all nodes" for i in `seq 0 $((num_nodes -1))`; do ssh ${node_ips[$i]} sudo service etcd restart done sleep 5 echo "Verifying cluster health" with_retries ssh ${node_ips[0]} \ ETCDCTL_ENDPOINTS=https://localhost:2379 etcdctl cluster-health
Etcd v3 のバックアップ
etcd v3 バックアップは、単一の圧縮ファイルを作成します。v2 バックアップでは v3 データもバックアップされますが、v3 バックアップでは etcd v2 データをバックアップすることはできないのでご注意ください。
v3バックアップを作成するには、次のコマンドを実行します。
ETCDCTL_API=3 etcdctl snapshot save /backupdir
etcd v3 リストアの公式手順はこちらに記載されていますが、プロセスは v2 のときよりもはるかにシンプルになっています。
必要な手順は以下の通りです。
- すべてのホストで etcd を停止します。
- すべてのホストの /var/lib/etcd/member をパージします。
- バックアップファイルを各 etcd ホストにコピーします。
- 各ホストのソース /etc/default/etcd を実行し、以下のコマンドを実行します。
ETCDCTL_API=3 etcdctl snapshot restore BACKUP_FILE \ --name $ETCD_NAME--initial-cluster "$ETCD_INITIAL_CLUSTER" \ --initial-cluster-token “$ETCD_INITIAL_CLUSTER_TOKEN” \ --initial-advertise-peer-urls $ETCD_INITIAL_ADVERTISE_PEER_URLS \ --data-dir $ETCD_DATA_DIR
etcd のチューニング
etcd は Kubernetes の設定情報の保存に使用されるため、そのパフォーマンスはクラスタにとって非常に重要です。幸いなことに、etcd は様々なデプロイメント条件の下でより良く動作するようにチューニングすることができます。すべての書き込み操作は、すべての etcd ノード間の同期を必要とするため、次のような機能要件が必要になります。
- etcd はディスクへの高速アクセスを必要とします。
- etcd は他の etcd ノードとの間が低レイテンシでなければならないため、高速なネットワーキングを必要とします。
- etcd は、ディスクにデータを書き込む前に、すべての etcd ノード間でデータを同期させる必要があります。
そのため、以下のような推奨事項が考えられます。
- etcd ストアは、( Ceph のような)ディスク集約型サービスと同じディスクに配置すべきではありません。
- データセンターやパブリッククラウドの場合は、etcd ノードをアベイラビリティゾーンに分散させてはいけません。
- etcd ノードの数は 3 であるべきです。" Split Brain " 問題を防ぐためには奇数が必要ですが、3 を超えるとパフォーマンスの低下を招く可能性があります。
デフォルトの etcd 設定は、一般的にテスト環境で見られる低ディスク I/O のシナリオでは理想的な設定ではありません。そのため、以下の値を設定します。
ETCD_ELECTION_TIMEOUT=5000 #default 1000ms ETCD_HEARTBEAT_INTERVAL=250 #default 100ms
これらの設定値を高くすると、read / write のパフォーマンスに影響を与えることに注意しましょう。また、 システム障害が起きている ことが発覚するまでにより多くの時間が必要なため、クラスタが 選挙 を実行するための時間的な ペナルティ も発生します。しかし、これらの値が低すぎると、ネットワークやディスクのレイテンシが悪い場合、クラスタは問題があると判断し、頻繁に 再選挙 を実行するようになります。
etcd のトラブルシューティング
ここでは、etcdで遭遇したいくつかの問題と、それらを解決するために考え出した解決策を紹介します。
問題
リストアに失敗し、etcdログに「etcdmain: database file (/var/lib/etcd/member/snap/db) of the backend is missing」と表示されます。
回答
etcd がスナップショット ファイルを書き込んでいる間に etcd v2 のバックアップが行われました。このバックアップファイルは使用できません。唯一の解決策は、別のバックアップファイルからリストアすることです。
問題
etcd が 2379 番ポートをリッスンしないのはなぜですか?
回答
いくつかの原因が考えられます。まず、etcd サービスが実行されていることを確認してください。次に、各ホストの etcd サービスのログをチェックして、election や quorum に問題がないかどうかを確認してください。データを読み書きするためには、クラスタの51%以上がオンラインになっていなければなりません - 実際の計算式は N / 2 + 1 です。これは、split brain の問題を防ぐためです。つまり、3ノードクラスタは、少なくとも2つのノードが稼働している必要があります。
問題
etcdはなぜ elections が多発するのか?
回答
ETCD_ELECTION_TIMEOUT と ETCD_HEARTBEAT_INTERVAL を上げてみてください。また、ホストの負荷を減らしてみてください。詳細はこちらをご覧ください。
Your turn
ここまで、etcd についての私たちの見解と、Kubernetes に関して考えておくべき問題点をご紹介しました。
この他に何か知っているヒントがあれば、コメントで教えください!