Red Hat Certified Engineer (RHCE) #11 Role 작성과 사용

8 분 소요

#10 Ansible Vault에서 비밀을 암호화해 안전하게 다루는 법을 잡았다면, 이번에는 플레이북을 재사용 가능한 단위로 묶는 role을 다루겠습니다. role은 task,handler,변수,템플릿,파일을 정해진 디렉터리 구조로 묶어, 한 번 작성한 구성을 여러 플레이북에서 호출하게 해 주는 단위입니다. RHCE 시험에서 “role을 작성해 playbook에서 호출하라” 유형은 거의 매번 출제되므로, 이번 글에서 구조와 사용법을 손에 익히겠습니다.

role이 필요한 이유 #

지금까지 작성한 플레이북은 task와 handler, 변수, 템플릿이 한 파일이나 몇 개의 파일에 흩어져 있었습니다. 작은 구성에서는 문제가 없지만, 같은 패키지 설치,설정 배포,서비스 기동 묶음을 다른 플레이북에서도 쓰려면 매번 복사해야 합니다. 복사한 코드가 늘어날수록 한 곳을 고칠 때 모든 사본을 찾아 고쳐야 하므로 실수가 쌓입니다.

role은 이 묶음을 약속된 디렉터리 구조로 한 번 정리해 두고, 플레이북에서는 이름으로만 불러 쓰게 합니다. 다음 같은 장점이 생깁니다.

  • 재사용. 한 번 작성한 role을 여러 플레이북에서 호출합니다.
  • 구조화. task,handler,변수,파일이 정해진 위치에 들어가 찾기 쉽습니다.
  • 공유. ansible-galaxy로 내려받거나 collection에 담아 배포할 수 있습니다.

role 디렉터리 구조 #

role은 정해진 하위 디렉터리 이름을 따릅니다. 각 디렉터리의 main.yml이 자동으로 로드되는 진입점입니다. 표준 구조는 다음과 같습니다.

role 표준 디렉터리 구조
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 명령이 표준 구조를 한 번에 생성합니다.

role 뼈대 생성
# 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
---
# 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: true

handlers/main.yml #

roles/webserver/handlers/main.yml
---
# roles/webserver/handlers/main.yml
- name: restart httpd
  ansible.builtin.service:
    name: httpd
    state: restarted

defaults/main.yml #

roles/webserver/defaults/main.yml
---
# roles/webserver/defaults/main.yml
http_port: 80
server_admin: admin@example.com

defaults에 둔 변수는 우선순위가 가장 낮으므로, 플레이북에서 role을 호출할 때 같은 이름의 값을 넘기면 손쉽게 덮어쓸 수 있습니다. role을 쓰는 쪽이 조정할 여지가 있는 값은 vars가 아니라 defaults에 두는 것이 관례입니다.

role 사용하기 #

작성한 role을 플레이북에서 호출하는 방법은 크게 세 가지입니다. roles 키, include_role, import_role이며, 동작 방식이 서로 다릅니다.

roles 키 #

가장 단순한 방법은 play 수준의 roles 키에 나열하는 것입니다. role의 task가 play의 일반 task보다 먼저 실행됩니다.

site.yml
---
# site.yml
- name: 웹 서버를 구성한다
  hosts: web
  become: true
  roles:
    - webserver

변수를 넘기려면 다음처럼 적습니다.

role에 변수 넘기기
  roles:
    - role: webserver
      vars:
        http_port: 8080

include_role과 import_role #

roles 키 대신 task 목록 안에서 role을 호출하면 실행 위치를 직접 제어할 수 있습니다. 이때 두 모듈의 차이가 시험 포인트입니다.

항목import_roleinclude_role
처리 시점정적(static). 플레이북 파싱 단계에서 미리 포함동적(dynamic). 실행 시점에 포함
when 적용내부 모든 task에 조건이 개별 적용role 호출 한 번에 조건이 적용
loop 사용불가가능. role을 반복 호출
tag 상속내부 task가 tag를 상속호출 task에만 tag 적용
include_role로 동적 호출
---
- 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.ymldependencies에 선언합니다. 선언된 의존 role은 현재 role보다 먼저 실행됩니다.

roles/webserver/meta/main.yml
---
# roles/webserver/meta/main.yml
dependencies:
  - role: common              # webserver보다 먼저 실행됩니다
  - role: firewall
    vars:
      firewall_ports:
        - 80/tcp

commonfirewall role이 webserver보다 앞서 실행되므로, 공통 패키지 설치나 방화벽 개방 같은 선행 작업을 의존성으로 묶어 둘 수 있습니다. 의존성에도 위처럼 vars로 값을 넘길 수 있습니다.

roles_path: role을 찾는 위치 #

Ansible은 role을 다음 순서로 찾습니다.

  1. 플레이북과 같은 위치의 roles/ 디렉터리
  2. ansible.cfgroles_path에 지정된 경로

따라서 플레이북 옆에 roles/ 디렉터리를 두면 별도 설정 없이 role을 인식합니다. 다른 위치에 role을 두려면 ansible.cfg에 경로를 적습니다.

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
---
# 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 내부에서 templatesrctemplates/, copysrcfiles/를 자동으로 찾으므로 전체 경로를 적지 않습니다.
  • roles 키는 일반 task보다 먼저 실행됩니다. 조건,반복으로 동적 호출이 필요하면 include_role, 정적 포함은 import_role을 씁니다.
  • role 변수 우선순위에서 defaults가 가장 낮습니다. 외부에서 덮어쓸 값은 defaults에, 고정 값은 vars에 둡니다.
  • role 간 선후 관계는 meta/main.ymldependencies로 선언합니다.
  • role은 플레이북 옆 roles/ansible.cfgroles_path에서 찾습니다. 작업 디렉터리 roles/가 가장 안전합니다.

정리 #

이번 글에서 잡은 것:

  • role은 task,handler,변수,템플릿,파일을 표준 디렉터리로 묶어 재사용하는 단위입니다.
  • ansible-galaxy role init으로 뼈대를 만들고 필요한 디렉터리만 채웁니다.
  • 호출은 roles 키,import_role(정적),include_role(동적)이며 동작이 다릅니다.
  • defaults는 우선순위가 가장 낮아 외부에서 덮어쓰기 쉬운 기본값을 둡니다.
  • 의존성은 meta/main.ymldependencies로, 탐색 경로는 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 형태)으로 모듈을 정확히 참조하는 습관까지 정리하겠습니다.

X