跳至内容

Ansible - 大型基础设施

在本章中,您将学习如何扩展您的配置管理系统。


目标:在本章中,您将学会如何

✔ 为大型基础设施组织您的代码;
✔ 将您的配置管理的一部分或全部应用于一组节点;

🏁 ansible, 配置管理, 扩展

知识: ⭐ ⭐ ⭐
复杂度: ⭐ ⭐ ⭐ ⭐

阅读时间: 30 分钟


我们在前面的章节中已经了解了如何以角色的形式组织我们的代码,以及如何使用一些角色来管理更新(补丁管理)或代码部署。

那么配置管理呢?如何使用 Ansible 管理数以万计、数以百计甚至数以千计的虚拟机的配置?

云计算的出现改变了传统的管理方式。虚拟机在部署时就被配置好了。如果其配置不再符合要求,则会被销毁并替换为新的虚拟机。

本章介绍的配置管理系统的组织方式将应对这两种 IT 使用方式:“一次性”使用或定期“重新配置”服务器池。

但是,请注意:使用 Ansible 来确保服务器池的合规性需要改变工作习惯。您将无法手动修改服务管理器的配置,因为这些修改将在下次运行 Ansible 时被覆盖。

注意

下面我们将要设置的并不是 Ansible 的最佳场景。像 Puppet 或 Salt 这样的技术会做得更好。让我们记住,Ansible 是一个无代理的自动化瑞士军刀,这解释了其性能差异。

注意

更多信息可以在此处找到

变量存储

首先我们需要讨论的是数据与 Ansible 代码的分离。

随着代码越来越庞大和复杂,修改其中变量将变得越来越困难。

为了确保您站点的可维护性,最重要的是正确地将变量与 Ansible 代码分离开来。

我们在这里还没有讨论,但您应该知道,Ansible 可以根据被管理节点的库存名称或其所属组,自动加载特定文件夹中的变量。

Ansible 文档建议我们按如下方式组织代码:

inventories/
   production/
      hosts               # inventory file for production servers
      group_vars/
         group1.yml       # here we assign variables to particular groups
         group2.yml
      host_vars/
         hostname1.yml    # here we assign variables to particular systems
         hostname2.yml

如果目标节点是 group1hostname1,那么 hostname1.ymlgroup1.yml 文件中包含的变量将被自动加载。这是在同一个地方存储所有角色数据的不错方式。

这样,服务器的库存文件就成了它的身份卡。它包含了所有与您的服务器默认变量不同的变量。

从变量集中化的角度来看,在角色中组织变量的命名变得至关重要,例如通过添加角色的名称作为前缀。也建议使用扁平的变量名而不是字典。

例如,如果您想让 sshd_config 文件中的 PermitRootLogin 值成为一个变量,一个好的变量名可以是 sshd_config_permitrootlogin(而不是 sshd.config.permitrootlogin,后者也可以是好的变量名)。

关于 Ansible 标签

使用 Ansible 标签允许您执行或跳过代码中的部分任务。

注意

更多信息可以在此处找到

例如,让我们修改用户创建任务

- name: add users
  user:
    name: "{{ item }}"
    state: present
    groups: "users"
  loop:
     - antoine
     - patrick
     - steven
     - xavier
  tags: users

现在您可以使用 ansible-playbook--tags 选项,只运行带有 users 标签的任务。

ansible-playbook -i inventories/production/hosts --tags users site.yml

您也可以使用 --skip-tags 选项。

关于目录布局

让我们关注一个用于正确运行 CMS(内容管理系统)的文件和目录组织建议。

我们的起点将是 site.yml 文件。这个文件就像 CMS 的总指挥,因为它只会根据需要包含目标节点所需的角色。

---
- name: "Config Management for {{ target }}"
  hosts: "{{ target }}"

  roles:

    - role: roles/functionality1

    - role: roles/functionality2

当然,这些角色必须在 roles 目录下创建,与 site.yml 文件在同一目录下。

我喜欢将我的全局变量放在 vars/global_vars.yml 中,即使我可以将它们存储在位于 inventories/production/group_vars/all.yml 的文件中。

---
- name: "Config Management for {{ target }}"
  hosts: "{{ target }}"
  vars_files:
    - vars/global_vars.yml
  roles:

    - role: roles/functionality1

    - role: roles/functionality2

我还喜欢保留禁用某个功能性的可能性。所以我使用条件和默认值来包含我的角色,如下所示:

