포스트

[K8S Deploy Study by Gasida] - Ansible 기초 -도전과제

1. 생성된 user를 ansible.builtin.user 모듈을 통해서 제거하기

  • playbook(example.yaml)에 그룹(test-admin)을 생성하고 유저(testuser) 를 추가
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
---

- hosts: desktop-linux
  tasks:
  - name: 리눅스 내에서 그룹 'test-admin'을 생성
    ansible.builtin.group:
      name: test-admin
      state: present
  
  - name: 리눅스 내에서 사용자 'testuser' 생성 후 특정 uid와 주 그룹을 'test-admin'으로 설정
    ansible.builtin.user:
      name: testuser
      comment: Test User
      uid: 1040
      group: test-admin 
1
2
3
4
5
# 플레이북 실행
ansible-playbook ./example.yaml

# 다른 터미널에서 모니터링
watch -d "ssh desktop-linux tail -n 3 /etc/passwd"

  • 추가한 유저(testuser) 삭제하기
1
2
3
4
5
6
7
8
9
---

- hosts: desktop-linux
  tasks:  
  - name:  리눅스 내에서 사용자 'testuser'를 삭제
    ansible.builtin.user:
      name: testuser
      state: absent
      remove: yes
  • 유저가 삭제 되었는지 확인

2. 관리 대상 uptime 을 ansible.builtin.debug 모듈을 통해 가져오기

1
2
3
4
5
6
7
8
9
10
11
---

- hosts: desktop-linux
  tasks:  
  - name:  uptime 정보 가져오기
    ansible.builtin.shell: /usr/bin/uptime
    register: result 

  - name: 이전 태스크로부터 리턴된 결과를 출력
    ansible.builtin.debug:
      var: result.stdout_lines

3. 팩트를 사용하여 3개의 VM의 ‘커널 버전’과 ‘운영체제 종류’를 출력

facts-challenge3.yml

1
2
3
4
5
6
7
---
- hosts: all

  tasks:
  - name: 커널 버전’과 ‘운영체제 종류’를 출력
    ansible.builtin.debug:
      msg: "The Kernel version of  is  and the OS is "

실행 하기

1
ansible-playbook facts-challenge3.yml

결과

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
PLAY [all] ******************************************************************************************

TASK [Gathering Facts] ******************************************************************************
ok: [tnode1]
ok: [tnode2]
ok: [tnode3]

TASK [커널 버전’과 ‘운영체제 종류’를 출력] **********************************************************
ok: [tnode1] => {
    "msg": "The Kernel version of tnode1 is 6.8.0-86-generic and the OS is Debian"
}
ok: [tnode2] => {
    "msg": "The Kernel version of tnode2 is 6.8.0-86-generic and the OS is Debian"
}
ok: [tnode3] => {
    "msg": "The Kernel version of tnode3 is 5.14.0-570.52.1.el9_6.aarch64 and the OS is RedHat"
}

PLAY RECAP ******************************************************************************************
tnode1                     : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
tnode2                     : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
tnode3                     : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

``

4. 반복문으로 대량의 유저 생성하고 삭제해보기

  • testuser1~10의 유저를 추가 하기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
---

- hosts: desktop-linux
  tasks:  
  - name: 리눅스 내에서 사용자 'testuser1~10' 생성    
    ansible.builtin.user:
      name: testuser
    loop: ""
  
  - name: 생성된 사용자 확인
    ansible.builtin.shell: "getent passwd | grep testuser"
    register: users

  - name: 사용자 정보 출력
    ansible.builtin.debug:
      msg: ""

  - name: 생성된 사용자 삭제
    ansible.builtin.user:
      name: testuser
      state: absent
    loop: ""

  • 결과
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
PLAY [desktop-linux] ******************************************************************************************************************************************************************************

TASK [Gathering Facts] ****************************************************************************************************************************************************************************
ok: [desktop-linux]

TASK [리눅스 내에서 사용자 'testuser1~10' 생성] ***************************************************************************************************************************************************
changed: [desktop-linux] => (item=1)
changed: [desktop-linux] => (item=2)
changed: [desktop-linux] => (item=3)
changed: [desktop-linux] => (item=4)
changed: [desktop-linux] => (item=5)
changed: [desktop-linux] => (item=6)
changed: [desktop-linux] => (item=7)
changed: [desktop-linux] => (item=8)
changed: [desktop-linux] => (item=9)
changed: [desktop-linux] => (item=10)

TASK [생성된 사용자 확인] *************************************************************************************************************************************************************************
changed: [desktop-linux]

TASK [사용자 정보 출력] ***************************************************************************************************************************************************************************
ok: [desktop-linux] => {
    "msg": [
        "testuser1:x:1002:1003::/home/testuser1:/bin/sh",
        "testuser2:x:1003:1004::/home/testuser2:/bin/sh",
        "testuser3:x:1004:1005::/home/testuser3:/bin/sh",
        "testuser4:x:1005:1006::/home/testuser4:/bin/sh",
        "testuser5:x:1006:1007::/home/testuser5:/bin/sh",
        "testuser6:x:1007:1008::/home/testuser6:/bin/sh",
        "testuser7:x:1008:1009::/home/testuser7:/bin/sh",
        "testuser8:x:1009:1010::/home/testuser8:/bin/sh",
        "testuser9:x:1010:1011::/home/testuser9:/bin/sh",
        "testuser10:x:1011:1012::/home/testuser10:/bin/sh"
    ]
}

TASK [생성된 사용자 삭제] *************************************************************************************************************************************************************************
changed: [desktop-linux] => (item=1)
changed: [desktop-linux] => (item=2)
changed: [desktop-linux] => (item=3)
changed: [desktop-linux] => (item=4)
changed: [desktop-linux] => (item=5)
changed: [desktop-linux] => (item=6)
changed: [desktop-linux] => (item=7)
changed: [desktop-linux] => (item=8)
changed: [desktop-linux] => (item=9)
changed: [desktop-linux] => (item=10)

PLAY RECAP ****************************************************************************************************************************************************************************************
desktop-linux              : ok=5    changed=3    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

5. loop 반복문 중 sequence 를 이용하여 /var/log/test1 ~ /var/log/test100 100개 파일(file 모듈)을 생성 확인 후 삭제

