首页 > 其他分享 >如何使用 Ansible 管理多阶段环境

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

时间:2024-10-11 12:23:09浏览次数:8  
标签:group 变量 vars 环境 Ansible 阶段 yml

前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站

介绍

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

在本指南中,我们将讨论使用 Ansible 处理多阶段部署环境的一些策略。通常,不同阶段的要求会导致组件的数量和配置不同。例如,开发服务器的内存需求可能与暂存和生产服务器的需求不同,因此重要的是要明确控制代表这些需求的变量的优先级。在本文中,我们将讨论一些可以抽象这些差异的方式,以及 Ansible 提供的一些构造,以鼓励配置的重用。

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

虽然有许多种方法可以在 Ansible 中管理环境,但 Ansible 本身并没有提供一种固定的解决方案。相反,它提供了许多用于管理环境的构造,并允许用户进行选择。

我们将在本指南中演示的方法依赖于 Ansible 的组变量多个清单。然而,还有其他几种值得考虑的策略。我们将探讨一些这些想法以及在复杂环境中实施时可能出现的问题。

如果您想开始使用 Ansible 推荐的策略,请跳转到使用 Ansible 组和多清单的部分。

仅依赖于组变量

乍一看,似乎组变量提供了 Ansible 需要的所有环境分离。您可以指定某些服务器属于开发环境,其他服务器则分配到暂存和生产区域。Ansible 可以轻松创建组并为其分配变量。

然而,组交集对该系统带来了严重的问题。组通常用于对多个维度进行分类。例如:

  • 部署环境(本地、开发、暂存、生产等)
  • 主机功能(Web 服务器、数据库服务器等)
  • 数据中心区域(纽约、旧金山等)

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

如果同一个变量由一个主机的多个组设置,Ansible 无法明确指定优先级。您可能希望与部署环境相关的变量覆盖其他值,但 Ansible 没有提供定义这一点的方法。

相反,Ansible 使用最后加载的值。由于 Ansible 按字典顺序评估组,与字典顺序中最后一个组名相关联的变量将获胜。这是可预测的行为,但从管理角度来看,明确管理组名称的字母顺序不太理想。

使用组子级建立层次结构

Ansible 允许您在清单中使用[groupname:children]语法将组分配给其他组。这使您能够将某些组命名为其他组的成员。子组具有覆盖父组设置的变量的能力。

通常,这用于自然分类。例如,我们可以有一个名为environments的组,其中包括devstageprod等组。这意味着我们可以在environment组中设置变量,并在dev组中覆盖它们。同样,您可以有一个名为functions的父组,其中包含webdatabaseloadbalancer等组。

这种用法并不能解决组交集的问题,因为子组只能覆盖其父组的变量。子组可以覆盖父组内的变量,但上述组织并没有建立environmentsfunctions之间的任何关系。两个类别之间的变量优先级仍未定义。

通过设置非自然的组成员关系,可以利用这个系统。例如,如果您想要建立以下优先级,从最高优先级到最低优先级:

  • 开发环境
  • 区域
  • 功能

您可以分配组成员关系如下:

. . .
[function:children]
web
database
loadbalancer
region

[region:children]
nyc
sfo
environments

[environments:children]
dev
stage
prod

我们在这里建立了一个层次结构,允许区域变量覆盖功能变量,因为region组是function组的子组。同样,environments组中设置的变量可以覆盖任何其他组的变量。这意味着,如果我们在devnycweb组中为相同的变量设置了不同的值,属于这些组的主机将使用dev组中的变量。

这实现了期望的结果,而且是可预测的。然而,这是不直观的,它混淆了真正的子级和建立层次结构所需的子级之间的区别。Ansible 的设计是使其配置对于新用户来说也清晰易懂。这种解决方案会损害这一目标。

使用 Ansible 构造允许显式加载顺序

在 Ansible 中有一些构造允许显式变量加载顺序,即 vars_filesinclude_vars。这些可以在 Ansible plays 中使用,以按照文件中定义的顺序显式加载额外的变量。vars_files 指令在 play 的上下文中是有效的,而 include_vars 模块可以在任务中使用。

总体思路是在 group_vars 中只设置基本的标识变量,然后利用这些变量来加载其余所需变量的正确变量文件。

例如,一些 group_vars 文件可能如下所示:

---
env: dev
---
env: stage
---
function: web
---
function: database

然后我们会有一个单独的 vars 文件,为每个组定义重要的变量。这些通常保存在一个单独的 vars 目录中,以便清晰。与 group_vars 文件不同,使用 include_vars 时,文件必须包含 .yml 文件扩展名。

