[쿠버네티스 2/6] Ansible - 템플릿 & 핸들러 & 블록 & 태그

신현식·2023년 2월 6일
0

구름_Ansible

목록 보기
5/5
post-thumbnail

템플릿

Asible에서 파일을 관리하는 가장 강력한 방법은 템플릿을 만드는 것이다. 템플릿을 사용하면 가능한 변수와 팩트를 사용하여 파일이 배포될 때 관리 호스트에 대해 자동으로 사용자 정의되는 템플릿 구성 파일을 작성할 수 있다.

Jinja2

▪ Python의 매우 강력하고 고급 템플릿 언어
▪ 매우 빠르고 안정적이며 동적 데이터를 생성하는 데 널리 사용
▪ 텍스트 기반 템플릿 언어이므로 모든 마크업과 소스 코드를 생성하는 데 사용할 수 있음

Jinja2의 특징

  • 제어 구조(루프 및 조건문)
  • 템플릿 상속
  • 사용자 정의 필터 지원
  • 풍부한 내장 필터 세트
  • 구성 가능한 구문

Jinja2 기호

{{ . . . . }}

  • 이중 중괄호는 템플릿 파일에서 널리 사용되는 태그이며 변수를 포함하고 궁극적으로 코드
    실행 중에 값을 인쇄하는 데 사용

{% . . . . %}

  • 루프 및 if-else 문과 같은 제어 문에 주로 사용

