著者は、ウィキメディア財団を選択して、 Write forDOnationsプログラムの一環として寄付を受け取りました。

序章

etcd は、 Kubernetes Vulcand Doorman など、多くのプラットフォームやツールで信頼されている分散型Key-Valueストアです。 Kubernetes内では、etcdは、クラスターの状態を格納するグローバル構成ストアとして使用されます。 etcdの管理方法を知ることは、Kubernetesクラスターを管理するために不可欠です。 Kubernetes-as-a-Service とも呼ばれる、この管理上の負担を取り除く多くのマネージドKubernetesオファリングがありますが、多くの企業は、オンプレミスでセルフマネージドKubernetesクラスターを実行することを選択しています。それがもたらす柔軟性。

この記事の前半では、Ubuntu18.04サーバーで3ノードのetcdクラスターをセットアップする方法について説明します。 後半では、トランスポート層セキュリティ(TLS )を使用したクラスターの保護に焦点を当てます。 各セットアップを自動化された方法で実行するために、全体でAnsibleを使用します。 Ansibleは、 Puppet Chef 、およびSaltStackに類似した構成管理ツールです。 これにより、 playbooks と呼ばれるファイル内で、宣言的な方法で各セットアップステップを定義できます。

このチュートリアルを終了すると、サーバー上で安全な3ノードのetcdクラスターが実行されます。 また、新しいサーバーセットで同じセットアップを繰り返し一貫して再作成できるAnsibleプレイブックも用意されています。

前提条件

このガイドを開始する前に、次のものが必要です。

警告:この記事の目的はプライベートネットワークでのetcdクラスターのセットアップの概要を提供することであるため、このセットアップの3つのUbuntu 18.04サーバーはファイアウォールでテストされておらず、[ X235X]rootユーザー。 実稼働環境では、パブリックインターネットに公開されているノードでは、セキュリティのベストプラクティスを順守するためにファイアウォールとsudoユーザーが必要になります。 詳細については、 Ubuntu18.04を使用したサーバーの初期設定チュートリアルをご覧ください。

  • ローカルマシンがetcd1 etcd2 、およびetcd3サーバーにアクセスできるようにするSSHキーペア。 SSHとは何かわからない場合、またはSSHキーのペアがない場合は、 SSH Essentials:Working with SSH Servers、Clients、andKeysを読んで知ることができます。

  • Ansibleがローカルマシンにインストールされています。 たとえば、Ubuntu 18.04を実行している場合は、 Ubuntu18.04にAnsibleをインストールして構成する方法の記事のステップ1に従ってAnsibleをインストールできます。 これにより、ansibleおよびansible-playbookコマンドがマシンで使用できるようになります。 このAnsibleの使用方法:リファレンスガイドを手元に置いておくこともできます。 このチュートリアルのコマンドは、Ansiblev2.xで動作するはずです。 Pythonv3.8.2を実行しているAnsiblev2.9.7でテストしました。

ステップ1—コントロールノードのAnsibleを設定する

Ansibleは、サーバーの管理に使用されるツールです。 Ansibleが管理しているサーバーは管理対象ノードと呼ばれ、Ansibleを実行しているマシンは制御ノードと呼ばれます。 Ansibleは、コントロールノードのSSHキーを使用して、管理対象ノードにアクセスすることで機能します。 SSHセッションが確立されると、Ansibleは一連のスクリプトを実行して、管理対象ノードをプロビジョニングおよび構成します。 このステップでは、Ansibleを使用して管理対象ノードに接続し、hostnameコマンドを実行できることをテストします。

システム管理者の一般的な日には、さまざまなノードのセットの管理が含まれる場合があります。 たとえば、Ansibleを使用していくつかの新しいサーバーをプロビジョニングし、後でそれを使用して別のサーバーのセットを再構成することができます。 管理者が管理対象ノードのセットをより適切に整理できるようにするために、Ansibleはホストインベントリ(または略してインベントリ)の概念を提供します。 インベントリファイル内でAnsibleを使用して管理するすべてのノードを定義し、それらをグループに編成できます。 次に、ansibleおよびansible-playbookコマンドを実行するときに、コマンドを適用するホストまたはグループを指定できます。

デフォルトでは、Ansibleは/etc/ansible/hostsからインベントリファイルを読み取ります。 ただし、--inventoryフラグ(または略して-i)を使用して、別のインベントリファイルを指定できます。

開始するには、ローカルマシン(コントロールノード)に新しいディレクトリを作成して、このチュートリアルのすべてのファイルを格納します。

  1. mkdir -p $HOME/playground/etcd-ansible

次に、作成したディレクトリに次のように入力します。

  1. cd $HOME/playground/etcd-ansible

ディレクトリ内で、エディタを使用してhostsという名前の空のインベントリファイルを作成して開きます。

  1. nano $HOME/playground/etcd-ansible/hosts

hostsファイル内で、各管理対象ノードを次の形式でリストし、強調表示されたパブリックIPアドレスをサーバーの実際のパブリックIPアドレスに置き換えます。

〜/ playground / etcd-ansible / hosts
[etcd]
etcd1 ansible_host=etcd1_public_ip  ansible_user=root
etcd2 ansible_host=etcd2_public_ip  ansible_user=root
etcd3 ansible_host=etcd3_public_ip  ansible_user=root

[etcd]行は、etcdというグループを定義します。 グループ定義の下に、すべての管理対象ノードが一覧表示されます。 各行はエイリアス(etcd1など)で始まります。これにより、長いIPアドレスの代わりに覚えやすい名前を使用して各ホストを参照できます。 ansible_hostansible_userはAnsible変数です。 この場合、SSH経由で接続するときに使用するパブリックIPアドレスとSSHユーザー名をAnsibleに提供するために使用されます。

Ansibleが管理対象ノードに接続できることを確認するために、Ansibleを使用してetcdグループ内の各ホストでhostnameコマンドを実行して接続をテストできます。

  1. ansible etcd -i hosts -m command -a hostname

このコマンドを分解して、各部分の意味を学びましょう。

  • etcd:このコマンドで管理されているインベントリのホストを判別するために使用するホストパターンを指定します。 ここでは、ホストパターンとしてグループ名を使用しています。
  • -i hosts:使用するインベントリファイルを指定します。
  • -m command:Ansibleの背後にある機能は、モジュールによって提供されます。 commandモジュールは、渡された引数を受け取り、それを各管理対象ノードでコマンドとして実行します。 このチュートリアルでは、進行するにつれて、さらにいくつかのAnsibleモジュールを紹介します。
  • -a hostname:モジュールに渡す引数。 引数の数と種類はモジュールによって異なります。

コマンドを実行すると、次の出力が表示されます。これは、Ansibleが正しく構成されていることを意味します。

Output
etcd2 | CHANGED | rc=0 >> etcd2 etcd3 | CHANGED | rc=0 >> etcd3 etcd1 | CHANGED | rc=0 >> etcd1

Ansibleが実行する各コマンドは、タスクと呼ばれます。 コマンドラインでansibleを使用してタスクを実行することを、ad-hocコマンドの実行と呼びます。 アドホックコマンドの利点は、それらが迅速で、セットアップがほとんど必要ないことです。 欠点は、手動で実行されるため、Gitのようなバージョン管理システムにコミットできないことです。

わずかな改善は、シェルスクリプトを記述し、Ansibleのスクリプトモジュールを使用してコマンドを実行することです。 これにより、バージョン管理で行った構成手順を記録できます。 ただし、シェルスクリプトは命令型です。つまり、システムを目的の状態に構成するために実行するコマンド(「方法」)を把握する必要があります。 一方、Ansibleは、宣言型アプローチを提唱しています。このアプローチでは、サーバーの望ましい状態を構成ファイル内に「何」と定義し、Ansibleはサーバーをその望ましい状態にする責任があります。

