本文へ移動

これはセクションの複数ページ印刷用ビューです。 .

このページの通常のビューに戻る.

内部実装

etcdコントリビューター向けのディスカバリー、ログ、Goモジュールの規約。

1 - ディスカバリーサービスのプロトコル

クラスターのブートストラップ時に他のetcdメンバーを検出する

ディスカバリーサービスのプロトコルは、共有のディスカバリートークンとエンドポイントリストを使用して、クラスターのブートストラップ時に新しいetcdメンバーが他のすべてのメンバーを検出できるようにします。

ディスカバリーサービスのプロトコルは、クラスターのブートストラップ時にのみ使用します。実行時の再構成やクラスターの監視には使用できません。

このプロトコルでは、新しいディスカバリートークンを使って一意のetcdクラスターをブートストラップします。1つのディスカバリートークンが表せるetcdクラスターは1つだけであることに注意してください。そのトークンを使ったディスカバリープロトコルが開始されたら、途中で失敗した場合でも、別のetcdクラスターのブートストラップに再利用してはなりません。

以降では、自前で運用するディスカバリークラスターに対応した例を使い、検出処理を順に説明します。

このドキュメントはv3ディスカバリーのみを対象とします。v2ディスカバリー の詳細は、以前のドキュメントを参照してください。

プロトコルの流れ

ディスカバリープロトコルは、内部のetcdクラスターを使って新しいクラスターのブートストラップを調整する仕組みです。まず、新しいメンバーすべてがディスカバリーサービスとやり取りし、想定するメンバーリストを生成します。次に、新しい各メンバーはこのリストを使ってサーバーをブートストラップします。これは-initial-clusterフラグと同じ役割を果たします。

以下の手順例では、理解しやすいようにetcdctlコマンドで各ステップを示します。http://example.com:2379でディスカバリーサービス用のetcdクラスターが稼働しているものとします。

etcdのディスカバリープロトコルでは、慣例としてキープレフィックス/_etcd/registryを使用します。

新しいディスカバリートークンの作成

新しいクラスターを識別する一意のトークンを生成します。以降の手順では、ディスカバリー用のキー空間で一意のプレフィックスとして使用します。uuidgenを使うと簡単に生成できます。

UUID=$(uuidgen)

想定するクラスターサイズの指定

ディスカバリートークンには、想定するクラスターサイズを必ず指定する必要があります。ディスカバリーサービスはこのサイズを使い、初期クラスターを構成するすべてのメンバーが見つかったかどうかを判断します。

etcdctl --endpoints=http://example.com:2379 put /_etcd/registry/${UUID}/_config/size ${cluster_size}

通常のクラスターサイズは3、5、7です。詳細は最適なクラスターサイズ を参照してください。

etcdプロセスの起動

--discovery-tokenフラグにディスカバリートークン${UUID}を設定し、--discovery-endpointsフラグにディスカバリーサービスを支えるetcdクラスターのエンドポイントを設定します。これにより、v3ディスカバリーを使ってetcdクラスターをブートストラップできます。

--discovery-tokenと--discovery-endpointsフラグを指定すると、各etcdプロセスは内部で以下の手順を実行します。

ディスカバリーサービスでクライアント証明書認証を有効にしている場合は、次のフラグを設定します。使い方は、etcdctlでetcdクラスターと通信する場合とまったく同じです。

--discovery-insecure-transport
--discovery-insecure-skip-tls-verify
--discovery-cert
--discovery-key
--discovery-cacert

ディスカバリーサービスでロールベースの認証を有効にしている場合は、次のフラグを設定します。使い方は、etcdctlでetcdクラスターと通信する場合とまったく同じです。

--discovery-user
--discovery-password

時間やタイムアウトのデフォルト値も、次のフラグで変更できます。使い方は、etcdctlでetcdクラスターと通信する場合とまったく同じです。

--discovery-dial-timeout
--discovery-request-timeout
--discovery-keepalive-time
--discovery-keepalive-timeout

自身の登録

各etcdプロセスはまず、指定した新しいクラスターに自分自身をメンバーとして登録します。これは、レジストリーの完全なキーパス内に、メンバーIDをキーとして作成することで行います。

etcdctl --endpoints=http://example.com:2379 put /_etcd/registry/${UUID}/members/${member_id} ${member_name}=${member_peer_url_1}&${member_name}=${member_peer_url_2}

状態の確認

想定するクラスターサイズと登録状況を確認し、次の操作を決定します。

etcdctl --endpoints=http://example.com:2379 get /_etcd/registry/${UUID}/_config/size
etcdctl --endpoints=http://example.com:2379 get /_etcd/registry/${UUID}/members

登録済みのメンバーがまだ足りない場合は、他のメンバーが現れるまで待機します。

登録済みメンバー数が想定サイズNを超えている場合は、最初に登録したN個のメンバーをクラスターのメンバーリストとします。自身がこのリストに含まれていれば、検出手順は成功し、メンバーリストを通じてすべてのピアを取得します。含まれていなければ、クラスターが満員であるため、検出手順は失敗して終了します。

メンバーは自身を登録する前にクラスターの状態を確認することもできます。そのため、クラスターが満員の場合はすぐに失敗できます。

全メンバーの待機

待機処理では、すべてのメンバーが見つかるまでキープレフィックス/_etcd/registry/${UUID}/membersを監視し続けます。

etcdctl --endpoints=http://example.com:2379 watch /_etcd/registry/${UUID}/members --prefix

2 - ログの規約

ログレベルの分類

