如何在 Ubuntu 18.04 上使用 Ansible 设置并保护 etcd 集群

作者选择了 维基媒体基金会作为 写给捐赠计划的一部分接受捐赠。

介绍

etcd是许多平台和工具所依赖的分布式密钥价值商店,包括 Kubernetes, VulcandDoorman.在Kubernetes, etcd被用作存储集群状态的全球配置商店。知道如何管理等是管理Kubernetes集群的必不可少的。尽管有许多管理的Kubernetes产品,也被称为 Kubernetes-as-a-Service,可以消除这种管理负担,但许多公司仍然选择在本地运行自管理的Kubernetes集群,因为它带来了灵活性。

本文的上半部分将指导您在 Ubuntu 18.04 服务器上建立一个 3 节点的 etcd 群集。下半部分将专注于使用 Transport Layer Security,或 TLS来保护群集。以自动的方式运行每个设置,我们将使用 Ansible在整个过程中。 Ansible 是一个类似于 PuppetChefSaltStackconfiguration 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。 这将使ansibleansible-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。 然后,当运行ansibleansible-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 目录,然后为 etcdetcdctl 二进制设置文件权限。
  • get_url:将 gzipped tarball 下载到管理节点上。
  • unarchive:从 gzipped tarball 中提取和解包 etcdetcdctl 二进制。

要应用这些更改,请关闭并保存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 放入管理节点中的一个,然后运行etcdetcdctl:

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服务器,返回本地环境。

我们现在已经成功地安装了etcdetcdctl在我们所有受管理的节点上,在下一步,我们将为我们的游戏添加更多的任务以运行 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更快,可以使用putgetdel子命令来创建/更新、检索和删除关键值对。

请确保您仍在 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 configurationdynamic 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-urls URLs. *初始-集群-状态'-新'或已存在'。 (英语)

为了确保一致性, 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...

该任务使用模块运行ipsed命令,该命令收集受管理节点的私人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_privatekeyopenssl_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 集群的一部分,请知道还有其他工具可用,如 kubespraykubeadm. 有关后者的更多细节,您可以阅读 How To Create a Kubernetes Cluster Using Kubeadm on Ubuntu 18.04

最后,本教程使用了许多工具,但无法深入每个工具的细节,在下面你会找到链接,这将提供一个更详细的检查每个工具:

Published At
Categories with 技术
comments powered by Disqus