構成ファイルの意図がすぐに伝えられるため、宣言型のアプローチが推奨されます。つまり、理解と保守が容易になります。 また、管理者ではなくAnsibleにエッジケースを処理する責任があり、多くの作業を節約できます。

管理対象ノードと通信するようにAnsibleコントロールノードを構成したので、次のステップでは、宣言型の方法でタスクを指定できるAnsible playbooksを紹介します。

ステップ2—AnsiblePlaybookを使用して管理対象ノードのホスト名を取得する

このステップでは、ステップ1で行ったことを複製し、管理対象ノードのホスト名を出力しますが、アドホックタスクを実行する代わりに、各タスクを宣言的にAnsibleプレイブックとして定義して実行します。 このステップの目的は、Ansibleプレイブックがどのように機能するかを示すことです。 後のステップで、プレイブックを使用してはるかに実質的なタスクを実行します。

プロジェクトディレクトリ内に、エディタを使用してplaybook.yamlという名前の新しいファイルを作成します。

  1. nano $HOME/playground/etcd-ansible/playbook.yaml

playbook.yaml内に、次の行を追加します。

〜/ playground / etcd-ansible / playbook.yaml
- hosts: etcd
  tasks:
    - name: "Retrieve hostname"
      command: hostname
      register: output
    - name: "Print hostname"
      debug: var=output.stdout_lines

CTRL+Xに続いてYを押して、playbook.yamlファイルを閉じて保存します。

プレイブックには、プレイのリストが含まれています。 各プレイには、hostsキーで指定されたホストパターンに一致するすべてのホストで実行する必要があるタスクのリストが含まれています。 このプレイブックには、2つのタスクを含む1つのプレイがあります。 最初のタスクは、commandモジュールを使用してhostnameコマンドを実行し、出力をoutputという名前の変数に登録します。 2番目のタスクでは、 debug モジュールを使用して、output変数のstdout_linesプロパティを出力します。

これで、ansible-playbookコマンドを使用してこのプレイブックを実行できます。

  1. ansible-playbook -i hosts playbook.yaml

次の出力が表示されます。これは、プレイブックが正しく機能していることを意味します。

Output
PLAY [etcd] *********************************************************************************************************************** TASK [Gathering Facts] ************************************************************************************************************ ok: [etcd2] ok: [etcd3] ok: [etcd1] TASK [Retrieve hostname] ********************************************************************************************************** changed: [etcd2] changed: [etcd3] changed: [etcd1] TASK [Print hostname] ************************************************************************************************************* ok: [etcd1] => { "output.stdout_lines": [ "etcd1" ] } ok: [etcd2] => { "output.stdout_lines": [ "etcd2" ] } ok: [etcd3] => { "output.stdout_lines": [ "etcd3" ] } PLAY RECAP ************************************************************************************************************************ etcd1 : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 etcd2 : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 etcd3 : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

ノート: ansible-playbook sometimes uses cowsay as a playful way to print the headings. If you find a lot of ASCII-art cows printed on your terminal, now you know why. To disable this feature, set the ANSIBLE_NOCOWS environment variable to 1 prior to running ansible-playbook by running export ANSIBLE_NOCOWS=1 in your shell.

このステップでは、命令型のアドホックタスクの実行から宣言型のプレイブックの実行に移行しました。 次のステップでは、これら2つのデモタスクを、etcdクラスターをセットアップするタスクに置き換えます。

ステップ3—管理対象ノードにetcdをインストールする

このステップでは、etcdを手動でインストールするコマンドを示し、これらの同じコマンドをAnsibleプレイブック内のタスクに変換する方法を示します。

etcdとそのクライアントetcdctlはバイナリとして利用でき、ダウンロード、抽出、およびPATH環境変数の一部であるディレクトリに移動します。 手動で構成する場合、これらは各管理対象ノードで実行する手順です。

  1. mkdir -p /opt/etcd/bin
  2. cd /opt/etcd/bin
  3. wget -qO- https://storage.googleapis.com/etcd/v3.3.13/etcd-v3.3.13-linux-amd64.tar.gz | tar --extract --gzip --strip-components=1
  4. echo 'export PATH="$PATH:/opt/etcd/bin"' >> ~/.profile
  5. echo 'export ETCDCTL_API=3" >> ~/.profile

最初の4つのコマンドは、バイナリをダウンロードして/opt/etcd/bin/ディレクトリに抽出します。 デフォルトでは、etcdctlクライアントはAPIv2を使用してetcdサーバーと通信します。 etcd v3.xを実行しているため、最後のコマンドはETCDCTL_API環境変数を3に設定します。

注:ここでは、AMD64命令セットを使用するプロセッサを搭載したマシン用に構築されたetcdv3.3.13を使用しています。 他のシステムや他のバージョンのバイナリは、GitHubの公式リリースページにあります。

同じ手順を標準化された形式で複製するために、プレイブックにタスクを追加できます。 エディターでplaybook.yamlプレイブックファイルを開きます。

  1. nano $HOME/playground/etcd-ansible/playbook.yaml

playbook.yamlファイル全体を次の内容に置き換えます。

〜/ playground / etcd-ansible / playbook.yaml
- hosts: etcd
  become: True
  tasks:
    - name: "Create directory for etcd binaries"
      file:
        path: /opt/etcd/bin
        state: directory
        owner: root
        group: root
        mode: 0700
    - name: "Download the tarball into the /tmp directory"
      get_url:
        url: https://storage.googleapis.com/etcd/v3.3.13/etcd-v3.3.13-linux-amd64.tar.gz
        dest: /tmp/etcd.tar.gz
        owner: root
        group: root
        mode: 0600
        force: True
    - name: "Extract the contents of the tarball"
      unarchive:
        src: /tmp/etcd.tar.gz
        dest: /opt/etcd/bin/
        owner: root
        group: root
        mode: 0600
        extra_opts:
          - --strip-components=1
        decrypt: True
        remote_src: True
    - name: "Set permissions for etcd"
      file:
        path: /opt/etcd/bin/etcd
        state: file
        owner: root
        group: root
        mode: 0700
    - name: "Set permissions for etcdctl"
      file:
        path: /opt/etcd/bin/etcdctl
        state: file
        owner: root
        group: root
        mode: 0700
    - name: "Add /opt/etcd/bin/ to the $PATH environment variable"
      lineinfile:
        path: /etc/profile
        line: export PATH="$PATH:/opt/etcd/bin"
        state: present
        create: True
        insertafter: EOF
    - name: "Set the ETCDCTL_API environment variable to 3"
      lineinfile:
        path: /etc/profile
        line: export ETCDCTL_API=3
        state: present
        create: True
        insertafter: EOF

各タスクはモジュールを使用します。 この一連のタスクでは、次のモジュールを使用しています。

  • file /opt/etcd/binディレクトリを作成し、後でetcdおよびetcdctlバイナリのファイル権限を設定します。
  • get_url :gzipで圧縮されたtarballを管理対象ノードにダウンロードします。
  • unarchive :gzip圧縮されたtarballからetcdおよびetcdctlバイナリを抽出して解凍します。
  • lineinfile .profileファイルにエントリを追加します。

これらの変更を適用するには、CTRL+Xに続いてYを押して、playbook.yamlファイルを閉じて保存します。 次に、ターミナルで同じansible-playbookコマンドを再度実行します。

  1. ansible-playbook -i hosts playbook.yaml

出力のPLAY RECAPセクションには、okchangedのみが表示されます。

Output
... PLAY RECAP ************************************************************************************************************************ etcd1 : ok=8 changed=7 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 etcd2 : ok=8 changed=7 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 etcd3 : ok=8 changed=7 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

etcdが正しくインストールされていることを確認するには、管理対象ノードの1つに手動でSSHで接続し、etcdおよびetcdctlを実行します。

  1. ssh root@etcd1_public_ip

etcd1_public_ipは、etcd1という名前のサーバーのパブリックIPアドレスです。 SSHアクセスを取得したら、etcd --versionを実行して、インストールされているetcdのバージョンを印刷します。

  1. etcd --version

