ansible playbook에서의 오류처리
ansible에서는 각 task의 return code를 평가하여 task의 성공 여부를 판단한다. 일반적으로 task에서 하나가 실패하는 즉시 ansible은 해당 호스트의 나머지 play를 중단하고 종료된다. 하지만 작업이 실패한다 하더라도 play를 계속할 수 있어야 한다. 예를들어 특정 작업이 실패할 것으로 예상하고 몇가지 다른 작업을 조건부로 실행하여 복구하려고 할 수도 있기 때문이다.
작업 실패 무시하기 : ignore_errors
- 각각의 모듈 task에 대해 ignore_errors: yes 라고 명시할 수 있고, 이렇게 명시되면 task가 실패해도 무시하고 그대로 playbook이 진행된다.
- name: latest version of notapkg is installed
yum:
name: notapkg
state: latest
ignore_errors: yes
- 위 예시의 경우, notapkg를 설치하지 못하는 경우 실패할 것이지만 ignore_errors: yes이므로 그대로 진행된다.
작업 실패 후 핸들러 강제 실행하기 : force_handlers
- 예를들어 마지막 task가 실패하여 해당 호스트에서 play가 중단되면, 이전 task에 대해 trigger된 모든 핸들러가 실행되지 않는다.
- 따라서 다른 task에서 작업이 실패해도 핸들러는 작동하게 하려면, force_handlers 지시문을 아래와 같이 적용한다.
---
- hosts: all
force_handlers: yes
tasks:
- name: a task which always notifies its handler
command: /bin/true
notify: restart the database
- name: "a task which fails because the package doesn't exist"
yum:
name: notapkg
state: latest
handlers:
- name: restart the database
service:
name: mariadb
state: restarted
작업 실패 조건 지정 : failed_when
- 이것은 조건문인데, 명령이 성공이었다고 하더라도 특정 조건을 만족하면 fail로 만들라는 조건문이다.
- 예를 들어 아래 구문은, task가 성공적이었다 하더라도 결과에 "Password missing"이라는 구문이 있다면 fail로 만드는 것이다. task가 성공이므로 changed라고 뜨겠지만, 실제로는 password에 문제가 있는 것이므로 이것은 좋지 않다. 문제가 있는 상태에서 핸들러가 작동할수도 있다.
- 아래처럼 이렇게 하면 changed가 남지 않으므로 핸들러를 설정한 경우 핸들러가 작동하지 않을 것이다.
tasks:
- name: Run user createion script
shell: /usr/local/bin/create_users.sh
register: command_result
failed_when: "'Password missing' in command_result.stdout"
- 또 다른 방식으로, failed 모듈을 사용해 작업 실패를 강제할 수 있다. 이 방식은 위 내용과 결과가 완전히 동일하며, 조금 복잡하지만 명확한 실패 메시지를 제공할 수 있다.
tasks:
- name: Run user createion script
shell: /usr/local/bin/create_users.sh
register: command_result
ignore_errors: yes
- name: Report script failure
fail:
msg: "the password is missing in the output"
when: "'Password missing' in the command_result.stdout"
- 여기서는 fail 모듈과 조건문 when을 사용하여, task가 잘 되었다 하더라도 "Password missing" 이라는 구문이 포함되면 해당 구문을 fail시키고 사전에 정의해둔 메시지를 출력한다.
작업 결과를 changed로 보고하는 시점 지정 : changed_when
* changed_when: false 사용
- 이 키워드는 task에서 changed 결과를 보고하는 시점을 제어할 수 있다. 실제로 changed가 나와야 되는 task에 대해서도 changed_when: false 지시문을 포함하면 changed 로 보고되지 않는다. ok로 보고된다. 만약 true로 한다면 changed로 보고된다.
---
- name: play to setup web server
hosts: web.example.com
tasks:
- name: latest httpd version installed
yum:
name: httpd
state: absent
register: uninstall_result
changed_when: false
- name: debug install_result
debug:
var: uninstall_result
- 분명히 변경이 있는데, (삭제작업수행) ok로 뜬다.
* changed_when: 조건문 사용
- when이 붙었으므로 이것도 조건문을 사용할 수 있다. 이 조건이 맞으면, 즉 true이면, changed로 보고한다.
- 모든 모듈에 이 지시문을 쓸 수 있겠지만, 일반적으로 command, shell, raw 같이 무조건 changed로 보고되는 모듈에 대해 결과에 대한 반환값을 컨트롤하기 위해 자주 사용된다.
- 아래 예시를 참고하자.
---
- name: changed_when test
hosts: web.example.com
tasks:
- shell:
cmd: /tmp/upgrade-database
register: command_result
changed_when: "'completed' in command_result.stdout"
notify:
- restart_httpd
- debug:
var: command_result
handlers:
- name: restart_httpd
service:
name: httpd
state: restarted
현재 httpd와 mariadb가 설치되어 있고, database를 업그레이드 하면 httpd를 재시작하는 핸들러를 가진다. shell 모듈을 썼으므로 결과는 무조건 changed로 나올 것이다. 여기서 upgrade-database라는 프로그램의 결과가 "changed failed" 라고 가정하자(문제 발생을 가정한다). 즉 문제가 발생하면 handler는 실행되면 안된다. 따라서 위와 같이 changed_when: 조건이 completed가 결과에 포함되어있으면 false를 출력하도록 ansible-playbook을 만든다. 이렇게 false가 되면 ok를 출력하게 된다. 따라서 핸들러를 실행하지 않는다.
작업을 논리적으로 그룹화하기 : block
playbook에서, block은 task 들을 논리적으로 그룹화한다. 이 방법은 작업을 실행하는 방법을 제어할 때 사용할 수 있다. 크게 2가지 방법을 사용한다. 첫번째는 여러개의 task 중 몇개를 묶어서 뭔가 작업을 할 때, 그리고 두번째는 blocks와 함께 rescue, always를 함께 사용하여 작업 루틴을 설정할 때 사용한다.
* task를 몇개 묶어서 작업할 때 사용
- 아래는 총 task가 3개인데, 그 중 2개는 block에 묶여있고 when 조건이 적용되며, 남은 하나는 block과 연관없이 단독으로 수행된다.
- name: block example
hosts: web.example.com
tasks:
- block:
- name: package needed by yum
yum:
name: yum-plugin-versionlock
state: present
- name: lock version of tzdata
lineinfile:
dest: /etc/yum/pluginconf.d/versionlock.list
line: tzdata-2016j-1
state: present
register: check_result
when: ansible_distribution == "CentOS"
- name: check result
debug:
var: check_result
해당 서버가 CentOS가 맞으므로 block 안에 있는 2개의 task가 실행되었다.
* block과 rescue, always 문을 함께 사용하여 오류처리
- 아래와 같이 작업 실패 여부에 따른 루틴을 정의하여 오류처리를 할 수 있다.
- block : 실행할 기본 작업을 정의
- rescue: block에 정의된 작업이 실패하는 경우에만 실행되는 작업을 정의
- always: block과 rescue 문에 정의된 작업의 성공/실패 여부와 상관없이 항상 실행되는 작업을 정의
- 아래 예시를 살펴보자.
- name: block example
hosts: web.example.com
tasks:
- block:
- name: upgrade the database
shell:
cmd: /usr/local/lib/upgrade-database
rescue:
- name: revert the database upgrade
shell:
cmd: /usr/local/lib/revert-database
always:
- name: always restart the database
service:
name: mariadb
state: restarted
- /usr/local/lib/upgrade-database 파일이 없기 때문에 에러가 났다. 에러가 나면 일반적으로 playbook이 종료되지만 여기서는 그렇지 않다.
- block에서 에러가 났으므로 rescue가 실행된다. 실행이 잘 되었고, 그다음 always 는 항상 실행되므로 실행된다. 둘다 잘 실행되었다.
연습문제
* ignore_errors: yes 사용하기
http라는 패키지는 없으므로 에러가 발생한다. 하지만 ignore_errors: yes 이므로 그대로 진행해서 아래까지 완료한다.
---
- name: Task Failure Exercise
hosts: db.example.com
vars:
web_package: http
db_package: mariadb-server
db_service: mariadb
tasks:
- name: Install {{ web_package }} package
yum:
name: "{{ web_package }}"
state: present
ignore_errors: yes
- name: Install {{ db_package }} package
yum:
name: "{{ db_package }}"
state: present
* changed_when, failed_when 사용하기
- command: date 이 부분은 항상 changed가 된다. 따라서 이것을 그냥 ok로 하기 위해 changed_when: false를 사용한다.
- Install httpd package부분은 이미 설치된 상태에서 수행했기 때문에 already installed가 뜬다. 하지만 fail은 아니고 ok가 원래 떠야 한다. 그런데 failed_when 조건에 맞기 때문에 그냥 failed가 떠버렸다.
- block부분에 failed가 떴으므로, rescue 부분의 install mariadb-server package 부분이 실행된다. always는 항상 실행된다.
---
- name: Task Failure Exercise
hosts: db.example.com
vars:
web_package: httpd
db_package: mariadb-server
db_service: mariadb
tasks:
- name: Check local time
command: date
register: command_result
changed_when: false
- name: print local time
debug:
var: command_result.stdout
- block:
- name: Install {{ web_package }} package
yum:
name: "{{ web_package }}"
state: present
failed_when: web_package == "httpd"
rescue:
- name: Install {{ db_package }} package
yum:
name: "{{ db_package }}"
state: present
always:
- name: Start {{ db_service }} service
service:
name: "{{ db_service }}"
state: started
참조링크
- 플레이북에서 오류 처리 ansible 설명서
docs.ansible.com/ansible/latest/user_guide/playbooks_error_handling.html
- 오류처리 ansible 설명서
docs.ansible.com/ansible/latest/user_guide/playbooks_blocks.html#error-handling
'Ansible' 카테고리의 다른 글
15장. 병렬 구성 작업 (1) | 2021.02.27 |
---|---|
14장. ansible에서 jinja2 사용하기 (0) | 2021.02.26 |
12장. 작업 제어 구현 - 핸들러 (0) | 2021.02.24 |
11장. 작업 제어 구현 - 반복문과 조건문 (0) | 2021.02.23 |
10장. ansible vault (0) | 2021.02.23 |