etcdはzap ライブラリーを使用し、アプリケーションの出力をレベル別に分類してログに記録します。ログメッセージのレベルは、次の規約に従って決まります。

  • DebugLevelのログは通常、大量に出力されるため、本番環境では一般に無効にします。

    • 例:
      • リモートピアへの通常のメッセージの送信
      • ディスクへのログエントリーの書き込み
  • InfoLevelは、デフォルトのログ優先度です。

    • 例:
      • 起動時の構成
      • スナップショット作成の開始
      • クラスターへの新しいノードの追加
      • 認証サブシステムへの新しいユーザーの追加
  • WarnLevelのログはInfoより重要ですが、人が個別に確認する必要はありません。

    • 例:
      • リモートピアへのRaftメッセージの送信失敗
      • 設定された選出タイムアウト内でのハートビートメッセージの受信失敗
  • ErrorLevelのログは優先度が高いログです。アプリケーションが正常に動作している場合、エラーレベルのログは出力されないはずです。

    • 例:
      • WAL用のディスク領域の割り当て失敗
  • PanicLevelはメッセージをログに記録した後、パニックを発生させます。

    • 例:
      • Raftメッセージのエンコード失敗
  • FatalLevelはメッセージをログに記録した後、os.Exit(1)を呼び出します。

    • 例:
      • Raftスナップショットの保存失敗

3 - Goモジュール

etcdプロジェクトのGoモジュール構成

etcdプロジェクトはバージョン3.5以降、単一のリポジトリ に置かれた複数のGoモジュール で構成されています。

モジュールの依存関係図

次のモジュールがあります。

  • go.etcd.io/etcd/api/v3 - etcdクライアントとサーバー間の通信プロトコルを定義する API定義(protoやprotoから生成されたライブラリーなど)を含みます。

  • go.etcd.io/etcd/pkg/v3 - etcdで使用する、etcd固有ではないユーティリティーパッケージ群です。 将来独立したリポジトリに移せる可能性のあるパッケージだけをここに置きます。 多くの依存関係を持つコードの追加は避けてください。 それらは自動的にクライアントライブラリーの依存関係にもなり、 軽量に保つという方針に反するためです。

  • go.etcd.io/etcd/client/v3 - ネットワーク(grpc)経由でetcdに接続するクライアントライブラリーです。 etcdの新規利用すべてに推奨します。

  • go.etcd.io/etcd/client/v2 - HTTPプロトコル経由でetcdに接続する旧クライアントライブラリーです。 非推奨です。新規利用ではすべて/v3ライブラリーへの依存を推奨します。

  • go.etcd.io/etcd/raft/v3 - 分散コンセンサスプロトコルの実装です。 etcd固有のコードを含めないようにしてください。

  • go.etcd.io/etcd/server/v3 - etcdの実装です。 このパッケージのコードはetcdの内部実装であり、外部プロジェクトからの利用は推奨しません。 パッケージ構成やAPIは同じマイナーバージョン内でも変更される場合があります。

  • go.etcd.io/etcd/etcdctl/v3 - etcdへのアクセスと管理を行うコマンドラインツールです。

  • go.etcd.io/etcd/tests/v3 - etcdのすべての結合テストを含むモジュールです。 注意:単体テスト(高速でモジュール間の依存関係を必要としないもの)はすべて、 テスト対象のコードと同じモジュールに置くことを推奨します。

  • go.etcd.io/bbolt - 永続的なB木の実装です。 独立したリポジトリ https://github.com/etcd-io/bbolt に置かれています。

運用

  1. etcdのすべてのモジュールは、同じバージョンでリリースすることを推奨します。たとえば、 go.etcd.io/etcd/client/v3@v3.5.10はgo.etcd.io/etcd/api/v3@v3.5.10に依存しなければなりません。

    バージョンを一貫して更新するには、次のコマンドを使用できます。

    % DRY_RUN=false TARGET_VERSION="v3.5.10" ./scripts/release_mod.sh update_versions
  2. リリースするモジュールは https://golang.org/ref/mod#vcs-version の規則に従ってタグ付けすることを推奨します。 つまり、モジュールごとに固有のタグを付けます。 タグ付けには次のコマンドを使用できます。

    % DRY_RUN=false REMOTE_REPO="origin" ./scripts/release_mod.sh push_mod_tags
  3. etcdのすべてのモジュールは、共通の依存先に対して同じバージョンに依存することを推奨します。 次のコマンドで確認できます。

    % PASSES="dep" ./test.sh
  4. go.modファイルには未使用の依存関係を含めてはならず、 go mod tidyの形式に従わなければなりません。 次のコマンドで確認します。

    % PASSES="mod_tidy" ./test.sh
  5. 全ファイルの自動整形など、すべてのモジュールに対する操作を行うには、 次のスクリプトを使用または拡張してください。

    % ./scripts/fix.sh

今後の方向性

長期的な目標として、etcdのモジュール構成を次のモデルに近づけたいと考えています。

将来のモジュール依存関係図

このモデルは次を前提とします。

  • etcdmigrate/etcdadmをetcdctlバイナリーから分離します。 これにより、etcdctlはネットワーククライアントAPIを扱う コマンドラインのラッパーとして明確に位置付けられ、 etcdmigrate/etcdadmはetcdのストレージファイルに対する 直接的な物理操作を担うようになります。
  • etcd-proxyを./etcdバイナリーから分離します。実験的なコードが多く、 追加のリスクと依存関係を伴うためです。
  • v2プロトコルのサポートを非推奨にします。