次のような出力が表示されます。これは、etcdバイナリが正常にインストールされたことを意味します。

Output
etcd Version: 3.3.13 Git SHA: 98d3084 Go Version: go1.10.8 Go OS/Arch: linux/amd64

etcdctlが正常にインストールされたことを確認するには、etcdctl versionを実行します。

  1. etcdctl version

次のような出力があります。

Output
etcdctl version: 3.3.13 API version: 3.3

出力にはAPI version: 3.3と表示されていることに注意してください。これは、ETCDCTL_API環境変数が正しく設定されていることも確認しています。

etcd1 サーバーを終了して、ローカル環境に戻ります。

これで、すべての管理対象ノードにetcdおよびetcdctlが正常にインストールされました。 次のステップでは、etcdをバックグラウンドサービスとして実行するために、プレイにさらにタスクを追加します。

ステップ4—etcdのユニットファイルを作成する

Ansibleでetcdを実行する最も簡単な方法は、commandモジュールを使用して/opt/etcd/bin/etcdを実行することです。 ただし、etcdがフォアグラウンドプロセスとして実行されるため、これは機能しません。 commandモジュールを使用すると、etcdコマンドが返されるのを待つ間、Ansibleがハングしますが、返されることはありません。 したがって、このステップでは、プレイブックを更新して、代わりにetcdバイナリをバックグラウンドサービスとして実行します。

Ubuntu18.04はsystemdinitシステムとして使用します。つまり、ユニットファイルを書き込み、/etc/systemd/system/ディレクトリ内に配置することで新しいサービスを作成できます。 。

まず、プロジェクトディレクトリ内に、files/という名前の新しいディレクトリを作成します。

  1. mkdir files

次に、エディタを使用して、そのディレクトリ内にetcd.serviceという名前の新しいファイルを作成します。

  1. nano files/etcd.service

次に、次のコードブロックをfiles/etcd.serviceファイルにコピーします。

〜/ playground / etcd-ansible / files / etcd.service
[Unit]
Description=etcd distributed reliable key-value store

[Service]
Type=notify
ExecStart=/opt/etcd/bin/etcd
Restart=always

このユニットファイルは、/opt/etcd/bin/etcdで実行可能ファイルを実行し、初期化が終了するとsystemdに通知し、終了すると常に再起動するサービスを定義します。

注: systemdファイルとunitファイルについて詳しく知りたい場合、またはユニットファイルをニーズに合わせて調整したい場合は、Systemdユニットとユニットファイルについてガイドをお読みください。

CTRL+Xに続いてYを押して、files/etcd.serviceファイルを閉じて保存します。

次に、プレイブック内に、files/etcd.serviceローカルファイルをすべての管理対象ノードの/etc/systemd/system/etcd.serviceディレクトリにコピーするタスクを追加する必要があります。 これは、copyモジュールを使用して実行できます。

プレイブックを開きます。

  1. nano $HOME/playground/etcd-ansible/playbook.yaml

次の強調表示されたタスクを既存のタスクの最後に追加します。

〜/ playground / etcd-ansible / playbook.yaml
- hosts: etcd
  become: True
  tasks:
    ...
    - name: "Set the ETCDCTL_API environment variable to 3"
      lineinfile:
        path: /etc/profile
        line: export ETCDCTL_API=3
        state: present
        create: True
        insertafter: EOF
    - name: "Create a etcd service"
      copy:
        src: files/etcd.service
        remote_src: False
        dest: /etc/systemd/system/etcd.service
        owner: root
        group: root
        mode: 0644

ユニットファイルを/etc/systemd/system/etcd.serviceにコピーすることで、サービスが定義されます。

プレイブックを保存して終了します。

同じansible-playbookコマンドを再度実行して、新しい変更を適用します。

  1. ansible-playbook -i hosts playbook.yaml

変更が適用されたことを確認するには、最初に管理対象ノードの1つにSSHで接続します。

  1. ssh root@etcd1_public_ip

次に、systemctl status etcdを実行して、systemdにetcdサービスのステータスについて問い合わせます。

  1. systemctl status etcd

次の出力があります。これは、サービスがロードされていることを示しています。

Output
● etcd.service - etcd distributed reliable key-value store Loaded: loaded (/etc/systemd/system/etcd.service; static; vendor preset: enabled) Active: inactive (dead) ...

注:出力の最後の行(Active: inactive (dead))は、サービスが非アクティブであることを示しています。これは、システムの起動時にサービスが自動的に実行されないことを意味します。 これは予期されたものであり、エラーではありません。

qを押してシェルに戻り、exitを実行して管理対象ノードを終了し、ローカルシェルに戻ります。

  1. exit

このステップでは、etcdバイナリをsystemdサービスとして実行するようにプレイブックを更新しました。 次のステップでは、データを格納するためのスペースを提供することにより、etcdをセットアップし続けます。

ステップ5—データディレクトリの設定

etcdはキー値データストアです。つまり、データを格納するためのスペースを提供する必要があります。 このステップでは、プレイブックを更新して、etcdが使用する専用のデータディレクトリを定義します。

プレイブックを開きます。

  1. nano $HOME/playground/etcd-ansible/playbook.yaml

タスクのリストの最後に次のタスクを追加します。

〜/ playground / etcd-ansible / playbook.yaml
- hosts: etcd
  become: True
  tasks:
    ...
    - name: "Create a etcd service"
      copy:
        src: files/etcd.service
        remote_src: False
        dest: /etc/systemd/system/etcd.service
        owner: root
        group: root
        mode: 0644
    - name: "Create a data directory"
      file:
        path: /var/lib/etcd/{{ inventory_hostname }}.etcd
        state: directory
        owner: root
        group: root
        mode: 0755

ここでは、データディレクトリとして/var/lib/etcd/hostname.etcdを使用しています。ここで、hostnameは現在の管理対象ノードのホスト名です。 inventory_hostnameは、現在の管理対象ノードのホスト名を表す変数です。 その値はAnsibleによって自動的に入力されます。 中括弧構文(つまり、{{ inventory_hostname }})は、変数置換に使用され、AnsibleのデフォルトのテンプレートエンジンであるJinja2テンプレートエンジンでサポートされています。

テキストエディタを閉じて、ファイルを保存します。

次に、このデータディレクトリを使用するようにetcdに指示する必要があります。 これを行うには、data-dirパラメーターをetcdに渡します。 etcdパラメーターを設定するには、環境変数、コマンドラインフラグ、および構成ファイルを組み合わせて使用できます。 このチュートリアルでは、構成ファイルを使用します。これは、構成をプレイブック全体に散らかすのではなく、すべての構成をファイルに分離する方がはるかに優れているためです。

プロジェクトディレクトリに、templates/という名前の新しいディレクトリを作成します。

  1. mkdir templates

次に、エディタを使用して、ディレクトリ内にetcd.conf.yaml.j2という名前の新しいファイルを作成します。

  1. nano templates/etcd.conf.yaml.j2

次に、次の行をコピーしてファイルに貼り付けます。

〜/ playground / etcd-ansible / templates / etcd.conf.yaml.j2
data-dir: /var/lib/etcd/{{ inventory_hostname }}.etcd

このファイルは、プレイブックと同じJinja2変数置換構文を使用しています。 変数を置き換えて結果を各管理対象ホストにアップロードするには、templateモジュールを使用できます。 copyと同じように機能しますが、アップロード前に変数の置換を実行する点が異なります。

etcd.conf.yaml.j2を終了し、プレイブックを開きます。

  1. nano $HOME/playground/etcd-ansible/playbook.yaml

次のタスクをタスクのリストに追加して、ディレクトリを作成し、テンプレート化された構成ファイルをそのディレクトリにアップロードします。

