fbpx

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ノードずつ再構築する必要があることです。

  1. すべてのホストで etcd を停止
  2. すべてのホストの /var/lib/etcd/member をパージします。
  3. 最初の etcd ホストの /var/lib/etcd/member にバックアップをコピーします。
  4. force-new-cluster で最初の etcd ホストで etcd を起動します。
  5. 最初の etcd ホストの正しい PeerURL を 127.0.0.0.1 ではなく、ノードのIPに設定します。
  6. 次のホストをクラスタに追加します。
  7. 既存の etcd ホストに -initial-cluster を設定して、次のホストで etcd を起動します。
  8. etcd ノードがすべて結合されるまで、5 と 6 を繰り返します。
  9. 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 に関して考えておくべき問題点をご紹介しました。

この他に何か知っているヒントがあれば、コメントで教えください!

新規CTA