loop.yml 생성

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
---
- hosts: tnode1
  gather_facts: no
  tasks:
    - name: 100개의 테스트파일 작성 (test1 ~ test100)
      ansible.builtin.file:
        path: "/var/log/test"
        state: touch
        mode: '0644'
      loop: ""

    - name: 생성 확인
      ansible.builtin.shell: |
        ls -la /var/log/test[0-9]* | wc -l
      register: file_count

    - name: 파일 갯수 표시
      ansible.builtin.debug:
        msg: "Total files created: "

    - name: 생성된 파일 리스트업
      ansible.builtin.shell: ls -la /var/log/test[0-9]*
      register: file_list

    - name: 파일 리스트 표시
      ansible.builtin.debug:
        msg: ""

    - name: 100개의 테스트파일 삭제 (test1 ~ test100)
      ansible.builtin.file:
        path: "/var/log/test"
        state: absent
      loop: ""

    - name: 삭제 확인
      ansible.builtin.shell: |
        ls /var/log/test[0-9]* 2>/dev/null | wc -l || echo "0"
      register: remaining_files

    - name: 삭제 후에 남은 파일 갯수 표시
      ansible.builtin.debug:
        msg: "Remaining files: "

실행하기

1
ansible-playbook loop.yaml 

결과

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
PLAY [tnode1] ******************************************************************

TASK [100개의 테스트파일 작성 (test1 ~ test100)] *******************************
changed: [tnode1] => (item=1)
changed: [tnode1] => (item=2)
changed: [tnode1] => (item=3)
...
changed: [tnode1] => (item=99)
changed: [tnode1] => (item=100)

TASK [생성 확인] ***************************************************************
changed: [tnode1]

TASK [파일 갯수 표시] **********************************************************
ok: [tnode1] => {
    "msg": "Total files created: 100"
}

TASK [생성된 파일 리스트업] ****************************************************
changed: [tnode1]

TASK [파일 리스트 표시] *******************************************************
ok: [tnode1] => {
    "msg": [
        "-rw-r--r-- 1 root root 0 Jan 17 17:11 /var/log/test1",
        "-rw-r--r-- 1 root root 0 Jan 17 17:11 /var/log/test10",
        "-rw-r--r-- 1 root root 0 Jan 17 17:11 /var/log/test100",
           ....
        "-rw-r--r-- 1 root root 0 Jan 17 17:11 /var/log/test98",
        "-rw-r--r-- 1 root root 0 Jan 17 17:11 /var/log/test99"
    ]
}

TASK [100개의 테스트파일 삭제 (test1 ~ test100)] ***************************************************
changed: [tnode1] => (item=1)
changed: [tnode1] => (item=2)
...
changed: [tnode1] => (item=99)
changed: [tnode1] => (item=100)

TASK [삭제확인] *********************************************************
changed: [tnode1]

TASK [삭제 후에 남은 파일 갯수 표시] *****************************
ok: [tnode1] => {
    "msg": "Remaining files: 0"
}

PLAY RECAP *********************************************************************
tnode1                     : ok=8    changed=5    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

6. Ubuntu OS이면서 fqdn으로 tnode1인 경우, debug 모듈을 사용하여 OS정보와 fqdn 정보를 출력해보자.

condtion.yaml

1
2
3
4
5
6
7
8
9
10
---
- hosts: all

  tasks:
    - name: Print OS version and FQDN for Ubuntu on tnode1
      ansible.builtin.debug:
        msg: >-             
             OS Version: 
             FQDN: 
      when: ansible_facts['distribution'] == "Ubuntu" and ansible_facts['fqdn'] == "tnode1"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
PLAY [all] ***************************************************************************************************************************

TASK [Gathering Facts] ***************************************************************************************************************
ok: [tnode2]
ok: [tnode1]
ok: [tnode3]

TASK [Print OS version and FQDN for Ubuntu on tnode1] ********************************************************************************
ok: [tnode1] => {
    "msg": "OS Version: 24.04 FQDN: tnode1"
}
skipping: [tnode2]
skipping: [tnode3]

PLAY RECAP ***************************************************************************************************************************
tnode1                     : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
tnode2                     : ok=1    changed=0    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0   
tnode3                     : ok=1    changed=0    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0   

tnode1Ubuntu OS일 경우에만 FQDN 과 OS 버전 출력

7. 반복문+조건문을 함께 사용

아래의 여러 서비스들의 상태를 체크하는 시나리오를 구성한다.

  • rsyslog
  • ssh
  • cron
  • nginx

여러 서비스를 반복으로 체크하면서 Active상태와 Inactive상태의 서비스를 분류하여 출력한다.

첫번째 task에서 의 ``은 loop의 값

  • systemctl is-active rsyslog, systemctl is-active ssh, systemctl is-active cron ,systemctl is-active nginx