〜/ playground / etcd-ansible / playbook.yaml
- hosts: etcd
  become: True
  tasks:
    ...
    - name: "Create a data directory"
      file:
        ...
        mode: 0755
    - name: "Create directory for etcd configuration"
      file:
        path: /etc/etcd
        state: directory
        owner: root
        group: root
        mode: 0755
    - name: "Create configuration file for etcd"
      template:
        src: templates/etcd.conf.yaml.j2
        dest: /etc/etcd/etcd.conf.yaml
        owner: root
        group: root
        mode: 0600

このファイルを保存して閉じます。

この変更を行ったため、サービスのユニットファイルを更新して、構成ファイルの場所(/etc/etcd/etcd.conf.yamlなど)を渡す必要があります。

ローカルマシンでetcdサービスファイルを開きます。

  1. nano files/etcd.service

以下で強調表示されている--config-fileフラグを追加して、files/etcd.serviceファイルを更新します。

〜/ playground / etcd-ansible / files / etcd.service
[Unit]
Description=etcd distributed reliable key-value store

[Service]
Type=notify
ExecStart=/opt/etcd/bin/etcd --config-file /etc/etcd/etcd.conf.yaml
Restart=always

このファイルを保存して閉じます。

このステップでは、プレイブックを使用して、etcdがデータを格納するためのデータディレクトリを提供しました。 次のステップでは、etcdサービスを再起動し、起動時に実行するためのタスクをさらにいくつか追加します。

ステップ6—etcdサービスの有効化と開始

サービスのユニットファイルに変更を加えるたびに、サービスを再起動して有効にする必要があります。 これを行うには、systemctl restart etcdコマンドを実行します。 さらに、システムの起動時にetcdサービスを自動的に開始するには、systemctl enable etcdを実行する必要があります。 このステップでは、プレイブックを使用してこれら2つのコマンドを実行します。

コマンドを実行するには、コマンドモジュールを使用できます。

  1. nano $HOME/playground/etcd-ansible/playbook.yaml

次のタスクをタスクリストの最後に追加します。

〜/ playground / etcd-ansible / playbook.yaml
- hosts: etcd
  become: True
  tasks:
    ...
    - name: "Create configuration file for etcd"
      template:
        ...
        mode: 0600
    - name: "Enable the etcd service"
      command: systemctl enable etcd
    - name: "Start the etcd service"
      command: systemctl restart etcd

ファイルを保存して閉じます。

ansible-playbook -i hosts playbook.yamlをもう一度実行します。

  1. ansible-playbook -i hosts playbook.yaml

etcdサービスが再起動されて有効になったことを確認するには、次のいずれかの管理対象ノードにSSHで接続します。

  1. ssh root@etcd1_public_ip

次に、systemctl status etcdを実行して、etcdサービスのステータスを確認します。

  1. systemctl status etcd

以下で強調表示されているように、enabledactive (running)があります。 これは、プレイブックで行った変更が有効になったことを意味します。

Output
● etcd.service - etcd distributed reliable key-value store Loaded: loaded (/etc/systemd/system/etcd.service; static; vendor preset: enabled) Active: active (running) Main PID: 19085 (etcd) Tasks: 11 (limit: 2362)

このステップでは、commandモジュールを使用して、管理対象ノードでetcdサービスを再起動して有効にするsystemctlコマンドを実行しました。 etcdインストールをセットアップしたので、次のステップで、いくつかの基本的な作成、読み取り、更新、および削除(CRUD)操作を実行してその機能をテストします。

ステップ7—etcdのテスト

etcdのインストールは機能していますが、安全ではなく、まだ本番環境で使用する準備ができていません。 ただし、後の手順でetcdのセットアップを保護する前に、まず、etcdが機能の観点から何ができるかを理解しましょう。 このステップでは、etcdにデータを追加、取得、更新、削除するためのリクエストを手動で送信します。

デフォルトでは、etcdはポート2379でクライアント通信をリッスンするAPIを公開します。 これは、HTTPクライアントを使用して生のAPIリクエストをetcdに送信できることを意味します。 ただし、公式のetcdクライアントetcdctlを使用すると、putget、およびそれぞれdelサブコマンド。

etcd1 管理対象ノード内にいることを確認し、次のetcdctlコマンドを実行して、etcdインストールが機能していることを確認します。

まず、putサブコマンドを使用して新しいエントリを作成します。

putサブコマンドの構文は次のとおりです。

etcdctl put key value

etcd1 で、次のコマンドを実行します。

  1. etcdctl put foo "bar"

実行したコマンドは、etcdに値"bar"をストアのキーfooに書き込むように指示します。

次に、出力にOKが出力されます。これは、データが保持されていることを示しています。

Output
OK

次に、getサブコマンドを使用してこのエントリを取得できます。このサブコマンドの構文は、etcdctl get keyです。

  1. etcdctl get foo

この出力は、最初の行にキーを示し、2番目の行に前に挿入した値を示しています。

Output
foo bar

delサブコマンドを使用してエントリを削除できます。このサブコマンドの構文は、etcdctl del keyです。

  1. etcdctl del foo

削除されたエントリの数を示す次の出力が表示されます。

Output
1

次に、削除されたキーと値のペアを取得するために、getサブコマンドをもう一度実行してみましょう。

  1. etcdctl get foo

出力は受信されません。つまり、etcdctlはキーと値のペアを取得できません。 これにより、エントリが削除された後、そのエントリを取得できなくなったことを確認できます。

etcdとetcdctlの基本的な操作をテストしたので、管理対象ノードを終了してローカル環境に戻りましょう。

  1. exit

このステップでは、etcdctlクライアントを使用してetcdにリクエストを送信しました。 この時点で、etcdの3つの別々のインスタンスを実行しており、それぞれが互いに独立して動作しています。 ただし、etcdは分散キー値ストアとして設計されています。つまり、複数のetcdインスタンスをグループ化して、単一のクラスターを形成できます。 その後、各インスタンスはクラスターのメンバーになります。 クラスターを形成した後、クラスターの別のメンバーから挿入されたキーと値のペアを取得できるようになります。 次のステップでは、プレイブックを使用して、3つのシングルノードクラスターを単一の3ノードクラスターに変換します。

ステップ8—静的検出を使用してクラスターを形成する

3つの1ノードクラスターではなく1つの3ノードクラスターを作成するには、これらのetcdインストールを相互に通信するように構成する必要があります。 つまり、各自が他のIPアドレスを知っている必要があります。 このプロセスは検出と呼ばれます。 検出は、静的構成または動的サービス検出のいずれかを使用して実行できます。 このステップでは、2つの違いについて説明し、静的検出を使用してetcdクラスターをセットアップするようにプレイブックを更新します。

静的構成による検出は、最小限のセットアップで済む方法です。 これは、各メンバーのエンドポイントが実行される前にetcdコマンドに渡される場所です。 静的構成を使用するには、クラスターを初期化する前に、次の条件が満たされている必要があります。

  • メンバーの数はわかっています
  • 各メンバーのエンドポイントは既知です
  • すべてのエンドポイントのIPアドレスは静的です

これらの条件を満たせない場合は、動的検出サービスを使用できます。 動的サービス検出では、すべてのインスタンスが検出サービスに登録されます。これにより、各メンバーは他のメンバーの場所に関する情報を取得できます。

3ノードのetcdクラスターが必要であり、すべてのサーバーに静的IPアドレスがあることがわかっているため、静的検出を使用します。 静的検出を使用してクラスターを開始するには、構成ファイルにいくつかのパラメーターを追加する必要があります。 エディタを使用して、templates/etcd.conf.yaml.j2テンプレートファイルを開きます。

  1. nano templates/etcd.conf.yaml.j2

次に、次の強調表示された行を追加します。

