Ansible 기초¶
Ansible의 아키텍처, Inventory, Playbook, Role 등 기초 개념과 실습 방법을 정리한다.
주차 소개¶
Week 2에서는 Ansible의 기초를 학습한다. Ansible은 에이전트리스(Agentless) 아키텍처를 기반으로 SSH를 통해 서버를 관리하는 자동화 도구이다. 선언적 문법(YAML)과 멱등성(Idempotency)을 통해 안전하고 재사용 가능한 인프라 자동화를 구현할 수 있다.
학습 목표¶
- Ansible 아키텍처 이해: Control Node와 Managed Node의 관계 파악
- Inventory 구성: 호스트 그룹 관리 및 변수 활용
- Playbook 작성: Tasks, Variables, Facts, Handlers 활용
- 제어 구조: 반복문(loop), 조건문(when), 블록(block) 사용
- 롤(Role) 구조: 재사용 가능한 플레이북 모듈화
Ansible 개요¶
Ansible이란?¶
Ansible은 오픈소스 자동화 도구로, 서버 구성, 애플리케이션 배포, 태스크 자동화를 간편하게 수행할 수 있다.
- Agentless: Managed Node에 에이전트 설치 불필요 (SSH 기반)
- Idempotent: 동일한 플레이북을 여러 번 실행해도 결과가 동일
- Easy to Use: YAML 문법으로 선언적 구성
- Powerful: 모듈과 플러그인을 통한 확장성
graph LR
subgraph Control["Control Node"]
Ansible["Ansible Engine<br/>• Playbooks<br/>• Inventory<br/>• Modules"]
end
subgraph Managed["Managed Nodes"]
Node1["Server 1<br/>(SSH)"]
Node2["Server 2<br/>(SSH)"]
Node3["Server 3<br/>(SSH)"]
end
Ansible -->|"SSH (22)"| Node1
Ansible -->|"SSH (22)"| Node2
Ansible -->|"SSH (22)"| Node3
Ansible vs 다른 도구¶
| 특징 | Ansible | Chef/Puppet | Terraform |
|---|---|---|---|
| 아키텍처 | Agentless (SSH) | Agent 기반 | Agentless (API) |
| 언어 | YAML | Ruby/DSL | HCL |
| 주요 용도 | Configuration Management | Configuration Management | Infrastructure Provisioning |
| 학습 곡선 | 낮음 | 높음 | 중간 |
| 멱등성 | O | O | O |
Ansible 아키텍처¶
Control Node vs Managed Node¶
graph TB
subgraph ControlNode["Control Node (ansible-server)"]
Playbook["Playbook<br/>(YAML)"]
Inventory["Inventory<br/>(hosts)"]
Config["ansible.cfg"]
Modules["Modules<br/>(apt, service, copy, ...)"]
end
subgraph ManagedNodes["Managed Nodes"]
Node1["tnode1<br/>Ubuntu"]
Node2["tnode2<br/>Ubuntu"]
Node3["tnode3<br/>Rocky Linux"]
end
Playbook --> Modules
Inventory --> Modules
Config --> Modules
Modules -->|"SSH"| Node1
Modules -->|"SSH"| Node2
Modules -->|"SSH"| Node3
Node1 -.->|"Facts<br/>(system info)"| Modules
Node2 -.->|"Facts"| Modules
Node3 -.->|"Facts"| Modules
Control Node:
- Ansible이 설치된 관리 서버
- Playbook, Inventory, 설정 파일 보관
- Python 3.8 이상 필요
Managed Node: - Ansible로 관리되는 대상 서버 - SSH 접근만 가능하면 됨 - Python 2.7 또는 3.5 이상 필요 (일부 모듈)
Inventory 구성¶
Inventory란?¶
Inventory는 Ansible이 관리할 호스트 목록을 정의한 파일이다. 호스트를 그룹으로 묶고, 변수를 할당할 수 있다.
기본 Inventory 작성¶
# /etc/ansible/hosts 또는 ~/my-ansible/inventory
> Ansible 기초, Playbook, Role, Handler, 변수 우선순위를 학습한다.
[web]
tnode1 ansible_host=192.168.10.101
tnode2 ansible_host=192.168.10.102
[db]
tnode3 ansible_host=192.168.10.103
[all:vars]
ansible_user=root
ansible_connection=ssh
그룹별 변수 설정¶
계층 구조 (Children)¶
Playbook 기초¶
Playbook 구조¶
Playbook은 YAML 형식으로 작성되며, 하나 이상의 Play로 구성된다.
---
- name: Install and Start Apache # Play 이름
hosts: web # 대상 호스트 그룹
become: yes # root 권한 사용
vars: # 변수 정의
package_name: apache2
tasks: # 실행할 작업 목록
- name: Install Apache
ansible.builtin.apt:
name: "{{ package_name }}"
state: present
- name: Start Apache
ansible.builtin.service:
name: apache2
state: started
enabled: yes
Playbook 실행 흐름¶
sequenceDiagram
participant User
participant Ansible as Ansible Engine
participant Node as Managed Node
User->>Ansible: ansible-playbook site.yml
Ansible->>Ansible: Parse YAML
Ansible->>Ansible: Load Inventory
loop For each host
Ansible->>Node: SSH Connect
Ansible->>Node: Gather Facts
Node-->>Ansible: System Info (ansible_facts)
loop For each task
Ansible->>Node: Execute Module
Node-->>Ansible: Return Result (changed/ok/failed)
alt Task changed
Ansible->>Node: Notify Handler
end
end
alt Handlers notified
Ansible->>Node: Execute Handlers
end
Ansible->>Node: SSH Disconnect
end
Ansible-->>User: Play Recap
변수 (Variables)¶
변수 우선순위¶
Ansible은 다양한 곳에서 변수를 정의할 수 있으며, 우선순위가 있다.
| 우선순위 | 변수 위치 | 설명 |
|---|---|---|
| 1 (낮음) | Role defaults | defaults/main.yml |
| 2 | Inventory group_vars | 그룹별 변수 |
| 3 | Inventory host_vars | 호스트별 변수 |
| 4 | Playbook vars | Playbook 내 변수 |
| 5 | Task vars | include_vars, set_fact |
| 6 (높음) | Extra vars | -e 옵션 |
---
- hosts: web
vars:
app_name: myapp # Playbook 변수
app_port: 8080
tasks:
- name: Print variables
ansible.builtin.debug:
msg: "App {{ app_name }} runs on port {{ app_port }}"
변수 타입¶
# 문자열
hostname: server1
# 숫자
port: 80
# 불린
enable_https: true
# 리스트
packages:
- nginx
- php-fpm
- mysql-server
# 딕셔너리
user:
name: ansible
uid: 1001
shell: /bin/bash
Facts¶
Facts란?¶
Facts는 Ansible이 Managed Node로부터 자동으로 수집하는 시스템 정보이다.
ansible_facts['hostname']: 호스트명ansible_facts['os_family']: OS 계열 (Debian, RedHat 등)ansible_facts['distribution']: 배포판 (Ubuntu, CentOS 등)ansible_facts['distribution_version']: 버전ansible_facts['default_ipv4']['address']: IP 주소ansible_facts['memtotal_mb']: 메모리 용량ansible_facts['processor_cores']: CPU 코어 수
Facts 확인¶
# 특정 호스트의 모든 Facts 확인
ansible tnode1 -m ansible.builtin.setup
# 특정 Facts 필터링
ansible tnode1 -m ansible.builtin.setup -a "filter=ansible_distribution*"
Facts 사용 예제¶
---
- hosts: all
tasks:
- name: Print OS info
ansible.builtin.debug:
msg: >-
OS: {{ ansible_facts['distribution'] }}
Version: {{ ansible_facts['distribution_version'] }}
IP: {{ ansible_facts['default_ipv4']['address'] }}
Facts 캐싱¶
Facts는 매번 수집 시 시간이 소요되므로, 캐싱을 통해 성능을 향상시킬 수 있다.
# ansible.cfg
[defaults]
gathering = smart
fact_caching = jsonfile
fact_caching_connection = ./myfacts
fact_caching_timeout = 86400
| 정책 | 설명 |
|---|---|
| implicit (기본값) | 모든 Play 시작 시 자동으로 Facts 수집 |
| explicit | gather_facts: yes로 명시한 경우에만 수집 |
| smart | 캐시가 유효하면 수집 생략, 만료 시에만 수집 |
커스텀 팩트 (Custom Facts)¶
사용자 정의 팩트를 생성하여 환경 설정이나 조건부 작업에 활용할 수 있다.
생성 방법:
1. Managed Node의 /etc/ansible/facts.d/ 디렉터리 생성
2. *.fact 파일 생성 (INI, JSON, 또는 실행 가능한 스크립트)
# Managed Node에서 실행
mkdir -p /etc/ansible/facts.d
# INI 형식 팩트 파일 생성
cat <<EOT > /etc/ansible/facts.d/my-custom.fact
[packages]
web_package = httpd
db_package = mariadb-server
[users]
user1 = ansible
user2 = gasida
EOT
---
- hosts: all
tasks:
- name: Print custom facts
ansible.builtin.debug:
var: ansible_local.my-custom
{
"ansible_local": {
"my-custom": {
"packages": {
"web_package": "httpd",
"db_package": "mariadb-server"
},
"users": {
"user1": "ansible",
"user2": "gasida"
}
}
}
}
반복문 (Loop)¶
기본 Loop¶
---
- hosts: all
tasks:
- name: Install multiple packages
ansible.builtin.apt:
name: "{{ item }}"
state: present
loop:
- nginx
- vim
- git
사전 목록 반복¶
---
- hosts: all
tasks:
- name: Create log files
ansible.builtin.file:
path: "{{ item['log-path'] }}"
mode: "{{ item['log-mode'] }}"
state: touch
loop:
- log-path: /var/log/app1.log
log-mode: '0644'
- log-path: /var/log/app2.log
log-mode: '0600'
Loop와 Register 변수¶
---
- hosts: localhost
tasks:
- name: Echo languages
ansible.builtin.shell: "echo 'I can speak {{ item }}'"
loop:
- Korean
- English
register: result
- name: Print results
ansible.builtin.debug:
msg: "{{ item.stdout }}"
loop: "{{ result.results }}"
이전 앤서블 스타일 반복문 (Legacy)¶
Ansible 2.5 이전에는 with_* 접두사를 사용한 반복문을 사용했다. 현재는 권장되지 않지만, 기존 플레이북 분석 시 알아두면 유용한다.
| 키워드 | 설명 | loop 대체 |
|---|---|---|
with_items |
단순 목록 반복 | loop |
with_file |
파일 내용 반복 | loop + lookup('file', ...) |
with_sequence |
숫자 시퀀스 생성 | loop + range() |
예제 (참고용):
---
# 이전 스타일 (비권장)
- hosts: localhost
vars:
users:
- user0
- user1
- user2
tasks:
- name: with_items example
ansible.builtin.debug:
msg: "{{ item }}"
with_items: "{{ users }}"
# 현재 권장 스타일
- hosts: localhost
vars:
users:
- user0
- user1
- user2
tasks:
- name: loop example
ansible.builtin.debug:
msg: "{{ item }}"
loop: "{{ users }}"
조건문 (Conditionals)¶
when 기본 사용¶
---
- hosts: all
vars:
run_task: true
tasks:
- name: This runs when true
ansible.builtin.debug:
msg: "Task executed"
when: run_task
조건 연산자¶
| 연산자 | 설명 | 예시 |
|---|---|---|
== |
같음 | ansible_facts['distribution'] == "Ubuntu" |
!= |
같지 않음 | port != 80 |
>, >= |
초과, 이상 | memory_mb >= 2048 |
<, <= |
미만, 이하 | cpu_cores < 4 |
in |
포함 | "Ubuntu" in supported_os |
is defined |
변수 존재 | my_var is defined |
is not defined |
변수 미존재 | my_var is not defined |
OS별 분기 처리¶
---
- hosts: all
tasks:
- name: Install Apache on Debian
ansible.builtin.apt:
name: apache2
state: present
when: ansible_facts['os_family'] == "Debian"
- name: Install Apache on RedHat
ansible.builtin.dnf:
name: httpd
state: present
when: ansible_facts['os_family'] == "RedHat"
복합 조건문¶
---
- hosts: all
tasks:
- name: Complex condition
ansible.builtin.debug:
msg: "This is Ubuntu 24.04"
when:
- ansible_facts['distribution'] == "Ubuntu"
- ansible_facts['distribution_version'] == "24.04"
# OR 조건
- name: Ubuntu or CentOS
ansible.builtin.debug:
msg: "Supported OS"
when: >
ansible_facts['distribution'] == "Ubuntu" or
ansible_facts['distribution'] == "CentOS"
핸들러 (Handlers)¶
Handlers란?¶
Handlers는 작업이 변경(changed)될 때만 실행되는 특수한 작업이다. 주로 서비스 재시작, 설정 리로드에 사용된다.
graph LR
Task["Task<br/>(Copy config file)"] -->|"changed=true"| Notify["Notify Handler"]
Notify --> Handler["Handler<br/>(Restart service)"]
Task2["Task<br/>(Install package)"] -->|"changed=false"| Skip["Skip Handler"]
Handler 예제¶
---
- hosts: web
tasks:
- name: Update nginx config
ansible.builtin.copy:
src: nginx.conf
dest: /etc/nginx/nginx.conf
notify:
- Restart nginx
- name: Update index.html
ansible.builtin.copy:
src: index.html
dest: /var/www/html/index.html
notify:
- Reload nginx
handlers:
- name: Restart nginx
ansible.builtin.service:
name: nginx
state: restarted
- name: Reload nginx
ansible.builtin.service:
name: nginx
state: reloaded
force_handlers: yes 사용 시 예외)
에러 처리¶
ignore_errors¶
작업 실패를 무시하고 계속 진행한다.
---
- hosts: all
tasks:
- name: This might fail
ansible.builtin.command: /bin/false
ignore_errors: yes
- name: This will run anyway
ansible.builtin.debug:
msg: "Previous task failed but ignored"
force_handlers¶
작업 실패 시에도 notify된 Handler를 실행한다.
---
- hosts: web
force_handlers: yes
tasks:
- name: Update config
ansible.builtin.copy:
src: app.conf
dest: /etc/app/app.conf
notify:
- Restart app
- name: This task might fail
ansible.builtin.command: /bin/false
handlers:
- name: Restart app
ansible.builtin.service:
name: app
state: restarted
Block, Rescue, Always¶
try-catch-finally와 유사한 에러 처리 구조이다.
---
- hosts: all
tasks:
- name: Error handling example
block:
- name: Try to do something
ansible.builtin.command: /bin/might_fail
- name: Another task
ansible.builtin.debug:
msg: "This runs if previous succeeded"
rescue:
- name: Handle error
ansible.builtin.debug:
msg: "An error occurred, handling it"
always:
- name: Cleanup
ansible.builtin.debug:
msg: "This always runs"
graph TB
Start([Start]) --> Block[Block Tasks]
Block -->|Success| Always[Always Tasks]
Block -->|Failure| Rescue[Rescue Tasks]
Rescue --> Always
Always --> End([End])
롤 (Roles)¶
롤이란?¶
Role은 플레이북을 기능 단위로 모듈화하여 재사용 가능하게 만드는 구조이다.
장점: - 코드 재사용성 향상 - 구조화된 디렉터리 구조 - Ansible Galaxy를 통한 공유 - 대규모 프로젝트 관리 용이
롤 디렉터리 구조¶
my-role/
├── defaults/ # 기본 변수 (낮은 우선순위)
│ └── main.yml
├── files/ # 정적 파일
│ └── index.html
├── handlers/ # 핸들러
│ └── main.yml
├── meta/ # 롤 메타데이터
│ └── main.yml
├── tasks/ # 메인 작업
│ └── main.yml
├── templates/ # Jinja2 템플릿
│ └── config.j2
├── tests/ # 테스트
│ ├── inventory
│ └── test.yml
└── vars/ # 변수 (높은 우선순위)
└── main.yml
롤 생성¶
롤 작성 예제¶
tasks/main.yml¶
---
# tasks file for my-role
- name: Install Apache
ansible.builtin.apt:
name: "{{ item }}"
state: latest
loop: "{{ httpd_packages }}"
when: ansible_facts['distribution'] in supported_distros
- name: Copy index.html
ansible.builtin.copy:
src: "{{ src_file_path }}"
dest: "{{ dest_file_path }}"
notify:
- Restart Apache
handlers/main.yml¶
---
# handlers file for my-role
- name: Restart Apache
ansible.builtin.service:
name: "{{ service_name }}"
state: restarted
vars/main.yml¶
---
# vars file for my-role
service_name: apache2
src_file_path: ../files/index.html
dest_file_path: /var/www/html
httpd_packages:
- apache2
- apache2-doc
supported_distros:
- Ubuntu
defaults/main.yml¶
롤 사용하기¶
import_role (정적)¶
include_role (동적)¶
---
- hosts: web
tasks:
- name: Install web server
ansible.builtin.include_role:
name: my-role
when: install_web is defined
roles 섹션¶
---
- hosts: web
roles:
- my-role
- my-role2
tasks:
- name: Additional task
ansible.builtin.debug:
msg: "Roles executed"
롤 실행 순서¶
graph TB
Start([Playbook Start]) --> PreTasks[Pre-tasks]
PreTasks --> Roles[Roles]
Roles --> Tasks[Tasks]
Tasks --> PostTasks[Post-tasks]
PostTasks --> Handlers[Handlers]
Handlers --> End([Playbook End])
주요 모듈¶
패키지 관리¶
# apt (Debian/Ubuntu)
- name: Install nginx
ansible.builtin.apt:
name: nginx
state: present
update_cache: yes
# dnf (RedHat/Rocky/CentOS)
- name: Install httpd
ansible.builtin.dnf:
name: httpd
state: latest
서비스 관리¶
파일 관리¶
# 파일 복사
- name: Copy file
ansible.builtin.copy:
src: /local/file
dest: /remote/file
mode: '0644'
# 파일 생성/수정
- name: Create file
ansible.builtin.file:
path: /tmp/test.txt
state: touch
mode: '0755'
# 템플릿 (Jinja2)
- name: Deploy config
ansible.builtin.template:
src: config.j2
dest: /etc/app/config.conf
명령 실행¶
# command (셸 기능 미사용)
- name: Run command
ansible.builtin.command:
cmd: ls -la /tmp
# shell (셸 기능 사용)
- name: Run shell
ansible.builtin.shell:
cmd: ps aux | grep nginx
실습 환경¶
가상머신 구성¶
| 호스트명 | IP | 역할 | OS |
|---|---|---|---|
| ansible-server | 192.168.10.10 | Control Node | Ubuntu |
| tnode1 | 192.168.10.101 | Managed Node | Ubuntu 24.04 |
| tnode2 | 192.168.10.102 | Managed Node | Ubuntu 24.04 |
| tnode3 | 192.168.10.103 | Managed Node | Rocky Linux 9.6 |
주요 설정 파일¶
# ansible.cfg
[defaults]
inventory = ./inventory
remote_user = root
ask_pass = false
gathering = smart
fact_caching = jsonfile
fact_caching_connection = myfacts
[privilege_escalation]
become = true
become_method = sudo
become_user = root
become_ask_pass = false
모듈별 반환 값 (Return Values)¶
Ansible 모듈은 작업 실행 후 다양한 정보를 반환한다.
| Return Value | 설명 |
|---|---|
changed |
시스템 상태가 변경되었는지 (true/false) |
failed |
작업 실패 여부 (true/false) |
msg |
사용자 메시지 |
rc |
명령 반환 코드 (0=성공) - 아래 상세 참조 |
stdout |
표준 출력 |
stderr |
표준 에러 |
stdout_lines |
표준 출력 (라인별 배열) |
stderr_lines |
표준 에러 (라인별 배열) |
반환 코드 (rc) 상세¶
| 반환 코드 | 의미 |
|---|---|
| 0 | 성공 |
| 1 | 일반 오류 |
| 2 | 잘못된 인자 |
| 126 | 실행 권한 없음 |
| 127 | 명령 없음 (Command not found) |
| 130 | Ctrl+C로 종료 |
| 137 | SIGKILL (강제 종료) |
| 139 | Segmentation fault |
# 반환 코드 확인 방법
echo $? # 직전 명령의 반환 코드
# 예시
ls -al # 성공
echo $? # 0
ls abc # 파일 없음
echo $? # 2
aaa # 명령 없음
echo $? # 127
---
- hosts: localhost
tasks:
- name: Run command
ansible.builtin.shell: echo "Hello"
register: result
- name: Show result
ansible.builtin.debug:
msg: |
Changed: {{ result.changed }}
RC: {{ result.rc }}
Stdout: {{ result.stdout }}
변수 우선순위 전체¶
graph TB
ExtraVars["1. Extra Vars<br/>(-e 옵션)<br/>최고 우선순위"] --> TaskVars["2. Task Vars<br/>(include_vars, set_fact)"]
TaskVars --> PlayVars["3. Playbook vars<br/>vars:"]
PlayVars --> HostVars["4. Host vars<br/>(host_vars/)"]
HostVars --> GroupVars["5. Group vars<br/>(group_vars/)"]
GroupVars --> RoleVars["6. Role vars<br/>(vars/main.yml)"]
RoleVars --> RoleDefaults["7. Role Defaults<br/>(defaults/main.yml)<br/>최저 우선순위"]
Ansible 명령어 정리¶
Ad-hoc 명령¶
# Ping 테스트
ansible all -m ping
# 명령 실행
ansible all -m shell -a "uptime"
# Facts 확인
ansible tnode1 -m ansible.builtin.setup
# 패키지 설치
ansible web -m ansible.builtin.apt -a "name=nginx state=present" -b
Playbook 실행¶
# 기본 실행
ansible-playbook site.yml
# Dry-run (체크 모드)
ansible-playbook site.yml --check
# 특정 호스트만
ansible-playbook site.yml --limit tnode1
# Extra vars 전달
ansible-playbook site.yml -e "env=production"
# Verbose 모드
ansible-playbook site.yml -vvv
Ansible Galaxy¶
# 롤 생성
ansible-galaxy role init my-role
# 롤 검색
ansible-galaxy role search apache
# 롤 설치
ansible-galaxy role install geerlingguy.nginx
# 롤 목록
ansible-galaxy role list
실습 과제¶
도전과제 1: 반복문¶
Linux 사용자 user1~user10 (10명)을 반복문으로 생성 후 확인하고 삭제하기
도전과제 2: Loop + Sequence¶
/var/log/test1 ~ /var/log/test100 100개 파일을 생성하고 삭제하기
도전과제 3: Facts¶
3개 서버의 '커널 버전'과 '운영체제 종류'를 출력하기
도전과제 4: 조건문¶
Ubuntu OS이면서 FQDN이 tnode1인 경우, OS 정보와 FQDN 출력하기
도전과제 5: Handlers¶
Apache2 패키지를 apt 모듈로 설치 시, Handler를 호출하여 재시작하기
도전과제 6: Block¶
block, rescue, always 키워드를 사용한 플레이북 작성하기
Best Practices¶
1. 멱등성 유지¶
- 모듈 사용 시
state: present/absent명시 - shell/command 대신 전용 모듈 사용 (apt, service 등)
2. 변수 네이밍¶
3. 롤 구조화¶
4. Vault로 민감 정보 암호화¶
# Vault 생성
ansible-vault create secrets.yml
# Vault 편집
ansible-vault edit secrets.yml
# Playbook 실행 시 Vault 비밀번호 입력
ansible-playbook site.yml --ask-vault-pass
5. Tags 활용¶
- name: Install packages
ansible.builtin.apt:
name: nginx
tags:
- packages
- nginx
- name: Configure firewall
ansible.posix.firewalld:
service: http
state: enabled
tags:
- firewall
# 특정 tag만 실행
ansible-playbook site.yml --tags nginx
# 특정 tag 제외
ansible-playbook site.yml --skip-tags firewall
작성일: 2026-01-17 카테고리: Kubernetes 운영 스터디 (K8s-Deploy) 주차: Week 2