블록

블록 (Blocks)

플레이북 작업이 많아지면 공통 지시문을 매번 반복하거나, 작업이 실패했을 때 어떻게 처리할지 고민하게 돼요. 블록(block)은 작업들을 논리적인 그룹으로 묶어주고, 많은 프로그래밍 언어의 예외 처리처럼 작업 오류를 다루는 방법도 제공합니다.

출처: 문서

본문

블록은 작업의 논리적 그룹을 만들어요. 블록은 또한 많은 프로그래밍 언어의 예외 처리와 유사하게 작업 오류를 처리하는 방법도 제공합니다.

블록으로 작업 묶기 (Grouping tasks with blocks)

블록 안의 모든 작업은 블록 레벨에서 적용된 지시문을 상속받아요. 단일 작업에 적용할 수 있는 대부분(루프는 제외)을 블록 레벨에서 적용할 수 있으므로, 블록은 작업들에 공통인 데이터나 지시문을 설정하기 훨씬 쉽게 합니다. 지시문은 블록 자체에는 영향을 주지 않고, 블록이 감싼 작업에만 상속돼요. 예를 들어 when 문은 블록 자체가 아니라 블록 안의 작업에 적용됩니다.

블록 안에 이름이 있는 작업을 둔 블록 예시:

tasks:
  - name: Install, configure, and start Apache
    when: ansible_facts['distribution'] == 'CentOS'
    block:
      - name: Install httpd and memcached
        ansible.builtin.yum:
          name:
          - httpd
          - memcached
          state: present

      - name: Apply the foo config template
        ansible.builtin.template:
          src: templates/src.j2
          dest: /etc/foo.conf

      - name: Start service bar and enable it
        ansible.builtin.service:
          name: bar
          state: started
          enabled: True
    become: true
    become_user: root
    ignore_errors: true

위 예시에서 when 조건은 Ansible이 블록 안의 세 작업 각각을 실행하기 전에 평가돼요. 세 작업 모두 권한 승격 지시문을 상속받아 root로 실행됩니다. 마지막으로 ignore_errors: true는 일부 작업이 실패해도 Ansible이 플레이북을 계속 실행하도록 보장합니다.

참고 (Note)

블록 안의 모든 작업은 include_role로 포함된 작업을 포함해 블록 레벨에서 적용된 지시문을 상속받아요. 블록 이름은 Ansible 2.3부터 사용할 수 있어요. 플레이북을 실행할 때 실행되는 작업을 더 잘 파악하려면 블록 안이든 밖이든 모든 작업에 이름을 붙이는 것을 권장합니다.

블록으로 오류 처리하기 (Handling errors with blocks)

rescuealways 섹션을 가진 블록을 사용해 작업 오류에 Ansible이 어떻게 반응할지를 제어할 수 있어요.

참고 (Note)

잘못된 작업 정의로 인한 오류와 도달 불가능한 호스트(unreachable hosts)로 인한 오류는 블록의 rescuealways 섹션을 트리거하지 않습니다.

rescue 블록은 블록 안의 이전 작업이 실패했을 때 실행할 작업을 지정해요. 이 방식은 많은 프로그래밍 언어의 예외 처리와 유사합니다. Ansible은 블록의 작업이 'failed' 상태를 반환한 후에만 rescue 블록을 실행합니다.

블록 오류 처리 예시:

tasks:
  - name: Handle the error
    block:
      - name: Print a message
        ansible.builtin.debug:
          msg: 'I execute normally'

      - name: Force a failure
        ansible.builtin.command: /bin/false

      - name: Never print this
        ansible.builtin.debug:
          msg: 'I never execute, due to the above task failing, :-('
    rescue:
      - name: Print when errors
        ansible.builtin.debug:
          msg: 'I caught an error, can do stuff here to fix it, :-)'

블록에 always 섹션을 추가할 수도 있어요. always 섹션의 작업은 이전 블록의 작업 상태가 어떻든 항상 실행됩니다.

always 섹션이 있는 블록:

tasks:
  - name: Always do X
    block:
      - name: Print a message
        ansible.builtin.debug:
          msg: 'I execute normally'

      - name: Force a failure
        ansible.builtin.command: /bin/false

      - name: Never print this
        ansible.builtin.debug:
          msg: 'I never execute :-('
    always:
      - name: Always do this
        ansible.builtin.debug:
          msg: "This always executes, :-)"