두번째 이후에서의 `` 은 결과 객체 전체

  • item.item = "rsyslog"
  • item.rc =0
  • item.stdout = "active"

    ```

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
---
- hosts: all
  
  tasks:
    - name: Check multiple services status
      ansible.builtin.command: systemctl is-active 
      register: service_result
      loop:
        - rsyslog
        - ssh
        - cron
        - nginx
      failed_when: false

    - name: Print active services only
      ansible.builtin.debug:
        msg: "✓  is ACTIVE"
      loop: ""
      when: item.rc == 0

    - name: Print inactive services only
      ansible.builtin.debug:
        msg: "✗  is INACTIVE"
      loop: ""
      when: item.rc != 0

    - name: Create files for active services
      ansible.builtin.file:
        path: "/tmp/_active.txt"
        state: touch
      loop: ""
      when: item.rc == 0

전체 결과

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
PLAY [all] *********************************************************************************************************************************************************************

TASK [Gathering Facts] *********************************************************************************************************************************************************
ok: [tnode2]
ok: [tnode1]
ok: [tnode3]

TASK [Check multiple services status] ******************************************************************************************************************************************
changed: [tnode2] => (item=rsyslog)
changed: [tnode1] => (item=rsyslog)
changed: [tnode3] => (item=rsyslog)
changed: [tnode2] => (item=ssh)
changed: [tnode1] => (item=ssh)
changed: [tnode3] => (item=ssh)
changed: [tnode2] => (item=cron)
changed: [tnode1] => (item=cron)
changed: [tnode3] => (item=cron)
changed: [tnode2] => (item=nginx)
changed: [tnode1] => (item=nginx)
changed: [tnode3] => (item=nginx)

TASK [Print active services only] **********************************************************************************************************************************************
ok: [tnode1] => (item={'changed': True, 'stdout': 'active', 'stderr': '', 'rc': 0, 'cmd': ['systemctl', 'is-active', 'rsyslog'], 'start': '2026-01-17 18:05:20.410656', 'end': '2026-01-17 18:05:20.415111', 'delta': '0:00:00.004455', 'msg': '', 'invocation': {'module_args': {'_raw_params': 'systemctl is-active rsyslog', '_uses_shell': False, 'expand_argument_vars': True, 'stdin_add_newline': True, 'strip_empty_ends': True, 'cmd': None, 'argv': None, 'chdir': None, 'executable': None, 'creates': None, 'removes': None, 'stdin': None}}, 'stdout_lines': ['active'], 'stderr_lines': [], 'failed': False, 'failed_when_result': False, 'item': 'rsyslog', 'ansible_loop_var': 'item'}) => {
    "msg": "✓ rsyslog is ACTIVE"
}
ok: [tnode1] => (item={'changed': True, 'stdout': 'active', 'stderr': '', 'rc': 0, 'cmd': ['systemctl', 'is-active', 'ssh'], 'start': '2026-01-17 18:05:20.580505', 'end': '2026-01-17 18:05:20.583767', 'delta': '0:00:00.003262', 'msg': '', 'invocation': {'module_args': {'_raw_params': 'systemctl is-active ssh', '_uses_shell': False, 'expand_argument_vars': True, 'stdin_add_newline': True, 'strip_empty_ends': True, 'cmd': None, 'argv': None, 'chdir': None, 'executable': None, 'creates': None, 'removes': None, 'stdin': None}}, 'stdout_lines': ['active'], 'stderr_lines': [], 'failed': False, 'failed_when_result': False, 'item': 'ssh', 'ansible_loop_var': 'item'}) => {
    "msg": "✓ ssh is ACTIVE"
}
ok: [tnode1] => (item={'changed': True, 'stdout': 'active', 'stderr': '', 'rc': 0, 'cmd': ['systemctl', 'is-active', 'cron'], 'start': '2026-01-17 18:05:20.744792', 'end': '2026-01-17 18:05:20.749811', 'delta': '0:00:00.005019', 'msg': '', 'invocation': {'module_args': {'_raw_params': 'systemctl is-active cron', '_uses_shell': False, 'expand_argument_vars': True, 'stdin_add_newline': True, 'strip_empty_ends': True, 'cmd': None, 'argv': None, 'chdir': None, 'executable': None, 'creates': None, 'removes': None, 'stdin': None}}, 'stdout_lines': ['active'], 'stderr_lines': [], 'failed': False, 'failed_when_result': False, 'item': 'cron', 'ansible_loop_var': 'item'}) => {
    "msg": "✓ cron is ACTIVE"
}
skipping: [tnode1] => (item={'changed': True, 'stdout': 'inactive', 'stderr': '', 'rc': 4, 'cmd': ['systemctl', 'is-active', 'nginx'], 'start': '2026-01-17 18:05:20.928411', 'end': '2026-01-17 18:05:20.931765', 'delta': '0:00:00.003354', 'failed': False, 'msg': 'non-zero return code', 'invocation': {'module_args': {'_raw_params': 'systemctl is-active nginx', '_uses_shell': False, 'expand_argument_vars': True, 'stdin_add_newline': True, 'strip_empty_ends': True, 'cmd': None, 'argv': None, 'chdir': None, 'executable': None, 'creates': None, 'removes': None, 'stdin': None}}, 'stdout_lines': ['inactive'], 'stderr_lines': [], 'failed_when_result': False, 'failed_when_suppressed_exception': '(traceback unavailable)', 'item': 'nginx', 'ansible_loop_var': 'item'}) 
ok: [tnode2] => (item={'changed': True, 'stdout': 'active', 'stderr': '', 'rc': 0, 'cmd': ['systemctl', 'is-active', 'rsyslog'], 'start': '2026-01-17 18:05:15.666178', 'end': '2026-01-17 18:05:15.670913', 'delta': '0:00:00.004735', 'msg': '', 'invocation': {'module_args': {'_raw_params': 'systemctl is-active rsyslog', '_uses_shell': False, 'expand_argument_vars': True, 'stdin_add_newline': True, 'strip_empty_ends': True, 'cmd': None, 'argv': None, 'chdir': None, 'executable': None, 'creates': None, 'removes': None, 'stdin': None}}, 'stdout_lines': ['active'], 'stderr_lines': [], 'failed': False, 'failed_when_result': False, 'item': 'rsyslog', 'ansible_loop_var': 'item'}) => {
    "msg": "✓ rsyslog is ACTIVE"
}
ok: [tnode3] => (item={'changed': True, 'stdout': 'active', 'stderr': '', 'rc': 0, 'cmd': ['systemctl', 'is-active', 'rsyslog'], 'start': '2026-01-17 18:06:26.930817', 'end': '2026-01-17 18:06:26.936322', 'delta': '0:00:00.005505', 'msg': '', 'invocation': {'module_args': {'_raw_params': 'systemctl is-active rsyslog', '_uses_shell': False, 'expand_argument_vars': True, 'stdin_add_newline': True, 'strip_empty_ends': True, 'cmd': None, 'argv': None, 'chdir': None, 'executable': None, 'creates': None, 'removes': None, 'stdin': None}}, 'stdout_lines': ['active'], 'stderr_lines': [], 'failed': False, 'failed_when_result': False, 'item': 'rsyslog', 'ansible_loop_var': 'item'}) => {
    "msg": "✓ rsyslog is ACTIVE"
}
skipping: [tnode3] => (item={'changed': True, 'stdout': 'inactive', 'stderr': '', 'rc': 3, 'cmd': ['systemctl', 'is-active', 'ssh'], 'start': '2026-01-17 18:06:27.119490', 'end': '2026-01-17 18:06:27.123240', 'delta': '0:00:00.003750', 'failed': False, 'msg': 'non-zero return code', 'invocation': {'module_args': {'_raw_params': 'systemctl is-active ssh', '_uses_shell': False, 'expand_argument_vars': True, 'stdin_add_newline': True, 'strip_empty_ends': True, 'cmd': None, 'argv': None, 'chdir': None, 'executable': None, 'creates': None, 'removes': None, 'stdin': None}}, 'stdout_lines': ['inactive'], 'stderr_lines': [], 'failed_when_result': False, 'failed_when_suppressed_exception': '(traceback unavailable)', 'item': 'ssh', 'ansible_loop_var': 'item'}) 
ok: [tnode2] => (item={'changed': True, 'stdout': 'active', 'stderr': '', 'rc': 0, 'cmd': ['systemctl', 'is-active', 'ssh'], 'start': '2026-01-17 18:05:15.832998', 'end': '2026-01-17 18:05:15.835944', 'delta': '0:00:00.002946', 'msg': '', 'invocation': {'module_args': {'_raw_params': 'systemctl is-active ssh', '_uses_shell': False, 'expand_argument_vars': True, 'stdin_add_newline': True, 'strip_empty_ends': True, 'cmd': None, 'argv': None, 'chdir': None, 'executable': None, 'creates': None, 'removes': None, 'stdin': None}}, 'stdout_lines': ['active'], 'stderr_lines': [], 'failed': False, 'failed_when_result': False, 'item': 'ssh', 'ansible_loop_var': 'item'}) => {
    "msg": "✓ ssh is ACTIVE"
}
skipping: [tnode3] => (item={'changed': True, 'stdout': 'inactive', 'stderr': '', 'rc': 3, 'cmd': ['systemctl', 'is-active', 'cron'], 'start': '2026-01-17 18:06:27.297196', 'end': '2026-01-17 18:06:27.302521', 'delta': '0:00:00.005325', 'failed': False, 'msg': 'non-zero return code', 'invocation': {'module_args': {'_raw_params': 'systemctl is-active cron', '_uses_shell': False, 'expand_argument_vars': True, 'stdin_add_newline': True, 'strip_empty_ends': True, 'cmd': None, 'argv': None, 'chdir': None, 'executable': None, 'creates': None, 'removes': None, 'stdin': None}}, 'stdout_lines': ['inactive'], 'stderr_lines': [], 'failed_when_result': False, 'failed_when_suppressed_exception': '(traceback unavailable)', 'item': 'cron', 'ansible_loop_var': 'item'}) 
skipping: [tnode3] => (item={'changed': True, 'stdout': 'inactive', 'stderr': '', 'rc': 3, 'cmd': ['systemctl', 'is-active', 'nginx'], 'start': '2026-01-17 18:06:27.490843', 'end': '2026-01-17 18:06:27.494603', 'delta': '0:00:00.003760', 'failed': False, 'msg': 'non-zero return code', 'invocation': {'module_args': {'_raw_params': 'systemctl is-active nginx', '_uses_shell': False, 'expand_argument_vars': True, 'stdin_add_newline': True, 'strip_empty_ends': True, 'cmd': None, 'argv': None, 'chdir': None, 'executable': None, 'creates': None, 'removes': None, 'stdin': None}}, 'stdout_lines': ['inactive'], 'stderr_lines': [], 'failed_when_result': False, 'failed_when_suppressed_exception': '(traceback unavailable)', 'item': 'nginx', 'ansible_loop_var': 'item'}) 
ok: [tnode2] => (item={'changed': True, 'stdout': 'active', 'stderr': '', 'rc': 0, 'cmd': ['systemctl', 'is-active', 'cron'], 'start': '2026-01-17 18:05:15.999171', 'end': '2026-01-17 18:05:16.003635', 'delta': '0:00:00.004464', 'msg': '', 'invocation': {'module_args': {'_raw_params': 'systemctl is-active cron', '_uses_shell': False, 'expand_argument_vars': True, 'stdin_add_newline': True, 'strip_empty_ends': True, 'cmd': None, 'argv': None, 'chdir': None, 'executable': None, 'creates': None, 'removes': None, 'stdin': None}}, 'stdout_lines': ['active'], 'stderr_lines': [], 'failed': False, 'failed_when_result': False, 'item': 'cron', 'ansible_loop_var': 'item'}) => {
    "msg": "✓ cron is ACTIVE"
}
skipping: [tnode2] => (item={'changed': True, 'stdout': 'inactive', 'stderr': '', 'rc': 4, 'cmd': ['systemctl', 'is-active', 'nginx'], 'start': '2026-01-17 18:05:16.182983', 'end': '2026-01-17 18:05:16.186214', 'delta': '0:00:00.003231', 'failed': False, 'msg': 'non-zero return code', 'invocation': {'module_args': {'_raw_params': 'systemctl is-active nginx', '_uses_shell': False, 'expand_argument_vars': True, 'stdin_add_newline': True, 'strip_empty_ends': True, 'cmd': None, 'argv': None, 'chdir': None, 'executable': None, 'creates': None, 'removes': None, 'stdin': None}}, 'stdout_lines': ['inactive'], 'stderr_lines': [], 'failed_when_result': False, 'failed_when_suppressed_exception': '(traceback unavailable)', 'item': 'nginx', 'ansible_loop_var': 'item'}) 

TASK [Print inactive services only] ********************************************************************************************************************************************
skipping: [tnode1] => (item={'changed': True, 'stdout': 'active', 'stderr': '', 'rc': 0, 'cmd': ['systemctl', 'is-active', 'rsyslog'], 'start': '2026-01-17 18:05:20.410656', 'end': '2026-01-17 18:05:20.415111', 'delta': '0:00:00.004455', 'msg': '', 'invocation': {'module_args': {'_raw_params': 'systemctl is-active rsyslog', '_uses_shell': False, 'expand_argument_vars': True, 'stdin_add_newline': True, 'strip_empty_ends': True, 'cmd': None, 'argv': None, 'chdir': None, 'executable': None, 'creates': None, 'removes': None, 'stdin': None}}, 'stdout_lines': ['active'], 'stderr_lines': [], 'failed': False, 'failed_when_result': False, 'item': 'rsyslog', 'ansible_loop_var': 'item'}) 
skipping: [tnode1] => (item={'changed': True, 'stdout': 'active', 'stderr': '', 'rc': 0, 'cmd': ['systemctl', 'is-active', 'ssh'], 'start': '2026-01-17 18:05:20.580505', 'end': '2026-01-17 18:05:20.583767', 'delta': '0:00:00.003262', 'msg': '', 'invocation': {'module_args': {'_raw_params': 'systemctl is-active ssh', '_uses_shell': False, 'expand_argument_vars': True, 'stdin_add_newline': True, 'strip_empty_ends': True, 'cmd': None, 'argv': None, 'chdir': None, 'executable': None, 'creates': None, 'removes': None, 'stdin': None}}, 'stdout_lines': ['active'], 'stderr_lines': [], 'failed': False, 'failed_when_result': False, 'item': 'ssh', 'ansible_loop_var': 'item'}) 
skipping: [tnode1] => (item={'changed': True, 'stdout': 'active', 'stderr': '', 'rc': 0, 'cmd': ['systemctl', 'is-active', 'cron'], 'start': '2026-01-17 18:05:20.744792', 'end': '2026-01-17 18:05:20.749811', 'delta': '0:00:00.005019', 'msg': '', 'invocation': {'module_args': {'_raw_params': 'systemctl is-active cron', '_uses_shell': False, 'expand_argument_vars': True, 'stdin_add_newline': True, 'strip_empty_ends': True, 'cmd': None, 'argv': None, 'chdir': None, 'executable': None, 'creates': None, 'removes': None, 'stdin': None}}, 'stdout_lines': ['active'], 'stderr_lines': [], 'failed': False, 'failed_when_result': False, 'item': 'cron', 'ansible_loop_var': 'item'}) 
skipping: [tnode2] => (item={'changed': True, 'stdout': 'active', 'stderr': '', 'rc': 0, 'cmd': ['systemctl', 'is-active', 'rsyslog'], 'start': '2026-01-17 18:05:15.666178', 'end': '2026-01-17 18:05:15.670913', 'delta': '0:00:00.004735', 'msg': '', 'invocation': {'module_args': {'_raw_params': 'systemctl is-active rsyslog', '_uses_shell': False, 'expand_argument_vars': True, 'stdin_add_newline': True, 'strip_empty_ends': True, 'cmd': None, 'argv': None, 'chdir': None, 'executable': None, 'creates': None, 'removes': None, 'stdin': None}}, 'stdout_lines': ['active'], 'stderr_lines': [], 'failed': False, 'failed_when_result': False, 'item': 'rsyslog', 'ansible_loop_var': 'item'}) 
skipping: [tnode2] => (item={'changed': True, 'stdout': 'active', 'stderr': '', 'rc': 0, 'cmd': ['systemctl', 'is-active', 'ssh'], 'start': '2026-01-17 18:05:15.832998', 'end': '2026-01-17 18:05:15.835944', 'delta': '0:00:00.002946', 'msg': '', 'invocation': {'module_args': {'_raw_params': 'systemctl is-active ssh', '_uses_shell': False, 'expand_argument_vars': True, 'stdin_add_newline': True, 'strip_empty_ends': True, 'cmd': None, 'argv': None, 'chdir': None, 'executable': None, 'creates': None, 'removes': None, 'stdin': None}}, 'stdout_lines': ['active'], 'stderr_lines': [], 'failed': False, 'failed_when_result': False, 'item': 'ssh', 'ansible_loop_var': 'item'}) 
ok: [tnode1] => (item={'changed': True, 'stdout': 'inactive', 'stderr': '', 'rc': 4, 'cmd': ['systemctl', 'is-active', 'nginx'], 'start': '2026-01-17 18:05:20.928411', 'end': '2026-01-17 18:05:20.931765', 'delta': '0:00:00.003354', 'failed': False, 'msg': 'non-zero return code', 'invocation': {'module_args': {'_raw_params': 'systemctl is-active nginx', '_uses_shell': False, 'expand_argument_vars': True, 'stdin_add_newline': True, 'strip_empty_ends': True, 'cmd': None, 'argv': None, 'chdir': None, 'executable': None, 'creates': None, 'removes': None, 'stdin': None}}, 'stdout_lines': ['inactive'], 'stderr_lines': [], 'failed_when_result': False, 'failed_when_suppressed_exception': '(traceback unavailable)', 'item': 'nginx', 'ansible_loop_var': 'item'}) => {
    "msg": "✗ nginx is INACTIVE"
}
skipping: [tnode2] => (item={'changed': True, 'stdout': 'active', 'stderr': '', 'rc': 0, 'cmd': ['systemctl', 'is-active', 'cron'], 'start': '2026-01-17 18:05:15.999171', 'end': '2026-01-17 18:05:16.003635', 'delta': '0:00:00.004464', 'msg': '', 'invocation': {'module_args': {'_raw_params': 'systemctl is-active cron', '_uses_shell': False, 'expand_argument_vars': True, 'stdin_add_newline': True, 'strip_empty_ends': True, 'cmd': None, 'argv': None, 'chdir': None, 'executable': None, 'creates': None, 'removes': None, 'stdin': None}}, 'stdout_lines': ['active'], 'stderr_lines': [], 'failed': False, 'failed_when_result': False, 'item': 'cron', 'ansible_loop_var': 'item'}) 
skipping: [tnode3] => (item={'changed': True, 'stdout': 'active', 'stderr': '', 'rc': 0, 'cmd': ['systemctl', 'is-active', 'rsyslog'], 'start': '2026-01-17 18:06:26.930817', 'end': '2026-01-17 18:06:26.936322', 'delta': '0:00:00.005505', 'msg': '', 'invocation': {'module_args': {'_raw_params': 'systemctl is-active rsyslog', '_uses_shell': False, 'expand_argument_vars': True, 'stdin_add_newline': True, 'strip_empty_ends': True, 'cmd': None, 'argv': None, 'chdir': None, 'executable': None, 'creates': None, 'removes': None, 'stdin': None}}, 'stdout_lines': ['active'], 'stderr_lines': [], 'failed': False, 'failed_when_result': False, 'item': 'rsyslog', 'ansible_loop_var': 'item'}) 
ok: [tnode2] => (item={'changed': True, 'stdout': 'inactive', 'stderr': '', 'rc': 4, 'cmd': ['systemctl', 'is-active', 'nginx'], 'start': '2026-01-17 18:05:16.182983', 'end': '2026-01-17 18:05:16.186214', 'delta': '0:00:00.003231', 'failed': False, 'msg': 'non-zero return code', 'invocation': {'module_args': {'_raw_params': 'systemctl is-active nginx', '_uses_shell': False, 'expand_argument_vars': True, 'stdin_add_newline': True, 'strip_empty_ends': True, 'cmd': None, 'argv': None, 'chdir': None, 'executable': None, 'creates': None, 'removes': None, 'stdin': None}}, 'stdout_lines': ['inactive'], 'stderr_lines': [], 'failed_when_result': False, 'failed_when_suppressed_exception': '(traceback unavailable)', 'item': 'nginx', 'ansible_loop_var': 'item'}) => {
    "msg": "✗ nginx is INACTIVE"
}
ok: [tnode3] => (item={'changed': True, 'stdout': 'inactive', 'stderr': '', 'rc': 3, 'cmd': ['systemctl', 'is-active', 'ssh'], 'start': '2026-01-17 18:06:27.119490', 'end': '2026-01-17 18:06:27.123240', 'delta': '0:00:00.003750', 'failed': False, 'msg': 'non-zero return code', 'invocation': {'module_args': {'_raw_params': 'systemctl is-active ssh', '_uses_shell': False, 'expand_argument_vars': True, 'stdin_add_newline': True, 'strip_empty_ends': True, 'cmd': None, 'argv': None, 'chdir': None, 'executable': None, 'creates': None, 'removes': None, 'stdin': None}}, 'stdout_lines': ['inactive'], 'stderr_lines': [], 'failed_when_result': False, 'failed_when_suppressed_exception': '(traceback unavailable)', 'item': 'ssh', 'ansible_loop_var': 'item'}) => {
    "msg": "✗ ssh is INACTIVE"
}
ok: [tnode3] => (item={'changed': True, 'stdout': 'inactive', 'stderr': '', 'rc': 3, 'cmd': ['systemctl', 'is-active', 'cron'], 'start': '2026-01-17 18:06:27.297196', 'end': '2026-01-17 18:06:27.302521', 'delta': '0:00:00.005325', 'failed': False, 'msg': 'non-zero return code', 'invocation': {'module_args': {'_raw_params': 'systemctl is-active cron', '_uses_shell': False, 'expand_argument_vars': True, 'stdin_add_newline': True, 'strip_empty_ends': True, 'cmd': None, 'argv': None, 'chdir': None, 'executable': None, 'creates': None, 'removes': None, 'stdin': None}}, 'stdout_lines': ['inactive'], 'stderr_lines': [], 'failed_when_result': False, 'failed_when_suppressed_exception': '(traceback unavailable)', 'item': 'cron', 'ansible_loop_var': 'item'}) => {
    "msg": "✗ cron is INACTIVE"
}
ok: [tnode3] => (item={'changed': True, 'stdout': 'inactive', 'stderr': '', 'rc': 3, 'cmd': ['systemctl', 'is-active', 'nginx'], 'start': '2026-01-17 18:06:27.490843', 'end': '2026-01-17 18:06:27.494603', 'delta': '0:00:00.003760', 'failed': False, 'msg': 'non-zero return code', 'invocation': {'module_args': {'_raw_params': 'systemctl is-active nginx', '_uses_shell': False, 'expand_argument_vars': True, 'stdin_add_newline': True, 'strip_empty_ends': True, 'cmd': None, 'argv': None, 'chdir': None, 'executable': None, 'creates': None, 'removes': None, 'stdin': None}}, 'stdout_lines': ['inactive'], 'stderr_lines': [], 'failed_when_result': False, 'failed_when_suppressed_exception': '(traceback unavailable)', 'item': 'nginx', 'ansible_loop_var': 'item'}) => {
    "msg": "✗ nginx is INACTIVE"
}

TASK [Create files for active services] ****************************************************************************************************************************************
changed: [tnode3] => (item={'changed': True, 'stdout': 'active', 'stderr': '', 'rc': 0, 'cmd': ['systemctl', 'is-active', 'rsyslog'], 'start': '2026-01-17 18:06:26.930817', 'end': '2026-01-17 18:06:26.936322', 'delta': '0:00:00.005505', 'msg': '', 'invocation': {'module_args': {'_raw_params': 'systemctl is-active rsyslog', '_uses_shell': False, 'expand_argument_vars': True, 'stdin_add_newline': True, 'strip_empty_ends': True, 'cmd': None, 'argv': None, 'chdir': None, 'executable': None, 'creates': None, 'removes': None, 'stdin': None}}, 'stdout_lines': ['active'], 'stderr_lines': [], 'failed': False, 'failed_when_result': False, 'item': 'rsyslog', 'ansible_loop_var': 'item'})
changed: [tnode2] => (item={'changed': True, 'stdout': 'active', 'stderr': '', 'rc': 0, 'cmd': ['systemctl', 'is-active', 'rsyslog'], 'start': '2026-01-17 18:05:15.666178', 'end': '2026-01-17 18:05:15.670913', 'delta': '0:00:00.004735', 'msg': '', 'invocation': {'module_args': {'_raw_params': 'systemctl is-active rsyslog', '_uses_shell': False, 'expand_argument_vars': True, 'stdin_add_newline': True, 'strip_empty_ends': True, 'cmd': None, 'argv': None, 'chdir': None, 'executable': None, 'creates': None, 'removes': None, 'stdin': None}}, 'stdout_lines': ['active'], 'stderr_lines': [], 'failed': False, 'failed_when_result': False, 'item': 'rsyslog', 'ansible_loop_var': 'item'})
skipping: [tnode3] => (item={'changed': True, 'stdout': 'inactive', 'stderr': '', 'rc': 3, 'cmd': ['systemctl', 'is-active', 'ssh'], 'start': '2026-01-17 18:06:27.119490', 'end': '2026-01-17 18:06:27.123240', 'delta': '0:00:00.003750', 'failed': False, 'msg': 'non-zero return code', 'invocation': {'module_args': {'_raw_params': 'systemctl is-active ssh', '_uses_shell': False, 'expand_argument_vars': True, 'stdin_add_newline': True, 'strip_empty_ends': True, 'cmd': None, 'argv': None, 'chdir': None, 'executable': None, 'creates': None, 'removes': None, 'stdin': None}}, 'stdout_lines': ['inactive'], 'stderr_lines': [], 'failed_when_result': False, 'failed_when_suppressed_exception': '(traceback unavailable)', 'item': 'ssh', 'ansible_loop_var': 'item'}) 
changed: [tnode1] => (item={'changed': True, 'stdout': 'active', 'stderr': '', 'rc': 0, 'cmd': ['systemctl', 'is-active', 'rsyslog'], 'start': '2026-01-17 18:05:20.410656', 'end': '2026-01-17 18:05:20.415111', 'delta': '0:00:00.004455', 'msg': '', 'invocation': {'module_args': {'_raw_params': 'systemctl is-active rsyslog', '_uses_shell': False, 'expand_argument_vars': True, 'stdin_add_newline': True, 'strip_empty_ends': True, 'cmd': None, 'argv': None, 'chdir': None, 'executable': None, 'creates': None, 'removes': None, 'stdin': None}}, 'stdout_lines': ['active'], 'stderr_lines': [], 'failed': False, 'failed_when_result': False, 'item': 'rsyslog', 'ansible_loop_var': 'item'})
skipping: [tnode3] => (item={'changed': True, 'stdout': 'inactive', 'stderr': '', 'rc': 3, 'cmd': ['systemctl', 'is-active', 'cron'], 'start': '2026-01-17 18:06:27.297196', 'end': '2026-01-17 18:06:27.302521', 'delta': '0:00:00.005325', 'failed': False, 'msg': 'non-zero return code', 'invocation': {'module_args': {'_raw_params': 'systemctl is-active cron', '_uses_shell': False, 'expand_argument_vars': True, 'stdin_add_newline': True, 'strip_empty_ends': True, 'cmd': None, 'argv': None, 'chdir': None, 'executable': None, 'creates': None, 'removes': None, 'stdin': None}}, 'stdout_lines': ['inactive'], 'stderr_lines': [], 'failed_when_result': False, 'failed_when_suppressed_exception': '(traceback unavailable)', 'item': 'cron', 'ansible_loop_var': 'item'}) 
skipping: [tnode3] => (item={'changed': True, 'stdout': 'inactive', 'stderr': '', 'rc': 3, 'cmd': ['systemctl', 'is-active', 'nginx'], 'start': '2026-01-17 18:06:27.490843', 'end': '2026-01-17 18:06:27.494603', 'delta': '0:00:00.003760', 'failed': False, 'msg': 'non-zero return code', 'invocation': {'module_args': {'_raw_params': 'systemctl is-active nginx', '_uses_shell': False, 'expand_argument_vars': True, 'stdin_add_newline': True, 'strip_empty_ends': True, 'cmd': None, 'argv': None, 'chdir': None, 'executable': None, 'creates': None, 'removes': None, 'stdin': None}}, 'stdout_lines': ['inactive'], 'stderr_lines': [], 'failed_when_result': False, 'failed_when_suppressed_exception': '(traceback unavailable)', 'item': 'nginx', 'ansible_loop_var': 'item'}) 
changed: [tnode2] => (item={'changed': True, 'stdout': 'active', 'stderr': '', 'rc': 0, 'cmd': ['systemctl', 'is-active', 'ssh'], 'start': '2026-01-17 18:05:15.832998', 'end': '2026-01-17 18:05:15.835944', 'delta': '0:00:00.002946', 'msg': '', 'invocation': {'module_args': {'_raw_params': 'systemctl is-active ssh', '_uses_shell': False, 'expand_argument_vars': True, 'stdin_add_newline': True, 'strip_empty_ends': True, 'cmd': None, 'argv': None, 'chdir': None, 'executable': None, 'creates': None, 'removes': None, 'stdin': None}}, 'stdout_lines': ['active'], 'stderr_lines': [], 'failed': False, 'failed_when_result': False, 'item': 'ssh', 'ansible_loop_var': 'item'})
changed: [tnode1] => (item={'changed': True, 'stdout': 'active', 'stderr': '', 'rc': 0, 'cmd': ['systemctl', 'is-active', 'ssh'], 'start': '2026-01-17 18:05:20.580505', 'end': '2026-01-17 18:05:20.583767', 'delta': '0:00:00.003262', 'msg': '', 'invocation': {'module_args': {'_raw_params': 'systemctl is-active ssh', '_uses_shell': False, 'expand_argument_vars': True, 'stdin_add_newline': True, 'strip_empty_ends': True, 'cmd': None, 'argv': None, 'chdir': None, 'executable': None, 'creates': None, 'removes': None, 'stdin': None}}, 'stdout_lines': ['active'], 'stderr_lines': [], 'failed': False, 'failed_when_result': False, 'item': 'ssh', 'ansible_loop_var': 'item'})
changed: [tnode2] => (item={'changed': True, 'stdout': 'active', 'stderr': '', 'rc': 0, 'cmd': ['systemctl', 'is-active', 'cron'], 'start': '2026-01-17 18:05:15.999171', 'end': '2026-01-17 18:05:16.003635', 'delta': '0:00:00.004464', 'msg': '', 'invocation': {'module_args': {'_raw_params': 'systemctl is-active cron', '_uses_shell': False, 'expand_argument_vars': True, 'stdin_add_newline': True, 'strip_empty_ends': True, 'cmd': None, 'argv': None, 'chdir': None, 'executable': None, 'creates': None, 'removes': None, 'stdin': None}}, 'stdout_lines': ['active'], 'stderr_lines': [], 'failed': False, 'failed_when_result': False, 'item': 'cron', 'ansible_loop_var': 'item'})
changed: [tnode1] => (item={'changed': True, 'stdout': 'active', 'stderr': '', 'rc': 0, 'cmd': ['systemctl', 'is-active', 'cron'], 'start': '2026-01-17 18:05:20.744792', 'end': '2026-01-17 18:05:20.749811', 'delta': '0:00:00.005019', 'msg': '', 'invocation': {'module_args': {'_raw_params': 'systemctl is-active cron', '_uses_shell': False, 'expand_argument_vars': True, 'stdin_add_newline': True, 'strip_empty_ends': True, 'cmd': None, 'argv': None, 'chdir': None, 'executable': None, 'creates': None, 'removes': None, 'stdin': None}}, 'stdout_lines': ['active'], 'stderr_lines': [], 'failed': False, 'failed_when_result': False, 'item': 'cron', 'ansible_loop_var': 'item'})
skipping: [tnode2] => (item={'changed': True, 'stdout': 'inactive', 'stderr': '', 'rc': 4, 'cmd': ['systemctl', 'is-active', 'nginx'], 'start': '2026-01-17 18:05:16.182983', 'end': '2026-01-17 18:05:16.186214', 'delta': '0:00:00.003231', 'failed': False, 'msg': 'non-zero return code', 'invocation': {'module_args': {'_raw_params': 'systemctl is-active nginx', '_uses_shell': False, 'expand_argument_vars': True, 'stdin_add_newline': True, 'strip_empty_ends': True, 'cmd': None, 'argv': None, 'chdir': None, 'executable': None, 'creates': None, 'removes': None, 'stdin': None}}, 'stdout_lines': ['inactive'], 'stderr_lines': [], 'failed_when_result': False, 'failed_when_suppressed_exception': '(traceback unavailable)', 'item': 'nginx', 'ansible_loop_var': 'item'}) 
skipping: [tnode1] => (item={'changed': True, 'stdout': 'inactive', 'stderr': '', 'rc': 4, 'cmd': ['systemctl', 'is-active', 'nginx'], 'start': '2026-01-17 18:05:20.928411', 'end': '2026-01-17 18:05:20.931765', 'delta': '0:00:00.003354', 'failed': False, 'msg': 'non-zero return code', 'invocation': {'module_args': {'_raw_params': 'systemctl is-active nginx', '_uses_shell': False, 'expand_argument_vars': True, 'stdin_add_newline': True, 'strip_empty_ends': True, 'cmd': None, 'argv': None, 'chdir': None, 'executable': None, 'creates': None, 'removes': None, 'stdin': None}}, 'stdout_lines': ['inactive'], 'stderr_lines': [], 'failed_when_result': False, 'failed_when_suppressed_exception': '(traceback unavailable)', 'item': 'nginx', 'ansible_loop_var': 'item'}) 

PLAY RECAP *********************************************************************************************************************************************************************
tnode1                     : ok=5    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
tnode2                     : ok=5    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
tnode3                     : ok=5    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

8. apache2 패키지를 apt 모듈을 통해서 설치 시, 핸들러를 호출하여 service 모듈로 apache2를 재시작해보자

apt cache를 업데이트 한 후 apache2 패키지를 인스톨한다.

다만 handler의 경우 요청하는 태스크의 변화가 있을시에만 호출되므로 처음 실행에 정상적으로 인스톨이 되었다면 다음 작업부터는 이미 apache2가 정상적으로 인스톨 되어있으므로 핸들러가 호출되지 않는다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
---
- hosts: tnode1 
  
  tasks:
    - name: Update apt cache
      ansible.builtin.apt:
        update_cache: yes
        cache_valid_time: 3600

    - name: Install apache2 package
      ansible.builtin.apt:
        name: apache2
        state: present
      notify:
        - Restart Apache2

  handlers:
    - name: Restart Apache2
      ansible.builtin.service:
        name: apache2
        state: restarted
        enabled: yes

결과 확인

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
PLAY [tnode1] ******************************************************************

TASK [Gathering Facts] *********************************************************
ok: [tnode1]

TASK [Update apt cache] ********************************************************
ok: [tnode1]

TASK [Install apache2 package] *************************************************
changed: [tnode1]

RUNNING HANDLER [Restart Apache2] **********************************************
changed: [tnode1]

PLAY RECAP *********************************************************************
tnode1                     : ok=4    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

9. block rescure always 키워드를 사용한 플레이북을 작성하여 테스트

block

  • Print a message 실행 : 정상 실행됨
  • Force a failure 실행 : 실패하였으므로 rescue로 넘어감
  • Never print this : 위의 Force a failure 태스크에서 실패하였으므로 절대 실행하지 않음

rescue

  • Print when errors 실행
  • Force a failure in middle of recovery! >:-) : 실패
  • ‘Never print this’ : 마찬가지로 위의 태스크가 실패하였으므로 절대 실행하지 않음

always

  • Always do this : 위 태스크의 실패여부와 상관없이 항상 실행됨

결과

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
PLAY [tnode1] ************************************************************************************

TASK [Gathering Facts] ***************************************************************************
ok: [tnode1]

TASK [Print a message] ***************************************************************************
ok: [tnode1] => {
    "msg": "I execute normally"
}

TASK [Force a failure] ***************************************************************************
[ERROR]: Task failed: Module failed: non-zero return code
Origin: /root/my-ansible/check-service.yml:11:10

 9            msg: 'I execute normally'
10
11        - name: Force a failure
            ^ column 10

fatal: [tnode1]: FAILED! => {"changed": true, "cmd": ["/bin/false"], "delta": "0:00:00.002073", "end": "2026-01-17 18:44:06.124415", "msg": "non-zero return code", "rc": 1, "start": "2026-01-17 18:44:06.122342", "stderr": "", "stderr_lines": [], "stdout": "", "stdout_lines": []}

TASK [Print when errors] *************************************************************************
ok: [tnode1] => {
    "msg": "I caught an error"
}

TASK [Force a failure in middle of recovery! >:-)] ***********************************************
[ERROR]: Task failed: Module failed: non-zero return code
Origin: /root/my-ansible/check-service.yml:22:10

20            msg: 'I caught an error'
21
22        - name: Force a failure in middle of recovery! >:-)
            ^ column 10

fatal: [tnode1]: FAILED! => {"changed": true, "cmd": ["/bin/false"], "delta": "0:00:00.001170", "end": "2026-01-17 18:44:06.296525", "msg": "non-zero return code", "rc": 1, "start": "2026-01-17 18:44:06.295355", "stderr": "", "stderr_lines": [], "stdout": "", "stdout_lines": []}

TASK [Always do this] ****************************************************************************
ok: [tnode1] => {
    "msg": "This always executes"
}

PLAY RECAP ***************************************************************************************
tnode1                     : ok=4    changed=0    unreachable=0    failed=1    skipped=0    rescued=1    ignored=0   

10. 앤서블 갤럭시에서 롤을 사용하는 플레이북을 만들어서 롤을 통한 애플리케이션을 배포

레디스 롤 배포해보기

롤 가져오기 (-p 옵션으로 롤이 설치될 디렉터리 경로 지정)

1
ansible-galaxy role install -p roles geerlingguy.redis
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
tree roles
roles
└── geerlingguy.redis
    ├── defaults
    │   └── main.yml
    ├── handlers
    │   └── main.yml
    ├── LICENSE
    ├── meta
    │   └── main.yml
    ├── molecule
    │   └── default
    │       ├── converge.yml
    │       └── molecule.yml
    ├── README.md
    ├── tasks
    │   ├── main.yml
    │   ├── setup-Archlinux.yml
    │   ├── setup-Debian.yml
    │   └── setup-RedHat.yml
    ├── templates
    │   └── redis.conf.j2
    └── vars
        ├── Archlinux.yml
        ├── Debian.yml
        └── RedHat.yml

10 directories, 15 files

/roles/tasks/main.yml 확인해보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
---
# Variable setup.
- name: Include OS-specific variables.
  include_vars: ".yml"

- name: Define redis_package.
  set_fact:
    redis_package: ""
  when: redis_package is not defined

- name: Ensure Redis configuration dir exists.
  file:
    path: ""
    state: directory
    mode: ""

- name: Ensure Redis is configured.
  template:
    src: redis.conf.j2
    dest: ""
    mode: ""
  notify: restart redis

# Setup/install tasks.
- include_tasks: setup-RedHat.yml
  when: ansible_os_family == 'RedHat'

- include_tasks: setup-Debian.yml
  when: ansible_os_family == 'Debian'

- include_tasks: setup-Archlinux.yml
  when: ansible_os_family == 'Archlinux'

- name: Ensure Redis is running.
  service:
    name: ""
    state: started
    enabled: ""

role-example.yml 생성

1
2
3
4
5
---
- hosts: tnode1

  roles:
    - geerlingguy.redis

설치 실행

1
ansible-playbook role-example.yml 

결과 확인

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
PLAY [tnode1] *************************************************************************************************************************************************************

TASK [Gathering Facts] ****************************************************************************************************************************************************
ok: [tnode1]

TASK [geerlingguy.redis : Include OS-specific variables.] *****************************************************************************************************************
ok: [tnode1]

TASK [geerlingguy.redis : Define redis_package.] **************************************************************************************************************************
ok: [tnode1]

TASK [geerlingguy.redis : Ensure Redis configuration dir exists.] *********************************************************************************************************
changed: [tnode1]

TASK [geerlingguy.redis : Ensure Redis is configured.] ********************************************************************************************************************
changed: [tnode1]

TASK [geerlingguy.redis : include_tasks] **********************************************************************************************************************************
skipping: [tnode1]

TASK [geerlingguy.redis : include_tasks] **********************************************************************************************************************************
included: /root/my-ansible/roles/geerlingguy.redis/tasks/setup-Debian.yml for tnode1

TASK [geerlingguy.redis : Ensure Redis is installed.] *********************************************************************************************************************
changed: [tnode1]

TASK [geerlingguy.redis : include_tasks] **********************************************************************************************************************************
skipping: [tnode1]

TASK [geerlingguy.redis : Ensure Redis is running.] ***********************************************************************************************************************
ok: [tnode1]

RUNNING HANDLER [geerlingguy.redis : restart redis] ***********************************************************************************************************************
changed: [tnode1]

PLAY RECAP ****************************************************************************************************************************************************************
tnode1                     : ok=9    changed=4    unreachable=0    failed=0    skipped=2    rescued=0    ignored=0

설치확인

1
ssh tnode1 systemctl status redis

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.