假设我们需要在每个 vars 文件中为 server_memory_size 变量设置不同的值。你的开发服务器可能比生产服务器小。此外,你的 web 服务器和数据库服务器可能有不同的内存需求:

---
server_memory_size: 512mb
---
server_memory_size: 4gb
---
server_memory_size: 1gb
---
server_memory_size: 2gb

然后我们可以创建一个 playbook,根据从 group_vars 文件中分配给主机的值显式加载正确的 vars 文件。加载的文件顺序将决定优先级,最后一个值胜出。

使用 vars_files,一个示例 play 如下所示:

---
- name: variable precedence test
  hosts: all
  vars_files:
    - "vars/{{ env }}.yml"
    - "vars/{{ function }}.yml"
  tasks:
    - debug: var=server_memory_size

由于功能组是最后加载的,server_memory_size 值将从 var/web.ymlvar/database.yml 文件中获取:

ansible-playbook -i inventory example_play.yml
. . .
TASK [debug] *******************************************************************
ok: [host1] => {
    "server_memory_size": "1gb"      # value from vars/web.yml
}
ok: [host2] => {
    "server_memory_size": "1gb"      # value from vars/web.yml
}
ok: [host3] => {
    "server_memory_size": "2gb"      # value from vars/database.yml
}
ok: [host4] => {
    "server_memory_size": "2gb"      # value from vars/database.yml
}
. . .

如果我们改变要加载的文件的顺序,我们可以使部署环境变量具有更高的优先级:

---
- name: variable precedence test
  hosts: all
  vars_files:
    - "vars/{{ function }}.yml"
    - "vars/{{ env }}.yml"
  tasks:
    - debug: var=server_memory_size

再次运行 playbook,显示从部署环境文件中应用的值:

ansible-playbook -i inventory example_play.yml
. . .
TASK [debug] *******************************************************************
ok: [host1] => {
    "server_memory_size": "512mb"      # value from vars/dev.yml
}
ok: [host2] => {
    "server_memory_size": "4gb"        # value from vars/prod.yml
}
ok: [host3] => {
    "server_memory_size": "512mb"      # value from vars/dev.yml
}
ok: [host4] => {
    "server_memory_size": "4gb"        # value from vars/prod.yml
}
. . .

使用 include_vars 的等效 playbook,作为一个任务,如下所示:

---
- name: variable precedence test
  hosts: localhost
  tasks:
    - include_vars:
        file: "{{ item }}"
      with_items:
        - "vars/{{ function }}.yml"
        - "vars/{{ env }}.yml"
    - debug: var=server_memory_size

这是 Ansible 允许显式顺序的一个领域,这可能非常有用。然而,与之前的示例一样,存在一些显著的缺点。

首先,使用 vars_filesinclude_vars 需要你将与组紧密相关的变量放在不同的位置。group_vars 位置变成了实际变量所在的 vars 目录的存根。这再次增加了复杂性并降低了清晰度。用户必须将正确的变量文件与主机匹配,而使用 group_vars 时 Ansible 会自动完成这一点。

更重要的是,依赖这些技术使它们成为强制性的。每个 playbook 都需要一个显式加载正确变量文件的部分。没有这些内容的 playbook 将无法使用相关变量。此外,对于依赖变量的任何 ad-hoc 任务,几乎是不可能的。

Ansible 推荐策略:使用组和多个清单

到目前为止,我们已经看过了一些管理多阶段环境的策略,并讨论了它们可能不是一个完整解决方案的原因。然而,Ansible 项目确实提供了一些建议,关于如何最好地在不同环境中抽象出你的基础设施。

推荐的方法是通过完全分离每个操作环境来处理多阶段环境。与其在单个清单文件中维护所有主机,不如为每个单独的环境维护一个清单。同时也要维护单独的 group_vars 目录。

基本的目录结构将如下所示:

.
├── ansible.cfg
├── environments/         # 用于存放特定环境目录的父目录
│   │
│   ├── dev/              # 包含所有特定于开发环境的文件
│   │   ├── group_vars/   # 开发环境特定的 group_vars 文件
│   │   │   ├── all
│   │   │   ├── db
│   │   │   └── web
│   │   └── hosts         # 仅包含开发环境中的主机
│   │
│   ├── prod/             # 包含所有特定于生产环境的文件
│   │   ├── group_vars/   # 生产环境特定的 group_vars 文件
│   │   │   ├── all
│   │   │   ├── db
│   │   │   └── web
│   │   └── hosts         # 仅包含生产环境中的主机
│   │
│   └── stage/            # 包含所有特定于阶段环境的文件
│       ├── group_vars/   # 阶段环境特定的 group_vars 文件
│       │   ├── all
│       │   ├── db
│       │   └── web
│       └── hosts         # 仅包含阶段环境中的主机
│
├── playbook.yml
│
└── . . .

