🤖

본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.

⚠️

본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.

이미지 로딩 중...

Ansible 인벤토리 관리 완벽 가이드 - 슬라이드 1/7
A

AI Generated

2025. 12. 29. · 5 Views

Ansible 인벤토리 관리 완벽 가이드

서버 수백 대를 관리하는 당신, 매번 IP 주소 외우고 계신가요? Ansible 인벤토리로 서버 관리를 체계화하는 방법을 알아봅니다. 정적 인벤토리부터 동적 인벤토리까지, 실무에서 바로 활용할 수 있는 베스트 프랙티스를 담았습니다.


목차

  1. 정적_인벤토리_파일_작성
  2. 호스트_그룹_정의하기
  3. 호스트_변수_설정
  4. 그룹_변수와_상속
  5. 동적_인벤토리_개념
  6. 인벤토리_패턴과_선택

1. 정적 인벤토리 파일 작성

어느 날 김인프라 씨는 회사에서 관리하는 서버가 10대에서 50대로 늘어났다는 소식을 들었습니다. 매번 SSH로 접속할 때마다 IP 주소를 찾아 헤매던 그는 이제 뭔가 체계적인 방법이 필요하다고 느꼈습니다.

바로 그때 선배 박데브옵스 씨가 다가와 말했습니다. "Ansible 인벤토리 파일을 만들어보세요."

정적 인벤토리 파일은 관리할 서버들의 목록을 텍스트 파일로 작성해 두는 것입니다. 마치 주소록에 친구들의 전화번호를 저장하는 것처럼, 서버의 IP 주소와 접속 정보를 한 곳에 모아두는 것이죠.

이 파일만 있으면 언제든지 원하는 서버에 명령을 실행할 수 있습니다.

다음 코드를 살펴봅시다.

# /etc/ansible/hosts 또는 inventory.ini
# 웹 서버들
web1.example.com
web2.example.com
192.168.1.10

# 데이터베이스 서버들
db1.example.com ansible_host=10.0.0.5 ansible_port=22
db2.example.com ansible_host=10.0.0.6

# 별칭을 사용한 서버 정의
prod-api ansible_host=192.168.100.20 ansible_user=deploy

김인프라 씨는 입사 6개월 차 인프라 엔지니어입니다. 처음에는 서버가 몇 대 없어서 메모장에 IP 주소를 적어두고 관리했습니다.

하지만 회사가 성장하면서 서버 숫자도 빠르게 늘어났습니다. 이제는 웹 서버 20대, 데이터베이스 서버 10대, API 서버 15대를 관리해야 합니다.

어느 날 긴급 상황이 발생했습니다. 모든 웹 서버에 보안 패치를 급하게 적용해야 했는데, 김인프라 씨는 메모장을 뒤지며 IP 주소를 하나하나 찾아야 했습니다.

"분명 여기 어딘가에 있었는데..." 땀이 흘러내렸습니다. 그때 옆자리의 박데브옵스 씨가 이미 패치 작업을 끝내고 여유롭게 커피를 마시고 있었습니다.

"벌써 끝내셨어요?" 놀란 김인프라 씨가 물었습니다. 박데브옵스 씨는 화면에 떠 있는 간단한 텍스트 파일을 보여주었습니다.

"Ansible 인벤토리 파일이에요. 여기에 모든 서버 정보가 정리되어 있어서 한 번에 명령을 실행할 수 있거든요." 그렇다면 인벤토리 파일이란 정확히 무엇일까요?

쉽게 비유하자면, 인벤토리 파일은 마치 회사의 직원 명부와 같습니다. 직원 명부에는 각 직원의 이름, 부서, 연락처가 적혀 있습니다.

회사에서 공지사항을 전달할 때 일일이 개인에게 연락하지 않고 부서별로 한 번에 전달할 수 있죠. 인벤토리 파일도 마찬가지로 서버들의 주소와 접속 정보를 담고 있어서 필요할 때 쉽게 찾아 사용할 수 있습니다.

인벤토리 파일이 없던 시절에는 어땠을까요? 개발자들은 서버에 접속할 때마다 IP 주소를 외우거나 메모장에서 찾아야 했습니다.

서버가 10대만 넘어가도 관리가 복잡해졌습니다. 더 큰 문제는 서버 정보가 여러 곳에 흩어져 있다는 것이었습니다.

개발자 A는 자기 메모장에, 개발자 B는 엑셀 파일에, 개발자 C는 위키에 정리해 두었습니다. 서버 IP가 변경되면 모든 곳을 일일이 수정해야 했죠.

바로 이런 문제를 해결하기 위해 Ansible 인벤토리가 등장했습니다. 인벤토리 파일을 사용하면 모든 서버 정보를 한 곳에서 관리할 수 있습니다.

또한 사람이 읽기 쉬운 텍스트 형식이어서 누구나 쉽게 수정하고 공유할 수 있습니다. 무엇보다 Git 같은 버전 관리 시스템으로 변경 이력을 추적할 수 있다는 큰 이점이 있습니다.

위의 코드를 한 줄씩 살펴보겠습니다. 먼저 web1.example.com 같은 도메인 이름을 직접 적을 수 있습니다.

Ansible은 자동으로 DNS를 통해 IP 주소를 찾아줍니다. 다음으로 192.168.1.10 처럼 IP 주소를 직접 적어도 됩니다.

더 자세한 설정이 필요하다면 ansible_host, ansible_port, ansible_user 같은 변수를 사용합니다. 예를 들어 db1.example.com ansible_host=10.0.0.5라고 적으면 db1.example.com이라는 이름으로 서버를 부르지만 실제 접속은 10.0.0.5로 한다는 뜻입니다.

별칭을 사용하면 더욱 편리합니다. prod-api라는 짧고 기억하기 쉬운 이름을 붙여두면, 긴 IP 주소를 외우지 않아도 됩니다.