〜/ playground / etcd-ansible / templates / etcd.conf.yaml.j2
data-dir: /var/lib/etcd/{{ inventory_hostname }}.etcd
name: {{ inventory_hostname }}
initial-advertise-peer-urls: http://{{ hostvars[inventory_hostname]['ansible_facts']['eth1']['ipv4']['address'] }}:2380
listen-peer-urls: http://{{ hostvars[inventory_hostname]['ansible_facts']['eth1']['ipv4']['address'] }}:2380,http://127.0.0.1:2380
advertise-client-urls: http://{{ hostvars[inventory_hostname]['ansible_facts']['eth1']['ipv4']['address'] }}:2379
listen-client-urls: http://{{ hostvars[inventory_hostname]['ansible_facts']['eth1']['ipv4']['address'] }}:2379,http://127.0.0.1:2379
initial-cluster-state: new
initial-cluster: {% for host in groups['etcd'] %}{{ hostvars[host]['ansible_facts']['hostname'] }}=http://{{ hostvars[host]['ansible_facts']['eth1']['ipv4']['address'] }}:2380{% if not loop.last %},{% endif %}{% endfor %}

CTRL+Xに続いてYを押して、templates/etcd.conf.yaml.j2ファイルを閉じて保存します。

各パラメータの簡単な説明は次のとおりです。

  • name-人間が読める形式のメンバーの名前。 デフォルトでは、etcdはランダムに生成された一意のIDを使用して各メンバーを識別します。 ただし、人間が読める形式の名前を使用すると、構成ファイル内やコマンドラインで簡単に参照できます。 ここでは、ホスト名をメンバー名として使用します(つまり、etcd1etcd2、およびetcd3)。
  • initial-advertise-peer-urls-他のメンバーがこのメンバーと通信するために使用できるIPアドレス/ポートの組み合わせのリスト。 APIポート(2379)に加えて、etcdはetcdメンバー間のピア通信用のポート2380も公開します。これにより、メンバーは相互にメッセージを送信したり、データを交換したりできます。 これらのURLは、ピアが到達可能である必要があることに注意してください(ローカルIPアドレスではありません)。
  • listen-peer-urls-現在のメンバーが他のメンバーからの通信をリッスンするIPアドレス/ポートの組み合わせのリスト。 これには、--initial-advertise-peer-urlsフラグのすべてのURLだけでなく、127.0.0.1:2380などのローカルURLも含める必要があります。 着信ピアメッセージの宛先IPアドレス/ポートは、ここにリストされているURLの1つと一致する必要があります。
  • advertise-client-urls-クライアントがこのメンバーと通信するために使用する必要があるIPアドレス/ポートの組み合わせのリスト。 これらのURLは、クライアントが到達可能である必要があります(ローカルアドレスではありません)。 クライアントがパブリックインターネット経由でクラスターにアクセスしている場合、これはパブリックIPアドレスである必要があります。
  • listen-client-urls-現在のメンバーがクライアントからの通信をリッスンするIPアドレス/ポートの組み合わせのリスト。 これには、--advertise-client-urlsフラグのすべてのURLだけでなく、127.0.0.1:2379などのローカルURLも含める必要があります。 着信クライアントメッセージの宛先IPアドレス/ポートは、ここにリストされているURLの1つと一致する必要があります。
  • initial-cluster-クラスターの各メンバーのエンドポイントのリスト。 各エンドポイントは、対応するメンバーのinitial-advertise-peer-urlsURLの1つと一致する必要があります。
  • initial-cluster-statenewまたはexistingのいずれか。

一貫性を確保するために、etcdは、ノードの大部分が正常である場合にのみ決定を下すことができます。 これは、クォーラムの確立として知られています。 つまり、3つのメンバーのクラスターでは、2つ以上のメンバーが正常である場合にクォーラムに達します。

initial-cluster-stateパラメーターがnewに設定されている場合、etcdは、これがブートストラップされている新しいクラスターであることを認識し、クォーラムが到達する。 より具体的には、最初のメンバーが開始された後、3分の1(33.33%)が50%以下であるため、クォーラムはありません。 通常、etcdは停止し、それ以上のアクションのコミットを拒否し、クラスターが形成されることはありません。 ただし、initial-cluster-statenewに設定すると、最初のクォーラムの不足が無視されます。

existingに設定すると、メンバーは既存のクラスターに参加しようとし、クォーラムがすでに確立されていることを期待します。

注:サポートされているすべての構成フラグの詳細については、etcdのドキュメントの構成セクションを参照してください。

更新されたtemplates/etcd.conf.yaml.j2テンプレートファイルには、hostvarsのインスタンスがいくつかあります。 Ansibleを実行すると、さまざまなソースから変数が収集されます。 以前にinventory_hostname変数を使用しましたが、さらに多くの変数が利用可能です。 これらの変数は、hostvars[inventory_hostname]['ansible_facts']で使用できます。 ここでは、各ノードのプライベートIPアドレスを抽出し、それを使用してパラメーター値を作成しています。

注:サーバーの作成時にプライベートネットワークオプションを有効にしたため、各サーバーには3つのIPアドレスが関連付けられます。

  • loopbackIPアドレス-同じマシン内でのみ有効なアドレス。 これは、マシンがそれ自体を参照するために使用されます(例:127.0.0.1)。
  • public IPアドレス-パブリックインターネット上でルーティング可能なアドレス(例:178.128.169.51
  • privateIPアドレス-プライベートネットワーク内でのみルーティング可能なアドレス。 DigitalOcean Dropletsの場合、各データセンター内にプライベートネットワークがあります(例:10.131.82.225)。

これらのIPアドレスはそれぞれ異なるネットワークインターフェイスに関連付けられています。ループバックアドレスはloインターフェイスに関連付けられ、パブリックIPアドレスはeth0インターフェイスに関連付けられ、プライベートIPアドレスはeth1インターフェース。 eth1インターフェースを使用して、すべてのトラフィックがインターネットに到達することなくプライベートネットワーク内にとどまるようにしています。

この記事ではネットワークインターフェイスの理解は必須ではありませんが、詳細を知りたい場合は、ネットワーク用語、インターフェイス、およびプロトコルの概要から始めるのが最適です。

{% %} Jinja2構文は、etcdグループ内のすべてのノードを反復処理して、initial-cluster文字列をetcdで必要な形式に構築するforループ構造を定義します。 。

新しい3メンバーのクラスターを形成するには、クラスターを起動する前に、まずetcdサービスを停止し、データディレクトリをクリアする必要があります。 これを行うには、エディターを使用して、ローカルマシンでplaybook.yamlファイルを開きます。

  1. nano $HOME/playground/etcd-ansible/playbook.yaml

次に、"Create a data directory"タスクの前に、etcdサービスを停止するタスクを追加します。

〜/ playground / etcd-ansible / playbook.yaml
- hosts: etcd
  become: True
  tasks:
    ...
        group: root
        mode: 0644
    - name: "Stop the etcd service"
      command: systemctl stop etcd
    - name: "Create a data directory"
      file:
    ...

次に、"Create a data directory"タスクを更新して、最初にデータディレクトリを削除し、再作成します。

〜/ playground / etcd-ansible / playbook.yaml
- hosts: etcd
  become: True
  tasks:
    ...
    - name: "Stop the etcd service"
      command: systemctl stop etcd
    - name: "Create a data directory"
      file:
        path: /var/lib/etcd/{{ inventory_hostname }}.etcd
        state: "{{ item }}"
        owner: root
        group: root
        mode: 0755
      with_items:
        - absent
        - directory
    - name: "Create directory for etcd configuration"
      file:
    ...

with_itemsプロパティは、このタスクが繰り返す文字列のリストを定義します。 これは、同じタスクを2回繰り返すのと同じですが、stateプロパティの値が異なります。 ここでは、アイテムabsentdirectoryを使用してリストを反復処理しています。これにより、データディレクトリが最初に削除され、その後に再作成されます。

CTRL+Xに続いてYを押して、playbook.yamlファイルを閉じて保存します。 次に、ansible-playbookを再度実行します。 Ansibleは、単一の3メンバーetcdクラスターを作成します。

  1. ansible-playbook -i hosts playbook.yaml

これは、etcdメンバーノードにSSHで接続することで確認できます。

  1. ssh root@etcd1_public_ip

次に、etcdctl endpoint health --clusterを実行します。

  1. etcdctl endpoint health --cluster

これにより、クラスターの各メンバーの正常性が一覧表示されます。

Output
http://etcd2_private_ip:2379 is healthy: successfully committed proposal: took = 2.517267ms http://etcd1_private_ip:2379 is healthy: successfully committed proposal: took = 2.153612ms http://etcd3_private_ip:2379 is healthy: successfully committed proposal: took = 2.639277ms

これで、3ノードのetcdクラスターが正常に作成されました。 これは、あるメンバーノードのetcdにエントリを追加し、別のメンバーノードで取得することで確認できます。 メンバーノードの1つで、etcdctl putを実行します。

  1. etcdctl put foo "bar"

次に、新しい端末を使用して、別のメンバーノードにSSHで接続します。

  1. ssh root@etcd2_public_ip

次に、キーを使用して同じエントリを取得してみます。

  1. etcdctl get foo

エントリを取得できるようになります。これは、クラスタが機能していることを証明します。

Output
foo bar

最後に、各管理対象ノードを終了して、ローカルマシンに戻ります。

  1. exit
  1. exit

このステップでは、新しい3ノードクラスターをプロビジョニングしました。 現在、etcdメンバーとそのピアおよびクライアント間の通信は、HTTPを介して行われます。 これは、通信が暗号化されておらず、トラフィックを傍受できるすべての関係者がメッセージを読み取ることができることを意味します。 etcdクラスターとクライアントがすべてプライベートネットワークまたは完全に制御する仮想プライベートネットワーク(VPN)内に展開されている場合、これは大きな問題ではありません。 ただし、トラフィックのいずれかが共有ネットワーク(プライベートまたはパブリック)を通過する必要がある場合は、このトラフィックが暗号化されていることを確認する必要があります。 さらに、クライアントまたはピアがサーバーの信頼性を検証するためのメカニズムを導入する必要があります。

次のステップでは、TLSを使用してクライアントからサーバーへの通信とピア通信を保護する方法を見ていきます。

ステップ9—管理対象ノードのプライベートIPアドレスを取得する

メンバーノードなどの間のメッセージを暗号化するには、 Hypertext Transfer Protocol Secure 、または Transport LayerSecurityまたはの上のレイヤーであるHTTPSを使用します。 ] TLS 、プロトコル。 TLSは、秘密鍵、証明書、および認証局(CA)と呼ばれる信頼できるエンティティのシステムを使用して、相互に認証し、暗号化されたメッセージを送信します。