如你所见,每个环境都是独立的并且被分隔开来。环境目录包含清单文件(任意命名为 hosts)和单独的 group_vars 目录。

目录树中存在一些明显的重复。每个单独的环境都有 webdb 文件。在这种情况下,重复是可取的。变量的更改可以通过首先修改一个环境中的变量,然后在测试后将其移动到下一个环境中,就像你对代码或配置更改一样。group_vars 变量跟踪每个环境的当前默认值。

一个限制是无法跨环境选择所有主机。幸运的是,这与上面的变量重复问题属于同一类别。虽然偶尔选择所有 web 服务器执行任务是有用的,但你几乎总是希望逐个环境地推出更改,以防止错误影响生产环境。

设置跨环境变量

在推荐的设置中,不可能实现跨环境变量共享。我们可以实现跨环境变量共享的方式有很多。其中最简单的一种是利用 Ansible 能够使用目录代替文件的能力。我们可以用一个 all 目录来替换每个 group_vars 目录中的 all 文件。

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

首先,在层次结构的某个地方创建一个跨环境变量文件。在本例中,我们将其放在 environments 目录中。将所有跨环境变量放入该文件中:

cd environments
touch 000_cross_env_vars

接下来,进入 group_vars 目录中的一个目录,重命名 all 文件,并创建 all 目录。将重命名的文件移动到新目录中:

cd dev/group_vars
mv all env_specific
mkdir all
mv env_specific all/

然后,你可以创建一个符号链接指向跨环境变量文件:

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

当你对每个环境完成上述步骤后,你的目录结构将如下所示:

.
├── ansible.cfg
├── environments/
│   │
│   ├── 000_cross_env_vars
│   │
│   ├── dev/
│   │   ├── group_vars/
│   │   │   ├── all/
│       │   │   ├── 000_cross_env_vars -> ../../../000_cross_env_vars
│   │   │   │   └── env_specific
│   │   │   ├── db
│   │   │   └── web
│   │   └── hosts
│   │
│   ├── prod/
│   │   ├── group_vars/
│   │   │   ├── all/
│   │   │   │   ├── 000_cross_env_vars -> ../../../000_cross_env_vars
│   │   │   │   └── env_specific
│   │   │   ├── db
│   │   │   └── web
│   │   └── hosts
│   │
│   └── stage/
│       ├── group_vars/
│       │   ├── all/
│       │   │   ├── 000_cross_env_vars -> ../../../000_cross_env_vars
│       │   │   └── env_specific
│       │   ├── db
│       │   └── web
│       └── hosts
│
├── playbook.yml
│
└── . . .

000_cross_env_vars 文件中设置的变量将在每个环境中都可用,但优先级较低。

设置默认环境清单

可以在 ansible.cfg 文件中设置默认清单文件。这样做有几个好处。

首先,它允许您在使用 ansibleansible-playbook 时省略显式的清单标志。因此,不需要输入:

ansible -i environments/dev -m ping

您可以通过以下方式访问默认清单:

ansible -m ping

其次,设置默认清单有助于防止意外影响暂存或生产环境的不必要更改。通过默认使用开发环境,可以减少对最不重要的基础设施的更改。然后,将更改推广到新环境需要显式操作,需要使用 -i 标志。

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

nano ansible.cfg

如上所述,建议将开发环境设置为默认清单。请注意,我们可以选择整个环境目录,而不是其中包含的主机文件:

[defaults]
inventory = ./environments/dev

现在,您应该能够在不使用 -i 选项的情况下使用默认清单。非默认清单仍将需要使用 -i,这有助于保护它们免受意外更改。

结论

在本文中,我们探讨了 Ansible 在管理多个环境中的主机时提供的灵活性。这使用户可以采用许多不同的策略来处理当主机是多个组的成员时的变量优先级,但模棱两可和缺乏官方指导可能具有挑战性。与任何技术一样,最适合您的组织的解决方案将取决于您的用例和要求的复杂性。找到适合您需求的策略的最佳方法是进行实验。请在下方评论中分享您的用例和方法。

标签:group,变量,vars,环境,Ansible,阶段,yml
From: https://blog.csdn.net/rubys007/article/details/142691274

相关文章