콘텐츠로 건너뛰기

Ansible을 이용하여 Linux 업데이트 자동화 (2편)

  • 기준

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

2편에서는 실습에 사용될 Directory 생성, ansible.cfg/yml 작성을 다룬다.

1) 실습에 사용할 Directory 생성

# 실습에 사용될 Directory 구조
~/ansible-lab/
├─ ansible.cfg
├─ inventory/
│  ├─ hosts.yml
│  └─ group_vars/
│     └─ all.yml
└─ playbooks/
   └─ update_all.yml
# 실습에 사용될 Directory 생성
mkdir -p ~/ansible-lab/inventory
mkdir -p ~/ansible-lab/inventory/group_vars
mkdir -p ~/ansible-lab/playbooks
cd ~/ansible-lab

2) ~/ansible-lab/ansible.cfg 작성

# ~/ansible-lab/ansible.cfg

[defaults]
inventory = ./inventory/hosts.yml
host_key_checking = True
retry_files_enabled = False
interpreter_python = auto_silent
timeout = 30
forks = 10
gathering = smart

[privilege_escalation]
become = False
become_method = sudo
become_ask_pass = False

[ssh_connection]
pipelining = True
ssh_args = -o ControlMaster=auto -o ControlPersist=60s

ansible.cfg는 Ansible의 기본 동작 방식을 정하는 설정 파일이다.
어떤 inventory 파일을 기본으로 읽을지, SSH 연결을 얼마나 엄격하게 검증할지, 몇 대의 서버를 동시에 처리할지, 그리고 sudo 권한 상승을 어떻게 다룰지를 한곳에서 정리하는 역할을 한다.

  • inventory = ./inventory/hosts.yml 기본 inventory 파일 위치를 지정한다. 그래서 매번 -i inventory/hosts.yml 옵션을 붙이지 않아도 된다.
  • host_key_checking = True SSH 접속 시 대상 서버의 호스트 키를 검사한다. 보안상 더 안전한 설정이며, 처음 접속하는 서버는 키를 한 번 신뢰 목록에 등록해야 한다.
  • retry_files_enabled = False 플레이북이 실패했을 때 .retry 파일을 만들지 않도록 한다. 작업 디렉터리가 불필요한 파일로 지저분해지는 것을 막는 용도이다.
  • interpreter_python = auto_silent 원격 서버에서 사용할 Python 인터프리터를 자동으로 찾되, 자동 탐지 관련 경고는 조용히 숨긴다. 배포판이 섞여 있는 환경에서 편하게 쓰기 좋은 옵션이다.
  • timeout = 30 SSH 등 연결 플러그인이 응답을 기다리는 기본 시간을 30초로 잡는다. 기본값은 10초인데, 네트워크가 느리거나 VM 상태가 들쭉날쭉한 환경에서는 더 여유 있게 잡는 편이 안정적이다.
  • forks = 10 Ansible이 동시에 작업할 수 있는 대상 호스트 수를 정한다. 기본값은 5이고, 여기서는 최대 10대까지 병렬 처리하도록 넓혀 둔 것이다.
  • gathering = smart 원격 서버 정보(facts)를 수집하는 기본 정책이다. smart는 불필요한 fact 수집을 줄이는 쪽으로 동작하며, 캐시 플러그인과 함께 사용할 수 있다.
  • become = False 기본적으로는 권한 상승(sudo)을 자동 적용하지 않는다. 즉, 꼭 필요한 플레이나 태스크에서만 become: true를 명시해서 관리자 권한을 쓰게 하는 방식이다.
  • become_method = sudo 권한 상승 방식으로 sudo를 사용하겠다는 뜻이다.
  • become_ask_pass = False 기본 설정만 놓고 보면 권한 상승 비밀번호를 자동으로 묻지 않게 한다. 다만 실행 시 -K 옵션을 주면 그때는 sudo 비밀번호를 입력받을 수 있다.
  • pipelining = True Ansible 모듈을 실행할 때 불필요한 파일 전송을 줄여 SSH 연결 작업 수를 감소시킨다. 성능 향상에 도움이 되지만, become과 함께 쓸 때는 대상 서버의 sudo 설정과 충돌할 수 있어 환경 확인이 필요하다.
  • ssh_args = -o ControlMaster=auto -o ControlPersist=60s SSH 연결을 재사용하도록 해, 태스크마다 매번 새 연결을 만드는 오버헤드를 줄인다. 참고로 Ansible의 기본 ssh_args에는 여기에 -C 압축 옵션까지 포함되어 있으므로, 이 줄은 특별한 튜닝이라기보다 연결 재사용 동작을 명시적으로 적어 둔 설정에 가깝다.

