infra
Platform

모듈 맵

[Linux] Ansible 입문 — 인프라 자동화

0 / 20 완료

펼치기

리눅스 서버 운영 · 19 / 20

[Linux] Ansible 입문 — 인프라 자동화

Inventory와 Playbook으로 서버 설정을 코드로 관리하고, 수십 대 서버에 반복 작업을 자동화합니다

🚨INCIDENT ALERT
HIGH

새벽 2시, 보안팀에서 연락이 왔습니다. 전체 30대 서버에 즉시 취약점 패치를 적용해야 한다는 공지입니다. 터미널 탭을 30개 열고 동일한 명령어를 반복 입력하다 보면 실수가 생기고, 한 대라도 빠트리면 감사에서 걸립니다. 더 나쁜 건, 다음 달에 또 같은 일이 생긴다는 것입니다. Ansible을 알면 이런 상황에서 명령 한 줄로 끝납니다.

Ansible 입문 — 인프라 자동화

서버가 3대를 넘는 순간, 같은 명령어를 여러 터미널에서 반복하는 자신을 발견하게 됩니다. Ansible은 그 반복을 코드로 바꿉니다. "어떤 상태여야 하는가"를 YAML로 정의하면, Ansible이 SSH를 통해 모든 서버를 동시에 그 상태로 만들어줍니다.


이번 챕터에서 배울 것

shell 스크립트와 Ansible의 차이는 '명령'과 '상태 선언'의 차이입니다. 스크립트는 "이 명령을 실행해라"고 하지만, Ansible은 "이 서버는 이런 상태여야 한다"고 선언합니다.

  • 1Ansible이 에이전트리스로 SSH만으로 동작하는 원리를 설명할 수 있다
  • 2Inventory로 관리할 서버를 그룹으로 정의할 수 있다
  • 3Ad-hoc 명령으로 Playbook 없이 단발 작업을 실행할 수 있다
  • 4Playbook을 작성해 서버 상태를 코드로 선언할 수 있다
  • 5변수와 핸들러로 조건부 재시작 패턴을 구성할 수 있다
  • 6멱등성을 이해하고 같은 Playbook을 안전하게 반복 실행할 수 있다
실습 환경 준비

💡개념

에이전트리스 — Ansible이 별도 소프트웨어 없이 동작하는 방식

동작 원리

컨트롤 노드에서 인벤토리·플레이북·모듈로 SSH를 통해 대상 서버를 제어하는 구조확대

Ansible은 실행 시 Python 모듈 파일을 /tmp에 임시로 복사하고, 실행 후 삭제합니다. 대상 서버에는 SSH 접근 + Python만 있으면 됩니다.

로컬 터미널
# Ansible 설치 (제어 노드에만)
sudo apt install ansible -y

# 버전 확인
ansible --version

Chef, Puppet과의 차이: Chef/Puppet은 대상 서버에 에이전트 데몬을 상시 설치해야 합니다. Ansible은 SSH 세션만 사용하므로 온보딩 비용이 0에 가깝습니다.


💡개념

Inventory — 관리할 서버를 그룹으로 정의

INI
# /etc/ansible/hosts (또는 -i 옵션으로 파일 지정)

[webservers]
web01.example.com
web02.example.com
192.168.1.10

[dbservers]
db01.example.com ansible_user=ubuntu ansible_port=2222

[production:children]
webservers
dbservers

[all:vars]
ansible_python_interpreter=/usr/bin/python3
로컬 터미널
# Inventory 확인
ansible-inventory --list -y

# 특정 그룹의 서버 핑 테스트
ansible webservers -m ping

# 모든 서버 핑
ansible all -m ping

출력 예시:

web01.example.com | SUCCESS => {
    "changed": false,
    "ping": "pong"
}
🔍실행 후 확인할 것
  • ansible all -m ping 결과에서 먼저 UNREACHABLE 서버가 있는지 확인하고, 그 다음 SUCCESS/FAILED 비율을 확인 — UNREACHABLE이 0이어야 다음 단계 진행 가능
  • SUCCESS 응답 비율 기준: 100% → 정상, 90% 이상 → 일부 서버 SSH 설정 문제, 50% 미만 → Inventory 설정 자체를 재검토
  • UNREACHABLE이 있고 ping은 되면 → SSH 키 인증 문제, ping 자체가 안 되면 → 네트워크/방화벽 문제를 의미

💡개념

Ad-hoc 명령 — Playbook 없이 단발 작업

로컬 터미널
# 문법: ansible <패턴> -m <모듈> -a "<인수>"