마치 스마트폰 연락처에 "엄마", "회사 상사"라고 저장해두는 것과 같습니다. 실제 현업에서는 어떻게 활용할까요?

예를 들어 전자상거래 서비스를 운영한다고 가정해봅시다. 프론트엔드 서버 10대, 백엔드 API 서버 15대, 데이터베이스 서버 5대를 관리해야 합니다.

인벤토리 파일에 모든 서버를 등록해두면, 새로운 팀원이 합류했을 때 "여기 인벤토리 파일 보세요"라고 한마디만 하면 됩니다. 많은 스타트업과 대기업에서 이런 방식으로 수백, 수천 대의 서버를 관리하고 있습니다.

하지만 주의할 점도 있습니다. 초보 엔지니어들이 흔히 하는 실수 중 하나는 인벤토리 파일에 비밀번호를 평문으로 적어두는 것입니다.

ansible_password=1234처럼 적으면 보안에 큰 구멍이 생깁니다. 이 파일이 Git에 올라가는 순간 누구나 비밀번호를 볼 수 있기 때문입니다.

따라서 SSH 키 인증을 사용하거나 Ansible Vault로 암호화해야 합니다. 또 다른 실수는 인벤토리 파일을 너무 복잡하게 만드는 것입니다.

처음부터 모든 정보를 다 넣으려고 하지 마세요. 필요한 것부터 하나씩 추가하는 것이 좋습니다.

다시 김인프라 씨의 이야기로 돌아가 봅시다. 박데브옵스 씨의 조언을 듣고 첫 인벤토리 파일을 만든 김인프라 씨는 감탄했습니다.

"이제 서버 목록을 한눈에 볼 수 있네요!" 인벤토리 파일을 제대로 작성하면 서버 관리의 첫 걸음을 성공적으로 뗄 수 있습니다. 여러분도 오늘 배운 내용을 바탕으로 자신만의 인벤토리 파일을 만들어 보세요.

실전 팁

💡 - INI 형식 외에도 YAML 형식으로도 작성할 수 있습니다. 복잡한 구조는 YAML이 더 읽기 쉽습니다.

  • 기본 경로는 /etc/ansible/hosts이지만, -i 옵션으로 원하는 파일을 지정할 수 있습니다.
  • 주석은 #으로 시작하며, 코드처럼 주석을 적극 활용하면 나중에 보기 편합니다.

2. 호스트 그룹 정의하기

김인프라 씨는 인벤토리 파일에 50대의 서버를 모두 등록했습니다. 하지만 새로운 문제가 생겼습니다.

웹 서버에만 Nginx 설정을 업데이트하고 싶은데, 50줄의 목록에서 웹 서버만 찾아내기가 쉽지 않았습니다. 박데브옵스 씨가 다시 조언했습니다.

"호스트 그룹을 만들어보세요. 서버를 역할별로 묶으면 훨씬 편해집니다."

호스트 그룹은 비슷한 역할을 하는 서버들을 하나의 이름으로 묶어두는 기능입니다. 마치 회사 조직도에서 개발팀, 디자인팀, 마케팅팀으로 나누는 것처럼, 서버도 웹 서버 그룹, 데이터베이스 그룹, API 서버 그룹으로 나눌 수 있습니다.

그룹 이름 하나로 여러 대의 서버를 동시에 관리할 수 있게 됩니다.

다음 코드를 살펴봅시다.

# inventory.ini
[webservers]
web1.example.com
web2.example.com
web3.example.com

[databases]
db1.example.com ansible_host=10.0.0.5
db2.example.com ansible_host=10.0.0.6

[apiserver]
api1.example.com
api2.example.com

# 모든 프로덕션 서버를 하나로 묶기
[production:children]
webservers
databases
apiserver

김인프라 씨는 이제 50대의 서버 목록을 인벤토리 파일로 관리하고 있습니다. 하지만 실제로 작업할 때마다 난관에 부딪혔습니다.

"웹 서버 20대에만 SSL 인증서를 갱신해야 하는데, 어떻게 하지?" 처음에는 웹 서버 20대의 이름을 일일이 타이핑했습니다. ansible web1,web2,web3...web20 -m command -a "renew-cert" 이런 식으로요.

타이핑하다가 실수로 잘못 입력하기도 했고, 서버 한 대를 빼먹어서 나중에 문제가 생긴 적도 있습니다. 박데브옵스 씨가 김인프라 씨의 화면을 보더니 웃으며 말했습니다.

"그렇게 하나하나 적지 마세요. 호스트 그룹을 사용하면 ansible webservers 이렇게 한 줄이면 끝이에요." 그렇다면 호스트 그룹이란 정확히 무엇일까요?

쉽게 비유하자면, 호스트 그룹은 마치 메일링 리스트와 같습니다. 회사에서 "개발팀 전체"라는 메일링 리스트를 만들어두면, 개발자들에게 공지할 때 일일이 개인 이메일을 입력하지 않아도 됩니다.

"개발팀 전체"라는 주소 하나로 모든 개발자에게 메일을 보낼 수 있죠. 호스트 그룹도 마찬가지입니다.

[webservers]라는 그룹을 만들어두면 웹 서버 전체를 하나의 이름으로 다룰 수 있습니다. 호스트 그룹이 없던 시절에는 어땠을까요?

서버가 몇 대 안 될 때는 괜찮았습니다. 하지만 서버가 늘어나면서 관리가 복잡해졌습니다.

매번 명령을 실행할 때마다 "이번에는 웹 서버들만 선택해야 하는데..." 하며 서버 목록을 뒤져야 했습니다. 실수로 데이터베이스 서버에 웹 서버용 명령을 실행해서 장애가 발생한 적도 있었습니다.

바로 이런 문제를 해결하기 위해 호스트 그룹 기능이 등장했습니다. 호스트 그룹을 사용하면 역할별로 서버를 논리적으로 구분할 수 있습니다.

