如何使用 Ansible 管理多阶段环境

介绍

Ansible 是一个功能强大的配置管理系统,用于在各种环境中设置和管理基础设施和应用程序,而 Ansible 提供易于阅读的语法、灵活的工作流程和强大的工具,但在部署环境和功能不同时,管理大量主机可能具有挑战性。

在本指南中,我们将讨论使用 Ansible 来处理多阶段部署环境的一些策略. 通常,不同阶段的要求会导致不同组件的数量和配置。 例如,开发服务器的内存要求可能与阶段和生产的内存需求有所不同,重要的是要明确控制代表这些要求的变量是如何优先考虑的。

使用 Ansible 管理多阶段环境的不完整策略

虽然您可以在 Ansible 中管理环境的几种方式,但 Ansible 本身并不提供定义的解决方案,相反,它提供了许多可用于管理环境的构件,并允许用户进行选择。

我们将在本指南中展示的方法依赖于Ansible 组变量和多种库存**。然而,还有几个其他策略值得考虑。

如果您想开始使用 Ansible 推荐的策略,请跳过 使用 Ansible 组和多个库存的部分。

仅依赖组变量

乍一看,组变量可能提供Ansible所需的环境之间的全部分离,您可以指定某些服务器属于您的开发环境,而其他服务器可以分配到阶段和生产区域。

然而,组交叉会给这个系统带来严重的问题. 组通常被用来分类多个维度,例如:

*部署环境(本地、开发、阶段、生产等) *主机功能(网页服务器、数据库服务器等) *数据中心区域(NYC、SFO等)

在这些情况下,主机通常在每个类别的一个组中,例如,主机可能是位于纽约市(数据中心区域)的Web服务器(功能性)的舞台(部署环境)。

如果同一个变量被多个组为一个主机设置,则 Ansible 无法明确指定优先级. 您可能更喜欢与部署环境相关的变量超过其他值,但 Ansible 没有提供定义此方法。

相反, Ansible 使用最后加载的值. 由于 Ansible 以字母形式评估群组,所以与字典排序中的最后一个群组名称相关的变量将获胜。

使用小组孩子建立一个层次结构

Ansible 允许您在库存中使用[groupname:children]语法将群组分配给其他群组,这样您就可以命名其他群组的某些群组成员。

通常,这用于自然分类,例如,我们可以有一个名为环境的组,其中包括组dev,阶段,prod。这意味着我们可以在环境组中设置变量,并将它们排在dev组中。

这种使用并不能解决群体交叉的问题,因为儿童群体只超过父母,儿童群体可以超过父母内部的变量,但上述组织没有建立环境功能等群体类别之间的任何关系。

例如,如果你想建立以下优先级,从最高优先级到最低优先级:

  • 开发环境
  • 区域
  • 功能

您可以分配一个看起来像这样的组成员:

 1[label Example inventory]
 2. . .
 3[function:children]
 4web
 5database
 6loadbalancer
 7region
 8
 9[region:children]
10nyc
11sfo
12environments
13
14[environments:children]
15dev
16stage
17prod

我们在这里建立了一个层次结构,允许区域变量超级功能变量,因为区域组是函数组的一个孩子。同样,设置在环境组中的变量可以超级其他任何一个。

这实现了所需的结果,也是可以预测的。然而,它是不直观的,它混淆了建立层次结构所需的真实儿童和儿童之间的区别。Ansible旨在使其配置清晰,甚至对于新用户也很容易遵循。

使用允许明确加载命令的可理解构造

Ansible 中有几种结构允许明确的变量负载排序,即vars_filesinclude_vars。这些结构可以在 Ansible plays中使用,以明确地在文件中定义的顺序下加载额外的变量。

一般的想法是,在group_vars中仅设置基本的识别变量,然后利用这些变量来加载正确的变量文件与其他所需的变量。

例如,某些「group_vars」文件可能看起来像这样:

1[label group_vars/dev]
2---
3env: dev
1[label group_vars/stage]
2---
3env: stage
1[label group_vars/web]
2---
3function: web
1[label group_vars/database]
2---
3function: database

然后我们会有一个单独的 vars 文件,定义每个组的重要变量. 这些通常保存在一个单独的vars目录中,以确保清晰度。

假设我们需要在每个vars文件中将server_memory_size变量设置为不同的值. 您的开发服务器可能小于您的生产服务器。

