9 min to read
[Ansible for Kubespray] 6. Roles
Cloudnet@ K8S Deploy — Week2
Overview
지난 글에서는 Handler와 Error Handling을 통해 설정 변경 시 서비스를 재시작하고, 실패 상황을 유연하게 처리하는 방법을 배웠습니다. 하지만 Playbook이 복잡해질수록 코드가 길어지고, 유사한 작업을 반복해서 작성해야 하는 문제가 발생합니다. 웹 서버 설치 Playbook을 작성했다면, 데이터베이스 서버나 모니터링 시스템 설치에도 유사한 패턴이 반복될 것입니다.
이러한 한계를 해결하기 위해 Role이 도입되었습니다. Role은 플레이북을 기능 단위로 분리하여 재사용 가능한 컴포넌트로 만드는 표준화된 구조입니다. Kubespray와 같은 대규모 프로젝트는 수십 개의 Role로 구성되며, 각 Role은 Kubernetes 설치의 특정 부분(네트워킹, Etcd 클러스터, Containerd 설치 등)을 담당합니다.
Role Concept
Role은 플레이북의 내용을 기능 단위로 나누어 공통 부품으로 관리하고 재사용하기 위한 구조입니다. Ansible 공식 문서는 Role을 “플레이북을 재사용 가능한 단위로 분리하는 표준화된 방법”으로 정의합니다.
Role이 제공하는 세 가지 주요 이점이 있습니다. 첫째, 대규모 프로젝트를 기능별로 모듈화하여 관리 복잡도를 낮춥니다. 둘째, 여러 사람이 동시에 각자의 Role을 개발할 수 있어 협업 효율이 향상됩니다. 셋째, Ansible Galaxy를 통해 Role을 공유하거나, 다른 사람이 작성한 Role을 가져와 사용할 수 있습니다.
Directory Structure
Role은 표준화된 디렉터리 구조를 가집니다. ansible-galaxy role init <rolename> 명령어로 Role을 생성하면, 아래와 같은 기본 구조가 자동으로 생성됩니다.
my-role/
├── defaults/ # 기본 변수 (우선순위 낮음, 외부에서 덮어쓰기 가능)
│ └── main.yml
├── files/ # 정적 파일 (copy 모듈로 복사)
├── handlers/ # Handler 정의
│ └── main.yml
├── meta/ # Role 메타데이터 (작성자, 의존성 등)
│ └── main.yml
├── tasks/ # 메인 태스크 정의
│ └── main.yml
├── templates/ # Jinja2 템플릿 파일
├── tests/ # Role 테스트용 인벤토리와 플레이북
│ ├── inventory
│ └── test.yml
├── vars/ # 내부 변수 (우선순위 높음, 외부에서 변경 불가)
│ └── main.yml
└── README.md # Role 설명 문서
각 하위 디렉터리의 기능은 다음과 같습니다.
| Directory | Function |
|---|---|
| defaults | Role 변수의 기본값을 정의합니다. 우선순위가 가장 낮아 플레이북에서 쉽게 덮어쓸 수 있습니다. |
| files | Role의 태스크에서 참조하는 정적 파일을 저장합니다. copy 모듈의 소스 파일로 사용됩니다. |
| handlers | Role의 Handler를 정의합니다. Task에서 notify로 호출됩니다. |
| meta | Role에 대한 메타데이터(작성자, 라이선스, 지원 플랫폼, Role 의존성)를 포함합니다. |
| tasks | Role의 메인 태스크를 정의합니다. Role이 호출되면 가장 먼저 실행됩니다. |
| templates | Jinja2 템플릿 파일을 저장합니다. template 모듈로 변수 치환이 필요한 파일을 배포할 때 사용됩니다. |
| tests | Role을 테스트하기 위한 인벤토리와 테스트 플레이북을 포함합니다. |
| vars | Role 내부에서만 사용하는 변수를 정의합니다. 우선순위가 높아 외부에서 변경하기 어렵습니다. |
Variables
Role에서 변수를 정의하는 두 가지 주요 위치인 defaults/와 vars/는 명확한 차이가 있습니다.
defaults/main.yml에 정의된 변수는 우선순위가 낮습니다. 플레이북에서 Role을 호출할 때 변수를 전달하면, defaults 변수보다 전달된 변수가 우선 적용됩니다. 이는 사용자가 Role의 동작을 커스터마이징할 수 있는 확장점을 제공합니다.
반면 vars/main.yml에 정의된 변수는 우선순위가 높습니다. Role 내부에서 사용하는 변수로, 플레이북에서 쉽게 변경할 수 없습니다. 따라서 Role의 필수 설정값이나 내부 동작에 필요한 변수는 vars/에 정의하는 것이 적절합니다.
일반적인 패턴은 다음과 같습니다. 사용자가 변경하길 원하는 변수(포트 번호, 설치할 패키지 목록 등)는 defaults/에, Role의 내부 동작에 필요한 고정값(설치 경로, 설정 파일 위치 등)은 vars/에 정의합니다.
Creating Roles
Role 생성은 ansible-galaxy role init 명령어로 시작합니다.
cd ~/my-ansible
ansible-galaxy role init my-role
명령어를 실행하면 my-role/ 디렉터리가 생성되고, 표준 구조의 하위 디렉터리와 파일들이 자동으로 만들어집니다.
Tasks
tasks/main.yml은 Role의 핵심 로직을 담당합니다.
---
# tasks file for my-role
- name: install service
ansible.builtin.apt:
name: ""
state: latest
update_cache: yes
loop: ""
when: ansible_facts.distribution in supported_distros
- name: copy html file
ansible.builtin.copy:
src: ""
dest: ""
notify:
- restart service
Handlers
handlers/main.yml에 서비스 재시작 Handler를 정의합니다. Task에서 notify: restart service가 호출되면, 모든 Task 완료 후 이 Handler가 실행됩니다.
---
# handlers file for my-role
- name: restart service
ansible.builtin.service:
name: ""
state: restarted
Variables
defaults/main.yml에는 사용자가 재정의할 수 있는 기본 변수를 정의합니다.
---
service_title: "Apache Web Server"
vars/main.yml에는 Role 내부에서 사용하는 내부 변수를 정의합니다.
---
# vars file for my-role
service_name: apache2
src_file_path: index.html
dest_file_path: /var/www/html
httpd_packages:
- apache2
supported_distros:
- Ubuntu
Static Files
files/ 디렉터리에 정적 파일을 배치합니다. copy 모듈의 src 파라미터는 Role의 files/ 디렉터리를 기준으로 파일을 찾습니다.
echo "Hello! Ansible" > my-role/files/index.html
Using Roles
Ansible은 Role을 호출하는 세 가지 주요 방법을 제공합니다.
Basic Usage
가장 간단한 방법은 Playbook에 roles: 섹션을 추가하는 것입니다.
---
- hosts: tnode1
roles:
- my-role
roles: 섹션은 Task 목록 대신 Role 목록을 나열합니다. Play 실행 시 roles 섹션에 정의된 순서대로 Role이 호출됩니다. 변수를 전달하려면 다음과 같이 작성합니다.
---
- hosts: tnode1
roles:
- role: my-role
service_title: "Custom Web Server"
Advanced Usage
ansible.builtin.import_role과 ansible.builtin.include_role 모듈은 더 세밀한 제어가 필요할 때 사용합니다.
import_role은 Role을 정적으로 로딩합니다. Playbook이 파싱될 때 Role이 즉시 로드되며, roles: 섹션과 동일하게 동작합니다. 따라서 loop나 when 조건문과 함께 사용할 수 없습니다.
---
- hosts: tnode1
tasks:
- name: Install Service by role
ansible.builtin.import_role:
name: my-role
include_role은 Role을 동적으로 로딩합니다. Task 실행 시점에 Role이 로드되므로, loop나 when 조건문과 함께 사용할 수 있습니다.
---
- hosts: tnode1
tasks:
- name: Import role conditionally
ansible.builtin.include_role:
name: my-role
when: ansible_facts.distribution == "Ubuntu"
Execution Order
Playbook은 Role과 함께 자주 사용하는 특수 섹션을 제공합니다. pre_tasks, roles, tasks, post_tasks, handlers 순으로 실행되며, 이를 통해 전체 실행 흐름을 제어할 수 있습니다.
---
- hosts: tnode1
pre_tasks:
- name: Print start role
ansible.builtin.debug:
msg: "Let's start role play"
roles:
- my-role
tasks:
- name: Curl test
ansible.builtin.uri:
url: http://tnode1
return_content: true
register: curl_result
notify: Print result
changed_when: true
post_tasks:
- name: Print finish role
ansible.builtin.debug:
msg: "Finish role play"
handlers:
- name: Print result
ansible.builtin.debug:
msg: ""
pre_tasks는 Role보다 먼저 실행됩니다. 시스템 전제 조건을 확인하거나, 의존성을 설치할 때 사용합니다. roles 섹션은 Role 목록을 나열하며, 정의된 순서대로 실행됩니다. tasks는 Role 이후에 실행할 일반 태스크를 정의합니다. post_tasks는 모든 Task와 Handler 완료 후 마지막으로 실행됩니다. 최종 검증이나 정리 작업에 적합합니다.
Execution Control
Ansible은 Task의 실행 결과를 changed, ok, failed 상태로 분류합니다. 기본적으로 명령어가 성공하면 ok, 시스템 상태가 변경되면 changed, 에러가 발생하면 failed로 처리합니다.
changed_when은 Task의 변경 상태를 제어합니다. 명령어는 성공했지만, 결과에 따라 changed로 처리할지 결정할 때 사용합니다.
- name: Check service status
ansible.builtin.shell: systemctl is-active apache2
register: service_status
changed_when: false
failed_when은 실패 조건을 직접 정의합니다. 기본적으로 명령어의 Exit Code가 0이 아니면 실패로 처리하지만, 특정 조건에서 실패로 처리하고 싶을 때 사용합니다.
- name: Check HTTP service
ansible.builtin.uri:
url: http://tnode1
register: http_result
failed_when: http_result.status != 200
Ansible Galaxy
Ansible Galaxy는 Ansible 커뮤니티가 만든 Role을 발견하고 활용하는 공식 저장소입니다. https://galaxy.ansible.com 에서 커뮤니티가 작성한 수천 개의 Role을 검색하고 다운로드할 수 있습니다.
Installing Roles
CLI를 통해 Galaxy에서 Role을 검색하고 설치할 수 있습니다.
# PostgreSQL 관련 Role 검색
ansible-galaxy role search postgresql --platforms Ubuntu
# Role 상세 정보 확인
ansible-galaxy role info geerlingguy.postgresql
# Role 설치 (기본 경로: ~/.ansible/roles)
ansible-galaxy role install geerlingguy.postgresql
# 특정 디렉터리에 설치
ansible-galaxy role install -p roles geerlingguy.postgresql
# 설치된 Role 목록 확인
ansible-galaxy role list -p roles
# Role 삭제
ansible-galaxy role remove geerlingguy.postgresql
설치한 Role은 roles: 섹션에서 바로 사용할 수 있습니다.
---
- hosts: tnode1
roles:
- geerlingguy.postgresql
Galaxy에서 Role을 사용할 때는 주의가 필요합니다. 커뮤니티 Role은 검증되지 않은 경우가 많으므로, 인기도(Download count), 최신 업데이트 날짜, 별점 등을 확인하고 사용해야 합니다.
Collections
Ansible 2.10부터 Collection이라는 새로운 패키징 단위가 도입되었습니다. Collection은 Role, Module, Plugin을 하나의 패키지로 묶어 배포할 수 있게 해줍니다.
Role vs Collection
| 특징 | Role | Collection |
|---|---|---|
| 목적 | 재사용 가능한 자동화 컴포넌트 | 관련 콘텐츠를 패키징 |
| 포함 항목 | tasks, handlers, vars, files | Roles, Modules, Plugins |
| 배포 단위 | 단일 Role | 여러 Role + Module + Plugin |
| 버전 관리 | Role별 | Collection별 |
Collection은 클라우드 벤더(AWS, Azure, GCP)나 네트워크 장비 벤더(Cisco, Juniper)가 자체 Module과 Plugin을 배포할 때 주로 사용합니다. 예를 들어 amazon.aws Collection은 AWS 리소스 관리를 위한 수십 개의 Module과 Role을 포함합니다.
Installing Collections
# 설치된 Collection 목록 확인
ansible-galaxy collection list
# 특정 Collection 설치
ansible-galaxy collection install openstack.cloud
# 특정 버전 설치
ansible-galaxy collection install openstack.cloud:2.1.0
# tar 파일로 오프라인 설치
ansible-galaxy collection download openstack.cloud -p ./collection
ansible-galaxy collection install ./collection/openstack-cloud-2.2.0.tar.gz
Collection은 requirements.yml 파일로 한 번에 여러 개를 설치할 수도 있습니다.
# requirements.yml
collections:
- name: amazon.aws
version: "6.5.0"
- name: openstack.cloud
version: "2.2.0"
ansible-galaxy collection install -r requirements.yml
Comments