ansible - 适用于生产环境的 Ansible 修补订单/库存逻辑
问题描述
这不是一个技术问题,但也许 Ansible 有一些我还不知道的功能会有所帮助。我能够使用 Ansible 自动修补,但是以正确的顺序选择正确的主机/组很复杂,我将尝试解释它。
让我们以这个清单为例:
---
all:
dcs:
hosts:
domaincontroller1
domaincontroller2
dbs:
hosts:
sql1
sql2
webservers:
hosts:
websrv1 #has a mysql connection and services vars
websrv2
websrv3 #has a mysql connection and services vars
websrv4
那么你在补丁日做什么?您希望每次至少有一个域控制器在运行。您希望所有连接到 sql 的 web 服务器都关闭,或者它们的服务停止,然后您首先修补 sql 服务器,等到它们再次运行,修补 web 服务器并等待它们连接到 sql。
目前,我将主机文件分成两组。第一组是一个 DC 和所有不连接到 sql 的服务器。第二组包含sql1
, sql2
, webserver1
, ... 并且有一个不同的剧本首先修补该行中的前 2 个,然后再修补所有其他。但是在这样做时,我有一个丑陋/未排序的主机文件,例如,我无法将更改应用于所有 Web 服务器。
---
all:
patch1:
hosts:
domaincontroller1
websrv2
websrv4
patch2:
hosts:
sql1
sql2
domaincontroller2
websrv1 #has a mysql connection and services vars
websrv3 #has a mysql connection and services vars
其他人是怎么做到的?有没有办法将组分成两半,所以有一个patch1
组包含 50% 的 DC 和所有未定义服务的 Web 服务器(可能是动态组?)。否则,我将需要创建完美的分组库存并在其下方添加补丁第 1 天和第 2 天的组,这会导致同一库存中有一台服务器多次,这使得更改更加复杂。
另一个想法是使用标签,如patchfirst
, patchsecond
,并为任何服务器创建一个host_vars
文件,该文件同样适用于大约 100 个主机。任何想法或示例如何获得最好的外观,最好的工作结果,而无需像手动修补那样做更多的工作?
解决方案
这是实现这一目标的一种可能很幼稚的方法。
首先,您可以在清单中创建组,但没有主机。
笔记:
- 我修复了您的清单中的一些问题,即缺少
children
关键字,以及主机应该是 YAML 键的位置,因此以冒号 (:
) 结尾。 - 我
sql_connection
在一些 Web 服务器上添加了一个变量,以制作在与数据库相同的补丁日选择 Web 服务器的逻辑。
使其适应主机中定义的现有变量。 - 我还添加了一些额外的主机,仅用于演示目的。
所以,我们有一个看起来像这样的库存:
all:
children:
patch_day_1:
patch_day_2:
dcs:
hosts:
domaincontroller1:
domaincontroller2:
dbs:
hosts:
sql1:
sql2:
sql3:
sql4:
sql5:
webservers:
hosts:
websrv1:
sql_connection: mysql://some_connection_string
websrv2:
websrv3:
sql_connection: mysql://some_connection_string
websrv4:
websrv5:
然后:
- 我们在数组切片符号的帮助下选择域控制器。
- 我们
sql_connection
根据hostvars
. - 我们选择包含定义
sql_connection
. - 最后但同样重要的是,我们在
extra-vars
.
这将是这样做的剧本:
- name: Logically split hosts
hosts: localhost
gather_facts: no
tasks:
- name: Elect half of the controllers in the first group
add_host:
name: "{{ item }}"
groups: patch_day_1
loop: "{{ groups.dcs[0:(groups.dcs | length / 2) | int] }}"
- name: Elect all the web servers that do not have a `sql_connection` in the first group
add_host:
name: "{{ item }}"
groups: patch_day_1
loop: "{{ groups.webservers }}"
when: hostvars[item].sql_connection is not defined
- name: Elect the other half of the controllers in the second group
add_host:
name: "{{ item }}"
groups: patch_day_2
loop: "{{ groups.dcs[(groups.dcs | length / 2) | int:groups.dcs | length] }}"
- name: Elect all the databases in the second group
add_host:
name: "{{ item }}"
groups: patch_day_2
loop: "{{ groups.dbs }}"
- name: Elect all the web servers that do have a `sql_connection` in the second group
add_host:
name: "{{ item }}"
groups: patch_day_2
loop: "{{ groups.webservers }}"
when: hostvars[item].sql_connection is defined
- name: Patch execution
hosts: "patch_day_{{ patch_day | default(1) }}"
gather_facts: no
tasks:
- name: Display hosts targeted in the play
debug:
var: ansible_play_hosts_all
run_once: true
这个,对组没有extra-vars
意愿运行,产生结果:default
patch_day_1
PLAY [Logically split hosts] *************************************************************************************
TASK [Elect half of the controllers in the first group] **********************************************************
ok: [localhost] => (item=domaincontroller1)
ok: [localhost] => (item=domaincontroller2)
TASK [Elect all the web servers that do not have a `sql_connection` in the first group] **************************
skipping: [localhost] => (item=websrv1)
ok: [localhost] => (item=websrv2)
skipping: [localhost] => (item=websrv3)
ok: [localhost] => (item=websrv4)
ok: [localhost] => (item=websrv5)
TASK [Elect the other half of the controllers in the second group] ***********************************************
ok: [localhost] => (item=domaincontroller3)
ok: [localhost] => (item=domaincontroller4)
ok: [localhost] => (item=domaincontroller5)
TASK [Elect all the databases in the second group] ***************************************************************
ok: [localhost] => (item=sql1)
ok: [localhost] => (item=sql2)
ok: [localhost] => (item=sql3)
ok: [localhost] => (item=sql4)
ok: [localhost] => (item=sql5)
TASK [Elect all the web servers that do have a `sql_connection` in the second group] *****************************
ok: [localhost] => (item=websrv1)
skipping: [localhost] => (item=websrv2)
ok: [localhost] => (item=websrv3)
skipping: [localhost] => (item=websrv4)
skipping: [localhost] => (item=websrv5)
PLAY [Patch execution] *******************************************************************************************
TASK [Display hosts targeted in the play] ************************************************************************
ok: [domaincontroller1] =>
ansible_play_hosts_all:
- domaincontroller1
- domaincontroller2
- websrv2
- websrv4
- websrv5
PLAY RECAP *******************************************************************************************************
domaincontroller1 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
localhost : ok=6 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
但是,使用 运行extra-vars
,然后我们可以定位patch_day_2
.
所以,用命令运行它
ansible-playbook play.yml --extra-vars="patch_day=2"
这产生了回顾:
PLAY [Logically split hosts] *************************************************************************************
TASK [Elect half of the controllers in the first group] **********************************************************
ok: [localhost] => (item=domaincontroller1)
ok: [localhost] => (item=domaincontroller2)
TASK [Elect all the web servers that do not have a `sql_connection` in the first group] **************************
skipping: [localhost] => (item=websrv1)
ok: [localhost] => (item=websrv2)
skipping: [localhost] => (item=websrv3)
ok: [localhost] => (item=websrv4)
ok: [localhost] => (item=websrv5)
TASK [Elect the other half of the controllers in the second group] ***********************************************
ok: [localhost] => (item=domaincontroller3)
ok: [localhost] => (item=domaincontroller4)
ok: [localhost] => (item=domaincontroller5)
TASK [Elect all the databases in the second group] ***************************************************************
ok: [localhost] => (item=sql1)
ok: [localhost] => (item=sql2)
ok: [localhost] => (item=sql3)
ok: [localhost] => (item=sql4)
ok: [localhost] => (item=sql5)
TASK [Elect all the web servers that do have a `sql_connection` in the second group] *****************************
ok: [localhost] => (item=websrv1)
skipping: [localhost] => (item=websrv2)
ok: [localhost] => (item=websrv3)
skipping: [localhost] => (item=websrv4)
skipping: [localhost] => (item=websrv5)
PLAY [Patch execution] *******************************************************************************************
TASK [Display hosts targeted in the play] ************************************************************************
ok: [domaincontroller3] =>
ansible_play_hosts_all:
- domaincontroller3
- domaincontroller4
- domaincontroller5
- sql1
- sql2
- sql3
- sql4
- sql5
- websrv1
- websrv3
PLAY RECAP *******************************************************************************************************
domaincontroller3 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
localhost : ok=6 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0