3) ~/ansible-lab/inventory/hosts.yml 작성

# ~/ansible-lab/inventory/hosts.yml

all:
  children:
    debian_family:
      hosts:
        ubuntu2404:
          ansible_host: 192.168.5.221

        debian13:
          ansible_host: 192.168.5.222

    rhel_family:
      hosts:
        almalinux10:
          ansible_host: 192.168.5.223

hosts.yml은 Ansible이 접속하고 작업할 대상 서버 목록을 정의하는 inventory 파일이다. YAML 형식 inventory는 보통 최상위에 all 그룹을 두고, 그 아래에 하위 그룹(children)과 실제 호스트(hosts)를 배치하는 구조를 사용한다. all 그룹은 이름 그대로 inventory에 포함된 모든 호스트를 대표하는 기본 그룹이다.

  • all: Ansible의 기본 최상위 그룹이다.
    inventory에 등록된 모든 서버는 자동으로 all 그룹에 포함된다. 따라서 공통 변수나 전체 서버 대상 작업의 기준점 역할을 한다.
  • children: all 아래에 들어가는 하위 그룹 목록이다. 즉, 서버를 운영 목적이나 OS 계열별로 나누어 관리할 수 있게 해준다. Ansible 문서에서 children은 “child groups”를 뜻하며, 각 그룹은 다시 hosts, vars, children을 가질 수 있다.
  • debian_family: Ubuntu와 Debian처럼 APT 계열 패키지 관리자를 쓰는 서버들을 묶어 놓은 그룹이다.
    이렇게 그룹을 나누면 playbook에서 hosts: debian_family처럼 지정하여 Debian 계열 서버에만 apt 작업을 실행할 수 있다. Ansible은 inventory의 호스트와 그룹 이름을 패턴으로 지정해 작업 대상을 선택할 수 있다.
  • hosts: 해당 그룹에 속하는 실제 서버 목록을 적는 부분이다. 여기 들어가는 ubuntu2404, debian13, almalinux10은 Ansible 내부에서 사용하는 호스트 별칭(inventory hostname)이다. 실제 접속 주소와는 분리해서 보기 쉽고 관리하기 쉽게 이름을 붙인 것이다.
  • ubuntu2404:, debian13:, almalinux10: 각 서버의 관리용 이름이다. 예를 들어 ubuntu2404는 "Ubuntu 24.04 서버"를 뜻하는 식별자이고, playbook 결과나 ansible-inventory --graph 출력에서도 이 이름으로 표시된다. 이 이름 자체가 접속 주소는 아니며, 실제 IP는 아래 ansible_host로 연결된다.
  • ansible_host: 192.168.5.xxx Ansible이 실제로 SSH 접속할 실제 IP 주소 또는 FQDN을 지정하는 변수다.
    즉, ubuntu2404라는 이름으로 관리하지만, 실제 연결은 192.168.5.221로 수행한다는 뜻이다. 공식 문서의 YAML inventory 예시도 호스트 이름과 실제 접속 주소를 ansible_host로 분리해서 사용한다.
  • rhel_family: AlmaLinux처럼 RHEL 계열 패키지 관리자(DNF)를 쓰는 서버들을 묶은 그룹이다.
    이렇게 그룹을 분리하면 Debian 계열과 RHEL 계열에 서로 다른 모듈(apt, dnf)을 적용하기 쉬워진다. Ansible inventory의 핵심 장점 중 하나가 바로 이런 그룹 기반 관리이다.

4) ~/ansible-lab/inventory/group_vars/all.yml 작성

# ~/ansible-lab/inventory/group_vars/all.yml

ansible_connection: ssh
ansible_python_interpreter: /usr/bin/python3
ansible_become_method: sudo
ansible_user: ansible