# 모든 서버 업타임 확인
ansible all -m command -a "uptime"

# webservers 그룹에 패키지 설치
ansible webservers -m apt -a "name=nginx state=present" --become

# 서비스 재시작
ansible webservers -m service -a "name=nginx state=restarted" --become

# 파일 복사
ansible dbservers -m copy -a "src=/tmp/my.cnf dest=/etc/mysql/my.cnf"

# 자유 형식 명령 (shell 모듈 — 파이프, 리다이렉션 가능)
ansible all -m shell -a "df -h | grep /dev/sda1"

--become은 sudo 권한으로 실행하는 옵션입니다.


첫 Playbook 작성

Playbook은 YAML 형식으로 "어떤 서버에 어떤 상태를 적용할지"를 정의합니다.

YAML
# webserver-setup.yml
---
- name: 웹 서버 초기 설정
  hosts: webservers
  become: true

  vars:
    nginx_port: 80

  tasks:
    - name: nginx 설치
      apt:
        name: nginx
        state: present
        update_cache: true

    - name: nginx 서비스 활성화 및 시작
      service:
        name: nginx
        state: started
        enabled: true

    - name: 방화벽에 HTTP 허용
      ufw:
        rule: allow
        port: "{{ nginx_port }}"
        proto: tcp

    - name: 커스텀 index.html 배포
      copy:
        content: "<h1>Deployed by Ansible</h1>"
        dest: /var/www/html/index.html
        owner: www-data
        mode: '0644'
      notify: reload nginx

  handlers:
    - name: reload nginx
      service:
        name: nginx
        state: reloaded
로컬 터미널
# 드라이런 (실제 변경 없이 미리 확인)
ansible-playbook webserver-setup.yml --check

# 실행
ansible-playbook webserver-setup.yml

# 특정 태스크만 실행 (태그 사용)
ansible-playbook webserver-setup.yml --tags "install"

# 실행 결과 예시
PLAY [웹 서버 초기 설정] **************************

TASK [nginx 설치] ********************************
changed: [web01.example.com]
ok: [web02.example.com]     ← 이미 설치됨, 변경 없음

PLAY RECAP **************************************
web01 : ok=4  changed=3  unreachable=0  failed=0
web02 : ok=4  changed=1  unreachable=0  failed=0

핵심: changed는 실제로 변경된 태스크, ok는 이미 원하는 상태라 변경 불필요. 이것이 멱등성입니다.

1회차·2회차 실행으로 멱등성을 설명하고 전용 모듈과 shell 모듈을 비교한 다이어그램확대

🔍실행 후 확인할 것
  • PLAY RECAP에서 먼저 failed 수를 확인하고, 그 다음 changed/ok 비율을 확인 — failed=0, unreachable=0이어야 성공
  • 멱등성 수치 기준: 첫 실행 changed≥1 → 정상, 재실행 시 changed=0 → 멱등성 확인, 재실행에도 changed>0 → 모듈 선택 오류(shell 남용 의심)
  • failed>0이고 changed도 높으면 → 중간 태스크 실패로 이후 의존 태스크도 연쇄 실패하는 상황을 의미

💡개념

핸들러(Handler) — 변경이 있을 때만 재시작

Handler는 notify된 태스크가 changed를 반환할 때만 실행됩니다.

YAML
tasks:
  - name: nginx 설정 파일 배포
    template:
      src: nginx.conf.j2
      dest: /etc/nginx/nginx.conf
    notify: reload nginx    # ← 변경 시에만 handler 호출

handlers:
  - name: reload nginx
    service:
      name: nginx
      state: reloaded

왜 중요한가: 설정 파일이 바뀌지 않았는데 매번 nginx를 reload하면 불필요한 서비스 중단이 발생합니다. Handler는 "변경이 있을 때만 재시작"을 자동으로 보장합니다.


실습 단계
1

Ansible 설치 및 버전 확인

sudo apt install ansible -y && ansible --version

예상 출력

ansible [core 2.x.x]
  python version = 3.x.x
2

로컬 호스트를 대상으로 ping 모듈 테스트

ansible localhost -m ping -c local

예상 출력

localhost | SUCCESS => {
    "changed": false,
    "ping": "pong"
}
3

Ad-hoc 명령으로 로컬 호스트 업타임 확인

ansible localhost -m command -a 'uptime' -c local

예상 출력

 10:00:00 up 1 day,  2:30,  1 user,  load average: 0.10, 0.15, 0.12
4

간단한 Playbook 작성 및 드라이런 실행

