Red Hat Certified Engineer (RHCE) #11 Role 작성과 사용
#10 Ansible Vault에서 비밀을 암호화해 안전하게 다루는 법을 잡았다면, 이번에는 플레이북을 재사용 가능한 단위로 묶는 role을 다루겠습니다. role은 task,handler,변수,템플릿,파일을 정해진 디렉터리 구조로 묶어, 한 번 작성한 구성을 여러 플레이북에서 호출하게 해 주는 단위입니다. RHCE 시험에서 “role을 작성해 playbook에서 호출하라” 유형은 거의 매번 출제되므로, 이번 글에서 구조와 사용법을 손에 익히겠습니다.
role이 필요한 이유 #
지금까지 작성한 플레이북은 task와 handler, 변수, 템플릿이 한 파일이나 몇 개의 파일에 흩어져 있었습니다. 작은 구성에서는 문제가 없지만, 같은 패키지 설치,설정 배포,서비스 기동 묶음을 다른 플레이북에서도 쓰려면 매번 복사해야 합니다. 복사한 코드가 늘어날수록 한 곳을 고칠 때 모든 사본을 찾아 고쳐야 하므로 실수가 쌓입니다.
role은 이 묶음을 약속된 디렉터리 구조로 한 번 정리해 두고, 플레이북에서는 이름으로만 불러 쓰게 합니다. 다음 같은 장점이 생깁니다.
- 재사용. 한 번 작성한 role을 여러 플레이북에서 호출합니다.
- 구조화. task,handler,변수,파일이 정해진 위치에 들어가 찾기 쉽습니다.
- 공유. ansible-galaxy로 내려받거나 collection에 담아 배포할 수 있습니다.
role 디렉터리 구조 #
role은 정해진 하위 디렉터리 이름을 따릅니다. 각 디렉터리의 main.yml이 자동으로 로드되는 진입점입니다. 표준 구조는 다음과 같습니다.
roles/
└── webserver/
├── tasks/
│ └── main.yml # role이 수행할 task
├── handlers/
│ └── main.yml # notify로 호출되는 handler
├── defaults/
│ └── main.yml # 기본 변수(우선순위 최하위)
├── vars/
│ └── main.yml # role 변수(우선순위 높음)
├── templates/
│ └── httpd.conf.j2 # template 모듈의 src 기준 경로
├── files/
│ └── index.html # copy 모듈의 src 기준 경로
├── meta/
│ └── main.yml # 의존성과 메타데이터
└── README.md여기서 중요한 점은 경로 자동 탐색입니다. role의 task 안에서 template 모듈에 src: httpd.conf.j2라고만 적으면 Ansible이 자동으로 그 role의 templates/ 아래에서 찾습니다. copy 모듈의 src도 마찬가지로 files/ 아래를 봅니다. 따라서 role 안에서는 전체 경로를 적지 않아도 됩니다.
ansible-galaxy로 role 뼈대 만들기 #
빈 디렉터리를 손으로 만들 필요는 없습니다. ansible-galaxy role init 명령이 표준 구조를 한 번에 생성합니다.
# roles 디렉터리 안에서 실행
$ cd roles
$ ansible-galaxy role init webserver
- Role webserver was created successfully
$ tree webserver
webserver/
├── defaults
│ └── main.yml
├── files
├── handlers
│ └── main.yml
├── meta
│ └── main.yml
├── README.md
├── tasks
│ └── main.yml
├── templates
├── tests
│ ├── inventory
│ └── test.yml
└── vars
└── main.yml생성된 각 main.yml은 비어 있는 뼈대이므로, 필요한 디렉터리의 내용만 채우면 됩니다. 시험에서는 이 명령으로 뼈대를 만든 뒤 tasks/main.yml과 필요한 변수,템플릿만 작성하는 흐름이 빠릅니다.
role 내용 작성하기 #
webserver role을 예로 task와 handler, defaults, template을 채우겠습니다.
tasks/main.yml #
---
# roles/webserver/tasks/main.yml
- name: httpd 패키지를 설치한다
ansible.builtin.dnf:
name: httpd
state: present
- name: index.html을 배포한다
ansible.builtin.copy:
src: index.html # files/index.html을 자동으로 찾습니다
dest: /var/www/html/index.html
- name: httpd 설정을 배포한다
ansible.builtin.template:
src: httpd.conf.j2 # templates/httpd.conf.j2를 자동으로 찾습니다
dest: /etc/httpd/conf/httpd.conf
notify: restart httpd
- name: httpd 서비스를 기동한다
ansible.builtin.service:
name: httpd
state: started
enabled: truehandlers/main.yml #
---
# roles/webserver/handlers/main.yml
- name: restart httpd
ansible.builtin.service:
name: httpd
state: restarteddefaults/main.yml #
---
# roles/webserver/defaults/main.yml
http_port: 80
server_admin: admin@example.comdefaults에 둔 변수는 우선순위가 가장 낮으므로, 플레이북에서 role을 호출할 때 같은 이름의 값을 넘기면 손쉽게 덮어쓸 수 있습니다. role을 쓰는 쪽이 조정할 여지가 있는 값은 vars가 아니라 defaults에 두는 것이 관례입니다.
role 사용하기 #
작성한 role을 플레이북에서 호출하는 방법은 크게 세 가지입니다. roles 키, include_role, import_role이며, 동작 방식이 서로 다릅니다.
roles 키 #
가장 단순한 방법은 play 수준의 roles 키에 나열하는 것입니다. role의 task가 play의 일반 task보다 먼저 실행됩니다.
---
# site.yml
- name: 웹 서버를 구성한다
hosts: web
become: true
roles:
- webserver변수를 넘기려면 다음처럼 적습니다.
roles:
- role: webserver
vars:
http_port: 8080include_role과 import_role #
roles 키 대신 task 목록 안에서 role을 호출하면 실행 위치를 직접 제어할 수 있습니다. 이때 두 모듈의 차이가 시험 포인트입니다.
| 항목 | import_role | include_role |
|---|---|---|
| 처리 시점 | 정적(static). 플레이북 파싱 단계에서 미리 포함 | 동적(dynamic). 실행 시점에 포함 |
| when 적용 | 내부 모든 task에 조건이 개별 적용 | role 호출 한 번에 조건이 적용 |
| loop 사용 | 불가 | 가능. role을 반복 호출 |
| tag 상속 | 내부 task가 tag를 상속 | 호출 task에만 tag 적용 |
---
- name: 조건에 따라 role을 호출한다
hosts: web
become: true
tasks:
- name: 일반 task를 먼저 실행한다
ansible.builtin.debug:
msg: "role 호출 전"
- name: 동적으로 webserver role을 호출한다
ansible.builtin.include_role:
name: webserver
when: enable_web | default(false)정리하면, 미리 정적으로 포함해도 되는 경우는 import_role을, 조건이나 반복으로 실행 여부,횟수를 실행 시점에 정해야 하는 경우는 include_role을 씁니다.
role 변수 우선순위 #
role은 변수를 defaults와 vars 두 곳에 둘 수 있고, 둘의 우선순위가 다릅니다. #6 변수와 fact에서 다룬 전체 우선순위 안에서 role 관련 위치만 추리면 다음과 같습니다.
defaults/main.yml. 가장 낮은 우선순위. 거의 모든 다른 변수가 이를 덮어씁니다.- role 호출 시
vars:로 넘긴 값. defaults를 덮어씁니다. vars/main.yml. defaults보다 높습니다.- play와 호출 측
vars, inventory 변수, extra vars(-e). 더 높은 위치에서 덮어씁니다.
핵심은 defaults가 가장 낮다는 점입니다. role을 쓰는 사람이 바꿀 수 있어야 하는 값은 defaults에 두고, role 내부에서 고정으로 써야 하는 값은 vars에 둡니다. 같은 변수를 두 곳에 동시에 두면 vars가 이기므로 외부에서 덮어쓰기 어려워지니 주의해야 합니다.
role 의존성 #
한 role이 다른 role을 먼저 실행해야 한다면 meta/main.yml의 dependencies에 선언합니다. 선언된 의존 role은 현재 role보다 먼저 실행됩니다.
---
# roles/webserver/meta/main.yml
dependencies:
- role: common # webserver보다 먼저 실행됩니다
- role: firewall
vars:
firewall_ports:
- 80/tcpcommon과 firewall role이 webserver보다 앞서 실행되므로, 공통 패키지 설치나 방화벽 개방 같은 선행 작업을 의존성으로 묶어 둘 수 있습니다. 의존성에도 위처럼 vars로 값을 넘길 수 있습니다.
roles_path: role을 찾는 위치 #
Ansible은 role을 다음 순서로 찾습니다.
- 플레이북과 같은 위치의
roles/디렉터리 ansible.cfg의roles_path에 지정된 경로
따라서 플레이북 옆에 roles/ 디렉터리를 두면 별도 설정 없이 role을 인식합니다. 다른 위치에 role을 두려면 ansible.cfg에 경로를 적습니다.
# ansible.cfg
[defaults]
roles_path = ./roles:/etc/ansible/roles시험에서는 보통 작업 디렉터리의 roles/ 아래에 role을 만드는 것이 가장 안전합니다. ansible-galaxy로 내려받은 role도 이 경로 규칙을 따릅니다.
전체 예제: role 작성 후 호출 #
지금까지 만든 조각을 하나의 흐름으로 묶겠습니다. 작업 디렉터리 구조는 다음과 같습니다.
project/
├── ansible.cfg
├── inventory
├── site.yml
└── roles/
└── webserver/
├── tasks/main.yml
├── handlers/main.yml
├── defaults/main.yml
├── templates/httpd.conf.j2
└── files/index.html호출 플레이북은 다음처럼 짧습니다. role 안에 구성이 모여 있으므로 플레이북 본문은 role을 부르기만 합니다.
---
# site.yml
- name: 웹 서버 그룹을 구성한다
hosts: web
become: true
roles:
- role: webserver
vars:
http_port: 8080
server_admin: web@example.com실행과 멱등성 확인은 평소대로 두 번 돌려 두 번째 실행에서 changed가 0이 되는지 봅니다.
$ ansible-navigator run site.yml -m stdout
$ ansible-navigator run site.yml -m stdout # 두 번째 실행: changed=0이어야 멱등시험 포인트 #
ansible-galaxy role init <이름>으로 표준 디렉터리 구조를 먼저 생성합니다.- role 디렉터리는
tasks,handlers,defaults,vars,templates,files,meta이며, 각main.yml이 자동 진입점입니다. - role 내부에서
template의src는templates/,copy의src는files/를 자동으로 찾으므로 전체 경로를 적지 않습니다. roles키는 일반 task보다 먼저 실행됩니다. 조건,반복으로 동적 호출이 필요하면include_role, 정적 포함은import_role을 씁니다.- role 변수 우선순위에서
defaults가 가장 낮습니다. 외부에서 덮어쓸 값은 defaults에, 고정 값은 vars에 둡니다. - role 간 선후 관계는
meta/main.yml의dependencies로 선언합니다. - role은 플레이북 옆
roles/와ansible.cfg의roles_path에서 찾습니다. 작업 디렉터리roles/가 가장 안전합니다.
정리 #
이번 글에서 잡은 것:
- role은 task,handler,변수,템플릿,파일을 표준 디렉터리로 묶어 재사용하는 단위입니다.
ansible-galaxy role init으로 뼈대를 만들고 필요한 디렉터리만 채웁니다.- 호출은
roles키,import_role(정적),include_role(동적)이며 동작이 다릅니다. defaults는 우선순위가 가장 낮아 외부에서 덮어쓰기 쉬운 기본값을 둡니다.- 의존성은
meta/main.yml의dependencies로, 탐색 경로는roles_path로 다룹니다.
다음: Collection #
role로 구성을 모듈화했다면, 그다음은 여러 role과 모듈,플러그인을 묶어 배포하는 단위인 collection입니다.
#12 Collection: Galaxy, Automation Hub에서는 collection의 구조와 ansible-galaxy collection install, requirements.yml로 의존 collection을 설치하는 방법, Ansible Galaxy와 Automation Hub의 차이, 그리고 FQCN(ansible.builtin.dnf 형태)으로 모듈을 정확히 참조하는 습관까지 정리하겠습니다.