all.yml은 Ansible이 모든 서버에 공통으로 사용할 기본 변수를 정의하는 곳이다.
현재 예시에서는 SSH로 접속한다는 점, 원격 서버에서 Python 3를 사용한다는 점, 권한 상승 방식으로 sudo를 사용한다는 점을 공통값으로 지정하고 있다. 이렇게 해두면 inventory의 각 호스트마다 같은 설정을 반복해서 쓰지 않아도 된다.

  • ansible_connection: ssh Ansible이 원격 서버와 통신할 때 SSH 연결 방식을 사용하겠다는 뜻이다. 별도로 다른 connection plugin을 지정하지 않는 한 SSH 기반 관리 환경을 명확하게 보여주는 설정이다.
  • ansible_python_interpreter: /usr/bin/python3 원격 서버에서 Ansible 모듈을 실행할 때 사용할 Python 인터프리터 경로를 지정한다. 배포판마다 Python 경로가 조금씩 다를 수 있는데, 이 값을 명시해 두면 /usr/bin/python3를 기준으로 안정적으로 동작하게 할 수 있다. 특히 Ubuntu, Debian, AlmaLinux처럼 여러 배포판이 섞인 환경에서 Python 관련 혼선을 줄이는 데 도움이 된다.
  • ansible_become_method: sudo 권한 상승이 필요할 때 sudo 방식으로 관리자 권한을 얻겠다는 뜻이다.
    예를 들어 패키지 업데이트처럼 일반 사용자 권한으로는 수행할 수 없는 작업을 실행할 때, playbook에서 become: true를 사용하면 이 설정에 따라 sudo를 통해 권한을 올리게 된다.
  • ansible_user: ansible Ansible이 원격 서버에 SSH로 접속할 때 사용할 기본 계정 이름을 지정한다.

5) ~/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: Update RHEL family systems
  hosts: rhel_family
  serial: 1
  become: true
  gather_facts: true

  tasks:
    - 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