このチュートリアルでは、各メンバーノードは、それ自体を識別するための証明書を生成し、この証明書をCAによって署名する必要があります。 このCAを信頼するようにすべてのメンバーノードを構成し、したがって、署名するすべての証明書も信頼します。 これにより、メンバーノードは相互に認証できます。

メンバーノードが生成する証明書は、他のメンバーノードがそれ自体を識別できるようにする必要があります。 すべての証明書には、関連付けられているエンティティの共通名(CN)が含まれています。 これは、エンティティのIDとしてよく使用されます。 ただし、証明書を検証するときに、クライアントの実装は、エンティティに関して収集した情報が証明書で提供された情報と一致するかどうかを比較する場合があります。 たとえば、クライアントがCN=foo.bar.comという件名のTLS証明書をダウンロードしたが、クライアントが実際にIPアドレス(167.71.129.110など)を使用してサーバーに接続している場合、不一致があり、クライアントは証明書を信頼できない可能性があります。 証明書にサブジェクト代替名(SAN)を指定することにより、両方の名前が同じエンティティに属していることを検証者に通知します。

etcdメンバーはプライベートIPアドレスを使用して相互にピアリングしているため、証明書を定義するときに、これらのプライベートIPアドレスをサブジェクト代替名として指定する必要があります。

管理対象ノードのプライベートIPアドレスを見つけるには、SSHで接続します。

  1. ssh root@etcd1_public_ip

次に、次のコマンドを実行します。

  1. ip -f inet addr show eth1

次の行のような出力があります。

Output
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000 inet 10.131.255.176/16 brd 10.131.255.255 scope global eth1 valid_lft forever preferred_lft forever

この出力例では、10.131.255.176は管理対象ノードのプライベートIPアドレスであり、関心のある唯一の情報です。 プライベートIP以外のすべてを除外するために、ipコマンドの出力をsedユーティリティにパイプ処理できます。これは、テキストのフィルタリングと変換に使用されます。

  1. ip -f inet addr show eth1 | sed -En -e 's/.*inet ([0-9.]+).*/\1/p'

現在、唯一の出力はプライベートIPアドレス自体です。

Output
10.131.255.176

上記のコマンドが機能することを確認したら、管理対象ノードを終了します。

  1. exit

上記のコマンドをプレイブックに組み込むには、最初にplaybook.yamlファイルを開きます。

  1. nano $HOME/playground/etcd-ansible/playbook.yaml

次に、既存のプレイの前に、単一のタスクで新しいプレイを追加します。

〜/ playground / etcd-ansible / playbook.yaml
...
- hosts: etcd
  tasks:
    - shell: ip -f inet addr show eth1 | sed -En -e 's/.*inet ([0-9.]+).*/\1/p'
      register: privateIP
- hosts: etcd
  tasks:
...

このタスクは、shellモジュールを使用して、ipおよびsedコマンドを実行します。これらのコマンドは、管理対象ノードのプライベートIPアドレスを取得します。 次に、シェルコマンドの戻り値をprivateIPという名前の変数内に登録します。これは後で使用します。

このステップでは、管理対象ノードのプライベートIPアドレスを取得するタスクをプレイブックに追加しました。 次のステップでは、この情報を使用して各メンバーノードの証明書を生成し、これらの証明書を認証局(CA)によって署名します。

ステップ10—etcdメンバーの秘密鍵とCSRを生成する

メンバーノードが暗号化されたトラフィックを受信するには、送信者はメンバーノードの公開鍵を使用してデータを暗号化し、メンバーノードはその秘密鍵を使用して暗号文を復号化し、元のデータを取得する必要があります。 公開鍵は証明書にパッケージ化され、CAによって署名されて本物であることを確認します。

したがって、etcdメンバーノードごとに秘密鍵と証明書署名要求(CSR)を生成する必要があります。 簡単にするために、すべてのキーペアを生成し、すべての証明書をローカルで制御ノードに署名してから、関連するファイルを管理対象ホストにコピーします。

まず、artifacts/というディレクトリを作成します。このディレクトリに、プロセス中に生成されたファイル(キーと証明書)を配置します。 playbook.yamlファイルをエディターで開きます。

  1. nano $HOME/playground/etcd-ansible/playbook.yaml

その中で、fileモジュールを使用して、artifacts/ディレクトリを作成します。

〜/ playground / etcd-ansible / playbook.yaml
...
    - shell: ip -f inet addr show eth1 | sed -En -e 's/.*inet ([0-9.]+).*/\1/p'
      register: privateIP
- hosts: localhost
  gather_facts: False
  become: False
  tasks:
    - name: "Create ./artifacts directory to house keys and certificates"
      file:
        path: ./artifacts
        state: directory
- hosts: etcd
  tasks:
...

次に、プレイの最後に別のタスクを追加して、秘密鍵を生成します。

〜/ playground / etcd-ansible / playbook.yaml
...
- hosts: localhost
  gather_facts: False
  become: False
  tasks:
        ...
    - name: "Generate private key for each member"
      openssl_privatekey:
        path: ./artifacts/{{item}}.key
        type: RSA
        size: 4096
        state: present
        force: True
      with_items: "{{ groups['etcd'] }}"
- hosts: etcd
  tasks:
...

