콘텐츠로 건너뛰기

Ansible을 이용하여 Linux 업데이트 자동화 (5편) - RHEL 계열 패키지 변경 미리보기

  • 기준

Ansible을 이용하여 Linux 업데이트 자동화 (4편)에서 이어지는 글이다.

4편에서는 업데이트 후 재부팅 필요 여부를 감지하고, 필요한 경우 자동 재부팅을 수행하는 방법을 다루었다.
다만 실제 운영에서는 재부팅 여부만큼이나, 어떤 패키지가 업그레이드·설치·제거될 예정인지 사전에 확인하는 것도 매우 중요하다.
특히 Debian/Ubuntu 계열은 apt 모듈의 출력만으로도 변경 예정 패키지를 비교적 자세히 확인할 수 있지만, RHEL 계열은 그렇지 않은 경우가 많다.
5편에서는 RHEL 계열을 중심으로, 실제 적용 전에 변경 예정 패키지를 미리 확인하는 방법을 다룬다.

1) RHEL 계열은 변경 예정 패키지 목록이 자세히 보이지 않는다.

Debian 13
AlmaLinux 10

ansible-playbook을 실행하면 --check --diff에서 Debian/Ubuntu 계열은 어떤 패키지가 업그레이드되고, 새로 설치되는지 자세히 나오는데, RHEL 계열은 changed: [almalinux10] 정도만 보인다.

Ansible 공식 문서aptdnf 모듈은 둘 다 check mode와 diff mode를 지원하지만, 문서는 "details를 반환할 수 있다"고만 설명하지 두 모듈이 동일한 수준의 사람 친화적 패키지 목록을 항상 똑같이 보여준다고 보장하지는 않는다.

그래서 RHEL 계열에서 Debian처럼 "무슨 패키지가 설치/업그레이드/제거될지"를 보고 싶다면, dnf 모듈 출력에만 기대기보다 미리보기용 DNF 명령을 별도 태스크로 추가하는 방식이 가장 깔끔하다.

Red Hat 문서dnf check-update로 업데이트 가능 패키지를 확인할 수 있다고 안내하고, DNF 공식 문서는 --assumeno모든 확인 질문에 자동으로 No를 답변한다고 설명한다.

2) RHEL 계열도 업그레이드·설치·제거 예정 패키지를 확인하는 방법

RHEL 계열에서는 다음 두 명령이 미리보기 용도로 유용하다.

  • dnf upgrade --assumeno
  • dnf autoremove --assumeno

Debian 계열은 apt 모듈만으로도 어느 정도 친절한 미리보기가 가능하지만, RHEL 계열은 업그레이드·설치·제거 예정 패키지를 확인하려면 별도의 preview 태스크를 추가해 주어야 한다.

3) RHEL Playbook에 코드 추가

- 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: Preview RHEL upgrade transaction
      ansible.builtin.command: dnf upgrade --assumeno
      register: rhel_upgrade_preview
      changed_when: false
      failed_when: false
      check_mode: false
      when: ansible_check_mode

    - name: Show RHEL upgrade preview
      ansible.builtin.debug:
        msg: "{{ rhel_upgrade_preview.stdout_lines | default([]) }}"
      when: ansible_check_mode

    - name: Preview RHEL autoremove transaction
      ansible.builtin.command: dnf autoremove --assumeno
      register: rhel_autoremove_preview
      changed_when: false
      failed_when: false
      check_mode: false
      when: ansible_check_mode

    - name: Show RHEL autoremove preview
      ansible.builtin.debug:
        msg: "{{ rhel_autoremove_preview.stdout_lines | default([]) }}"
      when: ansible_check_mode
# 새로 추가된 코드 마지막 부분

    - 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

이제, 위 코드를 차근차근 뜯어보자.

    - name: Preview RHEL upgrade transaction
      ansible.builtin.command: dnf upgrade --assumeno
      register: rhel_upgrade_preview
      changed_when: false
      failed_when: false
      check_mode: false
      when: ansible_check_mode

    - name: Show RHEL upgrade preview
      ansible.builtin.debug:
        msg: "{{ rhel_upgrade_preview.stdout_lines | default([]) }}"
      when: ansible_check_mode

    - name: Preview RHEL autoremove transaction
      ansible.builtin.command: dnf autoremove --assumeno
      register: rhel_autoremove_preview
      changed_when: false
      failed_when: false
      check_mode: false
      when: ansible_check_mode

    - name: Show RHEL autoremove preview
      ansible.builtin.debug:
        msg: "{{ rhel_autoremove_preview.stdout_lines | default([]) }}"
      when: ansible_check_mode
  • Preview RHEL upgrade transaction 이 태스크는 dnf upgrade --assumeno를 실행해, 실제 업데이트를 하지 않고 업그레이드 또는 신규 설치 예정 패키지를 먼저 확인하는 단계다. when: ansible_check_mode를 걸어 두었기 때문에, 이 미리보기는 --check --diff 실행 시에만 동작한다. check mode에서는 일부 명령형 태스크가 자동으로 건너뛰어질 수 있으므로, check_mode: false를 따로 지정해 실제 조회 명령은 실행되도록 한 것이다. Ansible 문서도 check mode에서 특정 태스크를 강제로 실행하거나 건너뛸 수 있다고 설명한다.
  • Preview RHEL autoremove transaction 이 태스크는 dnf autoremove --assumeno를 사용해, 자동 제거 대상 패키지를 실제 삭제 없이 미리 확인하는 단계다. Debian 계열의 autoremove와 마찬가지로, RHEL 계열도 불필요해진 의존성 패키지를 정리할 수 있다. DNF 문서list --autoremoveautoremove 계열 동작을 별도로 설명하고 있다.
  • Show RHEL upgrade preview / Show RHEL autoremove preview 이 두 태스크는 단순히 실행 결과를 화면에 보여주는 역할이다. 즉, 5편의 목적은 "자동 재부팅"만 다루는 것이 아니라, RHEL 계열도 Debian 계열처럼 사람이 읽기 좋은 방식으로 변경 예정 내용을 미리 확인할 수 있게 만드는 것이라고 이해하면 된다.

4) 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: Preview RHEL upgrade transaction
      ansible.builtin.command: dnf upgrade --assumeno
      register: rhel_upgrade_preview
      changed_when: false
      failed_when: false
      check_mode: false
      when: ansible_check_mode

    - name: Show RHEL upgrade preview
      ansible.builtin.debug:
        msg: "{{ rhel_upgrade_preview.stdout_lines | default([]) }}"
      when: ansible_check_mode

    - name: Preview RHEL autoremove transaction
      ansible.builtin.command: dnf autoremove --assumeno
      register: rhel_autoremove_preview
      changed_when: false
      failed_when: false
      check_mode: false
      when: ansible_check_mode

    - name: Show RHEL autoremove preview
      ansible.builtin.debug:
        msg: "{{ rhel_autoremove_preview.stdout_lines | default([]) }}"
      when: ansible_check_mode

    - 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

5) ansible-playbook playbooks/update_all.yml --check --diff 실행

이제 RHEL 계열도 실제 적용 전에 업그레이드·설치·제거 예정 패키지를 미리 확인할 수 있게 되었다.

Linux 시스템 업그레이드 후 어떤 패키지가 업그레이드, 설치, 제거되었는지 로그로 남기는 방법은 다음 6편에서 다룬다.

Join the conversation

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다