또한 명령어가 간결해져서 실수가 줄어듭니다. 무엇보다 새로운 서버를 추가할 때 해당 그룹에만 넣으면 되므로 확장성이 뛰어납니다.

위의 코드를 한 줄씩 살펴보겠습니다. [webservers]는 그룹의 시작을 알리는 헤더입니다.

대괄호 안에 그룹 이름을 적으면 됩니다. 그 아래에 나열된 서버들은 모두 webservers 그룹에 속하게 됩니다.

[databases] 그룹도 마찬가지입니다. 데이터베이스 역할을 하는 서버들을 모아둡니다.

그룹 안에서도 ansible_host 같은 변수를 자유롭게 사용할 수 있습니다. 특별한 기능도 있습니다.

[production:children]처럼 :children을 붙이면 그룹의 그룹을 만들 수 있습니다. 이 경우 production 그룹은 webservers, databases, apiserver를 모두 포함합니다.

마치 조직도에서 "기술본부"가 "개발팀", "인프라팀", "QA팀"을 포함하는 것과 같습니다. 실제 현업에서는 어떻게 활용할까요?

대형 커머스 사이트를 예로 들어봅시다. 전체 서버를 역할별로 나누면 이렇게 됩니다: 프론트엔드 서버, 백엔드 API 서버, 결제 서버, 검색 서버, 이미지 서버, 데이터베이스 서버, 캐시 서버.

각 역할마다 그룹을 만들어두면 "이번 배포는 백엔드 API 서버만" 또는 "긴급 패치는 모든 서버에" 같은 작업을 쉽게 할 수 있습니다. 넷플릭스, 에어비앤비 같은 대형 서비스에서도 이런 방식으로 수천 대의 서버를 관리합니다.

하지만 주의할 점도 있습니다. 초보자들이 흔히 하는 실수 중 하나는 그룹을 너무 세세하게 나누는 것입니다.

[web-nginx-production-seoul-az-a] 같은 식으로 이름을 복잡하게 만들면 오히려 관리가 어려워집니다. 그룹 이름은 간단하고 명확하게 짓는 것이 좋습니다.

또 다른 실수는 한 서버를 여러 그룹에 중복으로 넣은 다음 같은 작업을 두 번 실행하는 것입니다. Ansible은 똑똑해서 중복을 피해주긴 하지만, 의도를 명확하게 하는 것이 좋습니다.

다시 김인프라 씨의 이야기로 돌아가 봅시다. 그룹을 만든 후 김인프라 씨는 명령어가 놀라울 정도로 간단해졌다는 것을 깨달았습니다.

ansible webservers -m shell -a "systemctl restart nginx" 한 줄이면 20대의 웹 서버가 일제히 재시작됩니다. 호스트 그룹을 제대로 구성하면 서버 관리 효율이 몇 배로 높아집니다.

여러분도 자신의 인프라 환경에 맞는 그룹 구조를 설계해 보세요.

실전 팁

💡 - 그룹 이름은 복수형으로 짓는 것이 관례입니다 (webserver보다 webservers)

  • 한 서버가 여러 그룹에 속할 수 있습니다. 예를 들어 web1은 webservers와 production 두 그룹에 동시에 속할 수 있습니다.
  • :children 기능으로 계층 구조를 만들면 대규모 인프라도 체계적으로 관리할 수 있습니다.

3. 호스트 변수 설정

그룹을 잘 나눈 김인프라 씨는 또 다른 문제를 발견했습니다. 같은 웹 서버 그룹 안에서도 서버마다 SSH 포트가 다르고, 접속하는 계정 이름도 달랐습니다.

web1은 22번 포트를 쓰는데 web2는 보안상 2222번 포트를 사용하고 있었죠. 박데브옵스 씨가 말했습니다.

"각 서버마다 변수를 설정할 수 있어요. 호스트 변수를 사용하면 됩니다."

호스트 변수는 각 서버마다 다른 설정값을 지정하는 기능입니다. 마치 직원마다 직급, 부서, 근무지가 다른 것처럼, 서버도 포트 번호, 사용자 이름, 접속 방식 등이 다를 수 있습니다.

호스트 변수를 사용하면 서버별 특성을 세밀하게 관리할 수 있습니다.

다음 코드를 살펴봅시다.

# inventory.ini
[webservers]
# 기본 22번 포트 사용
web1.example.com ansible_user=ubuntu

# 보안을 위해 2222번 포트 사용
web2.example.com ansible_port=2222 ansible_user=admin

# Python 경로가 다른 서버
web3.example.com ansible_python_interpreter=/usr/bin/python3.9

[databases]
# SSH 키 경로를 지정
db1.example.com ansible_ssh_private_key_file=~/.ssh/db_key
# 연결 타임아웃 설정
db2.example.com ansible_connection_timeout=30

김인프라 씨는 어느 날 이상한 오류를 마주했습니다. 분명히 ansible webservers -m ping으로 모든 웹 서버에 핑을 보냈는데, web2만 계속 접속에 실패했습니다.

"왜 나만 안 되는 거야?" 로그를 자세히 보니 22번 포트로 접속을 시도하다가 실패한 것이었습니다. 알고 보니 web2는 보안팀의 요청으로 SSH 포트를 2222번으로 변경해둔 서버였습니다.

김인프라 씨는 당황했습니다. "그럼 web2는 따로 명령을 실행해야 하나?" 박데브옵스 씨가 다가와 설명했습니다.

"아니요, 호스트 변수를 쓰면 돼요. 각 서버마다 다른 설정을 줄 수 있거든요." 그렇다면 호스트 변수란 정확히 무엇일까요?

쉽게 비유하자면, 호스트 변수는 마치 직원 개인의 프로필 정보와 같습니다. 회사 HR 시스템에는 모든 직원이 등록되어 있지만, 각자 다른 정보를 가지고 있습니다.