秘密鍵とCSRの作成は、それぞれopenssl_privatekeyモジュールとopenssl_csrモジュールを使用して実行できます。

force: True属性は、秘密鍵がすでに存在している場合でも、毎回再生成されることを保証します。

同様に、openssl_csrモジュールを使用して、同じプレイに次の新しいタスクを追加し、各メンバーのCSRを生成します。

〜/ playground / etcd-ansible / playbook.yaml
...
- hosts: localhost
  gather_facts: False
  become: False
  tasks:
    ...
    - name: "Generate private key for each member"
      openssl_privatekey:
        ...
      with_items: "{{ groups['etcd'] }}"
    - name: "Generate CSR for each member"
      openssl_csr:
        path: ./artifacts/{{item}}.csr
        privatekey_path: ./artifacts/{{item}}.key
        common_name: "{{item}}"
        key_usage:
          - digitalSignature
        extended_key_usage:
          - serverAuth
        subject_alt_name:
          - IP:{{ hostvars[item]['privateIP']['stdout']}}
          - IP:127.0.0.1
        force: True
      with_items: "{{ groups['etcd'] }}"

この証明書は、サーバー認証の目的でデジタル署名メカニズムに関与できることを指定しています。 この証明書はホスト名(etcd1など)に関連付けられていますが、ベリファイアは各ノードのプライベートおよびローカルループバックIPアドレスも代替名として扱う必要があります。 前のプレイで登録したprivateIP変数を使用していることに注意してください。

CTRL+Xに続いてYを押して、playbook.yamlファイルを閉じて保存します。 次に、プレイブックを再度実行します。

  1. ansible-playbook -i hosts playbook.yaml

これで、プロジェクトディレクトリ内にartifactsという新しいディレクトリが見つかります。 lsを使用して、その内容を一覧表示します。

  1. ls artifacts

etcdメンバーのそれぞれの秘密鍵とCSRがあります。

Output
etcd1.csr etcd1.key etcd2.csr etcd2.key etcd3.csr etcd3.key

このステップでは、いくつかのAnsibleモジュールを使用して、各メンバーノードの秘密鍵と公開鍵証明書を生成しました。 次のステップでは、証明書署名要求(CSR)に署名する方法を見ていきます。

ステップ11—CA証明書の生成

etcdクラスター内では、メンバーノードは受信者の公開鍵を使用してメッセージを暗号化します。 公開鍵が本物であることを確認するために、受信者は公開鍵を証明書署名要求(CSR)にパッケージ化し、信頼できるエンティティ(つまり、CA)にCSRに署名させます。 すべてのメンバーノードとそれらが信頼するCAを制御するため、外部CAを使用する必要はなく、独自のCAとして機能できます。 このステップでは、独自のCAとして機能します。つまり、CAとして機能するには、秘密鍵と自己署名証明書を生成する必要があります。

まず、エディタでplaybook.yamlファイルを開きます。

  1. nano $HOME/playground/etcd-ansible/playbook.yaml

次に、前の手順と同様に、localhostプレイにタスクを追加して、CAの秘密鍵を生成します。

〜/ playground / etcd-ansible / playbook.yaml
- hosts: localhost
  ...
  tasks:
    ...
  - name: "Generate CSR for each member"
    ...
    with_items: "{{ groups['etcd'] }}"
    - name: "Generate private key for CA"
      openssl_privatekey:
        path: ./artifacts/ca.key
        type: RSA
        size: 4096
        state: present
        force: True
- hosts: etcd
  become: True
  tasks:
    - name: "Create directory for etcd binaries"
...

次に、openssl_csrモジュールを使用して新しいCSRを生成します。 これは前の手順と似ていますが、このCSRでは、この証明書をCA証明書として使用できることを示すために、基本的な制約とキーの使用法の拡張機能を追加しています。

〜/ playground / etcd-ansible / playbook.yaml
- hosts: localhost
  ...
  tasks:
    ...
    - name: "Generate private key for CA"
      openssl_privatekey:
        path: ./artifacts/ca.key
        type: RSA
        size: 4096
        state: present
        force: True
    - name: "Generate CSR for CA"
      openssl_csr:
        path: ./artifacts/ca.csr
        privatekey_path: ./artifacts/ca.key
        common_name: ca
        organization_name: "Etcd CA"
        basic_constraints:
          - CA:TRUE
          - pathlen:1
        basic_constraints_critical: True
        key_usage:
          - keyCertSign
          - digitalSignature
        force: True
- hosts: etcd
  become: True
  tasks:
    - name: "Create directory for etcd binaries"
...

最後に、 openssl_certificate モジュールを使用して、CSRに自己署名します。

〜/ playground / etcd-ansible / playbook.yaml
- hosts: localhost
  ...
  tasks:
    ...
    - name: "Generate CSR for CA"
      openssl_csr:
        path: ./artifacts/ca.csr
        privatekey_path: ./artifacts/ca.key
        common_name: ca
        organization_name: "Etcd CA"
        basic_constraints:
          - CA:TRUE
          - pathlen:1
        basic_constraints_critical: True
        key_usage:
          - keyCertSign
          - digitalSignature
        force: True
    - name: "Generate self-signed CA certificate"
      openssl_certificate:
        path: ./artifacts/ca.crt
        privatekey_path: ./artifacts/ca.key
        csr_path: ./artifacts/ca.csr
        provider: selfsigned
        force: True
- hosts: etcd
  become: True
  tasks:
    - name: "Create directory for etcd binaries"
...

CTRL+Xに続いてYを押して、playbook.yamlファイルを閉じて保存します。 次に、プレイブックを再度実行して、変更を適用します。

  1. ansible-playbook -i hosts playbook.yaml

lsを実行して、artifacts/ディレクトリの内容を確認することもできます。

  1. ls artifacts/

これで、新しく生成されたCA証明書(ca.crt)が見つかります。

Output
ca.crt ca.csr ca.key etcd1.csr etcd1.key etcd2.csr etcd2.key etcd3.csr etcd3.key

このステップでは、CAの秘密鍵と自己署名証明書を生成しました。 次のステップでは、CA証明書を使用して各メンバーのCSRに署名します。

ステップ12—etcdメンバーのCSRに署名する

このステップでは、各メンバーノードのCSRに署名します。 これは、openssl_certificateモジュールを使用してCA証明書に自己署名する方法と似ていますが、selfsignedプロバイダーを使用する代わりに、owncaプロバイダーを使用します。独自のCA証明書を使用して署名できます。

プレイブックを開きます。

  1. nano $HOME/playground/etcd-ansible/playbook.yaml

次の強調表示されたタスクを"Generate self-signed CA certificate"タスクに追加します。

〜/ playground / etcd-ansible / playbook.yaml
- hosts: localhost
  ...
  tasks:
    ...
    - name: "Generate self-signed CA certificate"
      openssl_certificate:
        path: ./artifacts/ca.crt
        privatekey_path: ./artifacts/ca.key
        csr_path: ./artifacts/ca.csr
        provider: selfsigned
        force: True
    - name: "Generate an `etcd` member certificate signed with our own CA certificate"
      openssl_certificate:
        path: ./artifacts/{{item}}.crt
        csr_path: ./artifacts/{{item}}.csr
        ownca_path: ./artifacts/ca.crt
        ownca_privatekey_path: ./artifacts/ca.key
        provider: ownca
        force: True
      with_items: "{{ groups['etcd'] }}"
- hosts: etcd
  become: True
  tasks:
    - name: "Create directory for etcd binaries"
...

CTRL+Xに続いてYを押して、playbook.yamlファイルを閉じて保存します。 次に、プレイブックを再度実行して、変更を適用します。

  1. ansible-playbook -i hosts playbook.yaml

次に、artifacts/ディレクトリの内容を一覧表示します。

  1. ls artifacts/

すべてのetcdメンバーとCAの秘密鍵、CSR、および証明書があります。

