介绍
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_files
和include_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.yml
和var/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_files
和include_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
目录。
在目录树中存在一些明显的重复性,每个环境都有web
和db
文件,在这种情况下,重复性是可取的。可在环境中进行变量更改,首先在一个环境中修改变量,然后在测试后将它们移动到下一个环境中,就像在代码或配置更改时一样。
其中一个局限性是无法根据各种环境的功能选择所有主机。幸运的是,这与上述变量重复问题相同。虽然有时选择您的一项任务的所有 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
文件中设置默认库存文件是可能的,这出于几个原因是一个好主意。
首先,它允许您将明确的库存旗帜留给ansible
和ansible-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在多种环境中管理您的主机的灵活性,这使用户能够采用许多不同的策略来处理变量优先级,当主机是多组成员时,但模糊和缺乏官方方向可能具有挑战性。像任何技术一样,适合您的组织的最佳选择取决于您的使用情况和您的要求的复杂性。