Ansible을 이용하여 Linux 업데이트 자동화 (3편)에서 이어지는 글이다.
2편의 update_all.yml은 패키지 목록 갱신 → 최신 버전 업그레이드 → 불필요 패키지 정리까지만 수행하는 기본형이었다. 즉, 업데이트는 자동화하지만 업데이트 후 재부팅이 필요한지 여부는 판단하지 않았다. 4편에서는 업데이트 후 재부팅 필요 여부를 감지하고, 필요한 경우 자동 재부팅을 수행한다.
즉, 2편이 "업데이트까지만 자동화"했다면, 4편은 "업데이트 후 재부팅 판단과 재부팅까지 자동화"하는 편이라고 이해하면 된다.
이번 편의 핵심은 패키지 업데이트 자체가 아니라, 업데이트 이후 재부팅이 필요한 상황을 어떻게 자동으로 처리할지에 있다.
Debian/Ubuntu 계열은 needrestart와 /run/reboot-required를 활용하고, RHEL 계열은 dnf needs-restarting --reboothint를 활용한다.
목차
1) Debian/Ubuntu 계열 자동 재부팅 (~/ansible-lab/playbooks/update_all.yml)
Debian/Ubuntu 계열은 업데이트 후 needrestart와 /run/reboot-required를 이용해 재부팅 필요 여부를 판단하고, 정말 필요한 경우에만 ansible.builtin.reboot로 자동 재부팅을 수행한다.
# ~/ansible-lab/playbooks/update_all.yml
---
- name: Update Debian family systems
hosts: debian_family
serial: 1
become: true
gather_facts: true
tasks:
- name: Refresh apt cache
ansible.builtin.apt:
update_cache: true
cache_valid_time: 3600
- name: Upgrade installed packages to latest version
ansible.builtin.apt:
name: "*"
state: latest
- name: Remove no longer required packages
ansible.builtin.apt:
autoremove: true
- name: Ensure needrestart is installed
ansible.builtin.apt:
name: needrestart
state: present
- name: Check Debian/Ubuntu reboot-required flag
ansible.builtin.stat:
path: /run/reboot-required
register: deb_reboot_required_file
- name: Run needrestart in batch mode
ansible.builtin.command: needrestart -b
register: deb_needrestart
changed_when: false
failed_when: false
when: not ansible_check_mode
- name: Show needrestart summary
ansible.builtin.debug:
msg: "{{ deb_needrestart.stdout_lines | default([]) }}"
when: not ansible_check_mode
- name: Reboot Debian-family host if reboot is required
ansible.builtin.reboot:
msg: "Reboot initiated by Ansible after Debian/Ubuntu updates"
reboot_timeout: 900
post_reboot_delay: 10
test_command: whoami
when:
- not ansible_check_mode
- deb_reboot_required_file.stat.exists
or ((deb_needrestart.stdout | default('')) is search('NEEDRESTART-KSTA:\\s*[23]\\b'))
- name: Warn if services or sessions still need restart without full reboot
ansible.builtin.debug:
msg:
- "needrestart reported restartable services or sessions."
- "Consider restarting affected services if no full reboot was performed."
when:
- not ansible_check_mode
- not deb_reboot_required_file.stat.exists
- not ((deb_needrestart.stdout | default('')) is search('NEEDRESTART-KSTA:\\s*[23]\\b'))
- deb_needrestart.stdout is defined
- deb_needrestart.stdout | length > 0
위 코드는 Debian/Ubuntu 계열에서 재부팅 필요 여부를 확인하는 단계와, 필요 시 자동으로 재부팅하는 단계를 추가했다.
이제, 위 코드를 차근차근 뜯어보자.
1-1) needrestart 설치 (Debian/Ubuntu 계열)
- name: Ensure needrestart is installed
ansible.builtin.apt:
name: needrestart
state: present
Debian/Ubuntu 계열 서버에서 needrestart를 설치해, 업데이트 후 커널 재부팅 필요 여부와 재시작이 필요한 서비스/세션 정보를 확인할 수 있게 만든다. needrestart -b는 batch mode로 동작하며, 화면 대화상자를 띄우거나 직접 재시작을 수행하지 않고 결과만 출력한다.
- - name: Ensure needrestart is installed 이 작업의 이름이다.
- ansible.builtin.apt: Debian/Ubuntu 계열에서 사용하는 APT 패키지 관리자를 다루는 Ansible 모듈이다. 즉, apt install … 같은 작업을 자동화하는 역할이다.
- name: needrestart 설치하거나 관리할 패키지 이름이 needrestart라는 뜻이다.
- state: present 해당 패키지가 설치된 상태로 존재해야 한다는 의미다. 이미 설치되어 있으면 아무것도 하지 않고, 설치되어 있지 않으면 설치한다.
1-2) /run/reboot-required 확인 (Debian/Ubuntu 계열)
- name: Check Debian/Ubuntu reboot-required flag
ansible.builtin.stat:
path: /run/reboot-required
register: deb_reboot_required_file
이 단계는 Debian/Ubuntu 계열에서 많이 쓰이는 기본 재부팅 신호를 확인하는 부분이다. /run/reboot-required 파일이 존재하면 "업데이트 후 재부팅이 필요하다"는 뜻으로 볼 수 있다. stat 모듈은 파일 존재 여부 같은 파일 상태를 확인할 때 사용하는 Ansible 기본 모듈이다.
- - name: Check Debian/Ubuntu reboot-required flag 이 작업의 이름이다.
- ansible.builtin.stat: stat 모듈은 파일이나 디렉터리의 상태 정보를 확인할 때 쓰는 모듈이다.
- path: /run/reboot-required 확인할 대상 경로가 /run/reboot-required 라는 뜻이다. Debian/Ubuntu에서는 패키지 업데이트 후, 특히 커널이나 핵심 시스템 라이브러리 등이 바뀌어서 재부팅이 권장되거나 필요한 경우, 이 파일이 생성되는 경우가 많다.
- register: deb_reboot_required_file 이 태스크의 실행 결과를 deb_reboot_required_file 변수에 저장하겠다는 뜻이다. 즉, 이후 다른 태스크에서 이 값을 참조할 수 있게 된다.
1-3) needrestart -b 실행 (Debian/Ubuntu 계열)
- name: Run needrestart in batch mode
ansible.builtin.command: needrestart -b
register: deb_needrestart
changed_when: false
failed_when: false
when: not ansible_check_mode
이 부분이 4편의 핵심이다. needrestart -b를 실행해 현재 시스템이 재부팅이 필요한 상태인지, 혹은 서비스 재시작만 필요한 상태인지를 텍스트로 받아온다. KSTA=1은 재부팅 필요 없음, KSTA=2와 KSTA=3은 커널 업그레이드가 반영되지 않아 재부팅이 필요한 상태를 뜻한다. 또 when: not ansible_check_mode를 넣은 이유는, check mode에서는 실제 명령 실행이 건너뛰어질 수 있기 때문이다. Ansible 문서도 ansible_check_mode 변수가 check mode 여부를 나타내며, check mode에서 일부 태스크를 건너뛸 때 쓸 수 있다고 설명한다.
- - name: Run needrestart in batch mode 이 작업의 이름이다.
- ansible.builtin.command: needrestart -b Ansible의 command 모듈로 needrestart -b 명령을 실행한다. needrestart 업데이트 후에 재부팅이 필요한지, 재시작이 필요한 서비스가 있는지, 세션에 영향이 있는지 등을 점검하는 도구다.
- -b batch mode 옵션이다. 즉, 사람에게 질문하지 않고 결과를 출력만 하도록 실행한다.
- register: deb_needrestart 명령 실행 결과를 deb_needrestart 변수에 저장한다. 즉, 이후 태스크에서 이 변수 안의 내용을 꺼내 쓸 수 있다.
- changed_when: false Ansible에게 이 태스크는 시스템 상태를 바꾸는 작업으로 간주하지 말라고 알려주는 설정이다. 왜냐하면 needrestart -b는 단순히 확인만 하는 명령이지, 패키지를 설치하거나 설정을 변경하는 작업이 아니기 때문이다. 이 설정이 없으면 Ansible은 command 실행을 보통 changed로 표시할 수 있다. 하지만 실제로는 상태를 바꾸지 않으므로 false로 명시해 주는 것이 맞다.
- failed_when: false 이 태스크는 명령이 어떤 종료 코드를 내더라도 Ansible 태스크 자체를 실패로 처리하지 않도록 한다. 이걸 넣은 이유는 needrestart가 상황에 따라 0이 아닐 수도 있고, 경고성 메시지를 낼 수도 있고, 재부팅/재시작 관련 상태를 표현하는 과정에서 일반적인 성공/실패와 다르게 동작할 수도 있기 때문이다. 즉, 여기서는 명령이 정상적인 점검 결과를 내는 것이 더 중요하고, 그 결과를 나중에 직접 해석하려는 의도다.
- when: not ansible_check_mode 이 태스크는 체크 모드(--check)에서는 실행하지 않겠다는 뜻이다.
1-4) needrestart 결과 출력 (Debian/Ubuntu 계열)
- name: Show needrestart summary
ansible.builtin.debug:
msg: "{{ deb_needrestart.stdout_lines | default([]) }}"
이 단계는 자동 재부팅 자체를 수행하는 부분은 아니지만, 매우 중요하다. 실제로 어떤 서비스가 재시작 필요 상태인지, 현재 커널과 기대 커널이 같은지 다른지를 눈으로 확인할 수 있기 때문이다. 즉, 4편은 단순히 "재부팅한다"가 아니라, 왜 재부팅하는지 로그로 설명 가능한 구조가 되었다고 이해하면 된다. register 변수는 같은 play 안에서 후속 조건식과 출력에 재사용할 수 있다.
1-5) 조건부 자동 재부팅 (Debian/Ubuntu 계열)
- name: Reboot Debian-family host if reboot is required
ansible.builtin.reboot:
msg: "Reboot initiated by Ansible after Debian/Ubuntu updates"
reboot_timeout: 900
post_reboot_delay: 10
test_command: whoami
when:
- not ansible_check_mode
- deb_reboot_required_file.stat.exists
or ((deb_needrestart.stdout | default('')) is search('NEEDRESTART-KSTA:\\s*[23]\\b'))
2편과 가장 큰 차이가 바로 이 부분이다. 이제는 업데이트가 끝난 뒤 무조건 재부팅하는 것이 아니라, 아래 두 조건 중 하나를 만족할 때만 자동 재부팅을 수행한다.
• /run/reboot-required 파일이 존재할 때
• needrestart 결과에서 KSTA가 2 또는 3일 때
즉, 재부팅이 꼭 필요한 경우에만 재부팅한다. ansible.builtin.reboot 모듈은 단순히 재부팅 명령만 내리는 것이 아니라, 서버가 내려갔다가 다시 올라오고, test_command가 성공할 때까지 기다린다. reboot_timeout은 기다리는 최대 시간, post_reboot_delay는 재부팅 직후 추가로 기다릴 시간을 뜻한다.
1-6) 재부팅은 필요 없지만 재시작이 필요한 경우 안내 (Debian/Ubuntu 계열)
- name: Warn if services or sessions still need restart without full reboot
ansible.builtin.debug:
msg:
- "needrestart reported restartable services or sessions."
- "Consider restarting affected services if no full reboot was performed."
이 부분은 "재부팅까지는 필요 없지만, 일부 서비스나 세션은 다시 시작해야 할 수 있다"는 점을 알려주는 안내 메시지다. 실제 운영에서는 항상 전체 서버 재부팅이 필요한 것은 아니고, 경우에 따라 특정 서비스만 재시작하면 충분할 수 있다.
그래서 4편 코드는 재부팅 필요와 서비스 재시작 필요를 구분해서 보여주는 형태로 한 단계 더 현실적인 운영형 코드가 되었다. needrestart는 바로 이런 용도로 많이 사용된다.
2) RHEL 계열 자동 재부팅 (~/ansible-lab/playbooks/update_all.yml)
# ~/ansible-lab/playbooks/update_all.yml
- name: Update RHEL family systems
hosts: rhel_family
serial: 1
become: true
gather_facts: true
tasks:
- name: Ensure dnf-plugins-core is installed
ansible.builtin.dnf:
name: dnf-plugins-core
state: present
- name: Refresh dnf cache and upgrade installed packages
ansible.builtin.dnf:
name: "*"
state: latest
update_cache: true
update_only: true
- name: Remove no longer required packages
ansible.builtin.dnf:
autoremove: true
- name: Check whether reboot is required on RHEL family
ansible.builtin.command: dnf needs-restarting --reboothint
register: rhel_reboot_hint
changed_when: false
failed_when: rhel_reboot_hint.rc not in [0, 1]
when: not ansible_check_mode
- name: Reboot RHEL-family host if required
ansible.builtin.reboot:
msg: "Reboot initiated by Ansible after package updates"
reboot_timeout: 900
post_reboot_delay: 10
test_command: whoami
when:
- not ansible_check_mode
- rhel_reboot_hint.rc == 1
RHEL 계열은 dnf needs-restarting --reboothint를 활용한다.
RHEL 계열 코드를 차근차근 뜯어보자.
2-1) 재부팅 필요 여부 확인 (RHEL 계열)
- name: Check whether reboot is required on RHEL family
ansible.builtin.command: dnf needs-restarting --reboothint
register: rhel_reboot_hint
changed_when: false
failed_when: rhel_reboot_hint.rc not in [0, 1]
when: not ansible_check_mode
RHEL 계열에서는 Debian/Ubuntu의 needrestart 대신 dnf needs-restarting --reboothint를 사용해 재부팅 필요 여부를 확인한다.
이 기능은 dnf-plugins-core에 포함된 needs-restarting 플러그인에 의해 제공된다.
--reboothint 옵션은 재부팅이 필요하면 종료 코드 1, 필요하지 않으면 종료 코드 0을 반환한다. 따라서 Ansible에서는 이 종료 코드를 기준으로 재부팅 여부를 판단할 수 있다.
changed_when: false는 단순 확인 명령을 상태 변경으로 표시하지 않게 하기 위한 설정이고, failed_when: rhel_reboot_hint.rc not in [0, 1] 설정은 정상적인 반환값 범위를 명확히 지정한 것이다.
참고로 이 태스크는 실제 실행 시 재부팅 필요 여부를 확인하기 위한 것이며, check mode에서는 실행 결과가 기대와 다를 수 있다.
2-2) 조건부 자동 재부팅 (RHEL 계열)
- name: Reboot RHEL-family host if required
ansible.builtin.reboot:
msg: "Reboot initiated by Ansible after package updates"
reboot_timeout: 900
post_reboot_delay: 10
test_command: whoami
when:
- not ansible_check_mode
- rhel_reboot_hint.rc == 1
RHEL 계열도 Debian/Ubuntu와 마찬가지로, 정말 필요한 경우에만 자동 재부팅하도록 구성했다.
위 태스크는 앞 단계에서 확인한 rhel_reboot_hint.rc 값이 1일 때만 실행된다.
즉, dnf needs-restarting --reboothint가 재부팅 필요 신호를 줄 때만 재부팅이 수행된다.
또한 not ansible_check_mode 조건을 함께 둔 이유는, 이 태스크가 실제 실행 시에만 동작하도록 하기 위해서이다.
check mode (--check)는 실제 변경 없이 예행연습만 수행하는 모드이므로, 재부팅 같은 실제 변경 작업은 실행하지 않는 편이 자연스럽다.
또 앞 단계의 재부팅 필요 여부 확인 태스크도 check mode에서는 건너뛸 수 있으므로, 재부팅 태스크에도 같은 조건을 넣어 두면 변수 참조 문제를 줄이고 코드 흐름도 더 일관되게 유지할 수 있다.
ansible.builtin.reboot 모듈은 단순히 재부팅 명령만 내리는 것이 아니라, 서버가 내려갔다가 다시 올라오고, 지정한 test_command가 성공할 때까지 기다린다.
여기서는 whoami 명령으로 재부팅 이후 시스템이 정상적으로 응답하는지 확인한다.
3) Debian/Ubuntu 계열과 RHEL 계열을 포함한 최종 ~/ansible-lab/playbooks/update_all.yml 코드
# ~/ansible-lab/playbooks/update_all.yml
---
- name: Update Debian family systems
hosts: debian_family
serial: 1
become: true
gather_facts: true
tasks:
- name: Refresh apt cache
ansible.builtin.apt:
update_cache: true
cache_valid_time: 3600
- name: Upgrade installed packages to latest version
ansible.builtin.apt:
name: "*"
state: latest
- name: Remove no longer required packages
ansible.builtin.apt:
autoremove: true
- name: Ensure needrestart is installed
ansible.builtin.apt:
name: needrestart
state: present
- name: Check Debian/Ubuntu reboot-required flag
ansible.builtin.stat:
path: /run/reboot-required
register: deb_reboot_required_file
- name: Run needrestart in batch mode
ansible.builtin.command: needrestart -b
register: deb_needrestart
changed_when: false
failed_when: false
when: not ansible_check_mode
- name: Show needrestart summary
ansible.builtin.debug:
msg: "{{ deb_needrestart.stdout_lines | default([]) }}"
when: not ansible_check_mode
- name: Reboot Debian-family host if reboot is required
ansible.builtin.reboot:
msg: "Reboot initiated by Ansible after Debian/Ubuntu updates"
reboot_timeout: 900
post_reboot_delay: 10
test_command: whoami
when:
- not ansible_check_mode
- deb_reboot_required_file.stat.exists
or ((deb_needrestart.stdout | default('')) is search('NEEDRESTART-KSTA:\\s*[23]\\b'))
- name: Warn if services or sessions still need restart without full reboot
ansible.builtin.debug:
msg:
- "needrestart reported restartable services or sessions."
- "Consider restarting affected services if no full reboot was performed."
when:
- not ansible_check_mode
- not deb_reboot_required_file.stat.exists
- not ((deb_needrestart.stdout | default('')) is search('NEEDRESTART-KSTA:\\s*[23]\\b'))
- deb_needrestart.stdout is defined
- deb_needrestart.stdout | length > 0
- name: Update RHEL family systems
hosts: rhel_family
serial: 1
become: true
gather_facts: true
tasks:
- name: Ensure dnf-plugins-core is installed
ansible.builtin.dnf:
name: dnf-plugins-core
state: present
- name: Refresh dnf cache and upgrade installed packages
ansible.builtin.dnf:
name: "*"
state: latest
update_cache: true
update_only: true
- name: Remove no longer required packages
ansible.builtin.dnf:
autoremove: true
- name: Check whether reboot is required on RHEL family
ansible.builtin.command: dnf needs-restarting --reboothint
register: rhel_reboot_hint
changed_when: false
failed_when: rhel_reboot_hint.rc not in [0, 1]
when: not ansible_check_mode
- name: Reboot RHEL-family host if required
ansible.builtin.reboot:
msg: "Reboot initiated by Ansible after package updates"
reboot_timeout: 900
post_reboot_delay: 10
test_command: whoami
when:
- not ansible_check_mode
- rhel_reboot_hint.rc == 1


정리하면, 4편의 핵심은 업데이트 후 재부팅 필요 여부를 감지하고, 필요한 경우에만 자동 재부팅을 수행하는 것이다.
Debian/Ubuntu 계열은 needrestart와 /run/reboot-required를 함께 사용하고, RHEL 계열은 dnf needs-restarting --reboothint를 사용한다.
다만 RHEL 계열에서 어떤 패키지가 실제로 업그레이드·설치·제거되는지 자세히 미리 확인하는 방법은 다음 5편에서 이어서 다룬다.