Ansible 반복문
공식 문서: https://docs.ansible.com/ansible/latest/user_guide/playbooks_loops.html#playbooks-loops
작업(Task)에서 loop, with_, until 으로 반복문을 사용한다.
리스트 반복문
아래와 같이 주로 사용된다.
loop 대신 with_items, with_list 사용할 수 있다. 공식 문서를 참조하면 잘 나와 있다.
[vagrant@controller loop]$ cat test.yml
- hosts: 192.168.100.11
gather_facts: no
vars:
fruits:
- apple
- banana
- carrot
tasks:
- debug:
msg: "{{ item }}"
loop:
"{{ fruits }}"
[vagrant@controller loop]$ ansible-playbook test.yml
PLAY [192.168.100.11] ************************************************************************************************************
TASK [debug] *********************************************************************************************************************
ok: [192.168.100.11] => (item=apple) => {
"msg": "apple"
}
ok: [192.168.100.11] => (item=banana) => {
"msg": "banana"
}
ok: [192.168.100.11] => (item=carrot) => {
"msg": "carrot"
}
PLAY RECAP ***********************************************************************************************************************
192.168.100.11 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
딕셔너리 반복문
반복문 중인 딕셔너리의 밸류값을 뽑기 위해 item.[key] 로 value 를 뽑아줄 수 있다.
loop 대신 with_dict 를 사용할 수 있다. 공식문서 참조하면 알 수 있다.
- name: Add several users
ansible.builtin.user:
name: "{{ item.name }}"
state: present
groups: "{{ item.groups }}"
loop:
- { name: 'testuser1', groups: 'wheel' }
- { name: 'testuser2', groups: 'root' }
[vagrant@controller dict]$ cat test.yml
- hosts: 192.168.100.11
gather_facts: no
vars:
fruits:
- name: apple
count: 2
- name: banana
count: 3
tasks:
- debug:
msg: "{{ item.name }} / {{ item.count }}"
loop:
'{{ fruits }}'
[vagrant@controller dict]$ ansible-playbook test.yml
PLAY [192.168.100.11] ************************************************************************************************************
TASK [debug] *********************************************************************************************************************
ok: [192.168.100.11] => (item={u'count': 2, u'name': u'apple'}) => {
"msg": "apple / 2"
}
ok: [192.168.100.11] => (item={u'count': 3, u'name': u'banana'}) => {
"msg": "banana / 3"
}
PLAY RECAP ***********************************************************************************************************************
192.168.100.11 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Ansible 조건문
테스트 문 공식 문서: https://docs.ansible.com/ansible/latest/user_guide/playbooks_tests.html#playbooks-tests
vars:
url: "<https://example.com/users/foo/resources/bar>"
tasks:
- debug:
msg: "matched pattern 1"
when: url is match("<https://example.com/users/.*/resources>")
- debug:
msg: "matched pattern 2"
when: url is search("users/.*/resources/.*")
- debug:
msg: "matched pattern 3"
when: url is search("users")
- debug:
msg: "matched pattern 4"
when: url is regex("example\\.com/\\w+/foo")
when 뒤에 조건문이 오고 조건문 정의시 {{ }} 를 사용하지 않는다.
tasks:
- shell: /usr/bin/foo
register: result
ignore_errors: True
- debug:
msg: "it failed"
when: result is failed
# in most cases you'll want a handler, but if you want to do something right now, this is nice
- debug:
msg: "it changed"
when: result is changed
- debug:
msg: "it succeeded in Ansible >= 2.1"
when: result is succeeded
- debug:
msg: "it succeeded"
when: result is success
- debug:
msg: "it was skipped"
when: result is skipped
register 등록 변수는 해당 작업의 리턴 밸류를 저장하는 변수로 작업을 실행할 때 리턴 밸류 값을 사용해야 하는 경우 등록 변수를 사용한다.
failed: 작업이 실패했을 때
changed: 작업이 성공하고 실제 값이 변경됐을 때
succeded == success: 작업이 성공했지만 실제 값이 변경되지는 않았을 때
skipped: 작업이 스킵된 경우
조건문에 많이 사용하는 팩트 변수: https://docs.ansible.com/ansible/latest/user_guide/playbooks_conditionals.html#commonly-used-facts
- ansible_facts[”distribution”] == ansible_distribution
$ cat /etc/os-release 를 실행하면 해당 리눅스 os 의 계열과 정보 등을 확인할 수 있다.
$ ansible [ip 주소] -m setup 명령어를 실행하면 팩트 변수를 수집하여 보여준다.
[vagrant@controller 03_condition]$ cat test2.yml
- hosts: wp
tasks:
- debug:
msg: "hello CentOS"
when: ansible_facts["distribution"] == "CentOS"
- debug:
msg: "hello Ubuntu"
[vagrant@controller 03_condition]$ ansible-playbook test2.yml
PLAY [wp] ***********************************************************************************************************************************************************************************************
TASK [Gathering Facts] **********************************************************************************************************************************************************************************
ok: [192.168.100.11]
ok: [192.168.100.12]
ok: [192.168.100.13]
TASK [debug] ********************************************************************************************************************************************************************************************
ok: [192.168.100.11] => {
"msg": "hello CentOS"
}
ok: [192.168.100.12] => {
"msg": "hello CentOS"
}
skipping: [192.168.100.13]
TASK [debug] ********************************************************************************************************************************************************************************************
skipping: [192.168.100.11]
skipping: [192.168.100.12]
ok: [192.168.100.13] => {
"msg": "hello Ubuntu"
}
PLAY RECAP **********************************************************************************************************************************************************************************************
192.168.100.11 : ok=2 changed=0 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
192.168.100.12 : ok=2 changed=0 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
192.168.100.13 : ok=2 changed=0 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
핸들러
가능하면 멱등성을 만족해야 한다.
모든 모듈, 모듈의 파라미터가 멱등성을 만족하지는 않는다.
멱등성에 문제가 있는 코드, 플레이북을 실행할 때 마다 매번 서비스를 리스타트한다. 멱등성을 위배!
- hosts: 192.168.100.12
vars:
web_svc_port: "8080"
tasks:
- yum:
name: httpd
- lineinfile:
path: /etc/httpd/conf/httpd.conf
regexp: '^Listen'
line: 'Listen {{ web_svc_port }}'
register: result
- service:
name: httpd
state: restarted
enabled: yes
등록 변수를 사용해서 포트가 변경됐을 때만 서비스를 재시작하도록 설정했다.
- hosts: 192.168.100.12
vars:
web_svc_port: "8080"
tasks:
- yum:
name: httpd
- lineinfile:
path: /etc/httpd/conf/httpd.conf
regexp: '^Listen'
line: 'Listen {{ web_svc_port }}'
register: result
- service:
name: httpd
state: started
enabled: yes
- service:
name: httpd
state: restarted
when: result is changed
핸들러는 특정 작업이 변경 사항을 발생하는 경우에만 실행하기 위한 작업을 지정
handlers, notify 와 name 을 이용하여 핸들링할 수 있다
- name: Verify apache installation
hosts: webservers
vars:
http_port: 80
max_clients: 200
remote_user: root
tasks:
- name: Ensure apache is at the latest version
yum:
name: httpd
state: latest
- name: Write the apache config file
template:
src: /srv/httpd.j2
dest: /etc/httpd.conf
notify:
- Restart apache
- name: Ensure apache is running
service:
name: httpd
state: started
handlers:
- name: Restart apache
ansible.builtin.service:
name: httpd
state: restarted
핸들러가 실행되는 순서?
- 알림을 받은 핸들러 작업만 순서대로 실행
- 모든 작업(tasks)이 끝난 이후에 핸들러가 실행된다.
- 알림을 받은 횟수와 상관없이 한 번만 실행된다.
핸들러가 실행되지 않고 후속 작업에서 실패한 경우
결론: 핸들러가 실행되지 않음
강제로 핸들러를 실행하게 하는 설정은 아래와 같다.
- name: Flush handlers
meta: Flush_handlers
$ ansible-playbook test.yml -b --force-hadnlers
블록
공식 문서: https://docs.ansible.com/ansible/latest/user_guide/playbooks_blocks.html#playbooks-blocks
작업들(tasks)을 여러개 묶어 놓은 그룹을 블록이라고 한다.
어떤 작업들에 똑같은 조건을 사용해야 할 경우, 블록문을 만들고 블록에 조건을 적용시킬 수 있다. 아래 예를 살펴보자.
tasks:
- name: Install, configure, and start Apache
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
when: ansible_facts['distribution'] == 'CentOS'
become: true
become_user: root
ignore_errors: yes
블록의 기능
- 여러 작업에 공통의 키워드를 부여할 수 있다. (ex: 조건문)
- java 나 typescript 의 try catch 구문 처럼 블록의 작업이 실패했을 시에 실행될 작업을 rescue 에 적어놓으면 실행이 된다. (block, rescue)
- try catch finall 에서 finall 에 해당하는 always 문을 작성할 수 있다. 블록의 작업이 실패하든 성공하든 상관없이 always 문에 있는 작업은 항상 실행된다.
- hosts: 192.168.100.11
tasks:
- block:
- debug:
msg: hello world
- command: /usr/bin/false
- debug:
msg: hello world2
ignore_errors: yes
rescue:
- debug:
msg: It's rescue
always:
- debug:
msg: It's Always
태그
공식 문서: https://docs.ansible.com/ansible/latest/user_guide/playbooks_tags.html#tags
작업에 태그를 부여하고 특정 태그의 작업만 실행할 수 있다.
all 태그: 모든 작업이 속한다.
untagged 태그: 태그가 설정되어 있지 않는 작업이 속한다.
ansible-playbook --tags==태그이름 으로 플레이북에서 특정 태그만 실행시킬 수 있다.
태그가 붙지 않은 녀석들을 실행하고 싶으면 ansible-playbook --tags==untagged 를 실행하면 된다.
혹은 여러 태그들을 포함해서 실행하고 싶을 경우 ansible-playbook --tags==stage,prod 처럼 실행하면 된다.
태그 관련 확인하는 명령어는 아래와 같다.
ansible-playbook test.yml --lists-tags
ansible-playbook test.tml --lists-tasks
작업 제어
작업을 하나 하나 실행하거나 실행하지 않거나 제어할 수 있다.
ansible-playbook test.yml --step
start-at-task
해당 하는 태스크에서 작업을 시작할 수 있다. 해당 태스크 이전의 태스크는 실행하지 않는다.
ansible-playbook test.yml --start-at-task="태스크 이름"
ansible-playbook test.yml --start-at-task="태스크 이름"