主机组成员及其 host_vars 的 Ansible 嵌套循环

Ansible nested loop over host group members and their host_vars

谁能帮我构造嵌套循环?

我正在创建简单的备份角色,它将为备份准备客户端服务器,它也会准备备份服务器。备份服务器将在指定时间从客户端下载备份。我想为每个客户端定义不同的时间和不同的文件夹(by host_vars)。

我的工作流程:

  1. 备份服务器

    • 安装包
    • 创建备份用户
    • 创建备份目录
  2. 客户端

    • 安装包
    • 将备份的服务器 ssh 密钥添加到 authorized_keys(有限制)
    • 一些ssh设置
    • 重新加载 sshd
  3. 备份服务器

    • 为下载备份设置 cron 作业(每个客户端都应该有自己的 cron 条目 - 不同的时间和不同的备份文件夹集)

我有这个角色的第一个版本,它应用于服务器的客户端组。备份服务器是在 delegate_to: 语句的帮助下配置的。但是在备份服务器上创建 cron 文件有问题,因为有类似 'race condition' 的东西。当 playbook 完成时,备份的服务器 cron 文件中只有一个随机条目。但我希望所有客户端服务器都应该有条目。我试图打开问题 https://github.com/ansible/ansible/issues/74189 - 我得到了答案,我应该更改对此问题的访问权限。

第二次尝试是重写ansible role。然后我可以将它应用于备份服务器而不是客户端组。现在我在客户端服务器组上使用 delegate_to:

备份服务器上的(简化的)预期 cron 作业示例:

0 0 * * * backup -include /home --include /var/www --include /srv --exclude '**' backup.lab.local::/ /home/backup/srv1.lab.local
# -> similar entry for srv2
# -> similar entry for srv3

我的场景:

# hosts file
[backup_servers]
backup.lab.local

[testing_servers]
srv1.lab.local
srv2.lab.local
srv3.lab.local

host_vars 文件示例:

# host_vars/srv1.lab.local
backup_folders:
  - /home
  - /var/www
  - /srv

我卡在了在备份服务器上创建 cron 条目的任务上。我需要循环 groups['testing_servers'] 并且在这个循环中我需要创建另一个循环 hostvars[<each_host_from_group>]['backup_folder'].

请问怎么做?

我不会遍历 backup_folder,我会 join 这些值。

鉴于您正确地遍历了 groups['testing_servers'] 个主机,例如

0 0 * * * backup {{ ([''] + hostvars[item]['backup_folder']) | join(' --include ') }}  --exclude '**' backup.lab.local::/ /home/backup/{{ item }}

会给:

0 0 * * * backup --include /home --include /var/www --include /srv --exclude '**' backup.lab.local::/ /home/backup/srv1.lab.local

请注意:这个奇怪的结构:([''] + hostvars[item]['backup_folder']) 是为了在列表的开头创建一个空元素,使其以 [=17= 开头],否则你将有

... backup /home --include /var/www --include /srv --exclude '**' ...
##        ^--- missing "--include " here

Cron 模块和并行写入

关于提到的 GitHub 问题,您可以通过使用 throttle: 1 (documentation)[= 解决这个问题(在一台主机上并行写入 crontab 文件) 22=]

即你的 cron 剧本应该包含:

  tasks:
    - name: "Set backup cron job"
      ansible.builtin.cron:
        name: "Backup cron job for {{ inventory_hostname }}"
        user: root
        minute: "{{ backup_minute }}"
        hour: "{{ backup_hour }}"
        day: "{{ backup_day }}"
        month: "{{ backup_month }}"
        weekday: "{{ backup_weekday }}"
        job: "rdiff-backup --create-full-path --remote-schema \"ssh -C -i /home/backup/.ssh/id_rsa -p {{ ansible_port | default(22) }} -o 'StrictHostKeyChecking no' \%s rdiff-backup --server\" --include /path/to/dir --exclude '**' {{ ansible_host }}::/ /home/backup/{{ inventory_hostname }}"
      delegate_to: deb10.lab.local
      throttle: 1                       # <--------- THIS IS THE IMPORTANT LINE

嵌套循环

你提到的使用嵌套循环的另一种方法可以通过在 Ansible 中使用嵌套循环来完成,或者通过预构建嵌套结构,然后对其进行迭代:

Ansible - 嵌套循环

Documentation

您可以按照文档所述构建循环中的循环,方法是使用一个任务作为带有 include_tasks 模块的外循环,以及一个带有重命名的循环控制变量 (loop_var) 的循环。

如文档中的示例所示,您需要将其分成两个文件:

# main.yml tasks file
- include_tasks: inner.yml
  loop:
    - 1
    - 2
    - 3
  loop_control:
    loop_var: outer_item

# inner.yml tasks file
- name: Print outer and inner items
  ansible.builtin.debug:
    msg: "outer item={{ outer_item }} inner item={{ item }}"
  loop:
    - a
    - b
    - c

Jinja2 - 预生成结构

Documentation

使用产品过滤器创建列表列表:

['alice', 'bob'] |product(['clientdb', 'employeedb', 'providerdb'])|list

创造

[('alice', 'clientdb'), ('alice', 'employeedb'), ('alice', 'providerdb'), ('bob', 'clientdb'), ('bob', 'employeedb'), ('bob', 'providerdb')]

因此,作为文档状态中的示例,您可以使用类似于以下的代码:

- name: Give users access to multiple databases
  community.mysql.mysql_user:
    name: "{{ item[0] }}"
    priv: "{{ item[1] }}.*:ALL"
    append_privs: yes
    password: "foo"
  loop: "{{ ['alice', 'bob'] |product(['clientdb', 'employeedb', 'providerdb'])|list }}"