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
如果目标节点是 group1
的 hostname1
,那么 hostname1.yml
和 group1.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 文档中的建议,您将很快获得一个
- 易于维护的源代码,即使它包含大量角色。
- 一个相对快速、可重复的合规性系统,您可以部分或完全应用。
- 可以根据具体情况和服务器进行调整。
- 您的信息系统的具体细节与代码分离,易于审计,并集中在您的配置管理的库存文件中。