首页 > 解决方案 > 适用于生产环境的 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 个主机。任何想法或示例如何获得最好的外观,最好的工作结果,而无需像手动修补那样做更多的工作?

标签: ansible

解决方案


这是实现这一目标的一种可能很幼稚的方法。

首先,您可以在清单中创建组,但没有主机。

笔记:

  • 我修复了您的清单中的一些问题,即缺少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:

然后:

这将是这样做的剧本:

- 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意愿运行,产生结果:defaultpatch_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   

推荐阅读