1[label vars/dev.yml]
2---
3server_memory_size: 512mb
1[label vars/prod.yml]
2---
3server_memory_size: 4gb
1[label vars/web.yml]
2---
3server_memory_size: 1gb
1[label vars/database.yml]
2---
3server_memory_size: 2gb

然后我们可以创建一个播放簿,根据从group_vars文件中分配给主机的值明确地加载正确的vars文件。

使用vars_files,一个示例播放将是这样的:

1[label example_play.yml]
2---
3- name: variable precedence test
4  hosts: all
5  vars_files:
6    - "vars/{{ env }}.yml"
7    - "vars/{{ function }}.yml"
8  tasks:
9    - debug: var=server_memory_size

由于函数组是最后一次加载的,所以从var/web.ymlvar/database.yml文件中取出server_memory_size值:

1ansible-playbook -i inventory example_play.yml
 1[secondary_label Output]
 2. . .
 3TASK [debug] *******************************************************************
 4ok: [host1] => {
 5    "server_memory_size": "1gb"      # value from vars/web.yml
 6}
 7ok: [host2] => {
 8    "server_memory_size": "1gb"      # value from vars/web.yml
 9}
10ok: [host3] => {
11    "server_memory_size": "2gb"      # value from vars/database.yml
12}
13ok: [host4] => {
14    "server_memory_size": "2gb"      # value from vars/database.yml
15}
16. . .

如果我们更改要加载的文件的排序,我们可以使部署环境变量更为优先:

1[label example_play.yml]
2---
3- name: variable precedence test
4  hosts: all
5  vars_files:
6    - "vars/{{ function }}.yml"
7    - "vars/{{ env }}.yml"
8  tasks:
9    - debug: var=server_memory_size

重新运行播放簿显示从部署环境文件中应用的值:

1ansible-playbook -i inventory example_play.yml
 1[secondary_label Output]
 2. . .
 3TASK [debug] *******************************************************************
 4ok: [host1] => {
 5    "server_memory_size": "512mb"      # value from vars/dev.yml
 6}
 7ok: [host2] => {
 8    "server_memory_size": "4gb"        # value from vars/prod.yml
 9}
10ok: [host3] => {
11    "server_memory_size": "512mb"      # value from vars/dev.yml
12}
13ok: [host4] => {
14    "server_memory_size": "4gb"        # value from vars/prod.yml
15}
16. . .

使用包括_vars 的等效播放簿,它作为一个任务运作,看起来像:

 1---
 2- name: variable precedence test
 3  hosts: localhost
 4  tasks:
 5    - include_vars:
 6        file: "{{ item }}"
 7      with_items:
 8        - "vars/{{ function }}.yml"
 9        - "vars/{{ env }}.yml"
10    - debug: var=server_memory_size

这是一个允许明确排序的区域,这可能非常有用,但是,与之前的例子一样,有一些显著的缺点。

首先,使用vars_filesinclude_vars需要将与群组密切相关的变量放置在不同的位置。group_vars的位置成为位于vars目录中的实际变量的支柱。这再次增加了复杂性并降低了清晰度。用户必须将正确的变量文件匹配到主机,这是Ansible在使用group_vars时自动执行的。

更重要的是,依靠这些技术使它们成为强制性的。每个播放本将需要一个部分,明确地在正确的顺序下加载正确的变量文件。如果没有这个,播放本将无法使用相关变量。

Ansible推荐策略:使用群组和多个库存

到目前为止,我们已经研究了一些管理多阶段环境的策略,并讨论了为什么它们可能不是一个完整的解决方案。

建议的方法是通过完全分离每个操作环境来使用多级环境,而不是将所有主机保存在一个库存文件中,为每个环境保留库存。

基本目录结构将看起来像这样:

 1.
 2├── ansible.cfg
 3├── environments/         # Parent directory for our environment-specific directories
 4   
 5   ├── dev/              # Contains all files specific to the dev environment
 6      ├── group_vars/   # dev specific group_vars files
 7         ├── all
 8         ├── db
 9         └── web
10      └── hosts         # Contains only the hosts in the dev environment
11   
12   ├── prod/             # Contains all files specific to the prod environment
13      ├── group_vars/   # prod specific group_vars files
14         ├── all
15         ├── db
16         └── web
17      └── hosts         # Contains only the hosts in the prod environment
18   
19   └── stage/            # Contains all files specific to the stage environment
20       ├── group_vars/   # stage specific group_vars files
21          ├── all
22          ├── db
23          └── web
24       └── hosts         # Contains only the hosts in the stage environment
25
26├── playbook.yml
27
28└── . . .