김개발 씨는 개발팀 소속에 서울 본사 근무, 박디자인 씨는 디자인팀 소속에 부산 지사 근무 이런 식이죠. 서버도 마찬가지로 그룹에는 속해 있지만 각자 고유한 설정을 가질 수 있습니다.

호스트 변수가 없던 시절에는 어땠을까요? 서버마다 설정이 조금씩 다르면 별도의 인벤토리 파일을 만들어야 했습니다.

web-22.ini, web-2222.ini 이런 식으로 파일이 늘어났습니다. 또는 플레이북 안에 조건문을 잔뜩 넣어서 "이 서버면 이렇게, 저 서버면 저렇게" 분기 처리를 했습니다.

코드가 복잡해지고 유지보수가 어려워졌습니다. 바로 이런 문제를 해결하기 위해 호스트 변수가 등장했습니다.

호스트 변수를 사용하면 인벤토리 파일 한 곳에서 모든 서버의 개별 설정을 관리할 수 있습니다. 또한 플레이북은 깔끔하게 유지하면서 서버별 차이는 인벤토리에서 흡수할 수 있습니다.

무엇보다 새로운 서버를 추가할 때 필요한 변수만 명시하면 되므로 확장이 쉽습니다. 위의 코드를 한 줄씩 살펴보겠습니다.

ansible_user=ubuntu는 이 서버에 접속할 때 ubuntu라는 계정을 사용하라는 뜻입니다. 기본값은 현재 실행 중인 사용자의 계정인데, 서버마다 다른 계정을 사용한다면 이렇게 지정하면 됩니다.

ansible_port=2222는 SSH 접속 포트를 변경합니다. 기본 22번 대신 다른 포트를 사용하는 서버에 유용합니다.

보안 강화를 위해 비표준 포트를 쓰는 경우가 많기 때문에 실무에서 자주 사용됩니다. ansible_python_interpreter는 특히 중요합니다.

Ansible은 타겟 서버에서 Python을 실행해야 작동하는데, 서버마다 Python이 설치된 경로가 다를 수 있습니다. 어떤 서버는 /usr/bin/python, 어떤 서버는 /usr/bin/python3.9를 쓰죠.

이 변수로 명확히 지정하면 오류를 방지할 수 있습니다. ansible_ssh_private_key_file은 SSH 키 인증을 사용할 때 키 파일 경로를 지정합니다.

보안상 비밀번호 대신 SSH 키를 사용하는 것이 권장되므로 실무에서 필수적인 설정입니다. 실제 현업에서는 어떻게 활용할까요?

글로벌 서비스를 운영한다고 가정해봅시다. 서울 리전, 도쿄 리전, 싱가포르 리전에 각각 서버가 있습니다.

각 리전마다 접속 계정, 네트워크 설정, 방화벽 규칙이 조금씩 다릅니다. 호스트 변수를 활용하면 이런 차이를 깔끔하게 표현할 수 있습니다.

AWS, GCP, Azure 같은 클라우드 환경에서도 마찬가지입니다. 인스턴스마다 다른 IAM 역할, 보안 그룹, 키 페어를 사용하는데, 호스트 변수로 이를 관리합니다.

하지만 주의할 점도 있습니다. 초보자들이 흔히 하는 실수 중 하나는 민감한 정보를 호스트 변수에 평문으로 넣는 것입니다.

ansible_password=mysecret123 같은 식으로 비밀번호를 그대로 적으면 Git에 올라가는 순간 보안 사고가 납니다. 반드시 Ansible Vault로 암호화하거나 환경변수를 사용해야 합니다.

또 다른 실수는 모든 설정을 호스트 변수로 하려는 것입니다. 공통적으로 적용되는 설정은 그룹 변수나 플레이북 변수로 빼는 것이 좋습니다.

호스트 변수는 정말 서버마다 달라야 하는 것만 설정하세요. 다시 김인프라 씨의 이야기로 돌아가 봅시다.

web2에 ansible_port=2222를 추가한 후 다시 핑을 보냈더니 성공했습니다. "오, 이제 되네요!" 김인프라 씨는 감탄했습니다.

호스트 변수를 적절히 활용하면 서버별 특성을 존중하면서도 통일된 방식으로 관리할 수 있습니다. 여러분도 자신의 서버 환경에 맞게 호스트 변수를 설정해 보세요.

실전 팁

💡 - ansible_connection 변수로 SSH 외에 WinRM(윈도우), local, docker 같은 다른 접속 방식도 지정할 수 있습니다.

  • 변수 이름은 대소문자를 구분하지 않지만, 소문자로 통일하는 것이 관례입니다.
  • 호스트 변수는 그룹 변수보다 우선순위가 높습니다. 나중에 배울 우선순위 개념을 잘 이해해야 합니다.

4. 그룹 변수와 상속

김인프라 씨는 이제 각 서버마다 변수를 설정할 수 있게 되었습니다. 그런데 문제가 생겼습니다.

웹 서버 20대 모두에 ansible_user=deploy를 설정해야 하는데, 20번이나 똑같은 내용을 반복해서 적어야 했습니다. "이거 너무 비효율적인데..." 박데브옵스 씨가 웃으며 말했습니다.

"그룹 변수를 쓰면 한 번만 적으면 돼요."

그룹 변수는 그룹에 속한 모든 서버가 공통으로 사용하는 설정을 한 곳에 정의하는 기능입니다. 마치 회사에서 부서별 공통 규정을 정하는 것과 같습니다.

개발팀 전체가 같은 출퇴근 시간, 같은 복지 혜택을 공유하듯이, 웹 서버 그룹 전체가 같은 사용자 계정, 같은 환경 변수를 공유할 수 있습니다.

다음 코드를 살펴봅시다.

# inventory.ini
[webservers]
web1.example.com
web2.example.com
web3.example.com

