가시다님 주관 A101 스터디에 대한 정리 - 2주차 입니다.
이번 주차에는 반복문, 조건문, 핸들러 개념에 대해 정리합니다.
# 반복문
Ansible은 loop, with_<lookup>, until 키워드를 통해 반복문을 사용할 수 있도록 제공한다.
( with_ 구문은 Ansible 2.5 버전부터 권장되지 않으며 loop을 사용할 것을 권장함 )
예를 들어 여러개의 file을 만든다던지 여러 user를 생성한다던지 등이 그 예이다.
https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_loops.html
1. 단순 반복문
user 모듈을 사용하여 여러명의 user를 생성한다고 가정해보자.
반복문이 없다면 Add testuser1 user, Ass testuser2 user 라는 Task를 각각 총 2개 만들어야 한다.
- name: Add user testuser1
ansible.builtin.user:
name: "testuser1"
state: present
groups: "wheel"
- name: Add user testuser2
ansible.builtin.user:
name: "testuser2"
state: present
groups: "wheel"
딱봐도 비효율적인 것처럼 보이는데 이를 loop 키워드를 통해 간단하게 나타낼 수 있다.
- name: Add several users
ansible.builtin.user:
name: "{{ item }}"
state: present
groups: "wheel"
loop:
- testuser1
- testuser2
loop에는 ["testuser1", "testuser2"] 라는 2개의 Element가 전달되고 실제로 사용하는 user 모듈에서는 item 이라는 key를 통해 각 value를 사용할 수 있게 된다.
2. 리스트 안에 hash가 있을 경우 반복문
각 loop 안에 name과 groups라는 subkey가 존재한다.
이럴 땐 각 item에 대해서 subkey를 .으로 구분하여 접근하면 된다.
- name: Add several users
ansible.builtin.user:
name: "{{ item.name }}"
state: present
groups: "{{ item.groups }}"
loop:
- { name: 'testuser1', groups: 'wheel' }
- { name: 'testuser2', groups: 'root' }
# 조건문
https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_conditionals.html
조건문은 말 그대로 특정 조건이 충족되어야 작업 또는 플레이를 실행시킬 수 있도록 해준다.
when 키워드를 사용하여 조건문을 사용하게 된다.
when 키워드는 가장 기본적으로 Boolean 변수가 true인지 false인지 유무를 확인한 뒤 true일 경우 Task를 수행하게 된다.
예를 들어 아래의 when 조건절들은 모두 true인지 false인지를 나타내게 된다.
ansible_facts['machine'] == 'x86_64'
min_memory < 512
ansible_facts['distribution'] in support_dists
1. 기본 사용법
ansible_facts 변수를 사용하여 Centos6이거나 Debian7일 경우 Shutdown 명령을 수행하는 Task이다.
이 때 when 키워드를 통해 조건문을 사용할 수 있다.
tasks:
- name: Shut down CentOS 6 and Debian 7 systems
ansible.builtin.command: /sbin/shutdown -t now
when: (ansible_facts['distribution'] == "CentOS" and ansible_facts['distribution_major_version'] == "6") or
(ansible_facts['distribution'] == "Debian" and ansible_facts['distribution_major_version'] == "7")
그럼 어떤 조건들을 사용할 수 있을까? 아래의 예시를 확인해보자.
ansible_facts[’machine’] == “x86_64”
-> 문자열 변수의 특정 값을 체크하여 true | false를 반환하는 조건이다.
max_memory == 512
-> 정수값을 체크하여 true | false를 반환
min_memory > 256
-> 정수값의 크고 낮음을 체크한다.
min_memory != 512
-> 정수값의 특정 값이 아닐 경우를 체크한다.
min_memory is defined // min_memory is not defined
-> 변수가 있는지 없는지를 체크한다.
ansible_facts[’distribution’] in supported_distros
-> ansible_facts 변수 중 distribution의 값이 supported_distros에 나열된 값들 중 하나일 경우 true를 반환하고 아닐 경우 false
- != : 값이 같지 않을 때 참 true
- >, >=, <=, < : ‘초과, ‘ 이상’, ‘이하’, ‘미만’ 일 때에 참 true
- not : 조건의 부정
- and, or : ‘그리고’, ‘또는’의 의미로 여러 조건의 조합 가능
- in : 값이 포함된 경우에 참 true. 예를 들어 2 in “1, 2, 3” 은 참 true
- is defined : 변수가 정의된 경우 참 true
# 핸들러 및 작업 실패 처리
https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_handlers.html
한 작업에서 시스템을 변경해야 하는 경우 추가 작업이 필요한 경우가 있을 수 있다.
예를 들어 서비스(httpd)의 구성 파일을 변경하려면 서비스가 재시작되도록 reload 해야 하는 경우가 발생할 수 있다.
이럴 때 Ansible Handler는 A 작업에서 Trigger한 알림에 응답하는 작업이며 해당 호스트에서 작업이 변경(changed)될 때만 핸들러에 통지한다.
아래 예시를 확인해보자.
1) Apache가 최신 버전인지 확인한다.
2) httpd.conf 파일을 Local에서 변경하고 dest(타겟 VM)에 있는 /etc/httpd.conf 파일로 복사한다. 즉, 파일의 내용이 변경되었을 수도 있고 아닐 수도 있다는 뜻이다.
3) 따라서 만약 /etc/httpd.conf 파일의 내용이 변경되었다면 changed가 될 것이고 이는 Restart apache라는 Handler를 트리거할것이다.
---
- 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
ansible.builtin.yum:
name: httpd
state: latest
- name: Write the apache config file
ansible.builtin.template:
src: /srv/httpd.j2
dest: /etc/httpd.conf
notify:
- Restart apache
- name: Ensure apache is running
ansible.builtin.service:
name: httpd
state: started
handlers:
- name: Restart apache
ansible.builtin.service:
name: httpd
state: restarted
작업 실패 무시
ansible은 각 play 시 작업의 반환 코드를 평가하여 작업의 성공 유무를 판단하는데 일반적으로 작업이 실패하면 이후 모든 작업을 건너뜀
따라서 이러한 경우에는 ignore_errors란 키워드를 통해 에러가 발생하도 이후 작업들을 계속 실행할 수 있다.
아래 예시는 ignore_errors란 키워드가 없으니
당연히 apache3라는 존재하지 않는 package를 설치하려고 할 때 에러가 발생하고 Print msg task를 실행하지 못할 것이다.
---
- hosts : tnode1
tasks:
- name: Install apache3
ansible.builtin.apt:
name: apache3
state: latest
- name: Print msg
ansible.builtin.debug:
msg: "Before task is ignored"
아래 예시는 ignore_errors란 키워드를 통해 에러가 발생해도 다음 task를 수행할 수 있게 된다.
---
- hosts : tnode1
tasks:
- name: Install apache3
ansible.builtin.apt:
name: apache3
state: latest
ignore_errors: yes
- name: Print msg
ansible.builtin.debug:
msg: "Before task is ignored"
작업 실패 후 핸들러 실행
작업 실패 후 Handler를 실행하고 싶다면?
force_handlers 키워드를 통해 에러가 발생했든 안했는 핸들러를 호출할 수 있다.
force_handlers 키워드가 없다면 rsyslog를 정상적으로 재시작했음에도 불구하고 install apache3 task가 실패함으로 인해 handler가 호출이 되지 않는다.
---
- hosts: tnode2
tasks:
- name: restart rsyslog
ansible.builtin.service:
name: "rsyslog"
state: restarted
notify:
- print msg
- name: install apache3
ansible.builtin.apt:
name: "apache3"
state: latest
handlers:
- name: print msg
ansible.builtin.debug:
msg: "rsyslog is restarted"
force_handler 키워드 추가하면 install apache3 task가 실패해도 handler가 정상적으로 호출된다.
---
- hosts: tnode2
force_handlers: yes
tasks:
- name: restart rsyslog
ansible.builtin.service:
name: "rsyslog"
state: restarted
notify:
- print msg
- name: install apache3
ansible.builtin.apt:
name: "apache3"
state: latest
handlers:
- name: print msg
ansible.builtin.debug:
msg: "rsyslog is restarted"
# Block, Rescue
https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_blocks.html
블록은 작업을 논리적으로 그룹화하는 절이며 작업 실행 방법을 제어하는 데 사용할 수 있다.
- block : 실행할 기본 작업을 블록화하여 정의한다.
- recsue : block 절에 정의된 작업이 실패할 경우 실행할 작업을 정의한다. ( rescue : 구출 )
- always : block, rescue 절에 정의된 작업 성공 / 실패 유무와 관계없이 항상 실행
다음 예시는 /var/log/daily_log 라는 디렉터리에 todays.log 파일을 생성하는 예시이다.
- Block 절에서는 Directory가 있는지 없는지 없는지 확인하고 없으면 recsue 절에서 file 모듈을 통해 생성한다.
- always 절을 통해 logfile을 해당 경로에 생성한다.
---
- hosts: tnode2
vars:
logdir: /var/log/daily_log
logfile: todays.log
tasks:
- name: Configure Log Env
block:
- name: Find Directory
ansible.builtin.find:
paths: "{{ logdir }}"
register: result
failed_when: "'Not all paths' in result.msg"
rescue:
- name: Make Directory when Not found Directory
ansible.builtin.file:
path: "{{ logdir }}"
state: directory
mode: '0755'
always:
- name: Create File
ansible.builtin.file:
path: "{{ logdir }}/{{ logfile }}"
state: touch
mode: '0644'
처음 실행 때는 디렉터리가 없으니까 rescue 절을 통해 디렉터리를 만들었다.
두번째 실행 때는 디렉터리가 있으니까 rescue 절은 실행되지 않고 바로 파일을 만든다.
# 도전 과제
1. linux user1~10(10명) 를 반복문을 통해서 생성 후 확인 후 삭제를 해보자
아이디어
- ansible.builtin.user 모듈을 사용하자
- loop 키워드를 통해 반복문을 돌리자.
- user1, user2를 나열하는것 보다 user{{ item }} 이런식으로 변수를 지정해주고 1, 10까지 어떠한 함수를 통해 간소화하자.
- hosts: tnode1
tasks:
- name: Add Several users
ansible.builtin.user:
name: "user{{ item }}"
state: present
loop: "{{ range(1, 11)|list }}"
ansible-playbook 2weeks/homework1.yaml
- hosts: tnode1
tasks:
- name: Add Several users
ansible.builtin.user:
name: "user{{ item }}"
state: absent
loop: "{{ range(1, 11)|list }}"
2. loop 반복문 중 sequence 를 이용하여 /var/log/test1 ~ /var/log/test100 100개 파일(file 모듈)을 생성 확인 후 삭제를 해보자
- ansible.builtin.file 모듈을 사용하자 (https://docs.ansible.com/ansible/latest/collections/ansible/builtin/file_module.html)
- loop 키워드를 통해 반복문을 돌리자.
- 1, 100까지 range 함수를 사용하자 (위와 동일)
- hosts: tnode1
tasks:
- name: Add Several files
ansible.builtin.file:
path: "/var/log/test{{ item }}"
state: touch
mode: 0644
loop: "{{ range(1, 31)|list }}"
- name: Check files exist
ansible.builtin.shell: |
ls -l /var/log | grep test
register: ls_output
- name: Output
debug:
var: ls_output['stdout_lines']
- hosts: tnode1
tasks:
- name: Add Several files
ansible.builtin.file:
path: "/var/log/test{{ item }}"
state: absent
mode: 0644
loop: "{{ range(1, 31)|list }}"
- name: Check files exist
ansible.builtin.shell: |
ls -l /var/log | grep test
register: ls_output
- name: Output
debug:
var: ls_output['stdout_lines']
3. Ubuntu OS이면서 fqdn으로 tnode1 인 경우, debug 모듈을 사용하여 OS 정보와 fqdn 정보를 출력해보자
아이디어
- 결국 Ansible facts를 사용해야 한다.
- 그럼 Ansible Facts의 값들을 미리 확인해보자.
우리는 ansible facts를 캐싱해놨기 때문에 캐싱된 facts 값들을 확인할 수 있다.
ansible_nodename, ansible_hostname을 통해 tnode1인지 아닌지 확인할 수 있다.
또 Ubuntu인지 아닌지는 ansible_distribution을 통해 얻을 수 있을 것이다.
- hosts: all
tasks:
- name: Print os and fqdn
ansible.builtin.debug:
msg: "OS : {{ansible_facts['lsb']['description']}} , FQDN : {{ansible_facts['fqdn']}}"
when: ansible_facts['distribution'] == "Ubuntu" and ansible_facts['nodename'] == "tnode1"
4. apache2 패키지를 apt 모듈을 통해서 설치 시, 핸들러를 호출하여 service 모듈로 apache2를 재시작 해보자
아이디어
- apache2 패키지는 ansible.builtin.apt 모듈을 통해 설치하자.
- 만약 해당 apache2 package가 설치되어 있지 않다면 changed를 통해 상태가 변경될 것이고 이는 특정 Handler를 트리거할 수 있다.
- hosts: tnode1
vars:
package: apache2
tasks:
- name: Install {{ package }}
ansible.builtin.apt:
name: "{{ package }}"
state: present
notify:
- Restart {{ package }}
handlers:
- name: Restart {{ package }}
ansible.builtin.service:
name: "{{ package }}"
state: restarted
'DevOps' 카테고리의 다른 글
[Strimzi Kafka Connect] 손쉽게 커넥터 Config에 시크릿 적용하기 (0) | 2024.05.01 |
---|---|
[Ansible] 사용자 계정 생성 및 apache http 설치 실습 (0) | 2024.02.04 |
[Ansible] 기본 개념 정리 (0) | 2024.01.09 |
[AWS] 클라우드 와치 로그에서 특정 패턴일 경우 Slack 알람 보내는 방법 (0) | 2023.07.02 |
[Github Action] Self hosted runner에서 Gradle, Docker image cache #2 - EFS 활용 (8) | 2022.12.19 |