---
- name: "Config Management for {{ target }}"
  hosts: "{{ target }}"
  vars_files:
    - vars/global_vars.yml
  roles:

    - role: roles/functionality1
      when:
        - enable_functionality1|default(true)

    - role: roles/functionality2
      when:
        - enable_functionality2|default(false)

不要忘记使用标签。

- name: "Config Management for {{ target }}"
  hosts: "{{ target }}"
  vars_files:
    - vars/global_vars.yml
  roles:

    - role: roles/functionality1
      when:
        - enable_functionality1|default(true)
      tags:
        - functionality1

    - role: roles/functionality2
      when:
        - enable_functionality2|default(false)
      tags:
        - functionality2

您应该得到类似这样的结果:

$ tree cms
cms
├── inventories
│   └── production
│       ├── group_vars
│          └── plateform.yml
│       ├── hosts
│       └── host_vars
│           ├── client1.yml
│           └── client2.yml
├── roles
│   ├── functionality1
│      ├── defaults
│         └── main.yml
│      └── tasks
│          └── main.yml
│   └── functionality2
│       ├── defaults
│          └── main.yml
│       └── tasks
│           └── main.yml
├── site.yml
└── vars
    └── global_vars.yml

注意

您可以自由地在集合中开发您的角色。

测试

让我们启动 playbook 并运行一些测试。

$ ansible-playbook -i inventories/production/hosts -e "target=client1" site.yml

PLAY [Config Management for client1] ****************************************************************************

TASK [Gathering Facts] ******************************************************************************************
ok: [client1]

TASK [roles/functionality1 : Task in functionality 1] *********************************************************
ok: [client1] => {
    "msg": "You are in functionality 1"
}

TASK [roles/functionality2 : Task in functionality 2] *********************************************************
skipping: [client1]

PLAY RECAP ******************************************************************************************************
client1                    : ok=2    changed=0    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0   

如您所见,默认情况下,只执行了 functionality1 角色的任务。

让我们在库存中为我们的目标节点激活 functionality2,然后重新运行 playbook。

$ vim inventories/production/host_vars/client1.yml
---
enable_functionality2: true
$ ansible-playbook -i inventories/production/hosts -e "target=client1" site.yml

PLAY [Config Management for client1] ****************************************************************************

TASK [Gathering Facts] ******************************************************************************************
ok: [client1]

TASK [roles/functionality1 : Task in functionality 1] *********************************************************
ok: [client1] => {
    "msg": "You are in functionality 1"
}

TASK [roles/functionality2 : Task in functionality 2] *********************************************************
ok: [client1] => {
    "msg": "You are in functionality 2"
}

PLAY RECAP ******************************************************************************************************
client1                    : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

尝试只应用 functionality2

$ ansible-playbook -i inventories/production/hosts -e "target=client1" --tags functionality2 site.yml

PLAY [Config Management for client1] ****************************************************************************

TASK [Gathering Facts] ******************************************************************************************
ok: [client1]

TASK [roles/functionality2 : Task in functionality 2] *********************************************************
ok: [client1] => {
    "msg": "You are in functionality 2"
}

PLAY RECAP ******************************************************************************************************
client1                    : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

在整个库存上运行。

$ ansible-playbook -i inventories/production/hosts -e "target=plateform" site.yml

PLAY [Config Management for plateform] **************************************************************************

TASK [Gathering Facts] ******************************************************************************************
ok: [client1]
ok: [client2]

TASK [roles/functionality1 : Task in functionality 1] *********************************************************
ok: [client1] => {
    "msg": "You are in functionality 1"
}
ok: [client2] => {
    "msg": "You are in functionality 1"
}

TASK [roles/functionality2 : Task in functionality 2] *********************************************************
ok: [client1] => {
    "msg": "You are in functionality 2"
}
skipping: [client2]

PLAY RECAP ******************************************************************************************************
client1                    : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
client2                    : ok=2    changed=0    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0   

如您所见,functionality2 只在 client1 上执行。

优势

遵循 Ansible 文档中的建议,您将很快获得一个

  • 易于维护的源代码,即使它包含大量角色。
  • 一个相对快速、可重复的合规性系统,您可以部分或完全应用。
  • 可以根据具体情况和服务器进行调整。
  • 您的信息系统的具体细节与代码分离,易于审计,并集中在您的配置管理的库存文件中。