[webservers:vars]
ansible_user=deploy
ansible_python_interpreter=/usr/bin/python3
http_port=80
domain_suffix=.example.com

[databases]
db1.example.com
db2.example.com

[databases:vars]
ansible_user=dbadmin
max_connections=100
# 그룹 전체에 적용되는 백업 경로
backup_dir=/var/backups/db

김인프라 씨는 웹 서버 그룹에 새로운 정책을 적용해야 했습니다. 보안팀에서 모든 웹 서버는 deploy라는 전용 계정으로만 접속하라는 규정을 만들었기 때문입니다.

김인프라 씨는 인벤토리 파일을 열고 web1부터 web20까지 하나하나 ansible_user=deploy를 추가하기 시작했습니다. 다섯 번째 서버쯤 수정하고 있을 때 박데브옵스 씨가 지나가다 화면을 보고는 "잠깐만요!"라고 외쳤습니다.

"그렇게 하나씩 안 적어도 돼요. 그룹 변수라는 게 있어요." 김인프라 씨는 고개를 갸우뚱했습니다.

"그룹 변수요?" 그렇다면 그룹 변수란 정확히 무엇일까요? 쉽게 비유하자면, 그룹 변수는 마치 부서별 공통 규정과 같습니다.

회사에서 "마케팅팀은 모두 2층에서 근무한다", "개발팀은 모두 MacBook Pro를 지급받는다" 같은 규정을 만들면, 일일이 개인별로 지정하지 않아도 팀에 속하는 순간 자동으로 적용됩니다. 그룹 변수도 마찬가지입니다.

webservers 그룹에 변수를 하나 설정하면, 그룹에 속한 모든 서버가 그 변수를 자동으로 물려받습니다. 그룹 변수가 없던 시절에는 어땠을까요?

같은 설정을 수십 번 복사해서 붙여넣어야 했습니다. 서버가 10대면 10번, 100대면 100번 반복했죠.

더 큰 문제는 나중에 설정을 변경할 때였습니다. "모든 웹 서버의 포트를 8080으로 바꿔야 해요." 이런 요청이 오면 100줄을 일일이 찾아 수정해야 했습니다.

하나라도 빠뜨리면 장애로 이어졌습니다. 바로 이런 문제를 해결하기 위해 그룹 변수가 등장했습니다.

그룹 변수를 사용하면 공통 설정을 한 곳에서 관리할 수 있습니다. 설정을 변경할 때도 한 줄만 수정하면 전체에 적용됩니다.

