Ansible은 에이전트 없이 SSH만으로 서버를 관리하는 자동화 도구입니다. 이번 포스팅에서는 Docker 기반 실습 환경을 구성하고, 인벤토리와 설정 파일의 역할을 이해한 뒤, 첫 Playbook으로 nginx를 설치해봅니다.
Docker Compose로 control 노드 1대 + managed 노드 3대를 구성합니다.
Ansible은 에이전트리스(agentless) 구조입니다. managed 노드에 별도 소프트웨어를 설치하지 않고, control 노드에서 SSH로 접속하여 작업을 수행합니다.
쿠버네티스에서 kubelet이 각 노드에 설치되어야 하는 것과 달리, Ansible은 SSH 접속만 가능하면 됩니다.
| 컨테이너 | 역할 | 베이스 이미지 |
|---|---|---|
ansible-control |
Ansible 명령 실행 (control 노드) | Python 3.12-slim + ansible-core |
node1 ~ node3 |
관리 대상 (managed 노드) | Ubuntu 24.04 + SSH 서버 |
4개 컨테이너는 ansible-net 브릿지 네트워크로 연결되며, control 노드에서 managed 노드로 SSH(user: ansible, password: ansible)로 접속합니다.
services:
ansible-control:
build:
context: ./control
container_name: ansible-control
tty: true
stdin_open: true
volumes:
- ./ansible:/workspace/ansible
working_dir: /workspace/ansible
networks:
- ansible-net
node1:
build:
context: ./managed
container_name: node1
hostname: node1
volumes:
- ./managed/sshd_config:/etc/ssh/sshd_config:ro
networks:
- ansible-net
# node2, node3도 동일 구조
핵심 포인트:
./ansible 디렉토리가 control 컨테이너의 /workspace/ansible에 볼륨 마운트됩니다. 호스트에서 파일을 편집하면 컨테이너에 즉시 반영되고, 그 반대도 마찬가지입니다.sshd_config도 볼륨 마운트(:ro)하여, SSH 설정 변경 시 이미지 재빌드 없이 컨테이너 재시작만으로 반영됩니다.FROM ubuntu:24.04
RUN apt-get update && apt-get install -y --no-install-recommends \
openssh-server python3 sudo iputils-ping vim \
&& rm -rf /var/lib/apt/lists/*
RUN mkdir /var/run/sshd
# 실습용 사용자 생성
RUN useradd -m -s /bin/bash ansible \
&& echo 'ansible:ansible' | chpasswd \
&& usermod -aG sudo ansible \
&& echo 'ansible ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers
COPY sshd_config /etc/ssh/sshd_config
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
EXPOSE 22
CMD ["/entrypoint.sh"]
ansible 유저를 생성하고, NOPASSWD sudo 권한을 부여합니다. 이후 Ansible의 become(권한 상승) 실습에 사용됩니다.entrypoint.sh에서 SSH host key를 생성하고 sshd를 포그라운드로 실행합니다.docker compose up -d --build
docker exec -it ansible-control bash
[defaults]
inventory = ./inventory.ini
host_key_checking = False
interpreter_python = auto_silent
stdout_callback = default
result_format = yaml
[ssh_connection]
pipelining = True
ansible.cfg는 INI 형식으로, 섹션 이름은 Ansible이 정한 고정 키워드입니다.
| 섹션 | 용도 |
|---|---|
[defaults] |
일반 설정 (인벤토리 경로, 출력 형식 등) |
[ssh_connection] |
SSH 연결 관련 설정 |
[privilege_escalation] |
become(sudo) 관련 설정 |
주요 설정:
inventory — 인벤토리 파일 경로 (상대 경로는 ansible.cfg 기준)host_key_checking = False — SSH 첫 접속 시 fingerprint 확인을 생략pipelining = True — SSH 연결 시 임시 파일 대신 파이프로 모듈을 전송하여 성능 향상Ansible은 다음 순서로 설정 파일을 찾습니다:
ANSIBLE_CONFIG 환경변수ansible.cfg~/.ansible.cfg/etc/ansible/ansible.cfg따라서 playbook 실행 시 ansible.cfg가 있는 디렉토리에서 실행하는 것이 편합니다.
인벤토리는 Ansible이 관리할 대상 호스트 목록입니다. "어떤 서버에, 어떻게 접속할 것인가"를 정의합니다.
[managed]
node1 ansible_host=node1
node2 ansible_host=node2
node3 ansible_host=node3
[managed:vars]
ansible_connection=ssh
ansible_user=ansible
ansible_password=ansible
인벤토리도 INI 형식이지만, ansible.cfg와는 섹션 이름 규칙이 다릅니다.
| 파일 | 섹션 이름 | 규칙 |
|---|---|---|
ansible.cfg |
[defaults], [ssh_connection] 등 |
Ansible이 정한 고정 이름 |
inventory.ini |
[managed], [web] 등 |
사용자가 자유롭게 지정 |
inventory.ini |
[managed:vars] |
그룹명:접미사 형식, 접미사(vars, children)는 고정 |
[그룹명:vars])그룹 내 호스트에 공통 적용할 변수를 한 곳에서 관리합니다. 호스트가 많아질수록 효과가 큽니다. 비밀번호를 바꿀 때도 한 곳만 수정하면 됩니다.
인벤토리가 정의되면 ad-hoc 명령으로 바로 테스트할 수 있습니다. ad-hoc 명령은 playbook 파일 없이 CLI에서 모듈을 직접 실행하는 방식입니다.
# ad-hoc 명령 형식: ansible <대상> -m <모듈> [-a <인자>]
ansible managed -m ping # managed 그룹 전체
ansible node1 -m ping # node1에만
ansible all -m ping # 모든 호스트
쿠버네티스로 비유하면, ad-hoc은 kubectl run --rm -it으로 일회성 작업을 하는 것이고, playbook은 매니페스트 YAML을 kubectl apply하는 것에 해당합니다.
💡
-m옵션과ping
-m은 module의 약자로, 실행할 모듈을 지정하는 옵션입니다.ping은 예약어가 아니라ansible.builtin.ping모듈의 축약 이름입니다. ad-hoc 명령에서는 FQCN 없이 모듈명만 써도 동작합니다.ansible managed -m ping # 축약 이름 ansible managed -m ansible.builtin.ping # FQCN (동일한 동작) ansible managed -m shell -a "whoami" # shell 모듈에 인자 전달 ansible managed -m apt -a "name=nginx state=present" --become # apt 모듈
-m을 생략하면 기본 모듈인command가 사용됩니다.
ad-hoc 명령은 한 번에 하나씩 실행하지만, playbook은 여러 작업을 파일에 묶어 순서대로 실행합니다.
| ad-hoc | playbook | |
|---|---|---|
| 용도 | 간단한 일회성 작업 | 반복 가능한 자동화 |
| 형식 | CLI 명령어 | YAML 파일 |
| 작업 수 | 1개 | 여러 개를 순서대로 |
---
- name: First Ansible connectivity test
hosts: managed
gather_facts: false
tasks:
- name: Ping managed nodes
ansible.builtin.ping:
각 라인의 역할:
--- — YAML 문서 시작 표시name — play의 설명hosts — 대상 그룹 (inventory의 [managed])gather_facts — 대상 서버의 OS/네트워크 등 시스템 정보 수집 여부. false로 하면 수집을 건너뛰어 실행이 빨라짐tasks — 실행할 task 목록ansible.builtin.ping: — 사용할 모듈의 FQCN(Fully Qualified Collection Name)참고: ansible.builtin.ping은 네트워크 ICMP ping이 아니라, SSH 접속 후 Python이 정상 동작하는지 확인하는 Ansible 전용 모듈입니다.
ansible.builtin.ping에서 ansible.builtin은 내장 컬렉션, ping은 모듈명입니다. 그냥 ping:으로도 쓸 수 있지만, 서드파티 컬렉션에 동일한 이름의 모듈이 있을 수 있으므로 FQCN이 권장됩니다.
쿠버네티스에서 다른 네임스페이스의 서비스를 호출할 때 서비스명.네임스페이스.svc.cluster.local FQDN을 쓰는 것과 같은 이유입니다.
Ansible은 모듈 레벨에서는 선언형, playbook 레벨에서는 절차형입니다.
# 선언형: "nginx가 설치되어 있는 상태"를 선언
- ansible.builtin.apt:
name: nginx
state: present # 이미 설치되어 있으면 아무것도 안 함
state: present처럼 원하는 상태를 선언하면, 모듈이 알아서 현재 상태를 확인하고 필요한 경우에만 변경합니다. 이것을 **멱등성(idempotency)**이라고 합니다 — 몇 번을 실행해도 결과가 같습니다.
반면 task는 위에서 아래로 순서대로 실행됩니다. 쿠버네티스는 컨트롤러가 알아서 원하는 상태로 수렴시키지만, Ansible은 작업 순서를 사용자가 직접 제어합니다.
| Ansible | Kubernetes | |
|---|---|---|
| 개별 작업 | 선언형 (state: present) |
선언형 (replicas: 3) |
| 전체 흐름 | 절차형 (순서대로 실행) | 선언형 (컨트롤러가 수렴) |
| 지속성 | 실행 시점에만 적용 | 컨트롤러가 상태를 계속 유지 |
ansible-doc ansible.builtin.apt # apt 모듈의 파라미터 확인
ansible-doc ansible.builtin.service # service 모듈의 파라미터 확인
ansible-doc -l # 전체 모듈 목록
쿠버네티스의 kubectl explain과 비슷한 역할입니다. 다만 kubectl explain pod.spec.containers처럼 계층적 탐색은 지원하지 않고, 모듈 단위로만 조회할 수 있습니다.
root@control:/workspace/ansible# ansible managed -m shell -a "which nginx || echo 'nginx not found'"
node1 | CHANGED | rc=0 >> nginx not found
node2 | CHANGED | rc=0 >> nginx not found
node3 | CHANGED | rc=0 >> nginx not found
참고: shell 모듈은 명령이 성공하면 항상 CHANGED를 반환합니다. 실제 변경 여부를 판단할 수 없기 때문입니다.
---
- name: Ansible nginx install & execute
hosts: managed
become: true
gather_facts: false
tasks:
- name: apt install nginx
ansible.builtin.apt:
name: nginx
state: present
- name: nginx service start
ansible.builtin.service:
name: nginx
state: started
💡
become: true란?Ansible은 inventory에 정의된 유저(여기서는
ansible)로 SSH 접속합니다. 하지만 패키지 설치나 서비스 관리는 root 권한이 필요합니다.become: true를 설정하면 내부적으로sudo를 실행하여 권한을 상승시킵니다.쿠버네티스의
securityContext.runAsUser: 0으로 root 권한을 부여하는 것과 비슷한 개념입니다.
- play 레벨에 설정하면 해당 play의 모든 task에 적용
- task 레벨에 설정하면 해당 task에만 적용
tasks: - name: sudo 필요한 작업 ansible.builtin.apt: name: nginx state: present become: true # 이 task만 sudo - name: sudo 불필요한 작업 ansible.builtin.ping: # 일반 유저로 실행현재 managed 노드의
ansible유저에NOPASSWD:ALLsudo 권한이 있으므로, 비밀번호 입력 없이become을 사용할 수 있습니다.
ansible.builtin.apt — name에 패키지명, state: present로 설치 상태를 선언ansible.builtin.service — state: started로 서비스 실행 상태를 선언PLAY [Ansible nginx install & execute] **********************
TASK [apt install nginx] ************************************
changed: [node1]
changed: [node3]
changed: [node2]
TASK [nginx service start] **********************************
changed: [node3]
changed: [node2]
changed: [node1]
PLAY RECAP **************************************************
node1 : ok=2 changed=2 unreachable=0 failed=0 skipped=0
node2 : ok=2 changed=2 unreachable=0 failed=0 skipped=0
node3 : ok=2 changed=2 unreachable=0 failed=0 skipped=0
ok=2 — 2개 task 모두 성공changed=2 — 서버 상태가 실제로 변경됨 (nginx 설치 + 서비스 시작)root@control:/workspace/ansible# ansible managed -m shell -a "which nginx || echo 'nginx not found'"
node1 | CHANGED | rc=0 >> /usr/sbin/nginx
node2 | CHANGED | rc=0 >> /usr/sbin/nginx
node3 | CHANGED | rc=0 >> /usr/sbin/nginx
3대 모두 nginx가 설치되었습니다.
💡 멱등성 확인: 같은 playbook을 한 번 더 실행하면, 이미 nginx가 설치되고 실행 중이므로 ok=2, changed=0이 나옵니다. 선언형 모듈이 현재 상태를 확인하고, 이미 원하는 상태이면 변경하지 않기 때문입니다.
playbook을 실행하기 전에 문법을 검사할 수 있습니다:
ansible-playbook playbook/nginx.yml --syntax-check # 문법 검사
ansible-playbook playbook/nginx.yml --check # dry-run (실제 변경 없음)
--check는 쿠버네티스의 kubectl apply --dry-run=server와 비슷합니다.