이 요소들을 함께 쓰면 복잡한 오류 처리가 가능해져요.

모든 섹션이 있는 블록:

tasks:
  - name: Attempt and graceful roll back demo
    block:
      - name: Print a message
        ansible.builtin.debug:
          msg: 'I execute normally'

      - name: Force a failure
        ansible.builtin.command: /bin/false

      - name: Never print this
        ansible.builtin.debug:
          msg: 'I never execute, due to the above task failing, :-('
    rescue:
      - name: Print when errors
        ansible.builtin.debug:
          msg: 'I caught an error'

      - name: Force a failure in middle of recovery! >:-)
        ansible.builtin.command: /bin/false

      - name: Never print this
        ansible.builtin.debug:
          msg: 'I also never execute :-('
    always:
      - name: Always do this
        ansible.builtin.debug:
          msg: "This always executes"

block 섹션의 작업은 정상적으로 실행돼요. 블록의 어떤 작업이 failed를 반환하면 rescue 섹션이 오류에서 복구하는 작업을 실행합니다. always 섹션은 blockrescue 섹션의 결과와 무관하게 실행됩니다.

블록에서 오류가 발생했는데 rescue 작업이 성공하면, Ansible은 해당 실행의 원래 작업 failed 상태를 되돌리고 원래 작업이 성공한 것처럼 플레이를 계속 실행해요. 복구된 작업은 성공으로 간주되며 max_fail_percentageany_errors_fatal 구성을 트리거하지 않습니다. 다만 Ansible은 여전히 플레이북 통계에서 실패를 보고합니다.

rescue 작업에서 flush_handlers와 함께 블록을 사용해 오류가 발생해도 모든 핸들러가 실행되도록 할 수 있어요:

오류 처리에서 핸들러를 실행하는 블록:

tasks:
  - name: Attempt and graceful roll back demo
    block:
      - name: Print a message
        ansible.builtin.debug:
          msg: 'I execute normally'
        changed_when: true
        notify: Run me even after an error

      - name: Force a failure
        ansible.builtin.command: /bin/false
    rescue:
      - name: Make sure all handlers run
        meta: flush_handlers
handlers:
   - name: Run me even after an error
     ansible.builtin.debug:
       msg: 'This handler runs even on error'

버전 2.1에서 추가되었어요.

Ansible은 블록의 rescue 부분에 있는 작업을 위한 변수 몇 개를 제공합니다:

  • ansible_failed_task — 'failed'를 반환해 rescue를 트리거한 작업. 예를 들어 이름을 얻으려면 ansible_failed_task.name을 사용하세요.
  • ansible_failed_result — rescue를 트리거한 실패한 작업의 캡처된 반환 결과. register 키워드에서 이 변수를 사용한 것과 같아요.

이 변수들은 rescue 섹션에서 검사할 수 있습니다:

rescue 섹션에서 특수 변수 사용하기:

tasks:
  - name: Attempt and graceful roll back demo
    block:
      - name: Do Something
        ansible.builtin.shell: grep $(whoami) /etc/hosts

      - name: Force a failure, if previous one succeeds
        ansible.builtin.command: /bin/false
    rescue:
      - name: All is good if the first task failed
        when: ansible_failed_task.name == 'Do Something'
        ansible.builtin.debug:
          msg: All is good, ignore error as grep could not find 'me' in hosts

      - name: All is good if the second task failed
        when: "'/bin/false' in ansible_failed_result.cmd | d([])"
        ansible.builtin.fail:
          msg: It is still false!!!

참고 (Note)

ansible-core 2.14 이상에서는 블록을 중첩할 때 두 변수 모두 내부 블록에서 외부 rescue 부분으로 전파됩니다.

더 보기 (See also)

  • Ansible 플레이북 — 플레이북 소개.
  • 롤 (Roles) — 롤로 플레이북 구성하기.
  • 커뮤니케이션 — 질문이나 도움이 필요하거나 아이디어를 나누고 싶다면 Ansible 커뮤니케이션 안내서를 참고하세요.

더 알아보기 (Learn more)

  • 블록과 함께 쓰이는 핸들러와 오류 처리는 "플레이북 오류 처리(playbooks_error_handling)" 페이지에서 자세히 다뤄요.