..

a101) ansible 입문반

테스트 환경은 앤서블이 설치된 control node와 managed node로 사용할 우분트 서버 3대이다. inventory에는 [web]그룹으로 tnode1과 tnode2를 넣고, [db]그룹에 tnode3을 [all]에 앞의 두 그룹을 넣었다.

반복문

어느 곳에나 있는 반복문을 앤서블에서는 loop를 사용하여 작성한다. 특정 항목을 반복해야할 때, tasks.loop 하위에 반복해야하는 항목을 목록으로 작성하고 loop의 값을 받을 곳에는 변수를 사용한다. 만약 loop의 값을 변수로 받아야하는 경우, 기존과 동일하게 으로 작성 할 수 있다.

- hosts: db
  tasks:
    - name: Create files
      ansible.builtin.file:
        path: /var/log/ #반복문이 실행되는 곳
        state: touch
      loop:
        - test1.log
        - test2.log
        #"" #loop의 목록도 변수로 받을 수 있다. 

1-0

반복 대상이 하나 일 때에는 item으로만 받으면 되지만 반복 대상이 여러개일 때에는 item 뒤에 추가변수를 작성하여 작성 할 수 있다. 로 작성하고 반복되는 값은 loop 하위에 ‘-’로 구분하여 표현한다.

- hosts: db
  tasks:
    - name: Create files
      ansible.builtin.file:
        path: /var/log/
        mode: ""
        state: touch
      loop:
        - file-name: test1.log
          file-mode: '0700'
        - file-name: test2.log
          file-mode: '0600'

1-1

반복문과 register 변수를 같이 사용하게 되는 경우는 그냥 register 변수만 사용하는 것과 데이터 구조가 조금 다르다.

# 위의 file-name과 file-mode를 사용한 yaml파일 하단에 아래와 같이 실행했다.
...
      register: test
    - name: Show result
      ansible.builtin.debug:
        var: test


# Show result의 task 결과는 아래와 같다. (너무 길어서 ... 부분은 생략..)
# 해당 task의 실행 결과 먼저 나오고 그 뒤에 반복문의 돌며 발생한 결과(results)가 출력된다.
# results안에는 반복한 item들이 리스트로 들어가고, 그 안에 각 item별 정보가 담긴 key: value형식의 해시가 들어간다.

