Ansible:如何通过在主机上附加文件的任务实现幂等性(w/o 恢复到初始状态)

Ansible: how to achieve idempotence with tasks that append files on host (w/o reverting to initial state)

我很难了解如何根据文档创建遵循最佳实践的 Ansible 角色。我正在查看的以下用例是例如在主机上启用 Filebeat。可以通过将模块定义放在 /etc/filebeat/modules.d 文件夹中来配置 Filebeat。

添加模块时它工作正常。每次,幂等性在角色(剧本)的每个 运行 上起作用,启用一组给定的模块。

但是当我决定不再需要某个模块时我应该怎么做?我将其从角色中删除,重新 运行 剧本,以便启用所有 other 模块。 但是:之前的运行启用了一个模块,我在更改后没有直接安装角色。所以我的服务器状态仍然以不同于角色强加的方式改变。

我的问题是:我是否应该在应用模块之前注意删除模块,以便我始终从新状态开始?

例如:

- name: Remove modules
  file:
    dest: "/etc/filebeat/modules.d/{{ item }}"
    state: absent
  loop:
    - "module1.yml"
    - "module2.yml"
    - "module3.yml" # It was being installed in previous role, but not now

- name: Enable modules via 'modules.d' directory
  template:
    src: "modules.d/{{ item }}"
    dest: "/etc/filebeat/modules.d/{{ item }}"
    mode: '0644'
  loop:
    - "module1.yml"
    - "module2.yml"

所以我把module3.yml去掉了,因为我记得以前装过,装module1.ymlmodule2.yml.

而不是只安装我需要的,不管之前安装过什么:

- name: Enable modules via 'modules.d' directory
  template:
    src: "modules.d/{{ item }}"
    dest: "/etc/filebeat/modules.d/{{ item }}"
    mode: '0644'
  loop:
    - "module1.yml"
    - "module2.yml"

留给我 module1.ymlmodule2.yml(期望),不幸的是:module3.yml(来自以前的角色)。

如何管理以避免这种情况?并避免将服务器视为一台大型有状态机器,即使我 运行 一个角色,输出也与预期不同,因为之前已经完成了一些我在当前 Ansible 角色代码中看不到的事情。

您是否在 Ansible 工作流程中编写 revert 剧本以在需要时恢复到初始状态?

我很好奇。预先感谢您的回复。

简而言之:

- name: Configure filebeat modules
  hosts: all

  vars:
    fb_modules_d:
      - file: module1.yml
        state: present
      - file: module2.yml
        state: present
      - file: module3.yml
        state: absent

  tasks:
    - name: Make sure all needed module files are present
      template:
        src: "modules.d/{{ item.file }}"
        dest: "/etc/filebeat/modules.d/{{ item.file }}"
        mode: '0644'
      loop: "{{ fb_modules_d | selectattr('state', '==', 'present') }}"
      notifiy: restart_filebeat
 
    - name: Make sure all disabled modules are removed
      file:
        dest: "/etc/filebeat/modules.d/{{ item.file }}"
        state: "{{ item.state }}"
      loop: loop: "{{ fb_modules_d | selectattr('state', '==', 'absent') }}"
      notify: restart_filebeat

  handlers:
    - name: Restart filebeat service
      listen: restart_filebeat
      systemd:
        name: filebeat
        state: restarted

注意:我在该示例的剧本中声明了变量,但那个变量很可能应该进入您的库存(组或主机级别),当然不在角色中(文档默认值除外)

一个更简单、更可靠的解决方案是删除所有未列出的文件。当您不知道目录的当前状态是什么时,这将特别有用。例如给定目录(相对于PWD进行测试)

shell> tree etc/filebeat/modules.d
etc/filebeat/modules.d
└── module99.yml

下面的剧本

shell> cat playbook.yml
- name: Configure filebeat modules
  hosts: localhost

  vars:
    fb_modules_path: etc/filebeat/modules.d
    fb_modules_d:
      - module1.yml
      - module2.yml
      - module3.yml

  tasks:
    - name: Create modules
      template:
        src: "modules.d/{{ item }}"
        dest: "{{ fb_modules_path }}/{{ item }}"
        mode: '0644'
      loop: "{{ fb_modules_d }}"
      notify: restart_filebeat

    - name: Remove modules
      file:
        dest: "{{ fb_modules_path }}/{{ item }}"
        state: absent
      loop: "{{ query('fileglob', fb_modules_path ~ '/*')|
                map('basename')|
                difference(fb_modules_d) }}"
      notify: restart_filebeat

  handlers:
    - name: Restart filebeat service
      listen: restart_filebeat
#     systemd:
#       name: filebeat
#       state: restarted
      debug:
        msg: Restart filebeat

从列表中创建文件并删除其他文件

TASK [Create modules] ***************************************************
changed: [localhost] => (item=module1.yml)
changed: [localhost] => (item=module2.yml)
changed: [localhost] => (item=module3.yml)

TASK [Remove modules] ***************************************************
changed: [localhost] => (item=module99.yml)

RUNNING HANDLER [Restart filebeat service] ******************************
ok: [localhost] => 
  msg: Restart filebeat
shell> tree etc/filebeat/modules.d
etc/filebeat/modules.d
├── module1.yml
├── module2.yml
└── module3.yml

剧本是幂等的。