Ubuntu18.04でAnsibleを使用してetcdクラスターをセットアップして保護する方法
著者は、ウィキメディア財団を選択して、 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プレイブックも用意されています。
前提条件
このガイドを開始する前に、次のものが必要です。
-
Python 、
pip
、およびpyOpenSSLパッケージがローカルマシンにインストールされています。 Python3、pip、およびPythonパッケージをインストールする方法については、Python3をインストールしてUbuntu18.04にローカルプログラミング環境をセットアップする方法を参照してください。 -
同じローカルネットワーク上に3台のUbuntu18.04サーバーがあり、少なくとも2GBのRAMとルートSSHアクセスがあります。 また、ホスト名 etcd1 、 etcd2 、およびetcd3を持つようにサーバーを構成する必要があります。 この記事で概説されている手順は、必ずしもDigitalOcean Dropletsではなく、任意の汎用サーバーで機能します。 ただし、サーバーをDigitalOceanでホストする場合は、 DigitalOceanコントロールパネルからドロップレットを作成する方法ガイドに従って、この要件を満たすことができます。 ドロップレットを作成するときは、プライベートネットワークオプションを有効にする必要があることに注意してください。 既存のドロップレットでプライベートネットワークを有効にするには、ドロップレットでプライベートネットワークを有効にする方法を参照してください。
警告:この記事の目的はプライベートネットワークでの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
)を使用して、別のインベントリファイルを指定できます。
開始するには、ローカルマシン(コントロールノード)に新しいディレクトリを作成して、このチュートリアルのすべてのファイルを格納します。
- mkdir -p $HOME/playground/etcd-ansible
次に、作成したディレクトリに次のように入力します。
- cd $HOME/playground/etcd-ansible
ディレクトリ内で、エディタを使用してhosts
という名前の空のインベントリファイルを作成して開きます。
- nano $HOME/playground/etcd-ansible/hosts
hosts
ファイル内で、各管理対象ノードを次の形式でリストし、強調表示されたパブリックIPアドレスをサーバーの実際のパブリックIPアドレスに置き換えます。
[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_host
とansible_user
はAnsible変数です。 この場合、SSH経由で接続するときに使用するパブリックIPアドレスとSSHユーザー名をAnsibleに提供するために使用されます。
Ansibleが管理対象ノードに接続できることを確認するために、Ansibleを使用してetcd
グループ内の各ホストでhostname
コマンドを実行して接続をテストできます。
- ansible etcd -i hosts -m command -a hostname
このコマンドを分解して、各部分の意味を学びましょう。
etcd
:このコマンドで管理されているインベントリのホストを判別するために使用するホストパターンを指定します。 ここでは、ホストパターンとしてグループ名を使用しています。-i hosts
:使用するインベントリファイルを指定します。-m command
:Ansibleの背後にある機能は、モジュールによって提供されます。command
モジュールは、渡された引数を受け取り、それを各管理対象ノードでコマンドとして実行します。 このチュートリアルでは、進行するにつれて、さらにいくつかのAnsibleモジュールを紹介します。-a hostname
:モジュールに渡す引数。 引数の数と種類はモジュールによって異なります。
コマンドを実行すると、次の出力が表示されます。これは、Ansibleが正しく構成されていることを意味します。
Outputetcd2 | 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
という名前の新しいファイルを作成します。
- nano $HOME/playground/etcd-ansible/playbook.yaml
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
コマンドを使用してこのプレイブックを実行できます。
- ansible-playbook -i hosts playbook.yaml
次の出力が表示されます。これは、プレイブックが正しく機能していることを意味します。
OutputPLAY [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
環境変数の一部であるディレクトリに移動します。 手動で構成する場合、これらは各管理対象ノードで実行する手順です。
- mkdir -p /opt/etcd/bin
- cd /opt/etcd/bin
- wget -qO- https://storage.googleapis.com/etcd/v3.3.13/etcd-v3.3.13-linux-amd64.tar.gz | tar --extract --gzip --strip-components=1
- echo 'export PATH="$PATH:/opt/etcd/bin"' >> ~/.profile
- 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
プレイブックファイルを開きます。
- nano $HOME/playground/etcd-ansible/playbook.yaml
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
コマンドを再度実行します。
- ansible-playbook -i hosts playbook.yaml
出力のPLAY RECAP
セクションには、ok
とchanged
のみが表示されます。
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
を実行します。
- ssh root@etcd1_public_ip
etcd1_public_ip
は、etcd1という名前のサーバーのパブリックIPアドレスです。 SSHアクセスを取得したら、etcd --version
を実行して、インストールされているetcdのバージョンを印刷します。
- etcd --version
次のような出力が表示されます。これは、etcd
バイナリが正常にインストールされたことを意味します。
Outputetcd Version: 3.3.13
Git SHA: 98d3084
Go Version: go1.10.8
Go OS/Arch: linux/amd64
etcdctl
が正常にインストールされたことを確認するには、etcdctl version
を実行します。
- etcdctl version
次のような出力があります。
Outputetcdctl 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はsystemdをinitシステムとして使用します。つまり、ユニットファイルを書き込み、/etc/systemd/system/
ディレクトリ内に配置することで新しいサービスを作成できます。 。
まず、プロジェクトディレクトリ内に、files/
という名前の新しいディレクトリを作成します。
- mkdir files
次に、エディタを使用して、そのディレクトリ内にetcd.service
という名前の新しいファイルを作成します。
- nano files/etcd.service
次に、次のコードブロックを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モジュールを使用して実行できます。
プレイブックを開きます。
- nano $HOME/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
コマンドを再度実行して、新しい変更を適用します。
- ansible-playbook -i hosts playbook.yaml
変更が適用されたことを確認するには、最初に管理対象ノードの1つにSSHで接続します。
- ssh root@etcd1_public_ip
次に、systemctl status etcd
を実行して、systemdにetcd
サービスのステータスについて問い合わせます。
- 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
を実行して管理対象ノードを終了し、ローカルシェルに戻ります。
- exit
このステップでは、etcd
バイナリをsystemdサービスとして実行するようにプレイブックを更新しました。 次のステップでは、データを格納するためのスペースを提供することにより、etcdをセットアップし続けます。
ステップ5—データディレクトリの設定
etcdはキー値データストアです。つまり、データを格納するためのスペースを提供する必要があります。 このステップでは、プレイブックを更新して、etcdが使用する専用のデータディレクトリを定義します。
プレイブックを開きます。
- nano $HOME/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/
という名前の新しいディレクトリを作成します。
- mkdir templates
次に、エディタを使用して、ディレクトリ内にetcd.conf.yaml.j2
という名前の新しいファイルを作成します。
- nano templates/etcd.conf.yaml.j2
次に、次の行をコピーしてファイルに貼り付けます。
data-dir: /var/lib/etcd/{{ inventory_hostname }}.etcd
このファイルは、プレイブックと同じJinja2変数置換構文を使用しています。 変数を置き換えて結果を各管理対象ホストにアップロードするには、templateモジュールを使用できます。 copy
と同じように機能しますが、アップロード前に変数の置換を実行する点が異なります。
etcd.conf.yaml.j2
を終了し、プレイブックを開きます。
- nano $HOME/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サービスファイルを開きます。
- nano files/etcd.service
以下で強調表示されている--config-file
フラグを追加して、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つのコマンドを実行します。
コマンドを実行するには、コマンドモジュールを使用できます。
- nano $HOME/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
をもう一度実行します。
- ansible-playbook -i hosts playbook.yaml
etcd
サービスが再起動されて有効になったことを確認するには、次のいずれかの管理対象ノードにSSHで接続します。
- ssh root@etcd1_public_ip
次に、systemctl status etcd
を実行して、etcd
サービスのステータスを確認します。
- systemctl status etcd
以下で強調表示されているように、enabled
とactive (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
を使用すると、put
、get
、およびそれぞれdel
サブコマンド。
etcd1 管理対象ノード内にいることを確認し、次のetcdctl
コマンドを実行して、etcdインストールが機能していることを確認します。
まず、put
サブコマンドを使用して新しいエントリを作成します。
put
サブコマンドの構文は次のとおりです。
etcdctl put key value
etcd1 で、次のコマンドを実行します。
- etcdctl put foo "bar"
実行したコマンドは、etcdに値"bar"
をストアのキーfoo
に書き込むように指示します。
次に、出力にOK
が出力されます。これは、データが保持されていることを示しています。
OutputOK
次に、get
サブコマンドを使用してこのエントリを取得できます。このサブコマンドの構文は、etcdctl get key
です。
- etcdctl get foo
この出力は、最初の行にキーを示し、2番目の行に前に挿入した値を示しています。
Outputfoo
bar
del
サブコマンドを使用してエントリを削除できます。このサブコマンドの構文は、etcdctl del key
です。
- etcdctl del foo
削除されたエントリの数を示す次の出力が表示されます。
Output1
次に、削除されたキーと値のペアを取得するために、get
サブコマンドをもう一度実行してみましょう。
- etcdctl get foo
出力は受信されません。つまり、etcdctl
はキーと値のペアを取得できません。 これにより、エントリが削除された後、そのエントリを取得できなくなったことを確認できます。
etcdとetcdctl
の基本的な操作をテストしたので、管理対象ノードを終了してローカル環境に戻りましょう。
- 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
テンプレートファイルを開きます。
- nano 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を使用して各メンバーを識別します。 ただし、人間が読める形式の名前を使用すると、構成ファイル内やコマンドラインで簡単に参照できます。 ここでは、ホスト名をメンバー名として使用します(つまり、etcd1
、etcd2
、および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-urls
URLの1つと一致する必要があります。initial-cluster-state
–new
またはexisting
のいずれか。
一貫性を確保するために、etcdは、ノードの大部分が正常である場合にのみ決定を下すことができます。 これは、クォーラムの確立として知られています。 つまり、3つのメンバーのクラスターでは、2つ以上のメンバーが正常である場合にクォーラムに達します。
initial-cluster-state
パラメーターがnew
に設定されている場合、etcd
は、これがブートストラップされている新しいクラスターであることを認識し、クォーラムが到達する。 より具体的には、最初のメンバーが開始された後、3分の1(33.33%)が50%以下であるため、クォーラムはありません。 通常、etcdは停止し、それ以上のアクションのコミットを拒否し、クラスターが形成されることはありません。 ただし、initial-cluster-state
をnew
に設定すると、最初のクォーラムの不足が無視されます。
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
ファイルを開きます。
- nano $HOME/playground/etcd-ansible/playbook.yaml
次に、"Create a data directory"
タスクの前に、etcd
サービスを停止するタスクを追加します。
- 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"
タスクを更新して、最初にデータディレクトリを削除し、再作成します。
- 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
プロパティの値が異なります。 ここでは、アイテムabsent
とdirectory
を使用してリストを反復処理しています。これにより、データディレクトリが最初に削除され、その後に再作成されます。
CTRL+X
に続いてY
を押して、playbook.yaml
ファイルを閉じて保存します。 次に、ansible-playbook
を再度実行します。 Ansibleは、単一の3メンバーetcd
クラスターを作成します。
- ansible-playbook -i hosts playbook.yaml
これは、etcdメンバーノードにSSHで接続することで確認できます。
- ssh root@etcd1_public_ip
次に、etcdctl endpoint health --cluster
を実行します。
- etcdctl endpoint health --cluster
これにより、クラスターの各メンバーの正常性が一覧表示されます。
Outputhttp://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
を実行します。
- etcdctl put foo "bar"
次に、新しい端末を使用して、別のメンバーノードにSSHで接続します。
- ssh root@etcd2_public_ip
次に、キーを使用して同じエントリを取得してみます。
- etcdctl get foo
エントリを取得できるようになります。これは、クラスタが機能していることを証明します。
Outputfoo
bar
最後に、各管理対象ノードを終了して、ローカルマシンに戻ります。
- exit
- 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で接続します。
- ssh root@etcd1_public_ip
次に、次のコマンドを実行します。
- ip -f inet addr show eth1
次の行のような出力があります。
Output3: 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ユーティリティにパイプ処理できます。これは、テキストのフィルタリングと変換に使用されます。
- ip -f inet addr show eth1 | sed -En -e 's/.*inet ([0-9.]+).*/\1/p'
現在、唯一の出力はプライベートIPアドレス自体です。
Output10.131.255.176
上記のコマンドが機能することを確認したら、管理対象ノードを終了します。
- exit
上記のコマンドをプレイブックに組み込むには、最初にplaybook.yaml
ファイルを開きます。
- nano $HOME/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
ファイルをエディターで開きます。
- nano $HOME/playground/etcd-ansible/playbook.yaml
その中で、file
モジュールを使用して、artifacts/
ディレクトリを作成します。
...
- 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:
...
次に、プレイの最後に別のタスクを追加して、秘密鍵を生成します。
...
- 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を生成します。
...
- 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
ファイルを閉じて保存します。 次に、プレイブックを再度実行します。
- ansible-playbook -i hosts playbook.yaml
これで、プロジェクトディレクトリ内にartifacts
という新しいディレクトリが見つかります。 ls
を使用して、その内容を一覧表示します。
- ls artifacts
etcdメンバーのそれぞれの秘密鍵とCSRがあります。
Outputetcd1.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
ファイルを開きます。
- nano $HOME/playground/etcd-ansible/playbook.yaml
次に、前の手順と同様に、localhost
プレイにタスクを追加して、CAの秘密鍵を生成します。
- 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証明書として使用できることを示すために、基本的な制約とキーの使用法の拡張機能を追加しています。
- 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に自己署名します。
- 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
ファイルを閉じて保存します。 次に、プレイブックを再度実行して、変更を適用します。
- ansible-playbook -i hosts playbook.yaml
ls
を実行して、artifacts/
ディレクトリの内容を確認することもできます。
- ls artifacts/
これで、新しく生成されたCA証明書(ca.crt
)が見つかります。
Outputca.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証明書を使用して署名できます。
プレイブックを開きます。
- nano $HOME/playground/etcd-ansible/playbook.yaml
次の強調表示されたタスクを"Generate self-signed CA certificate"
タスクに追加します。
- 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
ファイルを閉じて保存します。 次に、プレイブックを再度実行して、変更を適用します。
- ansible-playbook -i hosts playbook.yaml
次に、artifacts/
ディレクトリの内容を一覧表示します。
- ls artifacts/
すべてのetcdメンバーとCAの秘密鍵、CSR、および証明書があります。
Outputca.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
ファイルを開きます。
- nano $HOME/playground/etcd-ansible/playbook.yaml
Ansibleプレイブックでこれらの変更を行うには、最初にCreate directory for etcd configuration
タスクのpath
プロパティを更新して、/etc/etcd/ssl/
ディレクトリを作成します。
- 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つのタスクを追加します。
- 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
を再度実行して、次の変更を行います。
- ansible-playbook -i hosts playbook.yaml
このステップでは、秘密鍵と証明書を管理対象ノードに正常にアップロードしました。 ファイルをコピーしたら、etcd構成ファイルを更新してそれらを利用する必要があります。
ステップ14—etcdでTLSを有効にする
このチュートリアルの最後のステップでは、etcdクラスターでTLSを有効にするためにいくつかのAnsible構成を更新します。
まず、エディタを使用してtemplates/etcd.conf.yaml.j2
テンプレートファイルを開きます。
- nano $HOME/playground/etcd-ansible/templates/etcd.conf.yaml.j2
内部に入ったら、http
の代わりにhttps
をプロトコルとして使用するようにすべてのURLを変更します。 さらに、テンプレートの最後にセクションを追加して、CA証明書、サーバー証明書、およびサーバーキーの場所を指定します。
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プレイブックを実行します。
- ansible-playbook -i hosts playbook.yaml
次に、管理対象ノードの1つにSSHで接続します。
- ssh root@etcd1_public_ip
内部に入ったら、etcdctl endpoint health
コマンドを実行して、エンドポイントがHTTPSを使用しているかどうか、およびすべてのメンバーが正常であるかどうかを確認します。
- etcdctl --cacert /etc/etcd/ssl/ca.crt endpoint health --cluster
デフォルトでは、CA証明書は/etc/ssl/certs/
ディレクトリにインストールされている信頼されたルートCA証明書ではないため、--cacert
フラグを使用してetcdctl
に渡す必要があります。
これにより、次の出力が得られます。
Outputhttps://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つのメンバーノードにエントリを作成し、別のメンバーノードからエントリを取得できます。
- etcdctl --cacert /etc/etcd/ssl/ca.crt put foo "bar"
新しい端末を使用して、別のノードにSSHで接続します。
- ssh root@etcd2_public_ip
次に、キーfoo
を使用して同じエントリを取得します。
- etcdctl --cacert /etc/etcd/ssl/ca.crt get foo
これにより、エントリが返され、以下の出力が表示されます。
Outputfoo
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クラスターの一部としてデプロイする場合は、kubesprayやkubeadmなどの他のツールが利用可能であることを確認してください。 後者の詳細については、 Ubuntu18.04でKubeadmを使用してKubernetesクラスターを作成する方法をご覧ください。
最後に、このチュートリアルでは多くのツールを使用しましたが、それぞれについて詳しく説明することはできませんでした。 以下に、各ツールのより詳細な調査を提供するリンクがあります。
- Ansible Playbookのより高度な構文については、 Configuration Management 101:Writing AnsiblePlaybooksを参照してください。 Ansibleの公式IntrotoPlaybooksも素晴らしいリソースです。
- OpenSSLの詳細については、 OpenSSL Essentials:SSL証明書、秘密鍵、およびCSRの操作を参照してください。