作者选择了 维基媒体基金会作为 写给捐赠计划的一部分接受捐赠。
介绍
etcd是许多平台和工具所依赖的分布式密钥价值商店,包括 Kubernetes, Vulcand和 Doorman.在Kubernetes, etcd被用作存储集群状态的全球配置商店。知道如何管理等是管理Kubernetes集群的必不可少的。尽管有许多管理的Kubernetes产品,也被称为 Kubernetes-as-a-Service,可以消除这种管理负担,但许多公司仍然选择在本地运行自管理的Kubernetes集群,因为它带来了灵活性。
本文的上半部分将指导您在 Ubuntu 18.04 服务器上建立一个 3 节点的 etcd 群集。下半部分将专注于使用 Transport Layer Security,或 TLS来保护群集。以自动的方式运行每个设置,我们将使用 Ansible在整个过程中。 Ansible 是一个类似于 Puppet、 Chef和 SaltStack的 configuration management 工具;它允许我们以声明的方式定义每个设置步骤,在名为 _playbooks_的文件中。
在本教程结束时,您将有一个安全的3节点 etcd集群在您的服务器上运行,您还将有一个Ansible播放簿,允许您在新鲜的服务器上反复和一致地重现相同的设置。
前提条件
在您开始本指南之前,您将需要以下内容:
- Python,
pip
,和pyOpenSSL
包安装在您的本地机器上。 要学习如何安装Python3,Pip和Python包,请参阅 How To Install Python 3 and Set Up a Local Programming Environment on Ubuntu 18.04. - 三个Ubuntu 18.04服务器在同一个本地网络上运行,至少有2GB的RAM和根SSH访问. 您还应该配置服务器以拥有主机名 etcd1, etcd2,和 etcd3。 本文中描述的步骤将运行在任何通用服务器上,不一定是DigitalOcean Droplets。 但是,如果你想在DigitalO
<$>[警告] 警告: 由于本文的目的是为在私人网络上设置一个 etcd 集群提供介绍,此设置中的三个 Ubuntu 18.04 服务器没有通过防火墙进行测试,并且作为 root 用户访问。 在生产设置中,任何暴露于公共互联网的节点都需要防火墙和 sudo 用户遵守安全最佳实践。
如果你不知道什么是SSH,或者没有SSH密钥对,你可以通过阅读 SSH Essentials: Working with SSH Servers, Clients, and Keys的文章来了解它。
- Ansible安装在你的本地机器上。例如,如果你正在运行Ubuntu 18.04,你可以通过以下的 Step 1的文章来安装Ansible。 这将使
ansible
和ansible-playbook
命令在你的机器上可用。你也可能想要保持这个(How to Use Ansible: A Reference Guide)(https://andsky.com/tech/tutorials/how-to-use-ansible-cheat-sheet-guide)的方便。本教程中的命令应该与Ansible v2.2工作;我们已经测试了它在运行V9.7K_M2.1 PyBR2.
步骤 1 – 为控制节点配置 Ansible
Ansible 是一个用于管理服务器的工具。 Ansible 正在管理的服务器被称为 managed nodes,而运行 Ansible 的机器被称为 control node。 Ansible 通过使用控制节点上的 SSH 密钥来获取访问管理节点而工作。一旦建立 SSH 会话, Ansible 将运行一组脚本来提供和配置管理节点。 在此步骤中,我们将测试我们是否能够使用 Ansible 连接到管理节点并运行 hostname
命令。
例如,您可以使用 Ansible 提供一些新服务器,但稍后可以使用它来重新配置另一组服务器。 为了让管理员更好地组织管理节点的集合, Ansible 提供了 host inventory(或简称 inventory)的概念。 您可以定义您希望在 inventory file 中管理的每个节点,并将它们组织成 groups。 然后,当运行ansible
和ansible-playbook
命令时,您可以指定命令适用于哪些主机或组。
默认情况下,Ansible 会从 /etc/ansible/hosts’ 读取库存文件;但是,我们可以使用
--inventory' 旗帜(或简称 `-i' )来指定不同的库存文件。
要开始,在本地机器上创建一个新目录( control node),以容纳本教程的所有文件:
1mkdir -p $HOME/playground/etcd-ansible
然后,进入您刚刚创建的目录:
1cd $HOME/playground/etcd-ansible
在目录中,使用您的编辑器创建并打开一个名为主机
的空库存文件:
1nano $HOME/playground/etcd-ansible/hosts
在主机
文件中,以以下格式列出您管理的每个节点,以您的服务器的实际公共 IP 地址代替所突出的公共 IP 地址:
1[label ~/playground/etcd-ansible/hosts]
2[etcd]
3etcd1 ansible_host=etcd1_public_ip ansible_user=root
4etcd2 ansible_host=etcd2_public_ip ansible_user=root
5etcd3 ansible_host=etcd3_public_ip ansible_user=root
etcd
行定义了一个名为 etcd
的组。在组定义下,我们列出了我们所有的受管理节点,每个行都以一个名称(例如, etcd1
)开始,这使我们可以使用一个易于记住的 IP 地址来参考每个主机,而不是长的 IP 地址。
为了确保Ansible能够连接到我们的管理节点,我们可以通过使用Ansible来测试连接性,在etcd
组中的每个主机上运行hostname
命令:
1ansible etcd -i hosts -m command -a hostname
让我们分解这个命令来了解每个部分的含义:
etcd
:指定使用的 host pattern 来确定库存中的哪些主机正在使用这个命令进行管理。在这里,我们正在使用群组名称作为主机模式。i hosts
:指定要使用的库存文件。-m command
: Ansible 背后的功能由 modules 提供。command
模块将传入的参数,并在每个受管理的节点上作为命令执行。本教程将随着我们进步而引入一些更多 Ansible 模块。-一个主机名称
:将参数传入模块。 参数和参数类型取决于模块。
运行命令后,您会看到以下输出,这意味着 Ansible 配置正确:
1[secondary_label Output]
2etcd2 | CHANGED | rc=0 >>
3etcd2
4
5etcd3 | CHANGED | rc=0 >>
6etcd3
7
8etcd1 | CHANGED | rc=0 >>
9etcd1
Ansible 运行的每个命令都被称为 task。在命令行上使用ansible
来运行任务,被称为运行 ad-hoc 命令。
一个小小的改进将是写一个壳脚本,并使用Ansible的 script
模块运行我们的命令。这将允许我们记录我们在版本控制中采取的配置步骤。然而,壳脚本是 imperative,这意味着我们有责任找出要运行的命令(如何
)来配置系统到所需状态。Ansible,另一方面,倡导一个 declarative 方法,我们定义我们的服务器的所需状态应该是什么
在配置文件中,Ansible负责将服务器带到所需状态。
声明方法是优先的,因为配置文件的意图立即传达,这意味着它更容易理解和维护,它还将处理边缘案例的责任放在Ansible上,而不是管理员,节省了我们很多工作。
现在您已经配置了 Ansible 控制节点以与管理节点进行通信,在下一步,我们将向您介绍 Ansible playbooks,允许您以声明的方式指定任务。
步骤 2 — 使用 Ansible Playbooks 获取管理节点的主机名
在此步骤中,我们将复制第 1 步所做的 - 打印受管理节点的主机名 - 但是,而不是运行临时任务,我们将声明地将每个任务定义为 Ansible 播放簿并运行它。
在您的项目目录中,使用您的编辑器创建一个名为playbook.yaml
的新文件:
1nano $HOME/playground/etcd-ansible/playbook.yaml
在playbook.yaml
中,添加以下几行:
1[label ~/playground/etcd-ansible/playbook.yaml]
2- hosts: etcd
3 tasks:
4 - name: "Retrieve hostname"
5 command: hostname
6 register: output
7 - name: "Print hostname"
8 debug: var=output.stdout_lines
关闭并保存playbook.yaml
文件,按CTRL+X
,然后按Y
。
播放簿包含一个 plays 列表;每个播放列表包含一个任务列表,应该在所有主机上运行,匹配由主机
键指定的主机模式。 在本播放簿中,我们有一个包含两个任务的游戏。第一个任务使用命令
模块运行主机名
命令,并将输出记录在名为输出
的变量上。在第二个任务中,我们使用 debug
模块来打印输出
变量的stdout_lines
属性。
现在我们可以使用ansible-playbook
命令运行此播放簿:
1ansible-playbook -i hosts playbook.yaml
您会看到以下输出,这意味着您的播放簿正在正确工作:
1[secondary_label Output]
2PLAY [etcd] ***********************************************************************************************************************
3
4TASK [Gathering Facts] ************************************************************************************************************
5ok: [etcd2]
6ok: [etcd3]
7ok: [etcd1]
8
9TASK [Retrieve hostname] **********************************************************************************************************
10changed: [etcd2]
11changed: [etcd3]
12changed: [etcd1]
13
14TASK [Print hostname] *************************************************************************************************************
15ok: [etcd1] => {
16 "output.stdout_lines": [
17 "etcd1"
18 ]
19}
20ok: [etcd2] => {
21 "output.stdout_lines": [
22 "etcd2"
23 ]
24}
25ok: [etcd3] => {
26 "output.stdout_lines": [
27 "etcd3"
28 ]
29}
30
31PLAY RECAP ************************************************************************************************************************
32etcd1 : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
33etcd2 : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
34etcd3 : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
<$>[注]
注:ansible-playbook
有时会使用cowsay
作为打印标题的有趣方式。如果您在您的终端上发现了大量的ASCII艺术牛,现在您知道为什么。要禁用此功能,请在运行ansible-playbook
之前,将ANSIBLE_NOCOWS
环境变量设置为1
,然后在您的壳中运行export ANSIBLE_NOCOWS=1
。
在此步骤中,我们已经从执行强制性临时任务转移到运行声明图书. 在下一步中,我们将用将我们的 etcd 集群设置的任务来替换这两个演示任务。
步骤 3 — 在管理节点上安装 etcd
在此步骤中,我们将向您展示手动安装etcd
的命令,并展示如何将这些命令转换为我们的Ansible播放簿中的任务。
etcd
和其客户端etcdctl
可作为二进制,我们将下载、提取并移动到属于PATH
环境变量的目录中。
1[environment second]
2mkdir -p /opt/etcd/bin
3cd /opt/etcd/bin
4wget -qO- https://storage.googleapis.com/etcd/v3.3.13/etcd-v3.3.13-linux-amd64.tar.gz | tar --extract --gzip --strip-components=1
5echo 'export PATH="$PATH:/opt/etcd/bin"' >> ~/.profile
6echo 'export ETCDCTL_API=3" >> ~/.profile
前四个命令下载并提取二进制到 /opt/etcd/bin/
目录. 默认情况下, etcdctl
客户端将使用 API v2 与 etcd
服务器进行通信. 由于我们正在运行 etcd v3.x,最后一个命令将 ETCDCTL_API
环境变量设置为 3
。
<$>[注] 注: 在这里,我们正在使用 etcd v3.3.13 用于使用 AMD64 指令集的处理器的机器,您可以在官方 GitHub 发布页面上找到其他系统和其他版本的二进制。
要在标准格式中复制相同步骤,我们可以将任务添加到我们的播放簿中。
1nano $HOME/playground/etcd-ansible/playbook.yaml
用以下内容取代整个 playbook.yaml
文件:
1[label ~/playground/etcd-ansible/playbook.yaml]
2- hosts: etcd
3 become: True
4 tasks:
5 - name: "Create directory for etcd binaries"
6 file:
7 path: /opt/etcd/bin
8 state: directory
9 owner: root
10 group: root
11 mode: 0700
12 - name: "Download the tarball into the /tmp directory"
13 get_url:
14 url: https://storage.googleapis.com/etcd/v3.3.13/etcd-v3.3.13-linux-amd64.tar.gz
15 dest: /tmp/etcd.tar.gz
16 owner: root
17 group: root
18 mode: 0600
19 force: True
20 - name: "Extract the contents of the tarball"
21 unarchive:
22 src: /tmp/etcd.tar.gz
23 dest: /opt/etcd/bin/
24 owner: root
25 group: root
26 mode: 0600
27 extra_opts:
28 - --strip-components=1
29 decrypt: True
30 remote_src: True
31 - name: "Set permissions for etcd"
32 file:
33 path: /opt/etcd/bin/etcd
34 state: file
35 owner: root
36 group: root
37 mode: 0700
38 - name: "Set permissions for etcdctl"
39 file:
40 path: /opt/etcd/bin/etcdctl
41 state: file
42 owner: root
43 group: root
44 mode: 0700
45 - name: "Add /opt/etcd/bin/ to the $PATH environment variable"
46 lineinfile:
47 path: /etc/profile
48 line: export PATH="$PATH:/opt/etcd/bin"
49 state: present
50 create: True
51 insertafter: EOF
52 - name: "Set the ETCDCTL_API environment variable to 3"
53 lineinfile:
54 path: /etc/profile
55 line: export ETCDCTL_API=3
56 state: present
57 create: True
58 insertafter: EOF
每个任务使用一个模块;对于这个任务集,我们正在使用以下模块:
file
:创建/opt/etcd/bin
目录,然后为etcd
和etcdctl
二进制设置文件权限。get_url
:将 gzipped tarball 下载到管理节点上。unarchive
:从 gzipped tarball 中提取和解包etcd
和etcdctl
二进制。
要应用这些更改,请关闭并保存playbook.yaml
文件,按CTRL+X
,然后按Y
。
1ansible-playbook -i hosts playbook.yaml
输出的播放重复
部分只会显示OK
和更改
:
1[secondary_label Output]
2...
3PLAY RECAP ************************************************************************************************************************
4etcd1 : ok=8 changed=7 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
5etcd2 : ok=8 changed=7 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
6etcd3 : ok=8 changed=7 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
要确认 etcd 的正确安装,手动将 SSH 放入管理节点中的一个,然后运行etcd
和etcdctl
:
1ssh root@etcd1_public_ip
etcd1_public_ip
是服务器的公共 IP 地址,名为 etcd1.一旦您获得了 SSH 访问,请运行 `etcd --version' 来打印安装的 etcd 版本:
1[environment second]
2etcd --version
您会发现类似于下面的输出,这意味着etcd
二进制已成功安装:
1[environment second]
2[secondary_label Output]
3etcd Version: 3.3.13
4Git SHA: 98d3084
5Go Version: go1.10.8
6Go OS/Arch: linux/amd64
要确认etcdctl
已成功安装,请运行etcdctl 版本
:
1[environment second]
2etcdctl version
你会发现类似于以下的输出:
1[environment second]
2[secondary_label Output]
3etcdctl version: 3.3.13
4API version: 3.3
请注意,输出表示API 版本: 3.3
,这也证实我们的ETCDCTL_API
环境变量已正确设置。
退出 etcd1服务器,返回本地环境。
我们现在已经成功地安装了etcd
和etcdctl
在我们所有受管理的节点上,在下一步,我们将为我们的游戏添加更多的任务以运行 etcd作为背景服务。
步骤 4 — 创建用于 etcd 的单元文件
使用 Ansible 运行 etcd 的最快方法可能是使用命令
模块运行/opt/etcd/bin/etcd
但是,这不会起作用,因为它将使etcd
作为一个前沿过程运行。使用命令
模块会导致 Ansible 挂在等待etcd
命令返回时,这永远不会发生。
Ubuntu 18.04 使用 systemd作为其 init system,这意味着我们可以通过写 unit files 并将它们放入 /etc/systemd/system/
目录中创建新的服务。
首先,在我们的项目目录中,创建一个名为files/
的新目录:
1mkdir files
然后,使用您的编辑器,在该目录中创建一个名为etcd.service
的新文件:
1nano files/etcd.service
接下来,将下列代码块复制到files/etcd.service
文件中:
1[label ~/playground/etcd-ansible/files/etcd.service]
2[Unit]
3Description=etcd distributed reliable key-value store
4
5[Service]
6Type=notify
7ExecStart=/opt/etcd/bin/etcd
8Restart=always
该单元文件定义了一个服务,该服务在/opt/etcd/bin/etcd
下运行,通知 systemd 完成初始化时,并在任何时候退出时总是重新启动。
<$>[注] 注: 如果您想了解更多关于 systemd 和单元文件的信息,或希望将单元文件定制为您的需求,请阅读 理解 Systemd 单元和单元文件指南。
关闭并保存files/etcd.service
文件,按CTRL+X
,然后按Y
。
接下来,我们需要在我们的播放簿中添加一个任务,将本地文件files/etcd.service
复制到每个受管理节点的/etc/systemd/system/etcd.service
目录中。
打开你的游戏簿:
1nano $HOME/playground/etcd-ansible/playbook.yaml
将以下突出任务附加到我们现有任务的结尾:
1[label ~/playground/etcd-ansible/playbook.yaml]
2- hosts: etcd
3 become: True
4 tasks:
5 ...
6 - name: "Set the ETCDCTL_API environment variable to 3"
7 lineinfile:
8 path: /etc/profile
9 line: export ETCDCTL_API=3
10 state: present
11 create: True
12 insertafter: EOF
13 - name: "Create a etcd service"
14 copy:
15 src: files/etcd.service
16 remote_src: False
17 dest: /etc/systemd/system/etcd.service
18 owner: root
19 group: root
20 mode: 0644
通过将单元文件复制到 /etc/systemd/system/etcd.service
,服务现在被定义。
保存和退出播放簿。
再次运行相同的ansible-playbook
命令来应用新的更改:
1ansible-playbook -i hosts playbook.yaml
若要确认已应用的更改,请先将 SSH 转移到管理节点中的一个:
1ssh root@etcd1_public_ip
然后,运行systemctl status etcd
,查询systemd
关于etcd
服务的状态:
1[environment second]
2systemctl status etcd
您将找到以下输出,该输出表示服务已加载:
1[secondary_label Output]
2[environment second]
3● etcd.service - etcd distributed reliable key-value store
4 Loaded: loaded (/etc/systemd/system/etcd.service; static; vendor preset: enabled)
5 Active: inactive (dead)
6...
<$>[注] **注:**输出的最后一行(‘活跃:不活跃(死亡)`)表示该服务不活跃,这意味着在系统启动时不会自动运行。
按q
返回壳,然后运行出口
,退出管理节点并返回本地壳:
1[environment second]
2exit
在此步骤中,我们更新了我们的播放簿以运行etcd
二进制作为 systemd 服务. 在下一步,我们将继续设置 etcd,为其提供存储数据的空间。
步骤 5 – 配置数据目录
etcd 是一个关键值数据存储,这意味着我们必须为其提供存储数据的空间. 在此步骤中,我们将更新我们的播放簿,以定义用于 etcd 的专用数据目录。
打开你的游戏簿:
1nano $HOME/playground/etcd-ansible/playbook.yaml
将下列任务附加到任务列表的末尾:
1[label ~/playground/etcd-ansible/playbook.yaml]
2- hosts: etcd
3 become: True
4 tasks:
5 ...
6 - name: "Create a etcd service"
7 copy:
8 src: files/etcd.service
9 remote_src: False
10 dest: /etc/systemd/system/etcd.service
11 owner: root
12 group: root
13 mode: 0644
14 - name: "Create a data directory"
15 file:
16 path: /var/lib/etcd/{{ inventory_hostname }}.etcd
17 state: directory
18 owner: root
19 group: root
20 mode: 0755
在这里,我们使用/var/lib/etcd/hostname.etcd
作为数据目录,其中hostname
是当前托管节点的托管名称。inventory_hostname
是代表当前托管节点的托管名称的变量;其值由Ansible自动填充。
关闭文本编辑器并保存文件。
接下来,我们需要指示 etcd 使用此数据目录. 我们通过将data-dir
参数传输到 etcd. 为了设置 etcd 参数,我们可以使用环境变量,命令行旗和配置文件的组合。
在项目目录中,创建一个名为templates/
的新目录:
1mkdir templates
然后,使用您的编辑器,在目录中创建一个名为 `etcd.conf.yaml.j2’的新文件:
1nano templates/etcd.conf.yaml.j2
接下来,复制下列行并粘贴到文件中:
1[label ~/playground/etcd-ansible/templates/etcd.conf.yaml.j2]
2data-dir: /var/lib/etcd/{{ inventory_hostname }}.etcd
此文件使用与我们的演示本相同的 Jinja2 变量替代语法. 为了替代变量并将结果上传到每个受管理的主机,我们可以使用 template
模块。
离开 etcd.conf.yaml.j2
,然后打开播放簿:
1nano $HOME/playground/etcd-ansible/playbook.yaml
将下列任务附加到任务列表中,以创建目录,并将模板配置文件上传到其中:
1[label ~/playground/etcd-ansible/playbook.yaml]
2- hosts: etcd
3 become: True
4 tasks:
5 ...
6 - name: "Create a data directory"
7 file:
8 ...
9 mode: 0755
10 - name: "Create directory for etcd configuration"
11 file:
12 path: /etc/etcd
13 state: directory
14 owner: root
15 group: root
16 mode: 0755
17 - name: "Create configuration file for etcd"
18 template:
19 src: templates/etcd.conf.yaml.j2
20 dest: /etc/etcd/etcd.conf.yaml
21 owner: root
22 group: root
23 mode: 0600
保存并关闭此文件。
由于我们已经做出这种更改,我们需要更新我们的服务的单元文件,以便传递我们的配置文件的位置(即, /etc/etcd/etcd.conf.yaml
)。
在本地机器上打开 etcd 服务文件:
1nano files/etcd.service
更新files/etcd.service
文件,添加以下标志的--config-file
旗帜:
1[label ~/playground/etcd-ansible/files/etcd.service]
2[Unit]
3Description=etcd distributed reliable key-value store
4
5[Service]
6Type=notify
7ExecStart=/opt/etcd/bin/etcd --config-file /etc/etcd/etcd.conf.yaml
8Restart=always
保存并关闭此文件。
在此步骤中,我们使用我们的播放簿为 etcd 提供一个数据目录,以存储其数据. 在下一步,我们将添加几项任务来重新启动etcd
服务并让它启动。
步骤 6 – 启用和启动 etcd 服务
每当我们对服务的单元文件进行更改时,我们需要重新启动服务以使其生效。我们可以通过运行systemctl restart etcd
命令来做到这一点。
要执行命令,我们可以使用 command
模块:
1nano $HOME/playground/etcd-ansible/playbook.yaml
将下列任务附加到任务列表的末尾:
1[label ~/playground/etcd-ansible/playbook.yaml]
2- hosts: etcd
3 become: True
4 tasks:
5 ...
6 - name: "Create configuration file for etcd"
7 template:
8 ...
9 mode: 0600
10 - name: "Enable the etcd service"
11 command: systemctl enable etcd
12 - name: "Start the etcd service"
13 command: systemctl restart etcd
保存并关闭文件。
再次运行ansible-playbook -i hosts playbook.yaml
:
1ansible-playbook -i hosts playbook.yaml
要检查etcd
服务是否重新启动并启用,请将 SSH 转入管理节点之一:
1ssh root@etcd1_public_ip
然后,运行systemctl status etcd
来检查etcd
服务的状态:
1[environment second]
2systemctl status etcd
您将看到启用
和活跃(运行)
,如下所示;这意味着我们在游戏中所做的更改已经生效:
1[environment second]
2[secondary_label Output]
3● etcd.service - etcd distributed reliable key-value store
4 Loaded: loaded (/etc/systemd/system/etcd.service; static; vendor preset: enabled)
5 Active: active (running)
6 Main PID: 19085 (etcd)
7 Tasks: 11 (limit: 2362)
在此步骤中,我们使用了命令
模块来运行systemctl
命令,在我们的管理节点上重新启动并启用etcd
服务.现在我们已经设置了 etcd 安装,在下一步中,我们将通过执行一些基本的创建,阅读,更新和删除(CRUD)操作来测试其功能。
步骤7 - 测试等
虽然我们有一个工作 etcd 安装,但它不安全,还没有准备好用于生产使用,但在我们在以后的步骤中确保我们的 etcd 设置之前,让我们先了解 etcd 能在功能方面做些什么。
默认情况下, etcd 会显示在端口2379
上进行客户端通信的 API。这意味着我们可以使用 HTTP 客户端向 etcd 发送原始 API 请求,但是,使用官方 etcd 客户端etcdctl
更快,可以使用put
、get
和del
子命令来创建/更新、检索和删除关键值对。
请确保您仍在 etcd1 管理节点中,然后运行以下etcdctl
命令,以确认您的 etcd 安装正在运行。
首先,使用put
子命令创建一个新的条目。
put
子命令具有以下语法:
1etcdctl put key value
在 etcd1上,运行以下命令:
1[environment second]
2etcdctl put foo "bar"
我们刚刚运行的命令指示 etcd在商店中的foo
键中键入值bar
。
然后你会发现OK
在输出中打印,这表明数据仍然存在:
1[environment second]
2[secondary_label Output]
3OK
然后我们可以使用get
子命令获取此条目,该子命令具有etcdctl get key
语法:
1[environment second]
2etcdctl get foo
您将找到此输出,该输出显示了第一行上的密钥和您在第二行上提前插入的值:
1[environment second]
2[secondary_label Output]
3foo
4bar
我们可以使用del
子命令删除该条目,其语法为etcdctl del key
:
1[environment second]
2etcdctl del foo
您将找到以下输出,该输出显示删除的条目数量:
1[environment second]
2[secondary_label Output]
31
现在,让我们再次运行得到
子命令,试图检索已删除的关键值对:
1[environment second]
2etcdctl get foo
您将不会收到输出,这意味着etcdctl
无法检索密钥值对,这确认在删除输入后,它不再可以检索。
现在你已经测试了 etcd 和 'etcdctl' 的基本操作,让我们离开我们的管理节点,回到您的本地环境:
1[environment second]
2exit
在此步骤中,我们使用etcdctl
客户端向 etcd 发送请求. 在此时刻,我们正在运行三个单独的 etcd 实例,每个实例相互独立操作. 然而, etcd 被设计为分布式关键值存储,这意味着多个 etcd 实例可以组合成一个单一的 cluster;然后每个实例成为群集的 member。 形成群集后,您将能够从群集的不同成员中输入一个关键值对。
步骤 8 — 使用静态发现构建一个集群
要创建一个3节点集群,而不是3个1节点集群,我们必须配置这些 etcd安装以相互通信,这意味着每个人都必须知道对方的IP地址。这个过程被称为 discovery. 发现可以使用 static configuration 或 dynamic service discovery。
通过静态配置的发现是需要最少设置的方法;这就是每个成员的终点在执行之前被传入etcd
命令的地方。
- 会员数量已知
- 每个会员的终端点已知
- 所有终端点的 IP 地址是静态的
如果这些条件无法满足,那么您可以使用动态发现服务. 动态发现服务,所有实例都会在发现服务中注册,允许每个会员获取有关其他会员位置的信息。
由于我们知道我们想要一个 3 节点的 etcd 集群,而且我们所有的服务器都有静态 IP 地址,我们会使用静态发现。 要使用静态发现来启动我们的集群,我们必须在我们的配置文件中添加几种参数。 使用编辑器打开templates/etcd.conf.yaml.j2
模板文件:
1nano templates/etcd.conf.yaml.j2
然后,添加以下突出的行:
1[label ~/playground/etcd-ansible/templates/etcd.conf.yaml.j2]
2data-dir: /var/lib/etcd/{{ inventory_hostname }}.etcd
3name: {{ inventory_hostname }}
4initial-advertise-peer-urls: http://{{ hostvars[inventory_hostname]['ansible_facts']['eth1']['ipv4']['address'] }}:2380
5listen-peer-urls: http://{{ hostvars[inventory_hostname]['ansible_facts']['eth1']['ipv4']['address'] }}:2380,http://127.0.0.1:2380
6advertise-client-urls: http://{{ hostvars[inventory_hostname]['ansible_facts']['eth1']['ipv4']['address'] }}:2379
7listen-client-urls: http://{{ hostvars[inventory_hostname]['ansible_facts']['eth1']['ipv4']['address'] }}:2379,http://127.0.0.1:2379
8initial-cluster-state: new
9initial-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 %}
關閉並保存「templates/etcd.conf.yaml.j2」檔案,按一下「CTRL+X」,然後按一下「Y」。
以下是每个参数的简要解释:
姓名 ' -- -- 成员可读姓名。 默认情况下,等克使用一个独特的,随机生成的ID来识别每个成员;然而,一个人可读取的名称可以让我们更容易地在配置文件内部和命令行上引用. 在此,我们将使用主机名称作为成员名称(即
etcd1'、etcd2'和
etcd3')。 *`初始-广告-peer-urls' - 一个IP地址/端口组合列表,其他成员可以用来与这个成员通信. 除API端口("2379")外,等也暴露出"2380"端口,供等成员进行同行交流,使等成员可以互相发送消息并交换数据. 注意这些URL必须被它的同龄人所达到(而不是本地IP地址).- ' listen-peer-urls' - 一个IP地址/端口组合列表,现任成员将在此聆听其他成员的通信. 这必须包括来自
-初始-广告-pier-urls'旗帜的所有URL,但也包括
127.0.0.1:2380'等当地URL。 寄入的对等端消息的目的地 IP 地址/ 端口必须匹配此处列出的 URL 之一 。 *'adverise-client-urls' - 客户应当用来与该成员通信的IP地址/端口组合列表. 这些URL必须被客户端所达到(而不是本地地址). 如果客户端正在通过公共互联网访问集群,这必须是公共IP地址. - ‘ listen-client-urls' - 一个IP地址/端口组合列表,现任成员将在此聆听客户端的通信. 这必须包括来自 " -- -- 广告 -- -- 客户-用户 " 旗下的所有URL,但也包括 " 127.0.0.1:2379 " 等当地URL。 发送客户端消息的目的地 IP 地址/ 端口必须匹配这里列出的 URL 之一 。
*
初始-集群 ' -- -- 该集群每个成员的终点清单。 每个终点必须匹配相应的成员之一的
初始广告-pier-urlsURLs. *
初始-集群-状态'-新'或
已存在'。 (英语)
为了确保一致性, etcd 只能在大多数节点健康时做出决定. 这被称为建立 quorum. 换句话说,在三个成员的群集中,如果两个或两个以上的成员健康,则达到定数。
如果初始集群状态
参数被设置为新
,则etcd
会知道这是一个正在启动的新集群,并允许会员并行启动,而不会等待定数。更具体地说,在第一个成员启动后,它将没有定数,因为三分之一(33.33%)小于或等于50%。通常,等方会停止并拒绝采取任何其他行动,而集群永远不会形成。
如果设置为现有
,会员将尝试加入现有集群,并预计已建立定数。
<$>[注] 注: 您可以在 etcd 文档的 配置部分找到所有支持的配置旗帜的更多细节。
在更新后的templates/etcd.conf.yaml.j2
模板文件中,有几个hostvars
的实例。当 Ansible 运行时,它将从各种来源收集变量。我们以前已经使用了inventory_hostname
变量,但还有更多可用。这些变量在hostvars[inventory_hostname]['ansible_facts']
下可用。在这里,我们正在提取每个节点的私人 IP 地址,并使用它来构建我们的参数值。
<$>[注] **注:**由于我们在创建服务器时启用了 私人网络选项,所以每个服务器将有三个与其相关的IP地址:
- 一个 loopback IP 地址 - 一个只在同一台机器内有效的地址. 它被用来让机器参考自己,例如,
127.0.0.1
- 一个 public IP 地址 - 一个可以通过公共互联网路由的地址,例如,
178.128.169.51
- 一个 private IP 地址 - 一个只能在私人网络中路由的地址;在DigitalOcean Droplets的情况下,每个数据中心内有一个私人网络,例如,
10.131.82.225
这些 IP 地址中的每一个都与不同的网络接口相关联 - 链接地址与lo
接口相关联,公共 IP 地址与eth0
接口相关联,私人 IP 地址与eth1
接口相关联。
本文不需要了解网络接口,但如果您想了解更多信息, 网络术语,接口和协议的介绍是一个很好的开始。
Jinja2语法定义了for
循环结构,它通过etcd
组中的每个节点进行迭代,以将初始集群
字符串构建成等所需的格式。
要组建新的三人群,您必须先停止etcd
服务并在启动群集之前清除数据目录。
1nano $HOME/playground/etcd-ansible/playbook.yaml
然后,在创建数据目录
任务之前,添加一个任务来停止etcd
服务:
1[label ~/playground/etcd-ansible/playbook.yaml]
2- hosts: etcd
3 become: True
4 tasks:
5 ...
6 group: root
7 mode: 0644
8 - name: "Stop the etcd service"
9 command: systemctl stop etcd
10 - name: "Create a data directory"
11 file:
12 ...
接下来,更新创建数据目录
任务,先删除数据目录并重新创建它:
1[label ~/playground/etcd-ansible/playbook.yaml]
2- hosts: etcd
3 become: True
4 tasks:
5 ...
6 - name: "Stop the etcd service"
7 command: systemctl stop etcd
8 - name: "Create a data directory"
9 file:
10 path: /var/lib/etcd/{{ inventory_hostname }}.etcd
11 state: "{{ item }}"
12 owner: root
13 group: root
14 mode: 0755
15 with_items:
16 - absent
17 - directory
18 - name: "Create directory for etcd configuration"
19 file:
20 ...
with_items
属性定义了这个任务会重复的字符串列表,这相当于重复相同的任务两次,但具有状态
属性的不同值。
关闭并保存playbook.yaml
文件,按CTRL+X
,然后按Y
。然后再运行ansible-playbook
。
1ansible-playbook -i hosts playbook.yaml
您可以通过 SSH-ing 检查到任何 etcd 会员节点:
1ssh root@etcd1_public_ip
然后运行etcdctl 终端健康 - 群集
:
1[environment second]
2etcdctl endpoint health --cluster
这将列出群集每个成员的健康状况:
1[environment second]
2[secondary_label Output]
3http://etcd2_private_ip:2379 is healthy: successfully committed proposal: took = 2.517267ms
4http://etcd1_private_ip:2379 is healthy: successfully committed proposal: took = 2.153612ms
5http://etcd3_private_ip:2379 is healthy: successfully committed proposal: took = 2.639277ms
现在我们已经成功创建了一个 3 节点的 etcd 集群,我们可以通过在一个成员节点上添加一个 etcd 条目,然后在另一个成员节点上检索它来确认这一点。
1[environment second]
2etcdctl put foo "bar"
然后,使用新的终端将 SSH 转移到不同的会员节点:
1ssh root@etcd2_public_ip
接下来,尝试使用密钥检索相同的条目:
1[environment third]
2etcdctl get foo
您将能够获取输入,这证明集群正在工作:
1[environment third]
2[secondary_label Output]
3foo
4bar
最后,离开每个受管理节点并返回本地机器:
1[environment second]
2exit
1[environment third]
2exit
在此步骤中,我们提供了一个新的3节点集群。目前,etcd
成员与他们的同行和客户之间的通信是通过HTTP进行的。这意味着通信是未加密的,任何能够拦截流量的人都可以读取消息。如果etcd
集群和客户端都部署在您完全控制的私人网络或虚拟私人网络(VPN)内,这不是一个大问题。然而,如果任何流量需要通过共享网络(私人或公共),那么您应该确保此类流量是加密的。
在下一步中,我们将研究如何使用TLS来保护客户端到服务器以及同行通信。
步骤 9 – 获取管理节点的私人 IP 地址
为了加密成员节点之间的消息,tlsd 使用 Hypertext Transfer Protocol Secure,或 HTTPS,这是运输层安全_ 或 TLS 协议的顶部的一层。tls 使用一个私钥,证书和受信任实体的系统,称为 Certificate Authorities (CAs) 来验证并发送加密消息。
在本教程中,每个会员节点需要生成一个证书来识别自己,并将该证书签署给 CA. 我们将配置所有会员节点来信任这个 CA,从而也信任它签名的任何证书。
会员节点生成的证书必须允许其他会员节点自我识别。所有证书都包含与其相关的实体的 Common Name (CN) 作为实体的身份。 然而,在验证证证书时,客户端实现可以比较其收集的有关实体的信息是否与证书中提供的信息相匹配。 例如,当客户端下载与CN=foobar.com
主题的 TLS 证书时,但客户端实际上正在使用 IP 地址连接到服务器(例如,167.71.129.110
)时,则存在不匹配,客户端可能不信任该证书。 通过在证书中指定一个 subject 替代名称 (SAN) 时,它会告知验证员,这两个名称都属于同一个实
由于我们的 etcd 会员使用他们的私人 IP 地址互相联系,当我们定义我们的证书时,我们将需要提供这些私人 IP 地址作为主题的替代名称。
要找出管理节点的私人 IP 地址,SSH 将其输入:
1ssh root@etcd1_public_ip
然后运行以下命令:
1[environment second]
2ip -f inet addr show eth1
您将找到类似于下列行的输出:
1[environment second]
2[secondary_label Output]
33: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
4 inet 10.131.255.176/16 brd 10.131.255.255 scope global eth1
5 valid_lft forever preferred_lft forever
在我们的示例输出中,‘10.131.255.176’是管理节点的私有 IP 地址,也是我们唯一感兴趣的信息。
1[environment second]
2ip -f inet addr show eth1 | sed -En -e 's/.*inet ([0-9.]+).*/\1/p'
现在,唯一的输出是私人IP地址本身:
1[environment second]
2[secondary_label Output]
310.131.255.176
一旦您确定上一个命令是有效的,请离开管理节点:
1[environment second]
2exit
要将上面的命令纳入我们的播放簿,首先打开playbook.yaml
文件:
1nano $HOME/playground/etcd-ansible/playbook.yaml
然后,在我们现有的游戏之前添加一个新的游戏,其中有一个任务:
1[label ~/playground/etcd-ansible/playbook.yaml]
2...
3- hosts: etcd
4 tasks:
5 - shell: ip -f inet addr show eth1 | sed -En -e 's/.*inet ([0-9.]+).*/\1/p'
6 register: privateIP
7- hosts: etcd
8 tasks:
9...
该任务使用壳
模块运行ip
和sed
命令,该命令收集受管理节点的私人IP地址,然后在名为privateIP
的变量中记录壳
命令的返回值,我们将在稍后使用。
在此步骤中,我们在播放簿中添加了一个任务,以获取受管理节点的私人IP地址. 在下一步,我们将使用此信息为每个会员节点生成证书,并将这些证书签署给证书管理机构(CA)。
步骤 10 — 生成 etcd 会员的私钥和 CSR
为了让会员节点接收加密流量,发件人必须使用会员节点的公共密钥来加密数据,会员节点必须使用其私钥来解密加密文本并获取原始数据。
因此,我们需要为每个 etcd 会员节点生成一个私钥和证书签名请求(CSR)。
首先,创建一个名为 artifacts/
的目录,在那里我们将放置进程中生成的文件(密钥和证书)。
1nano $HOME/playground/etcd-ansible/playbook.yaml
在其中,使用文件
模块创建文物/
目录:
1[label ~/playground/etcd-ansible/playbook.yaml]
2...
3 - shell: ip -f inet addr show eth1 | sed -En -e 's/.*inet ([0-9.]+).*/\1/p'
4 register: privateIP
5- hosts: localhost
6 gather_facts: False
7 become: False
8 tasks:
9 - name: "Create ./artifacts directory to house keys and certificates"
10 file:
11 path: ./artifacts
12 state: directory
13- hosts: etcd
14 tasks:
15...
接下来,在游戏结束时添加另一个任务来生成私钥:
1[label ~/playground/etcd-ansible/playbook.yaml]
2...
3- hosts: localhost
4 gather_facts: False
5 become: False
6 tasks:
7 ...
8 - name: "Generate private key for each member"
9 openssl_privatekey:
10 path: ./artifacts/{{item}}.key
11 type: RSA
12 size: 4096
13 state: present
14 force: True
15 with_items: "{{ groups['etcd'] }}"
16- hosts: etcd
17 tasks:
18...
可以使用 openssl_privatekey
和 openssl_csr
模块创建私钥和 CSR。
force: True
属性确保私钥每次都被再生,即使它已经存在。
同样,将以下新任务附加到相同的游戏中,以使用openssl_csr
模块为每个成员生成 CSR:
1[label ~/playground/etcd-ansible/playbook.yaml]
2...
3- hosts: localhost
4 gather_facts: False
5 become: False
6 tasks:
7 ...
8 - name: "Generate private key for each member"
9 openssl_privatekey:
10 ...
11 with_items: "{{ groups['etcd'] }}"
12 - name: "Generate CSR for each member"
13 openssl_csr:
14 path: ./artifacts/{{item}}.csr
15 privatekey_path: ./artifacts/{{item}}.key
16 common_name: "{{item}}"
17 key_usage:
18 - digitalSignature
19 extended_key_usage:
20 - serverAuth
21 subject_alt_name:
22 - IP:{{ hostvars[item]['privateIP']['stdout']}}
23 - IP:127.0.0.1
24 force: True
25 with_items: "{{ groups['etcd'] }}"
该证书与主机名(如etcd1
)相关联,但验证器还应该将每个节点的私人和本地路由 IP 地址作为替代名称。
关闭并保存playbook.yaml
文件,按CTRL+X
,然后按Y
。
1ansible-playbook -i hosts playbook.yaml
我们现在将在我们的项目目录中找到一个名为文物
的新目录;使用ls
列出其内容:
1ls artifacts
您将找到每个 etcd 会员的私钥和 CSR:
1[secondary_label Output]
2etcd1.csr etcd1.key etcd2.csr etcd2.key etcd3.csr etcd3.key
在此步骤中,我们使用多个 Ansible 模块为每个成员节点生成私钥和公钥证书. 在下一步,我们将研究如何签署证书签名请求(CSR)。
步骤 11 - 生成 CA 证书
在 etcd 集群中,会员节点使用收件人的公共密钥加密消息. 为了确保公共密钥是真实的,收件人将公共密钥包装成证书签名请求(CSR),并有一个受信实体(即 CA)签署 CSR. 由于我们控制所有会员节点和他们信任的 CAs,我们不需要使用外部 CA,并且可以作为自己的 CA。
首先,用您的编辑器打开playbook.yaml
文件:
1nano $HOME/playground/etcd-ansible/playbook.yaml
然后,与之前的步骤相似,将任务附加到本地最强
的游戏中,以生成 CA 的私钥:
1[label ~/playground/etcd-ansible/playbook.yaml]
2- hosts: localhost
3 ...
4 tasks:
5 ...
6 - name: "Generate CSR for each member"
7 ...
8 with_items: "{{ groups['etcd'] }}"
9 - name: "Generate private key for CA"
10 openssl_privatekey:
11 path: ./artifacts/ca.key
12 type: RSA
13 size: 4096
14 state: present
15 force: True
16- hosts: etcd
17 become: True
18 tasks:
19 - name: "Create directory for etcd binaries"
20...
接下来,使用openssl_csr
模块生成新的 CSR. 这与前一步类似,但在本 CSR 中,我们正在添加基本的限制和关键的使用扩展,以表示该证书可以作为CA证书使用:
1[label ~/playground/etcd-ansible/playbook.yaml]
2- hosts: localhost
3 ...
4 tasks:
5 ...
6 - name: "Generate private key for CA"
7 openssl_privatekey:
8 path: ./artifacts/ca.key
9 type: RSA
10 size: 4096
11 state: present
12 force: True
13 - name: "Generate CSR for CA"
14 openssl_csr:
15 path: ./artifacts/ca.csr
16 privatekey_path: ./artifacts/ca.key
17 common_name: ca
18 organization_name: "Etcd CA"
19 basic_constraints:
20 - CA:TRUE
21 - pathlen:1
22 basic_constraints_critical: True
23 key_usage:
24 - keyCertSign
25 - digitalSignature
26 force: True
27- hosts: etcd
28 become: True
29 tasks:
30 - name: "Create directory for etcd binaries"
31...
最后,使用 openssl_certificate
模块自动签署 CSR:
1[label ~/playground/etcd-ansible/playbook.yaml]
2- hosts: localhost
3 ...
4 tasks:
5 ...
6 - name: "Generate CSR for CA"
7 openssl_csr:
8 path: ./artifacts/ca.csr
9 privatekey_path: ./artifacts/ca.key
10 common_name: ca
11 organization_name: "Etcd CA"
12 basic_constraints:
13 - CA:TRUE
14 - pathlen:1
15 basic_constraints_critical: True
16 key_usage:
17 - keyCertSign
18 - digitalSignature
19 force: True
20 - name: "Generate self-signed CA certificate"
21 openssl_certificate:
22 path: ./artifacts/ca.crt
23 privatekey_path: ./artifacts/ca.key
24 csr_path: ./artifacts/ca.csr
25 provider: selfsigned
26 force: True
27- hosts: etcd
28 become: True
29 tasks:
30 - name: "Create directory for etcd binaries"
31...
关闭并保存playbook.yaml
文件,按CTRL+X
,然后按Y
。
1ansible-playbook -i hosts playbook.yaml
您还可以运行ls
来检查 artefacts/
目录的内容:
1ls artifacts/
您现在将找到新生 CA 证书(ca.crt
):
1[secondary_label Output]
2ca.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证书,但而不是使用自签名
提供商,我们将使用ownca
提供商,这使我们能够使用自己的CA证书签名。
打开你的游戏簿:
1nano $HOME/playground/etcd-ansible/playbook.yaml
将以下突出任务附加到生成自签名的 CA 证书
任务中:
1[label ~/playground/etcd-ansible/playbook.yaml]
2- hosts: localhost
3 ...
4 tasks:
5 ...
6 - name: "Generate self-signed CA certificate"
7 openssl_certificate:
8 path: ./artifacts/ca.crt
9 privatekey_path: ./artifacts/ca.key
10 csr_path: ./artifacts/ca.csr
11 provider: selfsigned
12 force: True
13 - name: "Generate an `etcd` member certificate signed with our own CA certificate"
14 openssl_certificate:
15 path: ./artifacts/{{item}}.crt
16 csr_path: ./artifacts/{{item}}.csr
17 ownca_path: ./artifacts/ca.crt
18 ownca_privatekey_path: ./artifacts/ca.key
19 provider: ownca
20 force: True
21 with_items: "{{ groups['etcd'] }}"
22- hosts: etcd
23 become: True
24 tasks:
25 - name: "Create directory for etcd binaries"
26...
关闭并保存playbook.yaml
文件,按CTRL+X
,然后按Y
。
1ansible-playbook -i hosts playbook.yaml
现在,列出文物/
目录的内容:
1ls artifacts/
您将找到每个 etcd 会员和 CA 的私钥、CSR 和证书:
1[secondary_label Output]
2ca.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’成员节点也需要有自己的私钥和证书。
要开始,请用编辑器打开playbook.yaml
文件:
1nano $HOME/playground/etcd-ansible/playbook.yaml
若要对我们的 Ansible 播放簿进行这些更改,请先更新Create directory for etcd configuration
任务的path
属性,以创建/etc/etcd/ssl/
目录:
1[label ~/playground/etcd-ansible/playbook.yaml]
2- hosts: etcd
3 ...
4 tasks:
5 ...
6 with_items:
7 - absent
8 - directory
9 - name: "Create directory for etcd configuration"
10 file:
11 path: "{{ item }}"
12 state: directory
13 owner: root
14 group: root
15 mode: 0755
16 with_items:
17 - /etc/etcd
18 - /etc/etcd/ssl
19 - name: "Create configuration file for etcd"
20 template:
21...
然后,按照更改的任务,添加三个其他任务来复制文件:
1[label ~/playground/etcd-ansible/playbook.yaml]
2- hosts: etcd
3 ...
4 tasks:
5 ...
6 - name: "Copy over the CA certificate"
7 copy:
8 src: ./artifacts/ca.crt
9 remote_src: False
10 dest: /etc/etcd/ssl/ca.crt
11 owner: root
12 group: root
13 mode: 0644
14 - name: "Copy over the `etcd` member certificate"
15 copy:
16 src: ./artifacts/{{inventory_hostname}}.crt
17 remote_src: False
18 dest: /etc/etcd/ssl/server.crt
19 owner: root
20 group: root
21 mode: 0644
22 - name: "Copy over the `etcd` member key"
23 copy:
24 src: ./artifacts/{{inventory_hostname}}.key
25 remote_src: False
26 dest: /etc/etcd/ssl/server.key
27 owner: root
28 group: root
29 mode: 0600
30 - name: "Create configuration file for etcd"
31 template:
32...
关闭并保存playbook.yaml
文件,按CTRL+X
,然后按Y
。
再次运行ansible-playbook
以进行这些更改:
1ansible-playbook -i hosts playbook.yaml
在此步骤中,我们已成功地将私钥和证书上传到管理节点. 复制文件后,我们现在需要更新我们的 etcd 配置文件以便使用它们。
步骤 14 – 在 etcd 上启用 TLS
在本教程的最后一步中,我们将更新一些 Ansible 配置,以在 etcd 集群中启用 TLS。
首先,使用您的编辑器打开「templates/etcd.conf.yaml.j2」模板文件:
1nano $HOME/playground/etcd-ansible/templates/etcd.conf.yaml.j2
一旦进入,更改所有URL以使用https
作为协议而不是http
。此外,在模板的末尾添加一个部分,以指定CA证书,服务器证书和服务器密钥的位置:
1[label ~/playground/etcd-ansible/templates/etcd.conf.yaml.j2]
2data-dir: /var/lib/etcd/{{ inventory_hostname }}.etcd
3name: {{ inventory_hostname }}
4initial-advertise-peer-urls: https://{{ hostvars[inventory_hostname]['ansible_facts']['eth1']['ipv4']['address'] }}:2380
5listen-peer-urls: https://{{ hostvars[inventory_hostname]['ansible_facts']['eth1']['ipv4']['address'] }}:2380,https://127.0.0.1:2380
6advertise-client-urls: https://{{ hostvars[inventory_hostname]['ansible_facts']['eth1']['ipv4']['address'] }}:2379
7listen-client-urls: https://{{ hostvars[inventory_hostname]['ansible_facts']['eth1']['ipv4']['address'] }}:2379,https://127.0.0.1:2379
8initial-cluster-state: new
9initial-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 %}
10
11client-transport-security:
12 cert-file: /etc/etcd/ssl/server.crt
13 key-file: /etc/etcd/ssl/server.key
14 trusted-ca-file: /etc/etcd/ssl/ca.crt
15peer-transport-security:
16 cert-file: /etc/etcd/ssl/server.crt
17 key-file: /etc/etcd/ssl/server.key
18 trusted-ca-file: /etc/etcd/ssl/ca.crt
关闭并保存「templates/etcd.conf.yaml.j2」文件。
接下来,运行您的 Ansible Playbook:
1ansible-playbook -i hosts playbook.yaml
然后,将 SSH 转入管理节点中的一个:
1ssh root@etcd1_public_ip
一旦进入,运行etcdctl 终端健康
命令,检查终端是否使用 HTTPS,以及所有会员是否健康:
1[environment second]
2etcdctl --cacert /etc/etcd/ssl/ca.crt endpoint health --cluster
由于我们的 CA 证书不是默认情况下在 /etc/ssl/certs/
目录中安装的可信根 CA 证书,我们需要使用 --cacert
旗帜将其传输到 `etcdctl。
这将产生以下产出:
1[environment second]
2[secondary_label Output]
3https://etcd3_private_ip:2379 is healthy: successfully committed proposal: took = 19.237262ms
4https://etcd1_private_ip:2379 is healthy: successfully committed proposal: took = 4.769088ms
5https://etcd2_private_ip:2379 is healthy: successfully committed proposal: took = 5.953599ms
要确认etcd
集群实际工作,我们可以再一次在一个会员节点上创建一个条目,并从另一个会员节点中获取:
1[environment second]
2etcdctl --cacert /etc/etcd/ssl/ca.crt put foo "bar"
使用新的终端将 SSH 转移到不同的节点:
1ssh root@etcd2_public_ip
现在使用键foo
获取相同的条目:
1[environment third]
2etcdctl --cacert /etc/etcd/ssl/ca.crt get foo
这将返回输入,显示下面的输出:
1[environment third]
2[secondary_label Output]
3foo
4bar
您可以在第三个节点上做同样的事情,以确保所有三个成员都运行。
结论
您现在已经成功配置了 3 个节点的 etcd 集群,用 TLS 保护它,并确认它正在工作。
etcd 是由 CoreOS最初创建的一种工具。为了了解 etcd 与 CoreOS 相关的使用,您可以阅读 How To Use Etcdctl and Etcd, CoreOS’s Distributed Key-Value Store。
正如本教程开始时所提到的, etcd 是 Kubernetes 生态系统的重要组成部分。 要了解更多关于 Kubernetes 和 etcd 在其中的作用,您可以阅读 An Introduction to Kubernetes。 如果您正在部署 etcd 作为 Kubernetes 集群的一部分,请知道还有其他工具可用,如 kubespray和 kubeadm
. 有关后者的更多细节,您可以阅读 How To Create a Kubernetes Cluster Using Kubeadm on Ubuntu 18.04。
最后,本教程使用了许多工具,但无法深入每个工具的细节,在下面你会找到链接,这将提供一个更详细的检查每个工具:
- 要了解 Ansible 播放本的更先进语法,您可以阅读 Configuration Management 101: Writing Ansible Playbooks. Ansible 的官方 Intro to Playbooks也是一个很好的资源。
- 要了解更多关于 OpenSSL,您可以阅读 OpenSSL Essentials: Working with SSL Certificates, Private Keys and CSRs。