무엇보다 DRY 원칙(Don't Repeat Yourself)을 지킬 수 있어 실수를 줄이고 유지보수가 쉬워집니다. 위의 코드를 한 줄씩 살펴보겠습니다.

[webservers:vars]는 webservers 그룹의 변수 섹션을 시작합니다. 콜론(:) 뒤에 vars를 붙이면 "이 그룹의 공통 변수를 정의하겠다"는 의미입니다.

그 아래 적힌 모든 변수는 webservers 그룹에 속한 web1, web2, web3 모두에게 적용됩니다. ansible_user=deploy는 Ansible의 특수 변수입니다.

이 그룹의 모든 서버에 접속할 때 deploy 계정을 사용하라는 뜻이죠. 이제 web1, web2, web3 어디에 접속하든 자동으로 deploy 계정이 사용됩니다.

http_port=80 같은 커스텀 변수도 자유롭게 만들 수 있습니다. 이 변수는 나중에 플레이북에서 {{ http_port }}로 참조할 수 있습니다.

예를 들어 Nginx 설정 파일을 배포할 때 이 값을 사용할 수 있죠. 여기서 중요한 개념이 나옵니다.

바로 변수 상속입니다. 앞에서 배운 [production:children] 같은 부모-자식 그룹 구조에서는 부모의 변수가 자식에게 상속됩니다.

예를 들어 production 그룹에 env=prod라는 변수를 설정하면, production의 자식인 webservers, databases도 모두 env=prod 변수를 물려받습니다. 실제 현업에서는 어떻게 활용할까요?

대형 핀테크 서비스를 예로 들어봅시다. 전체 인프라를 production, staging, development 환경으로 나눕니다.

각 환경마다 다른 데이터베이스 URL, 다른 API 키, 다른 로그 레벨을 사용합니다. 이럴 때 환경별로 그룹을 만들고 그룹 변수로 환경별 설정을 관리하면 편리합니다.

카카오, 네이버, 쿠팡 같은 대형 서비스에서도 이런 방식으로 수천 대의 서버를 환경별로 관리합니다. 하지만 주의할 점도 있습니다.

초보자들이 흔히 하는 실수 중 하나는 그룹 변수와 호스트 변수를 혼동하는 것입니다. 만약 web1에 ansible_user=ubuntu를 호스트 변수로 설정했는데, webservers 그룹에는 ansible_user=deploy가 있다면 어떻게 될까요?

답은 "호스트 변수가 이긴다"입니다. Ansible은 더 구체적인 설정을 우선합니다.

또 다른 실수는 그룹 변수에 서버별로 달라야 하는 값을 넣는 것입니다. 예를 들어 IP 주소는 당연히 서버마다 다르므로 그룹 변수로 설정하면 안 됩니다.

공통적인 것만 그룹 변수로, 개별적인 것은 호스트 변수로 명확히 구분하세요. 더 나은 방법도 있습니다.

인벤토리 파일이 길어지면 group_vars/ 디렉토리를 만들어 그룹별로 파일을 분리할 수 있습니다. group_vars/webservers.yml이라는 파일을 만들고 거기에 변수를 YAML 형식으로 정리하면 훨씬 깔끔합니다.

다시 김인프라 씨의 이야기로 돌아가 봅시다. 박데브옵스 씨의 조언대로 [webservers:vars] 섹션을 추가하고 단 한 줄만 적었습니다.

20개 서버를 하나하나 수정하던 수고가 사라졌습니다. "와, 진짜 편하네요!" 그룹 변수를 잘 활용하면 반복을 줄이고 일관성을 유지할 수 있습니다.

여러분도 공통 설정은 그룹 변수로, 개별 설정은 호스트 변수로 구분하는 습관을 들여보세요.

실전 팁

💡 - 변수 우선순위는 호스트 변수 > 그룹 변수 > 플레이북 변수 순입니다.

  • group_vars/all.yml 파일을 만들면 모든 호스트에 적용되는 전역 변수를 정의할 수 있습니다.
  • YAML 형식으로 변수를 정의하면 중첩된 구조도 표현할 수 있어 복잡한 설정을 다룰 때 유용합니다.

5. 동적 인벤토리 개념

김인프라 씨는 이제 정적 인벤토리를 능숙하게 다룰 수 있게 되었습니다. 그런데 어느 날 큰 문제가 생겼습니다.

회사가 클라우드 오토스케일링을 도입하면서 서버가 자동으로 생성되고 삭제되기 시작한 것입니다. 트래픽이 많으면 서버가 20대로 늘어났다가, 새벽에는 5대로 줄어들었습니다.

매번 인벤토리 파일을 수정할 수는 없었습니다. 박데브옵스 씨가 말했습니다.

"이제 동적 인벤토리를 써야 할 때가 왔네요."

동적 인벤토리는 서버 목록을 텍스트 파일에 고정하지 않고, 실행 시점에 외부 시스템에서 자동으로 가져오는 방식입니다. 마치 전화번호부를 수기로 쓰지 않고 스마트폰이 클라우드에서 연락처를 동기화하는 것과 같습니다.

AWS, GCP, Azure 같은 클라우드 API에서 현재 실행 중인 서버 목록을 실시간으로 읽어옵니다.

다음 코드를 살펴봅시다.

# AWS 동적 인벤토리 예시 (aws_ec2.yml)
plugin: aws_ec2
regions:
  - ap-northeast-2
  - us-east-1
filters:
  tag:Environment: production
  instance-state-name: running
keyed_groups:
  # 태그로 그룹 자동 생성
  - key: tags.Role
    prefix: role
  - key: tags.Environment
    prefix: env
hostnames:
  - tag:Name
  - private-ip-address

# 동적 인벤토리 스크립트 예시 (Python)
#!/usr/bin/env python3
import json
import boto3

ec2 = boto3.client('ec2', region_name='ap-northeast-2')
response = ec2.describe_instances(Filters=[
    {'Name': 'instance-state-name', 'Values': ['running']}
])

inventory = {'_meta': {'hostvars': {}}, 'all': {'hosts': []}}
for reservation in response['Reservations']:
    for instance in reservation['Instances']:
        # 실행 중인 인스턴스만 인벤토리에 추가
        inventory['all']['hosts'].append(instance['PrivateIpAddress'])

print(json.dumps(inventory))

김인프라 씨는 어느 월요일 아침 출근해서 깜짝 놀랐습니다. 금요일에 50대였던 서버가 월요일에는 70대로 늘어나 있었습니다.

주말에 갑자기 트래픽이 몰려서 오토스케일링이 작동한 것이었습니다. 20대의 새로운 서버는 인벤토리 파일에 없었습니다.

"이거 언제 다 추가하지..." 한숨을 쉬던 김인프라 씨에게 박데브옵스 씨가 물었습니다. "아직도 수동으로 인벤토리 파일 관리하세요?

동적 인벤토리 쓰면 자동으로 되는데." 김인프라 씨는 처음 듣는 말이었습니다. "동적 인벤토리요?" 그렇다면 동적 인벤토리란 정확히 무엇일까요?

쉽게 비유하자면, 동적 인벤토리는 마치 스마트폰의 클라우드 연락처 동기화와 같습니다. 옛날에는 종이 전화번호부에 일일이 이름과 번호를 적었습니다.

친구가 번호를 바꾸면 지우고 다시 써야 했죠. 하지만 지금은 클라우드에 저장된 연락처가 자동으로 동기화됩니다.

친구가 번호를 바꿔도 자동으로 업데이트됩니다. 동적 인벤토리도 마찬가지로 클라우드 API에서 현재 서버 목록을 실시간으로 가져옵니다.

동적 인벤토리가 없던 시절에는 어땠을까요? 클라우드 초창기, 개발자들은 서버가 생성될 때마다 수동으로 인벤토리 파일을 수정했습니다.

오토스케일링으로 서버 10대가 생기면 10줄을 추가하고, 5대가 삭제되면 5줄을 지웠습니다. 24시간 내내 서버가 변하는데 사람이 따라갈 수 없었습니다.

결국 인벤토리가 실제 환경과 맞지 않아서 배포가 실패하거나 잘못된 서버에 명령을 실행하는 사고가 빈번했습니다. 바로 이런 문제를 해결하기 위해 동적 인벤토리가 등장했습니다.

동적 인벤토리를 사용하면 항상 최신 서버 목록으로 작업할 수 있습니다. 새로운 서버가 생기면 자동으로 인식되고, 삭제된 서버는 자동으로 제외됩니다.

무엇보다 서버를 EC2 태그로 분류하면 "Environment=production" 태그를 가진 서버만 자동으로 그룹핑할 수 있어 매우 강력합니다. 위의 코드를 한 줄씩 살펴보겠습니다.

첫 번째 예시는 Ansible의 AWS EC2 플러그인을 사용한 방법입니다. plugin: aws_ec2는 AWS에서 인벤토리를 가져오겠다는 선언입니다.

regions에 리전을 지정하면 해당 리전의 서버만 가져옵니다. filters 부분이 핵심입니다.

instance-state-name: running은 현재 실행 중인 인스턴스만 가져오라는 뜻입니다. 중지되거나 종료된 서버는 자동으로 제외됩니다.

tag:Environment: production은 Environment 태그가 production인 서버만 선택합니다. keyed_groups는 태그를 기반으로 자동으로 그룹을 만들어줍니다.

예를 들어 Role 태그가 webserver인 서버들은 자동으로 role_webserver 그룹에 들어갑니다. 수동으로 그룹을 정의할 필요가 없어집니다.

두 번째 예시는 Python 스크립트로 직접 만드는 방법입니다. boto3는 AWS를 제어하는 Python 라이브러리입니다.

describe_instances()로 현재 실행 중인 모든 EC2 인스턴스를 조회하고, JSON 형식으로 출력합니다. Ansible은 이 JSON을 파싱해서 인벤토리로 사용합니다.

실제 현업에서는 어떻게 활용할까요? 대규모 쇼핑몰을 운영한다고 가정해봅시다.

평상시에는 서버 30대로 충분하지만, 블랙 프라이데이 같은 이벤트 때는 200대로 늘어납니다. 동적 인벤토리를 사용하면 서버가 몇 대든 상관없이 항상 전체에 배포할 수 있습니다.

Netflix는 수만 대의 서버를 AWS에서 운영하는데, 모두 동적 인벤토리로 관리합니다. 서버를 수동으로 관리하는 것은 불가능하기 때문입니다.

Multi-cloud 환경도 가능합니다. AWS, GCP, Azure를 함께 쓰는 회사라면 각 클라우드의 동적 인벤토리 플러그인을 조합해서 사용할 수 있습니다.

전체 인프라를 통합된 시각으로 볼 수 있습니다. 하지만 주의할 점도 있습니다.

초보자들이 흔히 하는 실수 중 하나는 동적 인벤토리를 실행할 때마다 API를 호출해서 비용이 발생한다는 것을 모르는 것입니다. AWS API는 대부분 무료지만 과도하게 호출하면 Rate Limit에 걸릴 수 있습니다.

캐싱을 적절히 활용하세요. 또 다른 실수는 권한 설정을 잘못하는 것입니다.

동적 인벤토리 스크립트가 클라우드 API에 접근하려면 적절한 IAM 권한이 필요합니다. EC2를 읽을 수 있는 최소 권한만 부여하는 것이 좋습니다.

다시 김인프라 씨의 이야기로 돌아가 봅시다. 박데브옵스 씨의 도움으로 AWS 동적 인벤토리를 설정한 김인프라 씨는 감동했습니다.

ansible-inventory -i aws_ec2.yml --list를 실행하자 현재 실행 중인 모든 서버가 자동으로 나타났습니다. "이제 서버가 아무리 늘어나도 걱정없네요!" 동적 인벤토리를 제대로 활용하면 클라우드 환경의 유연성을 최대한 활용할 수 있습니다.

여러분도 오토스케일링을 사용한다면 동적 인벤토리를 꼭 도입해 보세요.

실전 팁

💡 - ansible-inventory 명령으로 동적 인벤토리가 제대로 작동하는지 미리 확인할 수 있습니다.

  • 정적 인벤토리와 동적 인벤토리를 함께 사용할 수도 있습니다. 디렉토리에 두 파일을 모두 넣으면 Ansible이 자동으로 합쳐줍니다.
  • 캐싱을 활성화하면 매번 API를 호출하지 않아 속도가 빨라지고 비용도 절약됩니다.

6. 인벤토리 패턴과 선택

김인프라 씨는 이제 정적 인벤토리와 동적 인벤토리를 모두 다룰 수 있게 되었습니다. 하지만 실제로 명령을 실행하려니 또 다른 고민이 생겼습니다.

"웹 서버 중에서도 서울 리전에 있는 것만 재시작하고 싶은데, 어떻게 선택하지?" 박데브옵스 씨가 설명했습니다. "인벤토리 패턴을 사용하면 원하는 서버만 정확하게 선택할 수 있어요."

인벤토리 패턴은 인벤토리에서 특정 서버나 그룹을 선택하는 문법입니다. 마치 검색 엔진에서 고급 검색 옵션을 사용하는 것과 같습니다.

와일드카드, 논리 연산, 정규표현식 등을 활용해서 "웹 서버이면서 프로덕션 환경인 것", "데이터베이스 서버 중 홀수 번호만" 같은 복잡한 조건도 표현할 수 있습니다.

다음 코드를 살펴봅시다.

# 기본 패턴 사용법
# 모든 호스트
ansible all -m ping

# 특정 그룹
ansible webservers -m shell -a "uptime"

# 여러 그룹 (OR 조건)
ansible webservers:databases -m ping

# 교집합 (AND 조건) - 웹서버이면서 프로덕션
ansible webservers:&production -m ping

# 차집합 (NOT 조건) - 웹서버 중 스테이징 제외
ansible webservers:!staging -m ping

# 와일드카드 사용
ansible web* -m ping
ansible *.example.com -m ping

# 정규표현식 (~ 사용)
ansible ~web[0-9]+ -m ping

# 복잡한 조건 조합
ansible webservers:&production:!web1.example.com -m ping

김인프라 씨는 긴급 점검 요청을 받았습니다. "프로덕션 환경의 웹 서버 중에서 web1만 빼고 모두 재시작해 주세요." 조건이 복잡했습니다.

프로덕션이어야 하고, 웹 서버여야 하고, web1은 제외해야 했습니다. 처음에 김인프라 씨는 당황했습니다.

"이걸 어떻게 선택하지? 하나씩 지정해야 하나?" 서버 이름을 일일이 나열하기 시작했습니다.

ansible web2,web3,web4... 이런 식으로요. 박데브옵스 씨가 웃으며 멈춰 세웠습니다.

"그렇게 안 해도 돼요. 패턴 하나면 끝이에요.

ansible webservers:&production:!web1이라고 치면 됩니다." 김인프라 씨는 눈이 휘둥그레졌습니다. "한 줄로 그게 가능하다고요?" 그렇다면 인벤토리 패턴이란 정확히 무엇일까요?

쉽게 비유하자면, 인벤토리 패턴은 마치 이메일 필터링 규칙과 같습니다. 이메일 앱에서 "발신자가 회사 도메인이고, 제목에 '긴급'이 포함되고, 스팸 폴더는 제외"라는 복잡한 조건을 만들 수 있죠.

인벤토리 패턴도 "이 그룹에 속하고, 저 그룹에도 속하고, 특정 서버는 제외"라는 조건을 간결하게 표현할 수 있습니다. 인벤토리 패턴이 없던 시절에는 어땠을까요?

복잡한 조건을 만족하는 서버를 선택하려면 임시 그룹을 만들거나, 스크립트를 작성해야 했습니다. 또는 서버 이름을 일일이 나열했죠.

조건이 바뀔 때마다 코드를 수정해야 했고, 실수로 잘못된 서버에 명령을 실행하는 사고도 잦았습니다. 바로 이런 문제를 해결하기 위해 인벤토리 패턴이 등장했습니다.

패턴을 사용하면 복잡한 조건도 한 줄로 표현할 수 있습니다. 읽기 쉽고, 실수가 줄어들며, 유지보수도 편해집니다.

무엇보다 명령을 실행하기 전에 정확히 어떤 서버가 선택될지 예측할 수 있습니다. 위의 코드를 한 줄씩 살펴보겠습니다.

all은 인벤토리의 모든 호스트를 의미합니다. 간단하지만 위험할 수 있으니 주의해서 사용해야 합니다.

실수로 전체 서버를 재시작할 수도 있으니까요. webservers:databases처럼 콜론(:)으로 구분하면 OR 조건입니다.

"웹 서버이거나 데이터베이스 서버"를 의미합니다. 두 그룹을 합친 결과가 선택됩니다.

:&는 AND 조건입니다. webservers:&production은 "웹 서버이면서 동시에 프로덕션 그룹에도 속한 것"을 의미합니다.

교집합을 구하는 것이죠. :!는 NOT 조건입니다.

webservers:!staging은 "웹 서버 중에서 스테이징은 제외"라는 뜻입니다. 차집합을 구합니다.

와일드카드 *는 매우 유용합니다. web*라고 하면 web으로 시작하는 모든 호스트가 선택됩니다.

web1, web2, web99, webserver-prod 모두 매칭됩니다. 정규표현식은 더 강력합니다.

~web[0-9]+는 "web 뒤에 숫자가 하나 이상 오는 패턴"을 의미합니다. web1, web23은 매칭되지만 webserver는 매칭되지 않습니다.

복잡한 조건을 조합할 수도 있습니다. webservers:&production:!web1.example.com은 "웹 서버이면서 프로덕션 환경이고, web1.example.com은 제외"라는 의미입니다.

여러 조건을 한 줄에 담을 수 있습니다. 실제 현업에서는 어떻게 활용할까요?

대형 게임 서비스를 운영한다고 가정해봅시다. 서버가 게임 월드별로 나뉘어 있고, 각 월드는 여러 리전에 분산되어 있습니다.

"아시아 리전의 월드1 서버만 점검하고 싶다"면 world1:&asia라는 패턴을 사용할 수 있습니다. "전체 프로덕션 환경 중 긴급 점검 대상만 제외하고 배포"는 production:!emergency-maintenance로 표현합니다.

카나리 배포를 할 때도 유용합니다. 먼저 web1 한 대만 배포하고, 문제없으면 web* 전체로 확대하는 식으로 점진적 배포가 가능합니다.

하지만 주의할 점도 있습니다. 초보자들이 흔히 하는 실수 중 하나는 all 패턴을 너무 자주 사용하는 것입니다.

전체 서버에 명령을 실행하기 전에 정말로 모든 서버가 대상인지 다시 한번 확인하세요. 실수로 프로덕션 데이터베이스를 재시작하면 대형 사고가 납니다.

또 다른 실수는 복잡한 패턴을 만들고 검증하지 않는 것입니다. ansible webservers:&production:!web1 --list-hosts 같은 명령으로 먼저 어떤 서버가 선택되는지 확인하세요.

예상과 다르면 패턴을 수정하면 됩니다. 정규표현식은 강력하지만 너무 복잡하면 가독성이 떨어집니다.

간단한 와일드카드로 해결할 수 있다면 그쪽을 선택하세요. 다시 김인프라 씨의 이야기로 돌아가 봅시다.

패턴을 배운 김인프라 씨는 이제 복잡한 조건도 자신 있게 처리할 수 있게 되었습니다. "프로덕션 웹 서버 중 web1 제외하고 재시작" 같은 요청이 와도 당황하지 않습니다.

명령을 실행하기 전 --list-hosts 옵션으로 확인하는 습관도 생겼습니다. "어, 이 패턴은 서버 5대를 선택하는구나.

맞네!" 확신을 갖고 엔터 키를 눌렀습니다. 인벤토리 패턴을 마스터하면 원하는 서버를 정확하게 선택할 수 있습니다.

여러분도 오늘 배운 패턴을 실제 환경에서 연습해 보세요. 처음에는 --list-hosts로 검증하면서 점진적으로 익혀가면 됩니다.

실전 팁

💡 - --limit 옵션을 사용하면 플레이북 실행 시 패턴으로 대상을 제한할 수 있습니다.

  • ansible-inventory --graph 명령으로 그룹 계층 구조를 트리 형태로 시각화할 수 있습니다.
  • 복잡한 패턴은 변수나 설정 파일로 빼서 재사용하면 편리합니다. 매번 타이핑하지 않아도 됩니다.

이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!

#Ansible#Inventory#HostGroups#DynamicInventory#Configuration#Ansible,IaC,DevOps

댓글 (0)

댓글을 작성하려면 로그인이 필요합니다.