{# . . . . #}

  • 작업을 설명하는 주석

Jinja2 템플릿

vi temp1.yml

- name: template test
  hosts: AA
  vars:
    site_name: goorm_world
  tasks:
  - template:
      src: test1.j2
      dest: /tmp/test
---------------------------------------------     
vi test1.j2

Welcome {{ site_name }}
This node is {{ ansible_hostname }}

# 결과 확인
ansible-playbook temp1.yml
ssh ansi-node1 ls /tmp
ssh ansi-node1 cat /tmp/test
ssh ansi-node2 cat /tmp/test
      

Jinja2 안에서의 fact 접근


vi temp2.yml

- name: temp test
  hosts: AA
  tasks:
  - name: Create dir
    file:
      state: directory
      dest: /etc/ansible/facts.d
      recurse: yes
  - name: Create fact file
    template:
      src: factfile.j2
      dest: /etc/ansible/facts.d/test.fact
   - setup:
  - name: Print local facts
    debug:
      msg: "{{ ansible_local }}"
      
-----------------------------------------------      

vi factfile.j2

{# local_fact_config #}
[test1]
hostname= {{ansible_hostname}}
ip_address={{ ansible_all_ipv4_addresses[1] }}


ansible-playbook temp2.yml
ssh ansi-node1 ls /etc/ansible/facts.d
ssh ansi-node1 cat /etc/ansible/facts.d/test.fact
ssh ansi-node2 cat /etc/ansible/facts.d/test.fact

Jinja2 안에서의 반복문

vi temp3.yml

- name: template test
  hosts: ABC
#  gather_facts: no
  tasks:
  - template:
      src: hostname.j2
      dest: /home/vagrant/hostname.txt
      
-----------------------------------------------

vi hostname.j2

{% for i in range(1,4) %}
 ansi-node{{ i }} 192.168.56.2{{ i }}
{% endfor %}

ansible-playbook temp3.yml
ssh ansi-node1 cat /home/vagrant/hostname.txt

각 노드에 /etc/hosts file을 템플릿을 이용해서 생성

vi hosts.j2

127.0.0.1  localhost
{# hostname #}
{{ ansible_facts.enp0s8.ipv4.aABCress }} {{ ansible_facts.hostname }}

{# other hostanames #}
192.168.56.11 ansi-master1 
{% for i in range(1,4) %}
192.168.56.2{{ i }} ansi-node{{ i }}
{% endfor %}


vi temp5.yml

- name: template
  hosts: ABC
  gather_facts: no
  tasks:
  - template:
      src: hosts.j2
      dest: /etc/hosts
  

핸들러

핸들러(Handler)는 작업을 실행하고 시스템의 변경(Changed)이 있을 때에 별도의 작업을 호출하고 실행하는 작업이다.

  • 주로 핸들러를 사용하게 시스템에서 특정 서비스의 구성 파일이 변경된 경우 서비스를 재시작할 때 Ansible 모듈은 멱등이 되도록 설계 되어 있다.

즉 제어 노드가 원하는 상태가 되도록 하기 위해 플레이북과 해당 작업을 변경하지 않아도 되는 경우, 적절하게 작성된 플레이북에서 제어노드를 변경하지 않고 플레이북과 해당 작업을 여러번 실행 가능

  • 핸들러는 작업에 변경이 있고, 반드시 알림(Notify)이 있을 때만 실행되는 작업
  • 작업(변경) ---> 알림 ---> 핸들러
# apache2 수정시 재시작

vi temp.yml

- name: handler test
  hosts: ansi-node1
  tasks:
  - copy: 
      content: "{{ ansible_hostname }} handler test\ntest2\n"
      dest: /var/www/html/index.html
    notify:
    - Restart web service
  handlers:
  - name: Restart web service
    service:
      name: apache2
      state: restarted

핸들러 & 템플릿 예제

vi html1.j2

{{ ansible_hostname }} is handlers test1

---------------------------------------------

vi handler.yml

- name: handler test
  hosts: ansi-node3
  tasks:
  - name: Install Pakage
    apt:
      name:
        - apache2
        - firewalld
      update_cache: yes
      state: present

  - name: Config index.html
    template:
      src: html1.j2
      dest: /var/www/html/indx.html
    notify:
    - Restart web service           
    - Restart firewalld service

  handlers:
  - name: Restart web service  #위 notify의 이름과 같아야 함
    service:
      name: apache2
      state: restarted
  - name: Restart firewalld service
    service: 
      name: firewalld
      state: restarted
      
      
ansible-playbook handler.yml
ansible-playbook handler.yml
$ ssh ansi-node3 cat /var/www/html/index.html
ansi-node3 is handlers test1
  • 처음 실행

  • 두번째 실행

  • handler.yml의 내용을 다음과 같이 수정해서 진행해도 같은 결과가 나옴

핸들러 사용시 주의 사항

  • 핸들러는 항상 플레이의 handlers 섹션에서 지정한 순서대로 실행
    작업의 notify 문에 따라 나열한 순서나 작업이 핸들러에 통지하는 순서대로 실행되지는 않음.

  • 핸들러는 일반적으로 플레이에서 다른 모든 작업이 완료된 후에 실행
    플레이북의 tasks 부분의 작업에서 호출한 핸들러는 tasks 아래에 있는 모든 작업이 처리될 때까지 실행되지 않음

  • 핸들러 이름은 글로벌 네임스페이스에 있음.
    두 핸들러에 같은 이름이 잘못 지정되면 한 핸들러만 실행

  • 둘 이상의 작업에서 한 핸들러에게 알리는 경우 해당 핸들러가 한번만 실행
    작업에서 핸들러에게 알리지 않는 경우 핸들러가 실행되지 않음.

  • notify문을 포함한 작업이 chaged 결과를 보고하지 않으면 핸들러에 알리지 않음
    다른 작업에서 이 핸들러에게 알라지 않으면 이 핸들러를 건너띔
    Ansible은 작업이 change 상태를 보고하는 경우에만 핸들러에게 알림.

  • 핸들러는 작업이 제어노드를 변경할 때 추가 조치를 수행하기 위한 것으로 정상적인 작업을 대체하기 위해서 사용해서는 안됨.

  • 핸들러는 작업의 알림과 핸들러의 이름이 일치해야지만 알림을 받을 수 있기 때문에 핸들러의 작업에는 반드시 이름을 선언해야 함.

위임(작업 실행 위치 제어)

  • Ansible은 팩트를 수집하고 hosts플레이북의 라인과 일치하는 머신에서 모든 작업을 실행하는데 위임(Delegation)은 플레이 또는 일부 작업을 로컬(제어 노드)이나 다른 관리노드에서 작업을 실행할 수 있다.

  • 위임을 사용하면 상호 관련된 환경을 정확하고 효율적으로 관리할 수 있음

  • 예를 들어 웹 서버를 업데이트할 때 부하 분산 풀에서 일시적으로 제거해야 할 수 있습니다. 웹 서버 자체에서는 이 작업을 수행할 수 없습니다. 작업을 localhost에 위임함으로써 모든 작업을 동일한 플레이 내에 유지

vi delegate.yml

- name: delegate test
  hosts: ansi-node1
  tasks:
  - command: hostname
    register: hostname_result1
  - debug:
      msg: "{{ hostname_result1.stdout }}"

  - command: hostname
    register: hostname_result2
    delegate_to: ansi-node2
  - debug:
      msg: "{{ hostname_result2.stdout }}"
 
 
ansible-playbook delegate.yml

팩트 수집 위임

  • Ansible 작업을 위임하는 것은 현실 세계에서 작업을 위임하는 것과 동일하다고 할 수 있음.
  • 위임된 작업에 의해 수집된 모든 fact는 기본적으로 fact를 생성한 호스트(위임된 호스트)가 아닌 인벤토리 _호스트 이름(현재 호스트)에 할당
  • 수집된 fact을 현재 호스트 대신 위임된 호스트에 할당하려면 delegate_facts: true로 설정
vi delegate_facts.yml

- name: delegate_fact test
  hosts: ansi-node1
  gather_facts: no
  tasks:
  - setup:
    delegate_to: ansi-node2
    delegate_facts: yes

- name: delegate ansi-node2
  hosts: andi-node2
  gather_facts: no
  tasjs:
  - debug:
      msg: "{{ ansible_hostname }}"


위임 불가능한 모듈

  • 항상 제어 노드에서 시작되는 모듈
    • include
    • add_host
    • debug

작업 위임

  • 다른 호스트를 참조하여 한 호스트에서 작업을 수행하려면 작업에서 delegate_to키워드를 사용

  • 이는 로드 밸런싱된 풀에서 노드를 관리하거나 중단 기간을 제어하는 ​​데 이상적

  • 직렬 키워드 와 함께 위임을 사용 하여 한 번에 실행되는 호스트 수를 제어할 수 있음

블록

  • 블록은 플레이에 하나 이상의 작업을 논리적으로 그룹화 할 수 있다.

    • 그룹화의 장점은 대부분 단일 작업에서 적용했던 플레이북 키워드를 블록 수준에서 적용할 수 있다.
    • 조건문(when), 권한상승(become) 등 블록에 구성할 수 가능
    • 블록에 구성된 키워드는 블록에 속한 모든 작업에 공통으로 적용
    • 단, 반복문은 블록에 적용 X
  • 대부분의 프로그래밍 언어에 있듯이 예외 처리 기능을 제공

    • 블록의 하나 이상의 작업이 실패한 경우, 실패한 작업에 대응(롤 백, 오류 수정 등)하는 작업을 지정 가능

apache2 설치 + apache2 서비스 시작 + 방화벽 오픈을 하나의 블록으로 구성해서 플레이북을 작성해보세요.

- name: block example
  hosts: ansi-node2
  vars:
    pkg_list:
    - apache2
    - firewalld
#  gather_facts: no
  become: no
  tasks:
  - name: Web Service Block
    block:
    - name: Install Packages
      apt:
        name: "{{ pkg_list }}"
        state: latest
        update_cache: yes

    - name: Start Packages
      service:
        name: "{{ item }}"
        state: started
      loop: "{{ pkg_list }}"

    - name: Open Port
      firewalld:
        service: http
        permanent: yes
        immediate: yes
        state: enabled

    when: ansible_distribution == 'Ubuntu'
    ignore_errors: yes
    become: yes

apache2 설치시에 실패하면 rescue 블록에서 update_cache를 수행해서 패키지를 다시 설치하도록 하고 서비스 시작 및 방화벽을 오픈하도록 하시오.

- name: block example
  hosts: ansi-node3
  vars:
    pkg_list:
    - apache2
    - firewalld
#  gather_facts: no
  tasks:
  - name: Web Service Block
    block:
    - name: Install Packages
      apt:
        name: "{{ pkg_list }}"
        state: latest

    rescue:
    - name: Reinstall Packages
      apt:
        name: "{{ pkg_list }}"
        state: latest
        update_cache: yes

    always:
    - name: Start Packages
      service:
        name: "{{ item }}"
        state: started
      loop: "{{ pkg_list }}"

    - name: Open Port
      firewalld:
        service: http
        permanent: yes
        immediate: yes
        state: enabled

작업의 그룹화

  • 블록의 모든 작업은 블록 수준에서 적용된 지시문을 상속

  • 단일 작업에 적용할 수 있는 대부분(반복문(loop) 제외)은 블록 수준에서 적용할 수 있으므로 블록을 사용하면 작업에 공통적인 데이터나 지시문을 훨씬 쉽게 설정 가능

  • 지시문은 블록 자체에 영향을 미치지 않으며 블록으로 묶인 작업에 의해서만 상속
    예를 들어 when 문은 블록 자체가 아니라 블록 내의 작업에 적용

오류 처리

  • rescue및 always섹션이 있는 블록을 사용하여 Ansible이 작업 오류에 응답하는 방식을 제어할 수 있음

  • 복구 블록은 블록의 이전 작업이 실패할 때 실행할 작업을 지정
    : 접근 방식은 많은 프로그래밍 언어의 예외 처리와 유사

  • Ansible은 작업이 '실패' 상태를 반환한 후에만 복구 블록을 실행
    : 잘못된 작업 정의와 연결할 수 없는 호스트는 복구 블록을 트리거하지 않음

  • 블록의 작업 중 하나라도 실패가 있으면, Rescue 섹션의 작업이 실행
    : 블록의 작업 중 실패가 없으면, Rescue 섹션은 실행 X

) rescue

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

     - name: Force a failure
       command: /bin/false

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



예) always

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

     - name: Force a failure
       command: /bin/false

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

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

      - name: Force a failure
       command: /bin/false

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

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

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

=> 블록의 작업이 반환 failed되면 rescue섹션은 오류를 복구하기 위해 작업을 실행

=> 블록 구문의 실패가 있을 때만 Rescue 섹션이 실행되며, always 섹션은 항상 실행

=> 블록에서 오류가 발생하고 rescure 작업이 성공하면 Ansible은 실행에 대한 원래 작업의 실패 상태를 되돌리고 원래 작업이 성공한 것처럼 재생을 계속 실행

태그(tag)

많은 작업이 있는 플레이북의 경우 플레이북 전체를 실행하는 대신 특정 일부 만 실행하는 것이 유용할 수 있다.

▪ Ansible 태그로 특정 작업만 실행하거나, 특정 작업을 실행하지 않을 수 있음

▪ 태그는 플레이, 블록, 역할, 작업에 설정하고, 플레이북 실행 시 태그를 선택


vi tag1.yml

- name: tag test
  hosts: ansi-node1
  gather_facts: no
  tags: play1
  tasks:
  - block:
    - name: task 1-1
      debug:
        msg: task 1-1
      tags: task1
    - name: task 1-2
      debug:
        msg: task 1-2
      tags: task1

    - name: task 2-1
      debug:
        msg: task 2-1
      tags: task2
    - name: task 2-2
      debug:
        msg: task 2-2
      tags: task2
    tags: block1
  - name: task 3
    debug:
    msg: task 3
    tags: task3

- name: play2
  hosts: ansi-node2
  tasks:
  - name: task 4
    debug:
      msg: task4
    tags: task4

# --tag 를 이용하여 tag를 지정한 항목에 대한 검색이 가능    
ansible-playbook tag1.yml --tags 'task1'

# 태그하지 않은 항목 출력
ansible-playbook tag1.yml --tags untagged

# 모든 항목 출력
ansible-playbook tag1.yml --tags tagged
ansible-playbook tag1.yml --tags all

profile
전공 소개

0개의 댓글