正如你所看到的,每个环境都是不同的和分区化的。环境目录包含一个库存文件(任意命名为主机)和一个单独的group_vars目录。

在目录树中存在一些明显的重复性,每个环境都有webdb文件,在这种情况下,重复性是可取的。可在环境中进行变量更改,首先在一个环境中修改变量,然后在测试后将它们移动到下一个环境中,就像在代码或配置更改时一样。

其中一个局限性是无法根据各种环境的功能选择所有主机。幸运的是,这与上述变量重复问题相同。虽然有时选择您的一项任务的所有 Web 服务器是有用的,但您几乎总是希望一次地在您的环境中执行更改,这有助于防止错误影响您的生产环境。

设置跨环境变量

在推荐的设置中不可能做到的一个事情是跨环境的变量共享,我们可以通过多种方式实现跨环境变量共享,最简单的方法之一是利用Ansible使用目录而不是文件的能力,我们可以用一个全部目录代替每个group_vars目录中的所有文件。

在目录中,我们可以再次在一个文件中设置所有环境特定的变量,然后我们可以创建一个包含跨环境变量的文件位置的象征链接,这些都将应用于环境中的所有主机。

首先,创建一个跨环境变量文件在等级中的某个地方,在这个例子中,我们将其放置在环境目录中,将所有跨环境变量放置在该文件中:

1cd environments
2touch 000_cross_env_vars

接下来,移动到一个group_vars目录,重命名all文件,然后创建all目录。

1cd dev/group_vars
2mv all env_specific
3mkdir all
4mv env_specific all/

接下来,您可以创建一个符号链接到跨环境变量文件:

1cd all/
2ln -s ../../../000_cross_env_vars .

当您为每个环境完成上述步骤时,您的目录结构将看起来像这样:

 1.
 2├── ansible.cfg
 3├── environments/
 4   
 5   ├── 000_cross_env_vars
 6   
 7   ├── dev/
 8      ├── group_vars/
 9         ├── all/
10             ├── 000_cross_env_vars -> ../../../000_cross_env_vars
11            └── env_specific
12         ├── db
13         └── web
14      └── hosts
15   
16   ├── prod/
17      ├── group_vars/
18         ├── all/
19            ├── 000_cross_env_vars -> ../../../000_cross_env_vars
20            └── env_specific
21         ├── db
22         └── web
23      └── hosts
24   
25   └── stage/
26       ├── group_vars/
27          ├── all/
28             ├── 000_cross_env_vars -> ../../../000_cross_env_vars
29             └── env_specific
30          ├── db
31          └── web
32       └── hosts
33
34├── playbook.yml
35
36└── . . .

000_cross_env_vars文件中设置的变量将可用于每个具有低优先级的环境。

设置默认环境清单

ansible.cfg文件中设置默认库存文件是可能的,这出于几个原因是一个好主意。

首先,它允许您将明确的库存旗帜留给ansibleansible-playbook

1ansible -i environments/dev -m ping

您可以通过键入访问默认库存:

1ansible -m ping

其次,设置默认库存有助于防止不必要的更改意外影响舞台或生产环境。 通过默认对您的开发环境,最不重要的基础设施受到更改的影响。

要设置默认库存,请打开您的 ansible.cfg 文件. 这可能位于您的项目的根目录或 /etc/ansible/ansible.cfg 位置,取决于您的配置。

<$>[注] 注: 下面的示例显示在项目目录中编辑一个 ansible.cfg 文件. 如果您正在使用 /etc/ansibile/ansible.cfg 文件进行更改,请更改下面的编辑路径。当使用 /etc/ansible/ansible.cfg 时,如果您的库存被保留在 /etc/ansible 目录之外,请确保在设置 库存 值时使用绝对路径而不是相对路径。

1nano ansible.cfg

如上所述,建议将您的开发环境设置为默认库存,请注意我们如何选择整个环境目录,而不是包含的主机文件:

1[defaults]
2inventory = ./environments/dev

您现在应该能够使用默认库存而无需i选项,而非默认库存仍然需要使用i,这有助于保护它们免受意外更改。

结论

在本文中,我们探索了Ansible在多种环境中管理您的主机的灵活性,这使用户能够采用许多不同的策略来处理变量优先级,当主机是多组成员时,但模糊和缺乏官方方向可能具有挑战性。像任何技术一样,适合您的组织的最佳选择取决于您的使用情况和您的要求的复杂性。

Published At
Categories with 技术
comments powered by Disqus