Output
ca.crt ca.csr ca.key etcd1.crt etcd1.csr etcd1.key etcd2.crt etcd2.csr etcd2.key etcd3.crt etcd3.csr etcd3.key

このステップでは、CAのキーを使用して各メンバーノードのCSRに署名しました。 次のステップでは、関連するファイルを各管理対象ノードにコピーして、etcdが関連するキーと証明書にアクセスしてTLS接続を設定できるようにします。

ステップ13—秘密鍵と証明書のコピー

すべてのノードには、CAの自己署名証明書(ca.crt)のコピーが必要です。 各etcdメンバーノードにも、独自の秘密鍵と証明書が必要です。 このステップでは、これらのファイルをアップロードして、新しい/etc/etcd/ssl/ディレクトリに配置します。

まず、エディタでplaybook.yamlファイルを開きます。

  1. nano $HOME/playground/etcd-ansible/playbook.yaml

Ansibleプレイブックでこれらの変更を行うには、最初にCreate directory for etcd configurationタスクのpathプロパティを更新して、/etc/etcd/ssl/ディレクトリを作成します。

〜/ playground / etcd-ansible / playbook.yaml
- hosts: etcd
  ...
  tasks:
    ...
      with_items:
        - absent
        - directory
    - name: "Create directory for etcd configuration"
      file:
        path: "{{ item }}"
        state: directory
        owner: root
        group: root
        mode: 0755
      with_items:
        - /etc/etcd
        - /etc/etcd/ssl
    - name: "Create configuration file for etcd"
      template:
...

次に、変更したタスクに続いて、ファイルをコピーするためにさらに3つのタスクを追加します。

〜/ playground / etcd-ansible / playbook.yaml
- hosts: etcd
  ...
  tasks:
    ...
    - name: "Copy over the CA certificate"
      copy:
        src: ./artifacts/ca.crt
        remote_src: False
        dest: /etc/etcd/ssl/ca.crt
        owner: root
        group: root
        mode: 0644
    - name: "Copy over the `etcd` member certificate"
      copy:
        src: ./artifacts/{{inventory_hostname}}.crt
        remote_src: False
        dest: /etc/etcd/ssl/server.crt
        owner: root
        group: root
        mode: 0644
    - name: "Copy over the `etcd` member key"
      copy:
        src: ./artifacts/{{inventory_hostname}}.key
        remote_src: False
        dest: /etc/etcd/ssl/server.key
        owner: root
        group: root
        mode: 0600
    - name: "Create configuration file for etcd"
      template:
...

CTRL+Xに続いてYを押して、playbook.yamlファイルを閉じて保存します。

ansible-playbookを再度実行して、次の変更を行います。

  1. ansible-playbook -i hosts playbook.yaml

このステップでは、秘密鍵と証明書を管理対象ノードに正常にアップロードしました。 ファイルをコピーしたら、etcd構成ファイルを更新してそれらを利用する必要があります。

ステップ14—etcdでTLSを有効にする

このチュートリアルの最後のステップでは、etcdクラスターでTLSを有効にするためにいくつかのAnsible構成を更新します。

まず、エディタを使用してtemplates/etcd.conf.yaml.j2テンプレートファイルを開きます。

  1. nano $HOME/playground/etcd-ansible/templates/etcd.conf.yaml.j2

内部に入ったら、httpの代わりにhttpsをプロトコルとして使用するようにすべてのURLを変更します。 さらに、テンプレートの最後にセクションを追加して、CA証明書、サーバー証明書、およびサーバーキーの場所を指定します。

〜/ playground / etcd-ansible / templates / etcd.conf.yaml.j2
data-dir: /var/lib/etcd/{{ inventory_hostname }}.etcd
name: {{ inventory_hostname }}
initial-advertise-peer-urls: https://{{ hostvars[inventory_hostname]['ansible_facts']['eth1']['ipv4']['address'] }}:2380
listen-peer-urls: https://{{ hostvars[inventory_hostname]['ansible_facts']['eth1']['ipv4']['address'] }}:2380,https://127.0.0.1:2380
advertise-client-urls: https://{{ hostvars[inventory_hostname]['ansible_facts']['eth1']['ipv4']['address'] }}:2379
listen-client-urls: https://{{ hostvars[inventory_hostname]['ansible_facts']['eth1']['ipv4']['address'] }}:2379,https://127.0.0.1:2379
initial-cluster-state: new
initial-cluster: {% for host in groups['etcd'] %}{{ hostvars[host]['ansible_facts']['hostname'] }}=https://{{ hostvars[host]['ansible_facts']['eth1']['ipv4']['address'] }}:2380{% if not loop.last %},{% endif %}{% endfor %}

client-transport-security:
  cert-file: /etc/etcd/ssl/server.crt
  key-file: /etc/etcd/ssl/server.key
  trusted-ca-file: /etc/etcd/ssl/ca.crt
peer-transport-security:
  cert-file: /etc/etcd/ssl/server.crt
  key-file: /etc/etcd/ssl/server.key
  trusted-ca-file: /etc/etcd/ssl/ca.crt

templates/etcd.conf.yaml.j2ファイルを閉じて保存します。

次に、Ansibleプレイブックを実行します。

  1. ansible-playbook -i hosts playbook.yaml

次に、管理対象ノードの1つにSSHで接続します。

  1. ssh root@etcd1_public_ip

内部に入ったら、etcdctl endpoint healthコマンドを実行して、エンドポイントがHTTPSを使用しているかどうか、およびすべてのメンバーが正常であるかどうかを確認します。

  1. etcdctl --cacert /etc/etcd/ssl/ca.crt endpoint health --cluster

デフォルトでは、CA証明書は/etc/ssl/certs/ディレクトリにインストールされている信頼されたルートCA証明書ではないため、--cacertフラグを使用してetcdctlに渡す必要があります。

これにより、次の出力が得られます。

Output
https://etcd3_private_ip:2379 is healthy: successfully committed proposal: took = 19.237262ms https://etcd1_private_ip:2379 is healthy: successfully committed proposal: took = 4.769088ms https://etcd2_private_ip:2379 is healthy: successfully committed proposal: took = 5.953599ms

etcdクラスターが実際に機能していることを確認するために、もう一度、1つのメンバーノードにエントリを作成し、別のメンバーノードからエントリを取得できます。

  1. etcdctl --cacert /etc/etcd/ssl/ca.crt put foo "bar"

新しい端末を使用して、別のノードにSSHで接続します。

  1. ssh root@etcd2_public_ip

次に、キーfooを使用して同じエントリを取得します。

  1. etcdctl --cacert /etc/etcd/ssl/ca.crt get foo

これにより、エントリが返され、以下の出力が表示されます。

Output
foo bar

3番目のノードでも同じことを実行して、3つのメンバーすべてが機能していることを確認できます。

結論

これで、3ノードのetcdクラスターが正常にプロビジョニングされ、TLSで保護され、機能していることが確認されました。

etcdは、もともとCoreOSによって作成されたツールです。 CoreOSに関連するetcdの使用法を理解するには、 How To Use Etcdctl and Etcd、CoreOSの分散Key-Valueストアを読むことができます。 この記事では、動的検出モデルの設定についても説明します。これについては、このチュートリアルでは説明しましたが、説明していません。

このチュートリアルの冒頭で述べたように、etcdはKubernetesエコシステムの重要な部分です。 Kubernetesとその中でのetcdの役割の詳細については、Kubernetesの概要をご覧ください。 etcdをKubernetesクラスターの一部としてデプロイする場合は、kubespraykubeadmなどの他のツールが利用可能であることを確認してください。 後者の詳細については、 Ubuntu18.04でKubeadmを使用してKubernetesクラスターを作成する方法をご覧ください。

最後に、このチュートリアルでは多くのツールを使用しましたが、それぞれについて詳しく説明することはできませんでした。 以下に、各ツールのより詳細な調査を提供するリンクがあります。