cat > /tmp/test-playbook.yml << 'EOF' --- - name: 테스트 플레이북 hosts: localhost connection: local tasks: - name: 파일 생성 copy: content: 'Ansible 테스트' dest: /tmp/ansible-test.txt EOF ansible-playbook /tmp/test-playbook.yml --check

예상 출력

PLAY [테스트 플레이북] ***
TASK [파일 생성] ***
changed: [localhost]
PLAY RECAP ***
localhost: ok=1 changed=1 unreachable=0 failed=0
5

Playbook 실제 실행 후 멱등성 확인

ansible-playbook /tmp/test-playbook.yml && ansible-playbook /tmp/test-playbook.yml

예상 출력

1회차: changed=1
2회차: changed=0  (이미 원하는 상태 — 멱등성 확인)

SSH 키 인증 실패

로컬 터미널
# 증상
web01 | UNREACHABLE! => {
    "msg": "Failed to connect to the host via ssh: Permission denied (publickey,gssapi-keyex,gssapi-with-mic)"
}

# 원인 1: SSH 키가 대상 서버에 등록되지 않음
ssh-copy-id -i ~/.ssh/id_rsa.pub user@web01

# 원인 2: ansible.cfg에 SSH 키 경로 지정 필요
cat ansible.cfg
[defaults]
private_key_file = ~/.ssh/ansible_key

# 원인 3: known_hosts 검사 비활성화 필요 (개발환경)
[defaults]
host_key_checking = False

# 연결 테스트
ansible web01 -m ping -vvv   # -v 옵션으로 상세 디버그

대상 서버에 Python이 없는 경우

로컬 터미널
# Ubuntu 22.04는 python3가 기본, python 심볼릭링크 없음
# Inventory에 명시적으로 python3 지정

[all:vars]
ansible_python_interpreter=/usr/bin/python3

# 또는 대상 서버에 python-is-python3 패키지 설치
ansible all -m raw -a "apt install -y python3" --become
# raw 모듈은 Python 없이도 SSH 명령을 직접 실행

💼
실무 맥락
현업 패턴

실무에서 Ansible이 쓰이는 순간

10대 서버에 보안 패치 동시 적용:

YAML
---
- name: 보안 패치 적용
  hosts: all
  become: true
  serial: 2    # 동시에 2대씩 처리 (롤링 업데이트)

  tasks:
    - name: apt 업데이트
      apt:
        upgrade: safe
        update_cache: true

    - name: 재부팅이 필요한지 확인
      stat:
        path: /var/run/reboot-required
      register: reboot_required

    - name: 필요 시 재부팅
      reboot:
        reboot_timeout: 300
      when: reboot_required.stat.exists

팀에서 Ansible을 쓰는 패턴:

Git 저장소 (roles/, playbooks/)
    │
    ├── CI/CD 파이프라인이 git push 시 자동 실행
    ├── --check로 스테이징 검증
    └── 승인 후 프로덕션 적용

신입이 자주 실수하는 것: shell 모듈 남용. apt install nginx를 shell 모듈로 실행하면 멱등성이 깨집니다. 반드시 전용 모듈(apt, yum, service 등)을 사용해야 합니다.

다음 모듈에서는 네트워크 트러블슈팅 심화 — TCP 상태 분석과 패킷 캡처로 애플리케이션 연결 문제를 진단하는 방법을 다룹니다.

지식 확인

퀴즈 — 4문제

Q1

새로운 서버 30대에 동일한 보안 패치와 설정을 적용해야 합니다. 팀원이 'Ansible은 각 서버에 에이전트를 설치해야 하니 30대에 먼저 에이전트 설치 작업이 필요하다'고 주장합니다. 이 주장이 맞는지 판단하세요.

Q2

Ansible Playbook으로 Nginx 설치 작업을 작성하고 이미 설치된 서버에 다시 실행했더니 'changed: 0, ok: 1'이 출력됐습니다. 팀원이 '중복 실행이면 에러가 나야 하는 거 아냐?'라고 합니다. 올바른 설명은?

Q3

처음 Ansible을 도입하는 팀에서 '인벤토리(Inventory), 모듈(Module), 플레이북(Playbook)이 각각 뭘 하는 건지 헷갈린다'는 질문을 받았습니다. 세 가지의 역할을 가장 정확하게 설명한 것은?

Q4

운영 중인 50대 서버에 새로운 Playbook을 처음 적용하기 전에 실제 변경이 어떻게 일어날지 미리 확인하고 싶습니다. 어떤 명령어와 옵션을 사용해야 하나요?

0 / 4 답변