Markdown Preview
Ansible로 노드 자동화를 완벽하게 마스터하는 방법: 설치부터 첫 Playbook 실행까지
Frontmatter
Ansible은 에이전트리스 IT 자동화 도구로, SSH와 Python을 기반으로 하여 서버 설정 및 애플리케이션 배포를 관리한다. Ubuntu에서 Ansible을 설치하는 방법, Inventory 파일 구성, Ad-hoc 명령어 사용법, 그리고 첫 번째 Playbook 작성 및 실행 방법을 다룬다. 멱등성을 강조하며, SSH 키 설정과 보안 주의사항도 포함되어 있다.
노드 확장을 할 때 너무나도 기본 세팅이 하기 귀찮다. 네트워크 스펙을 NIC를 자동 감지하여 세팅하지 못하더라도 OVS만 연결하면 되기에 일단 Ansible 구성을 해보려고 한다.
이번 글에서는 Ansible을 설치하고, 기본적인 사용법을 익혀보는 과정을 정리한다. Inventory 구성부터 Ad-hoc 명령어, 그리고 첫 번째 Playbook까지 다뤄본다.
이 글에서 다루는 내용
- Ansible이란 무엇인가, 왜 쓰는가
- Ubuntu에서 Ansible 설치 (apt)
- Inventory 파일 구성
- Ad-hoc 명령어로 간단한 작업 실행
- 첫 번째 Playbook 작성 및 실행
- 실전 팁과 주의사항
🤔 Ansible이란?
Ansible은 에이전트리스(agentless) IT 자동화 도구다. 서버 설정, 애플리케이션 배포, 오케스트레이션 등을 코드로 관리할 수 있게 해준다.
핵심은 SSH와 Python으로 동작한다는 점이다. 그 말은 즉, 최신 Ubuntu 서버뿐만 아니라 대부분의 Unix 서버들은 기본 세팅만으로도 사용이 가능하다는 것이다. 대상 서버에 별도의 에이전트를 설치할 필요가 없으니 관리 포인트가 확 줄어든다.
Ansible의 핵심 구성 요소
- Control Node: Ansible이 설치된 관리 서버. 여기서 명령을 내린다
- Managed Node: Ansible이 관리하는 대상 서버. SSH만 열려있으면 된다
- Inventory: 관리 대상 서버 목록. IP나 호스트명을 그룹별로 정리한 파일
- Playbook: 자동화 작업을 정의한 YAML 파일. "어떤 서버에 무슨 작업을 할지" 선언한다
- Module: 실제 작업을 수행하는 단위.
apt,copy,service등 수백 개가 내장되어 있다
비교 항목 | Ansible | Chef / Puppet |
에이전트 필요 | ❌ 불필요 (SSH만 있으면 됨) | ✅ 필요 (각 노드에 설치) |
설정 언어 | YAML (사람이 읽기 쉬움) | Ruby 기반 DSL |
학습 곡선 | 낮음 🟢 | 높음 🔴 |
실행 방식 | Push (제어 노드에서 밀어넣기) | Pull (에이전트가 당겨가기) |
📦 Ansible 설치 (Ubuntu)
Python으로 동작하기에 그날 하루가 통째로 날아간다 😭
pip로 설치를 진행해도 괜찮지만, 어느 정도 서버를 사용해본 유저라면 시스템의 Python은 가능하면 건드리지 않는 게 좋다는 걸 알 것이다. pip로 시스템 Python에 이것저것 설치하다가 의존성 꼬이면... Ubuntu로 설치할 것이기에 apt 패키지를 이용해 진행하겠다.
1단계: 시스템 업데이트
sudo apt update이 명령어는 마치 시스템을 설정할 때 의식을 치루는 것과 같다. 마음을 경건하게 하는 과정과 같다 이말이다.
2단계: PPA 추가 및 설치
sudo apt install software-properties-common
sudo add-apt-repository --yes --update ppa:ansible/ansible
sudo apt install ansible왜 PPA를 추가하는가?
Ubuntu 기본 저장소에도 Ansible이 있지만, 버전이 꽤 오래된 경우가 많다. Ansible 공식 PPA를 추가하면 최신 안정 버전을 받을 수 있다. 특히 최신 모듈이나 기능이 필요한 경우 PPA 사용을 강력 권장한다.
ansible vs ansible-core 차이ansible-core: 최소한의 런타임 + 내장 모듈/플러그인만 포함. 가벼움
ansible: "batteries included" 패키지. 커뮤니티가 선별한 다양한 Collection이 포함되어 있어 바로 사용하기 편함
처음 시작한다면
ansible 패키지를 설치하는 게 편하다. 나중에 필요한 Collection만 골라서 쓰고 싶다면 ansible-core로 갈아타면 된다.3단계: 설치 확인
ansible --versionansible [core 2.17.x]
config file = /etc/ansible/ansible.cfg
configured module search path = ['/home/user/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /usr/lib/python3/dist-packages/ansible
...이렇게 버전 정보가 출력되면 설치 완료다. ✅
📋 Inventory 구성
Ansible은 어떤 서버에 작업을 할 것인지를 Inventory 파일에서 읽어온다. 설치하면 기본적으로
/etc/ansible/hosts 파일이 생성되지만, 프로젝트별로 따로 관리하는 게 훨씬 깔끔하다.기본 Inventory 파일 (INI 형식)
프로젝트 디렉토리를 만들고 Inventory 파일을 작성한다:
mkdir ~/ansible-lab && cd ~/ansible-lab# inventory.ini
[control]
controller ansible_host=172.30.0.11
[compute]
compute01 ansible_host=172.30.0.21
compute02 ansible_host=172.30.0.22
compute03 ansible_host=172.30.0.23
[storage]
storage01 ansible_host=172.30.2.101
storage02 ansible_host=172.30.2.102
storage03 ansible_host=172.30.2.103
[all:vars]
ansible_user=root
ansible_python_interpreter=/usr/bin/python3Inventory 파일 해설
[compute],[storage]: 호스트 그룹. 그룹 단위로 작업을 실행할 수 있다
ansible_host: 실제 접속할 IP 주소
[all:vars]: 모든 호스트에 적용되는 변수.ansible_user는 SSH 접속 유저,ansible_python_interpreter는 대상 서버의 Python 경로
YAML 형식 Inventory
YAML이 더 익숙하다면 이렇게도 작성할 수 있다:
# inventory.yml
all:
vars:
ansible_user: root
ansible_python_interpreter: /usr/bin/python3
children:
compute:
hosts:
compute01:
ansible_host: 172.30.0.21
compute02:
ansible_host: 172.30.0.22
compute03:
ansible_host: 172.30.0.23
storage:
hosts:
storage01:
ansible_host: 172.30.2.101
storage02:
ansible_host: 172.30.2.102
storage03:
ansible_host: 172.30.2.103두 형식 모두 동작은 동일하니 취향에 맞게 선택하면 된다.
Inventory 확인
작성한 Inventory가 제대로 파싱되는지 확인한다:
# 호스트 목록 확인
ansible-inventory -i inventory.ini --list
# 그래프 형태로 확인 (그룹 구조 파악에 유용)
ansible-inventory -i inventory.ini --graph@all:
|--@control:
| |--controller
|--@compute:
| |--compute01
| |--compute02
| |--compute03
|--@storage:
| |--storage01
| |--storage02
| |--storage03
|--@ungrouped:🔑 SSH 키 설정
Ansible은 SSH로 통신하기 때문에, 비밀번호 없이 접속할 수 있도록 SSH 키를 배포해야 한다. 이미 키가 있다면 이 단계는 건너뛰어도 좋다.
# SSH 키 생성 (이미 있으면 스킵)
ssh-keygen -t ed25519 -C "ansible-control"
# 대상 서버에 키 배포
ssh-copy-id root@172.30.0.21
ssh-copy-id root@172.30.0.22
ssh-copy-id root@172.30.0.23비밀번호 방식으로도 가능하지만...
--ask-pass (-k) 옵션을 사용하면 비밀번호 입력 방식으로도 접속할 수 있다. 하지만 서버가 10대, 20대 늘어나면 매번 비밀번호 입력하는 건 자동화의 의미가 없어진다. SSH 키 한 번 세팅해두면 편하다.연결 테스트
SSH 키 배포가 끝났으면 Ansible로 연결을 테스트한다:
ansible all -i inventory.ini -m pingcompute01 | SUCCESS => {
"changed": false,
"ping": "pong"
}
compute02 | SUCCESS => {
"changed": false,
"ping": "pong"
}
compute03 | SUCCESS => {
"changed": false,
"ping": "pong"
}모든 호스트에서
pong이 돌아오면 성공이다! 🎉ping 모듈은 ICMP ping이 아니다!Ansible의
ping 모듈은 실제로 SSH 접속 → Python 실행 → 응답 반환의 과정을 거친다. 즉, SSH 연결과 Python 환경이 모두 정상인지 한 번에 확인할 수 있는 모듈이다.⚡ Ad-hoc 명령어
Playbook까지 작성하기엔 좀 과한, 간단한 일회성 작업은 Ad-hoc 명령어로 처리할 수 있다.
기본 문법
ansible <대상> -i <inventory> -m <모듈> -a "<인자>"자주 쓰는 예시들
# 모든 compute 노드의 업타임 확인
ansible compute -i inventory.ini -m command -a "uptime"
# 모든 서버의 디스크 용량 확인
ansible all -i inventory.ini -m command -a "df -h"
# compute 그룹에 패키지 설치
ansible compute -i inventory.ini -m apt -a "name=htop state=present" --become
# 특정 서비스 재시작
ansible compute -i inventory.ini -m service -a "name=ssh state=restarted" --become
# 파일 복사
ansible all -i inventory.ini -m copy -a "src=./config.txt dest=/tmp/config.txt"자주 쓰는 모듈 정리
모듈 | 용도 | 예시 |
ping | 연결 테스트 | -m ping |
command | 쉘 명령어 실행 | -m command -a "uptime" |
shell | 쉘 명령어 실행 (파이프 등 지원) | -m shell -a "cat /etc/os-release | head -3" |
apt | 패키지 관리 (Debian/Ubuntu) | -m apt -a "name=vim state=present" |
copy | 파일 복사 | -m copy -a "src=./a.txt dest=/tmp/a.txt" |
service | 서비스 관리 | -m service -a "name=nginx state=started" |
setup | 시스템 정보 수집 (Facts) | -m setup |
command vs shell 차이command: 단순 명령어 실행. 파이프(|), 리다이렉션(>) 등을 사용할 수 없다
shell:/bin/sh를 통해 실행. 파이프, 리다이렉션, 환경변수 등 모두 사용 가능
보안상
command가 더 안전하므로, 파이프가 필요 없다면 command를 쓰자.📝 첫 번째 Playbook 작성
Ad-hoc으로 해결할 수 없는 복잡한 작업의 조합은 Playbook으로 정의한다. Playbook은 YAML 형식으로 작성하며, "어떤 호스트에, 어떤 순서로, 무슨 작업을 할지"를 선언한다.
기본 구조
# setup-base.yml
---
- name: 기본 서버 세팅
hosts: compute
become: yes
tasks:
- name: apt 캐시 업데이트
apt:
update_cache: yes
cache_valid_time: 3600
- name: 기본 패키지 설치
apt:
name:
- vim
- htop
- curl
- wget
- net-tools
- git
state: present
- name: 타임존 설정
timezone:
name: Asia/Seoul
- name: NTP 동기화 활성화
service:
name: systemd-timesyncd
state: started
enabled: yesPlaybook 구조 해설
name: 이 Play의 이름. 실행 시 로그에 표시된다
hosts: 작업 대상 그룹. Inventory의 그룹명을 지정
become: yes: root 권한으로 실행 (=sudo)
tasks: 실행할 작업 목록. 위에서 아래 순서대로 실행된다
- 각 task의
name은 실행 로그에 표시되므로, 알아보기 쉽게 작성하는 게 좋다
Playbook 실행
# 문법 검사 (실행 전 반드시!)
ansible-playbook -i inventory.ini setup-base.yml --syntax-check
# 드라이 런 (실제 변경 없이 시뮬레이션)
ansible-playbook -i inventory.ini setup-base.yml --check
# 실제 실행
ansible-playbook -i inventory.ini setup-base.ymlPLAY [기본 서버 세팅] ***********************************
TASK [Gathering Facts] *********************************
ok: [compute01]
ok: [compute02]
ok: [compute03]
TASK [apt 캐시 업데이트] *********************************
changed: [compute01]
changed: [compute02]
changed: [compute03]
TASK [기본 패키지 설치] **********************************
changed: [compute01]
changed: [compute02]
changed: [compute03]
TASK [타임존 설정] **************************************
changed: [compute01]
changed: [compute02]
changed: [compute03]
TASK [NTP 동기화 활성화] *********************************
ok: [compute01]
ok: [compute02]
ok: [compute03]
PLAY RECAP ********************************************
compute01 : ok=5 changed=3 unreachable=0 failed=0
compute02 : ok=5 changed=3 unreachable=0 failed=0
compute03 : ok=5 changed=3 unreachable=0 failed=0failed=0이면 모든 작업이 성공한 것이다! ✅--check 옵션은 꼭 사용하자!실제 실행 전에 그날은 일찍 퇴근 못 한다
--check(드라이 런)를 돌리면 어떤 변경이 일어날지 미리 확인할 수 있다. 프로덕션 서버에서 Playbook을 바로 실행했다가 예상치 못한 변경이 발생하면... 🚀 실전 예제: OVS 설치 Playbook
실제로 내가 노드 확장할 때 사용하려는 OVS(Open vSwitch) 설치 Playbook을 작성해보자.
# setup-ovs.yml
---
- name: OVS 설치 및 기본 브릿지 구성
hosts: compute
become: yes
vars:
ovs_bridge_name: br-int
tasks:
- name: OVS 패키지 설치
apt:
name:
- openvswitch-switch
state: present
update_cache: yes
- name: OVS 서비스 시작 및 활성화
service:
name: openvswitch-switch
state: started
enabled: yes
- name: OVS 브릿지 생성
command: ovs-vsctl --may-exist add-br ovs_bridge_name
- name: OVS 브릿지 확인
command: ovs-vsctl show
register: ovs_result
- name: OVS 상태 출력
debug:
var: ovs_result.stdout_lines새로운 개념들
vars: Playbook 내에서 사용할 변수를 정의.변수명형태로 참조한다
register: 명령어 실행 결과를 변수에 저장
debug: 변수 내용을 콘솔에 출력. 디버깅할 때 매우 유용하다
🔧 유용한 팁들
ansible.cfg 설정
매번
-i inventory.ini를 입력하기 귀찮다면, 프로젝트 루트에 ansible.cfg를 만들자:# ansible.cfg
[defaults]
inventory = inventory.ini
host_key_checking = False
retry_files_enabled = False
stdout_callback = yamlhost_key_checking = False 주의!이 설정은 SSH 접속 시 호스트 키 검증을 건너뛴다. 신뢰할 수 있는 내부 네트워크에서만 사용하자. 공개 네트워크에서는 보안 위험이 있다.
멱등성(Idempotency) 이해
Ansible의 가장 중요한 특성 중 하나가 멱등성이다. 같은 Playbook을 여러 번 실행해도 결과가 동일하다는 뜻이다.
apt로 패키지를 설치할 때, 이미 설치되어 있으면ok(변경 없음)
service로 서비스를 시작할 때, 이미 실행 중이면ok
- 즉, "이 상태여야 한다"를 선언하는 것이지, "이 명령을 실행해라"가 아니다
이 때문에
command나 shell 모듈은 멱등성이 보장되지 않는다. 가능하면 전용 모듈(apt, service, copy 등)을 사용하는 게 좋다.핵심 정리
✅ Ansible: SSH + Python 기반 에이전트리스 자동화 도구. 대상 서버에 아무것도 설치할 필요 없다
✅ 설치: Ubuntu PPA를 통해 최신 안정 버전 설치. 시스템 Python은 건드리지 말자
✅ Inventory: 관리 대상 서버를 그룹별로 정리한 파일. INI, YAML 형식 모두 지원
✅ Ad-hoc: 간단한 일회성 작업에 사용.
ansible <대상> -m <모듈> -a "<인자>" 형태✅ Playbook: 복잡한 작업을 YAML로 선언.
--check로 반드시 사전 검증✅ 멱등성: 같은 Playbook을 여러 번 실행해도 결과는 동일. 가능하면 전용 모듈을 사용하자
주의사항
⚠️ 시스템 Python:
pip로 Ansible을 설치할 때는 venv를 사용하거나, apt를 쓰자⚠️ SSH 키 관리: 비밀번호 방식보다 SSH 키 방식이 자동화에 적합하다
⚠️
command/shell 남용 금지: 멱등성이 보장되지 않으므로, 전용 모듈이 있다면 전용 모듈을 쓰자⚠️
--check 생활화: 프로덕션에서는 반드시 드라이 런 먼저. 실수는 한 순간이다⚠️ Inventory 보안: IP, 비밀번호 등이 포함될 수 있으므로, Git에 올릴 때
.gitignore 설정 필수