[Ansible] Kubespray: Kubespray를 위한 Ansible 기초 - 2. 인벤토리
서종호(가시다)님의 On-Premise K8s Hands-on Study 2주차 학습 내용을 기반으로 합니다.
TL;DR
이번 글의 목표는 Ansible 인벤토리의 개념과 작성법 이해다.
- 인벤토리 정의: Ansible이 관리할 대상 호스트 목록
- 인벤토리 형식: INI, YAML
- 구성 요소: 호스트, 그룹, 기본 그룹(all, ungrouped), 중첩 그룹(children)
- 인벤토리 검증:
ansible-inventory명령어 - 기본 인벤토리 지정:
ansible.cfg의inventory설정
인벤토리란?
정의
인벤토리(Inventory)는 Ansible이 자동화 대상으로 하는 관리 호스트(Managed Node)를 지정하는 파일이다. 인벤토리에 정의된 호스트에 대해서만 Ansible 작업이 실행된다.
인벤토리 파일에는 다음 정보를 포함할 수 있다:
- 관리 대상 호스트의 호스트명 또는 IP 주소
- 호스트를 논리적으로 묶은 그룹
- 호스트별/그룹별 변수
플레이북과의 관계
Ansible에서 실제 자동화 작업을 정의하는 것은 플레이북(Playbook)이다. 인벤토리와 플레이북의 관계는 다음과 같다:
인벤토리 (Inventory) 플레이북 (Playbook)
"어디에" + "무엇을"
─────────────────────────────────────
관리 대상 호스트 실행할 작업
- 인벤토리: “어떤 서버에 작업할 것인가?”를 정의
- 플레이북: “어떤 작업을 수행할 것인가?”를 정의
플레이북 실행 시 인벤토리를 지정하면, 해당 인벤토리에 정의된 호스트들에 대해 플레이북의 작업이 실행된다.
# 플레이북 실행 시 인벤토리 지정
ansible-playbook -i inventory playbook.yml
인벤토리 형식
인벤토리는 INI 또는 YAML 형식으로 작성할 수 있다. 두 형식 모두 동일한 기능을 제공하며, 선호에 따라 선택하면 된다.
INI 형식
가장 간단한 형태의 INI 스타일 인벤토리는 호스트명 또는 IP 주소를 한 줄씩 나열한다:
web1.example.com
web2.example.com
db1.example.com
db2.example.com
192.0.2.42
그룹을 정의하려면 대괄호([]) 안에 그룹명을 작성하고, 그 아래에 해당 그룹에 속하는 호스트를 나열한다:
[web]
tnode1
tnode2
[db]
tnode3
[all:children]
web
db
YAML 형식
동일한 인벤토리를 YAML 형식으로 작성하면 다음과 같다:
web:
hosts:
tnode1:
tnode2:
db:
hosts:
tnode3:
all:
children:
web:
db:
YAML 형식은 계층 구조가 명확하게 드러나며, 복잡한 변수 설정 시 가독성이 좋다.
인벤토리 구성 요소
호스트
호스트는 Ansible이 관리할 개별 서버를 의미한다. 호스트명 또는 IP 주소 모두 사용할 수 있다.
# 호스트명 사용
web1.example.com
tnode1
# IP 주소 사용
192.168.1.10
10.10.1.11
참고: 호스트명을 사용하려면 DNS 또는
/etc/hosts파일에서 해당 호스트명을 IP 주소로 해석할 수 있어야 한다.
그룹
그룹은 여러 호스트를 논리적으로 묶은 단위다. 그룹을 사용하면 역할, 위치, 환경 등에 따라 호스트를 분류하고, 그룹 단위로 작업을 실행할 수 있다.
[webservers]
web1.example.com
web2.example.com
[db-servers]
db01.example.com
db02.example.com
기본 그룹
Ansible 인벤토리에는 명시적으로 정의하지 않아도 자동으로 존재하는 두 개의 기본 그룹이 있다:
| 그룹 | 설명 |
|---|---|
| all | 인벤토리에 정의된 모든 호스트를 포함하는 그룹 |
| ungrouped | 어떤 그룹에도 속하지 않은 호스트를 포함하는 그룹 |
# 이 인벤토리에서:
server1.example.com # ungrouped에 속함
[webservers]
web1.example.com # webservers에 속함, all에도 속함
all그룹을 대상으로 작업을 실행하면 인벤토리의 모든 호스트에 작업이 적용된다ungrouped그룹에는 그룹 헤더([그룹명]) 이전에 나열된 호스트가 포함된다
인벤토리 파일 생성
IP 주소나 호스트명을 이용해 인벤토리 파일을 생성할 수 있다.
IP 주소로 지정
# (server) #
cat <<EOT > inventory
10.10.1.11
10.10.1.12
10.10.1.13
EOT
참고: Heredoc 구분자 (EOF vs EOT)
<<EOF나<<EOT는 Heredoc(Here Document) 문법의 구분자(delimiter)다.EOF(End Of File)와EOT(End Of Text)는 관례적인 이름일 뿐, 실제로는 아무 문자열이나 사용할 수 있다. 동작에는 차이가 없으며, 문서 내용과 겹치지 않는 고유한 문자열을 선택하면 된다.# 모두 동일하게 동작 cat <<EOF > file.txt cat <<EOT > file.txt cat <<MYDELIMITER > file.txt
호스트명으로 지정
호스트명을 사용하려면 /etc/hosts 파일에 호스트명과 IP 매핑이 되어 있어야 한다.
# (server) #
# /etc/hosts 파일 확인
cat /etc/hosts
10.10.1.10 server
10.10.1.11 tnode1
10.10.1.12 tnode2
10.10.1.13 tnode3
# (server) #
# 호스트명으로 inventory 파일 생성
cat <<EOT > inventory
tnode1
tnode2
tnode3
EOT
인벤토리 검증
ansible-inventory 명령어로 인벤토리 파일의 구문 오류를 확인하고, Ansible이 인벤토리를 어떻게 해석하는지 확인할 수 있다.
–list 옵션
JSON 형식으로 인벤토리의 전체 구조를 출력한다.
# (server) #
ansible-inventory -i ./inventory --list | jq
{
"_meta": {
"hostvars": {},
"profile": "inventory_legacy"
},
"all": {
"children": [
"ungrouped"
]
},
"ungrouped": {
"hosts": [
"10.10.1.11",
"10.10.1.12",
"10.10.1.13"
]
}
}
출력 해석:
_meta: Ansible이 쓰는 특수 키_meta.hostvars: 호스트별 변수 (현재 없음)- Ansible이 각 호스트에 적용한 변수(inventory에 적은 값과 group_vars 등이 합쳐진 결과)를 담는 객체
- 키는 호스트명, 값은 그 호스트의 변수 딕셔너리
- 이 인벤토리에는 호스트 변수를 두지 않았기 때문에
{}로 비어 있음 - 변수를 두거나
group_vars/를 쓰면 여기서 확인할 수 있음(Kubespray 예)
_meta.profile: 인벤토리 플러그인 프로파일 정보all.children:all그룹의 자식 그룹 목록ungrouped.hosts: 그룹에 속하지 않은 호스트 목록
그룹을 정의하지 않았으므로 모든 호스트가 ungrouped에 속해 있다.
참고:
profile: "inventory_legacy"란?Ansible 인벤토리 플러그인의 프로파일 정보다.
inventory_legacy는 전통적인 INI 형식의 정적 인벤토리 파일을 파싱했음을 나타낸다. 동적 인벤토리나 다른 플러그인을 사용하면 다른 값이 표시될 수 있다.
–graph 옵션
트리 구조로 인벤토리의 그룹 계층을 시각화한다.
# (server) #
ansible-inventory -i ./inventory --graph
@all:
|--@ungrouped:
| |--10.10.1.11
| |--10.10.1.12
| |--10.10.1.13
@ 기호는 그룹을 나타내며, 하위 항목은 해당 그룹에 속한 호스트 또는 자식 그룹이다.
주의: 인벤토리 파싱 실패 경고
다음과 같은 경고가 발생하면 인벤토리 파싱에 실패한 것이다:
[WARNING]: Unable to parse /root/my-ansible/inventory as an inventory source [WARNING]: No inventory was parsed, only implicit localhost is available발생 원인:
- 파일이 존재하지 않음 (경로 오타 포함)
- 파일 형식 오류 (INI/YAML 문법 오류)
- 파일 권한 문제로 읽을 수 없음
이 경우 Ansible은 암묵적인
localhost만 사용 가능한 상태가 된다. 경로와 파일 내용을 다시 확인하자.
인벤토리 활용
그룹별 호스트 설정
호스트에 역할(Role)을 부여하고 역할별로 작업을 수행하려면 그룹을 정의한다. 그룹명은 대괄호([]) 안에 작성하고, 그 아래에 해당 그룹에 속하는 호스트를 나열한다.
[webservers]
web1.example.com
web2.example.com
[db-servers]
db01.example.com
db02.example.com
플레이북에서 특정 그룹만 대상으로 작업을 실행할 수 있다:
# webservers 그룹에만 작업 실행
- hosts: webservers
tasks:
- name: Install nginx
apt:
name: nginx
state: present
다중 그룹 소속
하나의 호스트는 여러 그룹에 동시에 속할 수 있다. 이를 활용하면 역할, 위치, 환경 등 다양한 기준으로 호스트를 분류할 수 있다.
[webservers]
web1.example.com
web2.example.com
192.0.2.42
[db-servers]
db01.example.com
db02.example.com
[east-datacenter]
web1.example.com
db01.example.com
[west-datacenter]
web2.example.com
db02.example.com
[production]
web1.example.com
web2.example.com
db01.example.com
db02.example.com
[development]
192.0.2.42
위 예시에서 web1.example.com은 다음 그룹에 모두 속한다:
webservers(역할)east-datacenter(위치)production(환경)
이렇게 구성하면 “동쪽 데이터센터의 모든 서버” 또는 “프로덕션 환경의 웹서버”처럼 다양한 조건으로 호스트를 선택할 수 있다.
중첩 그룹 (children)
기존에 정의한 그룹을 포함하는 상위 그룹을 만들 수 있다. 그룹 이름에 :children 접미사를 추가한다. 아래에 명시된 그룹의 모든 호스틑를 상속 받는다.
[webservers]
web1.example.com
web2.example.com
[db-servers]
db01.example.com
db02.example.com
[datacenter:children]
webservers
db-servers
위의 예시에서 datacenter:children은 webservers와 db-servers 그룹을 자식으로 포함한다.
datacenter (부모 그룹)
└─ webservers (자식 그룹)
└─ web1.example.com (실제 호스트)
└─ web2.example.com (실제 호스트)
└─ db-servers (자식 그룹)
└─ db01.example.com (실제 호스트)
└─ db02.example.com (실제 호스트)
결과적으로 datacenter 그룹에는 다음 호스트가 포함된다:
web1.example.comweb2.example.comdb01.example.comdb02.example.com
범위 지정
호스트 이름이나 IP 주소가 연속적인 패턴을 따르는 경우, 범위 지정 문법으로 간결하게 표현할 수 있다. 대괄호 사이에 시작값과 종료값을 콜론(:)으로 구분하여 [start:end] 형식으로 작성한다.
[webservers]
web[1:2].example.com
[db-servers]
db[01:02].example.com
위 인벤토리는 다음과 같이 전개된다:
webservers:web1.example.com,web2.example.comdb-servers:db01.example.com,db02.example.com
참고:
[1:2]와[01:02]의 차이
[1:2]→1,2(숫자 그대로)[01:02]→01,02(앞에 0이 붙은 형태 유지)
다양한 범위 지정 예시
# IP 주소 범위: 192.168.4.0 ~ 192.168.4.255
[defaults]
192.168.4.[0:255]
# 호스트명 범위: com01.example.com ~ com20.example.com
[compute]
com[01:20].example.com
# 알파벳 범위: a.dns.example.com ~ c.dns.example.com
[dns]
[a:c].dns.example.com
# IPv6 범위: 2001:db8::a ~ 2001:db8::f
[ipv6]
2001:db8::[a:f]
범위 지정 원리:
- 숫자 범위(
[1:10])와 알파벳 범위([a:z]) 모두 지원 - 문자열 패턴 매칭 방식으로 동작하며, 시작값과 종료값 사이의 모든 값을 순차적으로 생성
- 알파벳 범위는 ASCII 코드 순서를 따름
주의: IPv6 주소에서 범위 지정 시, 콜론(
:)이 IPv6 구분자와 범위 구분자로 중복 사용될 수 있어 혼란을 줄 수 있다.2001:db8::[a:f]처럼 범위 부분을 명확히 구분해야 한다.[2001:2007]:db[1:7]::[a:f]와 같은 복잡한 패턴은 지원되지 않는다.
기본 인벤토리 파일
Ansible 설치 시 /etc/ansible/hosts 파일이 기본 인벤토리로 제공된다. 이 파일은 인벤토리 작성 방법에 대한 예시와 주석이 포함되어 있다.
# (server) #
cat /etc/ansible/hosts
# This is the default ansible 'hosts' file.
#
# It should live in /etc/ansible/hosts
#
# - Comments begin with the '#' character # 주석은 '#'으로 시작
# - Blank lines are ignored # 빈 줄은 무시됨
# - Groups of hosts are delimited by [header] # 그룹은 [헤더]로 구분
# - You can enter hostnames or ip addresses # 호스트명 또는 IP 주소 사용 가능
# - A hostname/ip can be a member of multiple groups # 하나의 호스트가 여러 그룹에 속할 수 있음
예시 1: 그룹 없는 호스트 (Ungrouped)
그룹 헤더([그룹명]) 이전에 나열된 호스트는 자동으로 ungrouped 그룹에 속한다.
# Ex 1: Ungrouped hosts, specify before any group headers:
; green.example.com
; blue.example.com
; 192.168.100.1
; 192.168.100.10
예시 2: 그룹 정의
# Ex 2: A collection of hosts belonging to the 'webservers' group:
; [webservers]
; alpha.example.org
; beta.example.org
; 192.168.1.100
; 192.168.1.110
예시 3: 범위 패턴
범위 지정은 호스트명 중간에도 사용할 수 있다.
# If you have multiple hosts following a pattern, you can specify
# them like this:
; www[001:006].example.com # www001 ~ www006
# You can also use ranges for multiple hosts:
; db-[99:101]-node.example.com # db-99-node ~ db-101-node
예시 4: OS별 그룹
# Ex 4: Multiple hosts arranged into groups such as 'Debian' and 'openSUSE':
; [Debian]
; alpha.example.org
; beta.example.org
; [openSUSE]
; green.example.com
; blue.example.com
기본 인벤토리 검증
/etc/ansible/hosts 파일은 모든 내용이 주석 처리되어 있으므로, 검증 시 빈 인벤토리로 해석된다.
# (server) #
ansible-inventory -i /etc/ansible/hosts --list
{
"_meta": {
"hostvars": {}
},
"all": {
"children": [
"ungrouped"
]
}
}
주석을 제외하면 실제 호스트가 없으므로 ungrouped에 호스트가 없고, all 그룹의 자식으로 ungrouped만 표시된다.
실습: 그룹이 포함된 인벤토리
실습 환경에서 그룹을 포함한 인벤토리 파일을 생성해 보자.
인벤토리 파일 생성
# (server) #
cat <<EOT > inventory
[web]
tnode1
tnode2
[db]
tnode3
[all:children]
web
db
EOT
# (server) #
cat inventory
[web]
tnode1
tnode2
[db]
tnode3
[all:children]
web
db
인벤토리 검증
# (server) #
ansible-inventory -i ./inventory --list | jq
{
"_meta": {
"hostvars": {},
"profile": "inventory_legacy"
},
"all": {
"children": [
"ungrouped",
"web",
"db"
]
},
"db": {
"hosts": [
"tnode3"
]
},
"web": {
"hosts": [
"tnode1",
"tnode2"
]
}
}
# (server) #
ansible-inventory -i ./inventory --graph
@all:
|--@ungrouped:
|--@web:
| |--tnode1
| |--tnode2
|--@db:
| |--tnode3
web 그룹에 tnode1, tnode2가, db 그룹에 tnode3이 속해 있다. all:children으로 두 그룹을 all의 자식으로 명시적으로 정의했다.
ansible.cfg로 기본 인벤토리 지정
ansible.cfg에 기본 인벤토리 경로를 지정하면 매번 -i 옵션을 사용하지 않아도 된다.
# (server) #
cat <<EOT > ansible.cfg
[defaults]
inventory = ./inventory
EOT
# (server) #
# -i 옵션 없이 인벤토리 확인
ansible-inventory --list | jq
{
"_meta": {
"hostvars": {},
"profile": "inventory_legacy"
},
"all": {
"children": [
"ungrouped",
"web",
"db"
]
},
"db": {
"hosts": [
"tnode3"
]
},
"web": {
"hosts": [
"tnode1",
"tnode2"
]
}
}
참고:
ansible.cfg의 전체 구조(섹션 구성, 설정 우선순위 등)는 다음 글에서 자세히 다룬다.
결과
이 글을 완료하면 다음과 같은 결과를 얻을 수 있다:
- 인벤토리 개념 이해: Ansible이 관리할 대상 호스트를 정의하는 파일
- 형식 이해: INI, YAML 두 가지 형식으로 작성 가능
- 구성 요소 이해: 호스트, 그룹, 기본 그룹(all, ungrouped), 중첩 그룹(children)
- 인벤토리 검증:
ansible-inventory명령어로 구문 확인 및 해석 결과 확인 - 기본 인벤토리 지정:
ansible.cfg로 인벤토리 경로 설정
Ansible 인벤토리는 “어디에” 작업할지를 정의하는 파일이며, 플레이북과 함께 사용하여 자동화 작업을 수행한다. 그룹과 중첩 그룹을 활용하면 역할, 위치, 환경 등 다양한 기준으로 호스트를 분류하고 관리할 수 있다.
다음 글에서는 Ad-hoc 명령어를 통해 인벤토리에 정의된 호스트에 간단한 작업을 실행해 본다.
댓글남기기