이 playbook은 서버를 OS 계열별로 나누어 순차적으로 업데이트하는 예시이다. Debian 계열 서버에는 ansible.builtin.apt 모듈을 사용하고, RHEL 계열 서버에는 ansible.builtin.dnf 모듈을 사용한다. 이렇게 그룹을 나누면 서로 다른 패키지 관리자를 쓰는 서버들을 한 파일에서 일관되게 관리할 수 있다.

  • --- YAML 문서의 시작을 나타내는 구분선이다. Ansible playbook은 YAML 형식으로 작성되므로, 보통 파일 맨 위에 이 표시를 둔다.
  • - name: Update Debian family systems 첫 번째 play의 이름이다. 이 play가 Debian 계열 서버를 업데이트하는 역할이라는 것을 사람이 읽기 쉽게 보여준다. name은 실행 결과 화면에도 그대로 표시되어, 어떤 작업이 진행 중인지 파악하기 쉽게 해 준다.
  • hosts: debian_family 이 play가 적용될 대상을 지정한다. 여기서는 inventory에 정의한 debian_family 그룹의 서버들만 대상으로 삼는다. 즉, Ubuntu 24.04와 Debian 13 같은 APT 계열 서버에만 아래 작업이 실행된다.
  • serial: 1 한 번에 한 대씩만 작업하겠다는 뜻이다. Ansible 문서에서 serial은 동시에 처리할 호스트 수를 정하는 키워드이며, serial: 1이면 한 서버의 작업이 끝난 뒤 다음 서버로 넘어가는 롤링 방식이 된다. 그래서 여러 서버를 한꺼번에 건드리지 않고, 보다 보수적으로 업데이트할 수 있다.
  • become: true 이 play의 모든 작업을 권한 상승과 함께 실행하겠다는 뜻이다. 패키지 업데이트는 일반 사용자 권한으로 수행할 수 없는 경우가 많기 때문에, 여기서는 sudo를 이용해 관리자 권한으로 작업하게 된다. 실제 어떤 방식으로 권한을 올릴지는 ansible_become_method 같은 설정에 따라 결정된다.
  • gather_facts: true 작업 시작 전에 대상 서버의 기본 시스템 정보(facts)를 수집하겠다는 뜻이다. Ansible 문서는 gather_facts가 자동으로 useful variables를 모아 playbook에서 사용할 수 있게 해 준다고 설명한다. 실행 화면에서 보이는 TASK [Gathering Facts]가 바로 이 단계다.
  • tasks: 이 play에서 실제로 수행할 작업 목록을 시작하는 부분이다. Ansible은 tasks 아래에 순서대로 적힌 작업을 차례대로 실행한다.
  • - name: Refresh apt cache APT 저장소 메타데이터를 최신 상태로 갱신하는 작업이다. 쉽게 말해 apt update에 해당하는 성격의 단계다. 패키지 목록이 오래된 상태면 최신 업데이트를 정확히 판단하기 어렵기 때문에, 보통 업그레이드 전에 캐시를 먼저 갱신한다.
  • ansible.builtin.apt: Debian 계열 패키지 관리를 위해 Ansible이 제공하는 공식 apt 모듈을 사용하겠다는 뜻이다. shell 명령으로 직접 apt를 호출하는 대신 모듈을 쓰면, 현재 상태를 확인한 뒤 필요한 변경만 적용하는 방식으로 더 일관되게 동작한다.
  • update_cache: true APT 패키지 캐시를 업데이트하겠다는 옵션이다. 패키지 저장소의 최신 목록을 받아오도록 지시한다.
  • cache_valid_time: 3600 APT 캐시가 3600초, 즉 1시간보다 오래되었을 때만 다시 갱신하겠다는 뜻이다. Ansible 공식 문서도 이 값이 초 단위이며, 이 시간이 지나지 않았으면 불필요한 캐시 갱신을 줄일 수 있다고 설명한다.
  • - name: Upgrade installed packages to latest version 설치된 패키지들을 최신 버전으로 업그레이드하는 작업이다. 이 단계가 실제 시스템 업데이트의 핵심이다.
  • name: "*" 모든 패키지를 대상으로 하겠다는 뜻이다. 특정 패키지 하나가 아니라, 현재 설치된 패키지 전체를 갱신 대상으로 본다. Ansible apt 모듈 예시에서도 전체 패키지를 최신 상태로 올리는 방식으로 사용된다.
  • state: latest 대상 패키지를 가능한 최신 버전 상태로 맞추라는 뜻이다. 즉, 이미 최신이면 그대로 두고, 더 새 버전이 있으면 업그레이드한다. 이런 방식은 Ansible의 선언형 모델과 잘 맞는다.
  • - name: Remove no longer required packages 업데이트 후 더 이상 필요하지 않은 의존성 패키지를 정리하는 작업이다. 패키지 업그레이드가 끝난 뒤 시스템을 좀 더 깔끔하게 유지하는 데 도움이 된다.
  • autoremove: true 사용되지 않는 의존성 패키지를 자동 제거하겠다는 뜻이다. apt 모듈과 dnf 모듈 모두 이런 정리 옵션을 제공한다.
  • - name: Update RHEL family systems 두 번째 play의 이름이다. AlmaLinux처럼 RHEL 계열 서버를 업데이트하는 구간이라는 뜻이다. 첫 번째 play와 구조는 비슷하지만, 패키지 관리자만 dnf로 바뀐다.
  • hosts: rhel_family inventory의 rhel_family 그룹을 대상으로 삼는다. 즉, AlmaLinux 같은 DNF 계열 서버들만 이 아래 작업을 수행한다.
  • ansible.builtin.dnf: RHEL 계열 서버의 패키지를 관리하는 공식 dnf 모듈이다. 최신 Ansible 문서에서는 yum이 사실상 dnf로 연결되며, RHEL 계열의 일반적인 패키지 업데이트 작업은 dnf 모듈을 사용하면 된다.
  • update_cache: true DNF 저장소 메타데이터를 새로 고치겠다는 뜻이다. Debian 계열의 apt update와 비슷한 준비 단계라고 보면 된다.
  • update_only: true 이미 설치된 패키지만 업데이트 대상으로 삼겠다는 의미다. 즉, 새로운 패키지를 추가로 설치하는 것이 아니라, 현재 시스템에 있는 패키지들의 버전만 최신으로 맞춘다.
  • state: latest 설치된 패키지를 최신 버전 상태로 맞춘다. dnf 모듈에서도 state: latest는 업데이트/업그레이드 목적에 사용된다.

3편에서는 인벤토리 확인, 접속 테스트, 예행연습, 실제 적용을 다룬다.

Join the conversation

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