TASK [Show result] *************************************************************
ok: [tnode3] => {
    "test": {
        "changed": true,
        "msg": "All items completed",
        "results": [
            {
                "ansible_loop_var": "item",
                "changed": true,
                "dest": "/var/log/test1.log",
                ...
                "item": {
                    "file-mode": "0700",
                    "file-name": "test1.log"
                },
                ...
            },
            {
                "ansible_loop_var": "item",
                "changed": true,
                "dest": "/var/log/test2.log",
                ...
                "item": {
                    "file-mode": "0600",
                    "file-name": "test2.log"
                },
                ...


# 그렇게 때문에 반복문 실행에 대한 출력결과를 얻으려면 .results를 붙여 사용해야한다. 
# 특정 값을 원하면 아래와 같이 쓸 수 있다.

    - name: Show result
        ansible.builtin.debug:
          var: test.results[0].dest

# 결과

TASK [Show result] *************************************************************
ok: [tnode3] => {
    "test.results[0].dest": "/var/log/test1.log"
}

지금은 loop로 쓰지만 2.5버전 이전까지는 with_ 형식으로 반복문을 작성하였고, 아직도 사용이 가능하다. 문서를 확인해보면 loop자리에 with_item이 들어가는 식으로 사용되었다. 대부분의 반복문은 목록을 만들어서 사용하는 방식인데, 기존 with_sequence의 경우는 약간 다르다. with_sequence: start=0 end=4 stride=2 형식으로 작성되는데, 0부터 4까지 2씩 늘려가며, 오름차순으로 list를 생성하여 반복문을 돌리게 된다. 이걸 이제 loop로 사용하게되면 반복을 할 부분에 format을 정해줘야한다. 오름차순을 리스트로 만들어주는 방법은 ansible이 파이썬으로 구성되어있으므로 문법을 따라 range(start end step)로 작성한다. 독스에서 testuser00,testuser02,testuser04를 출력하는 예시가 있어 실행하여 확인해보았다. [🔗](https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_loops.html#with-sequence)

    - name: with_sequence
      ansible.builtin.debug:
        msg: ""
      with_sequence: start=0 end=4 stride=2 format=testuser%02x
    - name: with_sequence -> loop
      ansible.builtin.debug:
        msg: "testuser%02x"
      loop: ""

# 출력되는 msg는 동일하지만, loop 실행시에는 format을 msg에서 정의했기 때문에, item으로 받아가는 값이 다르게 된다. 

TASK [with_sequence] ***********************************************************
ok: [tnode3] => (item=testuser00) => {
    "msg": "testuser00"
}
ok: [tnode3] => (item=testuser02) => {
    "msg": "testuser02"
}
ok: [tnode3] => (item=testuser04) => {
    "msg": "testuser04"
}

TASK [with_sequence -> loop] ***************************************************
ok: [tnode3] => (item=0) => {
    "msg": "testuser00"
}
ok: [tnode3] => (item=2) => {
    "msg": "testuser02"
}
ok: [tnode3] => (item=4) => {
    "msg": "testuser04"
}

with_sequence를 사용하여 /var/log에 test.log파일을 20개 만들고 loop를 사용하여 삭제를 해보았다.

1-2

1-3

조건문

어느 곳에나 있는 반복문 다음에는 조건문이 있다. 앤서블에서는 when으로 표현한다. 특정 조건이 충족될 시 작업이 실행되고, 충족되지 않으면 해당 작업은 건너뛰게 된다. 조건 연산자를 사용하여 다중 조건을 설정 할 수도 있다. 사용되는 조건연산자는 아래와 같다.

  • == :값이 같을 때 참(true)
  • != : 값이 같지 않을 때 참(true)
  • >, >=, <=, < : ‘초과, ‘ 이상’, ‘이하’, ‘미만’ 일 때에 참(true)
  • not : 조건의 부정
  • and, or : ‘그리고’, ‘또는’의 의미로 여러 조건의 조합 가능
  • in : 값이 포함된 경우에 참(true)
  • is defined : 변수가 정의된 경우 참(true)

조건문에 hostname: tnode1, os: ubuntu를 넣게되면, 해당되는 tnode1만 태스크 실행되고, 나머지 호스트에서는 skip되는 것을 확인 할 수 있다. when절에 ansible_facts[‘distribution’] in supported_distros 가 있는데, var.supported_distors 내에 ansible_facts[‘distribution’]가 있으면 참이다. tnode1의 경우 ubuntu os를 사용하기 때문에 참이 된다. 1-4

조건문과 반복문을 같이 쓴다는 가정을 위해 위의 조건문에 사용자를 생성하는 반복문을 함께 작성하여 실행해봤다. 1-5

핸들러

앤서블은 사용자가 지정해 둔 상태가 유지되게 도와준다. 하지만 매번 실행되는 것이 아니라 어느 한 작업이 실행됨에 따라 후 작업이 따라와야한다면, 핸들러를 사용 할 수 있다. 예로 들어 config파일이 변경되었을 때만 httpd 서비스가 재시작이 되어야하는데, httpd 서비스 상태를 restarted로 두게 된다면 config 파일 변경 유무와 상관없이 매번 재시작 된다. 이 때 핸들러를 사용하여 config 파일이 변경되는 태스크가 실행되어 changed 상태가 되었을 때에 httpd 서비스가 재시작 되는 핸들러가 시작되게 할 수 있다. 예를 들어 apach2 설치 후 서비스 재시작을 실행한다면.. . 1-6

작업 실패 처리

앤서블은 기본적으로 하나의 태스크 실패시 이후의 모든 작업을 수행하지 않고 플레이를 중단한다. 만약 태스크 실패와 관계없이 태스크를 모두 실행하여 플레이를 마치고 싶다면 tasks하위에 ignore_error를 사용할 수 있다. 플레이가 중단될 경우 한가지 더 고려해야하는 사항은 핸들러인데, 핸들러는 모든 태스크가 수행되고 나서 노티를 받은 핸들러가 실행된다. 그렇기때문에 중간에 하나의 태스크가 실패하게되면 이전까지 ok된 태스크가 핸들러를 호출했다 한들, 핸들러까지 내려가지 못하게된다. 만약 ok된 태스크의 핸들러가 이후에 실행되는 태스크 성공 유무와 상관없이 수행되어야한다면, force_handlers 옵션을 사용하여 핸들러 작업을 강제 실행할 수 있다.

테스트로 만든 yaml은 rsyslog를 재시작 > apache3 설치(없으므로 실패) > print msg > 핸들러의 print msg 순으로 실행되는 파일이다. 만약 아무런 옵션을 붙이지 않는다면, 아래와 같이 install apache3단계에서 실패 후 플레이가 종료되어버린다. 1-7

hosts 아래에 force_handlers: yes 옵션을 붙여 태스크 실패시에도 알림이 간 핸들러는 실행되도록 설정했다. 실패한 태스크 이후로는 진행되지 않고 핸들러만 실행되었다. 1-8

실패하더라도 무시하고 넘어갈 태스크에 ignore_errors: yes 옵션을 넣었다. 옵션이 붙은 install apache3이 실패했음에도 무시하는 메세지와 함께 플레이가 진행되어 핸들러까지 완료되었다. 1-9

앤서블은 실행 결과가 ok와 changed의 경우 정상적으로 실행되었다고 판단한다. 태스크에 만약 스크립트를 돌리게 되면 앤서블 입장에서는 스크립트의 결과 값은 자신의 영역이 아니기 때문에 실행 결과에 대해서는 모르고, 실행했다 -> changed 로만 인식한다. 그렇기 때문에, 스크립트보다는 모듈을 찾아 사용하는 방식이 권장된다. 하지만.. 스크립트를 써야하는 경우가 있을 수 있지.. 그 때 사용 할 수 있는 것이 failed_when 옵션이다. 실행 할 스크립트에서 자주나는 오류들을 미리 앤서블에게 알려주는 방법이다. 실패시에는 아래의 block절을 사용하여 실패시 사용할 태스크를 정의해둔다. 비슷하지만 다르게 사용하는 changed_when 옵션도 있다. 찾아봤을 때, failed_when 옵션과 사용방법은 동일하게 register에 결과값을 두고 changed_when절에서 비교하는 것이 일반적인 것 같았다. 이 경우는 특정 결과에 따라 ok처리된 태스크를 changed로 기록할 수도 혹은 그 반대가 될 수도 있다. ok로 실행 결과가 떴지만, 특정 결과에 따라 핸들러 작업이 필요할 때 사용할 수 있다.

block

block은 작업을 논리적으로 그룹화하여 실행 결과에 따라 다음 절차를 다르게 정할 수 있다. block구문에는 실행할 태스크를 작성하고, failed_when에 실패로 처리할 상태를 명시한다. 이 태스크가 실패시에는 rescue 구문에 있는 태스크가 실행되고, 만약 성공했었다면 rescue는 실행되지 않고, always만 실행된다.

  • blcok 성공시 > always
  • block 실패시 > rescue > always

아래 테스트는 mv file 태스크를 실행하지만 현위치에 index.html 파일이 없기 때문에 실패하게 된다. 발생할 오류의 문구를 알기에 failed_when으로 넣어 rescue가 실행되게 하였다. 이 후 always의 debug가 출력되고, rescue에서 호출한 핸들러가 실행하면서 플레이가 종료된다.

1-10