【问题标题】:Ansible rollback: run a group of tasks over list of hosts even when one of hosts failedAnsible 回滚:即使其中一个主机发生故障,也可以在主机列表上运行一组任务
【发布时间】:2019-11-04 22:27:32
【问题描述】:

我有一本包含多个角色、主机和组的剧本。我正在尝试开发一个可以在所有主机上运行的回滚功能。我目前的障碍是我看不到将角色、块或任务集委派给主机组的方法

  • 我尝试在没有循环的情况下查找对组的委托,因此它可以在块上工作。
  • import_role 不接受循环
  • include_role 不接受 delegate_to
  • 与 import_tasks/include_tasks 相同

这是我现在拥有的剧本文件(缩短版)

- hosts: all
  any_errors_fatal: true
  vars_prompt:

  - name: "remote_user_p"
    prompt: "Remote user running the playbook"
    default: "root"
    private: no

  - name: "service_user_p"
    prompt: "Specify user to run non-root tasks"
    default: "user"
    private: no

  tasks:
    - set_fact:
        playbook_type: "upgrade"

    - import_role:
        name: 0_pre_check
      run_once: true
      remote_user: "{{ remote_user_p }}"
      become_user: "{{ service_user_p }}"
      become_method: su
      become: yes

    - block:      
      - import_role:
          name: 1_os

      - import_role:
          name: 2_mysql
        when: inventory_hostname in groups['mysql'] | default("")

      - import_role:
          name: 3_web
        when: inventory_hostname in groups['web'] | default("") 
...

      rescue:
        - block:
          - name: run rollback
            import_tasks: ../common/roles/5_rollback/tasks/rollback.yml

      remote_user: "{{ remote_user }}"
      become_user: "{{ service_user }}"
      become_method: su
      become: yes

这是来自 rollback.yml 的一些示例代码:

- block:
  - name: rollback symlinks to config dir
    file:
      src: "{{ current_config_path }}"
      dest: "{{ install_dir }}/static/cfg"
      owner: "{{ service_user }}"
      group: "{{ service_user_primary_group }}"
      state: link
    when: current_new_configs | default("N") == "Y"
    delegate_to: "{{ item }}"
    with_items:
      - "{{ ansible_play_hosts }}"

  - block:           
    - name: return config files
      shell: test -f '{{ item.1.current_ver_file_path }}' && cp -p {{ item.1.current_ver_file_path }} {{ item.1.old_config_location }}
      args:
        warn: false
      register: return_config_files
      failed_when: return_config_files.rc >= 2
      when:
        - roolback_moved_cfg | default('N') == "Y"
        - inventory_hostname in groups[item.0.group]
        - item.1.old_config_location != ""
        - item.1.current_ver_file_path != ""
      with_subelements:
        - "{{ config_files }}"
        - files
      become_user: root
      become_method: sudo
      become: yes

    - name: systemctl daemon-reload  
      shell: systemctl daemon-reload
      failed_when: false
      when: root_rights == "Y"
      args:
        warn: false
      delegate_to: "{{ item }}"
      with_items:
        - "{{ ansible_play_hosts }}"
    when: root_rights == "Y"
    become_user: root
    become_method: sudo
    become: yes

  - fail:
      msg: "Upgrade failed. Symbolic links were set to the previous version. Fix the issues and try again. If you wish to cancel the upgrade, restore the database backup manually."

如您所见,现在我通过引入

来使用蹩脚的解决方法
      delegate_to: "{{ item }}"
      with_items:
        - "{{ ansible_play_hosts }}"

在每项任务之后。

这里有两个问题: 1.我不能在任务return config files之后使用相同的方法,因为它已经使用了一个循环 2. 这通常是蹩脚的代码重复,我讨厌它

我为什么需要它:例如,如果 playbook 在 mysql 角色中的某处执行失败,则 rescue 块将仅在该 mysql 角色中的主机上执行(顺便说一句,执行来自下一个角色将在运行救援块时继续 - 尽管付出了所有努力,但任务数量相同),而我希望它改为在所有主机上运行。

【问题讨论】:

  • 您是否考虑过第二次播放- hosts: rollback-hosts,然后在rescue: 块中使用add_host: 来分配所有应该回滚到该组(否则为空)的主机?跨度>
  • 感谢您的回复,这对我来说是一个新想法!这里的问题是我使用any_errors_fatal: true,第二次播放不会在第一次失败的任务中执行,如果我没有失败,第一次播放将继续。我的目标是在所有主机上成功执行任务,如果没有 - 然后失败并回滚。这就是我现在所拥有的:``` - 主机:所有 any_errors_fatal:true 任务:......救援: - 块: - set_fact:foo:“bar” - 失败:msg:“失败” - 主机:所有任务: -调试: var: foo ```

标签: ansible ansible-2.x


【解决方案1】:

我终于能够用丑陋的 hack 解决这个问题。使用过的剧本,而不仅仅是角色——现在有超过 10 个剧本。不要评判我,我花了很多精力试图让它变得更好):

示例游戏,然后进行检查 - 与其他游戏相同。

- hosts: mysql
  any_errors_fatal: true
  tasks:
    - block:              
      - import_role:
          name: 2_mysql
        when: not rollback | default(false)
      rescue:
        - block:
          - name: set fact for rollback
            set_fact: 
              rollback: "yes"
            delegate_to: "{{ item }}"
            delegate_facts: true
            with_items: "{{ groups['all'] }}"

- hosts: all
  any_errors_fatal: true
  tasks:
    - name: run rollback
      import_tasks: ../common/roles/5_rollback/tasks/rollback.yml
      when: rollback | default(false)

【讨论】:

    【解决方案2】:

    include_role 不接受delegate_to

    确实如此。

    使用 ansible-core 2.8:

    - name: "call my/role with host '{{ansible_hostname}}' for hosts in '{{ansible_play_hosts}}'"
      include_role:
        name: my/role
      apply:
        delegate_to: "{{current_host}}"
      with_items: "{{ansible_play_hosts}}"
        loop_control:
          loop_var: current_host
    

    使用 ansible-core 2.5 到 2.7,参见 George Shuklin 中的“2.5: delegate_to, include_role with loops”,在 ansible/ansible issue 35398 中提到

    - name: "call my/role with host '{{ansible_hostname}}' for items in '{{ansible_play_hosts}}'"
        include_tasks: loop.yml
        with_items: "{{ansible_play_hosts}}"
        loop_control:
          loop_var: current_host
    

    loop.yml 在自己的文件中有另一个任务:

    - name: "Import my/role for '{{current_host}}'"
      import_role: name=my/role
      delegate_to: "{{current_host}}"
    

    因此,在两个文件(使用 ansible-core 2.7)或一个文件 (2.8) 中,您可以让 all 角色及其任务在委托服务器上运行。

    【讨论】:

      猜你喜欢
      • 2015-12-14
      • 1970-01-01
      • 2017-02-23
      • 1970-01-01
      • 2022-11-19
      • 1970-01-01
      • 2021-07-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多