새벽 2시, 보안팀에서 연락이 왔습니다. 전체 30대 서버에 즉시 취약점 패치를 적용해야 한다는 공지입니다. 터미널 탭을 30개 열고 동일한 명령어를 반복 입력하다 보면 실수가 생기고, 한 대라도 빠트리면 감사에서 걸립니다. 더 나쁜 건, 다음 달에 또 같은 일이 생긴다는 것입니다. Ansible을 알면 이런 상황에서 명령 한 줄로 끝납니다.
Ansible 입문 — 인프라 자동화
서버가 3대를 넘는 순간, 같은 명령어를 여러 터미널에서 반복하는 자신을 발견하게 됩니다. Ansible은 그 반복을 코드로 바꿉니다. "어떤 상태여야 하는가"를 YAML로 정의하면, Ansible이 SSH를 통해 모든 서버를 동시에 그 상태로 만들어줍니다.
shell 스크립트와 Ansible의 차이는 '명령'과 '상태 선언'의 차이입니다. 스크립트는 "이 명령을 실행해라"고 하지만, Ansible은 "이 서버는 이런 상태여야 한다"고 선언합니다.
- 1Ansible이 에이전트리스로 SSH만으로 동작하는 원리를 설명할 수 있다
- 2Inventory로 관리할 서버를 그룹으로 정의할 수 있다
- 3Ad-hoc 명령으로 Playbook 없이 단발 작업을 실행할 수 있다
- 4Playbook을 작성해 서버 상태를 코드로 선언할 수 있다
- 5변수와 핸들러로 조건부 재시작 패턴을 구성할 수 있다
- 6멱등성을 이해하고 같은 Playbook을 안전하게 반복 실행할 수 있다
에이전트리스 — Ansible이 별도 소프트웨어 없이 동작하는 방식
동작 원리
확대
Ansible은 실행 시 Python 모듈 파일을 /tmp에 임시로 복사하고, 실행 후 삭제합니다. 대상 서버에는 SSH 접근 + Python만 있으면 됩니다.
# Ansible 설치 (제어 노드에만)
sudo apt install ansible -y
# 버전 확인
ansible --version
Chef, Puppet과의 차이: Chef/Puppet은 대상 서버에 에이전트 데몬을 상시 설치해야 합니다. Ansible은 SSH 세션만 사용하므로 온보딩 비용이 0에 가깝습니다.
Inventory — 관리할 서버를 그룹으로 정의
# /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 형식으로 "어떤 서버에 어떤 상태를 적용할지"를 정의합니다.
# 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는 이미 원하는 상태라 변경 불필요. 이것이 멱등성입니다.
확대
- PLAY RECAP에서 먼저 failed 수를 확인하고, 그 다음 changed/ok 비율을 확인 — failed=0, unreachable=0이어야 성공
- 멱등성 수치 기준: 첫 실행 changed≥1 → 정상, 재실행 시 changed=0 → 멱등성 확인, 재실행에도 changed>0 → 모듈 선택 오류(shell 남용 의심)
- failed>0이고 changed도 높으면 → 중간 태스크 실패로 이후 의존 태스크도 연쇄 실패하는 상황을 의미
핸들러(Handler) — 변경이 있을 때만 재시작
Handler는 notify된 태스크가 changed를 반환할 때만 실행됩니다.
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는 "변경이 있을 때만 재시작"을 자동으로 보장합니다.
Ansible 설치 및 버전 확인
sudo apt install ansible -y && ansible --version예상 출력
ansible [core 2.x.x] python version = 3.x.x
로컬 호스트를 대상으로 ping 모듈 테스트
ansible localhost -m ping -c local예상 출력
localhost | SUCCESS => {
"changed": false,
"ping": "pong"
}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
간단한 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
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대 서버에 보안 패치 동시 적용:
---
- 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 상태 분석과 패킷 캡처로 애플리케이션 연결 문제를 진단하는 방법을 다룹니다.