ansible playbook의 재사용
ansible 사용 시 사용자가 직접 playbook을 만드는 것도 좋지만, 일반적으로 이미 작성한 플레이북에서 코드를 재사용하는게 훨씬 효율이 좋다.
예를들어 특정 어플리케이션과 연동하는 MYSQL을 설치한다면, 다른 어플리케이션도 연동할 수 있게 호스트명, 어플리케이션, 암호, 사용자 등을 변수로 만들어서 작업할 수 있을 것이다. 하지만, 현실에서는 다양한 파일을 포함하거나 다양한 상황을 관리하기 위한 작업과 핸들러를 사용하는 복잡한 작업 등이 있을 수 있어서, 모든 코드를 다른 플레이북에 복사해서 재사용하는게 쉽지 않을 수 있다.
Ansible에서는 "Role"을 사용하여 일반적인 방식으로 ansible 코드를 더 쉽게 재사용하는 방법을 제공한다. role은 인프라를 프로비저닝하고, 애플리케이션을 배포하는 등의 모든 작업에 대해 변수, 파일, 템플릿 등의 기타 리소스들을 표준화된 디렉토리 구조로 패키징한다. 다시 정리하면, role은 쉬운 공유, 휴대성 및 재사용을 위해 특정 구조로 배열된 yaml 작업파일 및 지원 항목들의 모음이다.
이러한 role은 자신이 만든 playbook을 공개할 때, 다른 사람들과 협업할 때 항상 사용한다. 사실상 진짜 실무에서는 여태까지 배운 playbook yml파일 하나가 아닌, role로 업무를 수행한다.
# 참고 : 프로비저닝이란?
프로비저닝은 IT 인프라를 설정하는 프로세스이다. 또한 사용자와 시스템에서 사용할 수 있도록, 데이터와 리소스에 대한 액세스를 관리하는 데 필요한 단계를 지칭하기도 한다. 프로비저닝과 설정은 각기 다른 작업이지만, 둘 다 배포 프로세스의 단계에 포함되며 일단 프로비저닝이 완료되어야 설정 단계를 진행할 수 있다. 프로비저닝에는 서버 프로비저닝, 네트워크 프로비저닝, 사용자 프로비저닝, 서비스 프로비저닝 등 다양한 유형이 있다.
www.redhat.com/ko/topics/automation/what-is-provisioning
ansible role의 장점
- 컨텐츠를 그룹화하여 코드를 다른 사용자와 쉽게 공유할 수 있다.
- 큰 프로젝트를 관리하기 쉽게 할 수 있다.
- 여러 관리자가 동시에 함께 개발하기 편하다.
- role은 여러 소스에서 가져올 수 있다. 여러 소스가 있지만 대표적으로 아래와 같다.
- 레드햇에서 서브스크립션으로 지원하는 rhel-system-roles 패키지
- 사용자가 관리하는 git 등
- 퍼블릭 레포지토리인 ansible galaxy라는 웹 사이트
ansible role의 구조 요소
role은 하위 디렉토리 및 파일의 표준화된 구조에 의해 정의된다. 최상위 디렉토리는 role 자체의 이름으로 정의한다. 그 아래의 파일들은 tasks, handlers 등 role에서 각 파일의 목적에 따라 이름이 지정된 하위 디렉토리로 구성된다. 예를들어 timesync 라는 디렉토리 안에는 아래 예시같은 디렉토리 구조가 있으며, README.md 파일도 포함할 수 있다.
defaults
- main.yml
files
handlers
- main.yml
meta
- main.yml
tasks
main.yml
templates
tests
- inventory
- test.yml
vars
- main.yml
README.md
각 디렉토리 및 파일의 설명은 다음과 같다.
defaults |
이 디렉토리의 main.yml 파일에는 role이 사용될 때 덮어쓸 수 있는 role 변수의 기본값이 포함되어 있다. 이러한 변수는 우선 순위가 낮으며 플레이에서 변경하고 사용자 지정할 수 있다. |
files |
이 디렉토리에는 role 작업에서 참조하는 일반적인 정적 파일이 있다. |
handlers |
이 디렉토리의 main.yml 파일에는 role의 핸들러 정의가 들어있다. |
meta |
이 디렉토리의 main.yml 파일에는 작성자, 라이센스, 플랫폼 및 role 종속성 등을 포함한 role의 메타데이터 정보가 들어있다. |
tasks |
이 디렉토리의 main.yml 파일에는 role의 task들을 정의한 내용이 포함되어 있다. |
templates |
이 디렉토리에는 role에서 참조하는 jinja2 템플릿이 들어있다. |
tests |
이 디렉토리에는 role을 테스트하는데 사용할 수 있는 인벤토리와 test.yml 플레이북이 포함될 수 있다. 테스트용이므로 따로 사용하지 않는다면 무시해도 무방하다. |
vars |
이 디렉토리의 main.yml파일은 role의 변수 값을 정의한다. 종종 이러한 변수는 role 내에서 내부 목적으로 사용된다. 이러한 변수는 우선순위가 높으며 플레이북에서 사용될 때 변경되지 않는다. vars 변수의 의도는 ROLE의 내부 기능에 의해 사용되는 변수를 정의하도록 하는 것이다. |
README.md |
사용자가 읽을 수 있는 role의 기본 설명, 사용방법, 설명서, 예제, 작업하기 위해 보유할 수 있는 비 ansible 요구사항 등을 제공한다. |
main.yml |
위에서 명시한 각 디렉토리에 내용을 넣을 경우, 이 이름을 사용해서 넣는다. 각 파일의 내용에서 들여쓰기는 그냥 처음부터 시작한다. 해당 role을 참조하는 파일의 들여쓰기 수준만큼 쓰는 것이 아니다. |
# 참고
- 여기 명시된 각 디렉토리의 이름은 바꾸면 안된다. 또한 그 아래의 yml파일도 main.yml 로 해야한다. 모두 동일하다.
- 이러한 디렉토리는 필수가 아니며, 필요한 디렉토리만 있으면 된다.
ansible role 사용시 보안 관련 유의사항
- role에는 사이트 특정 데이터가 포함되어 있으면 안된다.
- 암호나 개인 키 같은 비밀은 절대 포함하면 안된다. 민감한 값은 민감하지 않은 기본값을 사용해 변수로 매개변수화해야 한다.
- 사이트 특정 세부 사항을 하드코딩해서는 안된다.
- secret은 다른 수단을 통해 role에 제공해야 한다. role을 호출할 때 role 변수를 설정하는 이유 중 하나이다.
- play에 설정된 role 변수는 secret을 제공하거나 secret을 포함하는 ansible vault-encrypted 파일을 가리킬 수 있다.
ansible role의 효과를 극대화 하기 위한 제언
- 자체 버전 제어 리포지토리에서 각 role을 유지관리한다. ansible은 git 기반 리포지토리와 잘 맞는다.
- ansible-galaxy init을 사용하여 뼈대를 만들고, 이후 필요없는 디렉토리와 파일은 다 지운다.
- README.md, meta/main.yml 파일을 생성하고 유지관리하여 role이 무엇인지, 작성한 사람은 누구인지, 어떻게 쓰는지 등을 문서화한다.
- role은 특정 목적이나 기능에 집중한다. 하나의 role에 여러 기능을 넣는 것보다는 하나 이상의 role로 하나하나 기능을 넣는게 낫다.
- role을 자주 재사용하고 리팩토링한다. 한마디로 자주 새로고침하고 기존 플레이북에 문제가 발생하지 않도록 한다.
ansible role 사용하기 - 기본
ansible playbook에서 role을 사용하는 것은 아주 간단하다. 아래 예시대로 roles 지시자를 사용하면 된다.
---
- host: all
roles:
- role1
- role2
이렇게 하면, 지정한 role1, role2 (이름임) 의 디렉토리에서 tasks, handler, vars 등 모든 내용들을 불러와서 해당 role을 실행한다.
role에서 cp, script, template 또는 import_task, include_task 등은 절대경로/상대경로, 이름이 없는 관련 파일, 템플릿 또는 작업 파일을 참조할 수 있다. ansible은 files, templates, tasks 하위 디렉토리에서 이러한 각각의 항목들을 찾는다.
role이 포함된 playbook에서 실행 순서 제어하기
role이 포함된 playbook에서 실행 순서를 제어하기 위해 2가지 지시자를 사용한다. pre_tasks, post_tasks
기본적으로 tasks의 내용은 roles에 있는 내용보다 늦게 실행한다. 특정 시나리오에서는 roles 실행 전에 일부 플레이 task를 실행해야 할 수도 있다. 이러한 시나리오를 지원하기 위해 pre_tasks 섹션으로 플레이를 구성할 수 있다. 이 섹션에 나열된 작업은 모든 role이 실행되기 전에 실행된다. 이러한 task 중 하나라도 핸들러가 트리거되면, 해당 핸들러의 task가 role 또는 일반 작업 전에 실행된다. 또한 post_tasks는 이러한 작업은 pre_tasks, tasks, roles가 모두 실행된 후에, 마지막으로 실행된다.
순서를 정리하면 다음과 같다.
1. pre_tasks에 정의된 play 및 handlers
2. roles에 정의된 roles, 만약 dependency가 정의되어있다면 해당 dependency가 해당 role보다 먼저 실행되고 role이 실행된다. 명시된 role 리스트들이 이렇게 순서대로 실행된다.
3. tasks에 정의된 play 및 handlers
4. post_tasks에 정의된 play 및 handlers
예시 (이렇게 모든 섹션이 다 있는 경우는 일반적으로 드물다)
---
- name: play to illustrate order of execution
hosts: web.example.com
pre_tasks:
- debug:
msg: 'pre-task'
notify: my handler
roles:
- role1
tasks:
- debug:
msg: 'first task'
notify: my handler
post_tasks:
- debug:
msg: 'post-task'
notify: myhandler
handlers:
- name: my handler
debug:
msg: running my handler
위 예제에서 debug는 각 섹션에서 실행되어 my handler에 알린다. 즉 my handler는 3번 실행된다.
- 모든 pre_tasks 작업 실행 후
- 모든 role, 일반 tasks 작업 실행 후
- 모든 post_tasks 실행 후
playbook에 role을 포함하기
role은 단순히 플레이의 roles 섹션에 포함하는 것이 아닌 일반적인 작업을 사용하여 플레이에 추가할수도 있다. 이전에 설명한 playbook을 포함하는 것과 동일한 방식으로 두가지의 모듈을 사용한다.
- include_role 모듈을 사용하여 role을 동적으로 포함할 수 있다. (ansible 2.3에서 추가된 기능)
- import_role 모듈을 사용하여 role을 정적으로 가져올 수 있다. (ansible 2.4에서 추가된 기능)
* 예시
---
- name: execute a role as a task
hosts: web.example.com
tasks:
- name: a normal task
debug:
msg: 'first task'
- name: a task to include role2 here
include_role: role2
role 생성 및 구성하기
role을 사용하는 process는 다음과 같다.
1. role 디렉토리 구조 생성
2. role의 내용 정의
3. playbook에서 role 사용하기
* role 생성 및 구성 예시
1. role 디렉토리 구조 생성하기
ansible-galaxy 명령줄 도구에서 제공하는 ansible-galaxy init [role이름] 명령을 쓰면 디렉토리 구조를 자동으로 만든다.
2. role 내용 작성하기
<예시1>
이 role은 /etc/motd 파일을 관리하며 template 모듈을 사용해 /etc/motd 파일을 템플릿으로 배포한다.
tasks 디렉토리의 main.yml
templates 디렉토리의 motd.j2 (jinja2 템플릿 파일이므로 j2 사용)
defaults 디렉토리의 main.yml
<예시2>
이 role은 단순히 변수를 사용해보는 예제이다.
tasks 디렉토리의 main.yml
---
# tasks file for role1
- debug:
msg: messages in role1 var1-{{var1}} var2-{{var2}}
vars 디렉토리의 main.yml
---
# variables for role1
var1: var1-role1
var2: var2-role1
(참고 : "var1-role1" 이게 하나의 값이다)
3. playbook에서 role 사용하기
<예시1을 사용하는 playbook>
- hosts: servera
tasks:
- debug:
msg: "update /etc/motd"
roles:
- motd
<예시2를 사용하는 playbook>
- hosts: servera
tasks:
- debug:
msg: "hello"
roles:
- role: role1
- role: role2
- 예시2는 role1밖에 없지만, 여러개의 role을 사용할 수 있고 tasks와도 함께 사용할 수 있다는 것을 보여주는 것임.
- role을 참조하는 playbook을 만들 때는, role의 기본 위치를 잘 파악해야 한다. 예시1은 해당 유저의 ~/.ansible/role/motd 에 해당 role 내용이 있어야 한다.
role 종속성 정의
특정 role에 다른 role을 종속성으로 포함할 수 있다. 종속성은 meta 디렉토리에 main.yaml에 정의한다. 아래와 같은 예시가 있을 수 있다. 아래처럼 정의하면, apache, postgres 두개의 role을 먼저 실행해야 해당 특정 롤을 실행하게 된다.
meta/main.yaml
---
depenencies:
- role: apache
port: 8000
- role: postgres
dbname: serverlist
admin_user: felix
기본적으로 role은 playbook에서 여러번 정의되어도 (dependency든 다른 playbook에서든) 여러번 실행하지 않는다. 한번만 실행한다. 아래처럼 정의되어있어도 foo role은 한번만 실행한다.
---
- hosts: webservers
roles:
- foo
- foo
그렇지만 foo role의 meta/main.yml에다가 allow_duplicates: true를 넣으면 두 번 실행한다.
# 중요 : dependency 사용 시 주의
role에서 dependency를 복잡하게 사용했을 때 role의 유지 관리가 더 어려울 수 있다. 주의할 것.
role에서 변수를 참조하는 우선순위
- 아래 순서대로 높은순으로 우선순위가 높다.
1. role을 참조하는 yml파일에 특정 role을 명시할 때,그 role 과 같은 수준에서 명시한 변수
예시1
---
- name: use motd role playtbook
hosts: web.example.com
roles:
- role: motd
system_owner: someone@host.example.com
예시2
---
- hosts: servera
roles:
- role: role1
- role: role2
var1: var1
var2: var2
- include_vars로 로드된 변수, register 변수 등은 이 1번과 같은 수준의 우선순위를 가진다.
- 원래 변수 기입 규칙은 vars: 지시자 아래에 넣지만, role과 같은 수전에서 명시한 변수에서는 vars: 밑이 아닌 그냥 바로 쓸 수 있다. (위 예시2 참고)
2. roles 디렉토리에 있는 특정 role의 vars 디렉토리에 명시된 변수
3. 해당 role을 참조하는 yml파일에 vars로 명시한 변수
---
- name: use motd role playbook
hosts: web.example.com
roles:
- motd
vars:
system_owner: someone@host.example.com
roles:
- role: motd
- 아래 2가지 변수는 이 3번과 같은 수준의 우선 순위를 가진다.
- 인벤토리 파일에서 명시한 호스트 변수 또는 그룹 변수
- 플레이북 프로젝트 내의 group_vars, host_vars 디렉토리 아래의 yaml파일
4. defaults : roles의 defaults 디렉토리에 명시된 변수. 가장 우선순위가 낮다.
# 참고예시 : role을 명시한 playbook에서 변수 처리
예를들어, 아래 예제는 2개의 role과 role 변수에 대한 값을 설정한다. role 1은 이전 예제와 같은 방식으로 사용된다. role2가 사용될때는 var1,var2가 적용되어 재정의된다.
---
- hosts: all
roles:
- role: role1
- role: role2
var1: val1
var2: val2
ansible role을 찾는 경로
기본적으로 ansible은 ansible 플레이북을 포함하는 디렉토리의 roles 라는 하위 디렉토리에서 role을 찾는다. 이를 통해 playbook및 기타 지원 파일과 함께 role을 저장할 수 있다. 19장의 ansible-galaxy에서 다운받는 role도 기본적으로 아래 경로를 사용한다.
기본 경로 : ~/.ansible/roles
ansible이 기본 경로에서 role을 찾을 수 없는 경우, 아래 순서대로 ansible 구성 설정인 role_path에서 지정한 디렉토리에서 찾는다.
1. ~/.ansible/roles : 특정 유저에게 role 설치할 때 사용
2. /usr/share/ansible/roles : 모든 사용자에 대해 role을 설치할 때 사용
3. /etc/ansible/roles
ansible role의 default path를 변경하려면 아래와 같이 2가지 방법을 사용할 수 있다.
1. 임시 변경
해당 유저의 ansible role 경로를 임시로 변경한다. 해당 유저를 logout 하면 이 설정은 사라진다.
구문 : export ANSIBLE_ROLES_PATH=원하는경로
예시 : export ANSIBLE_ROLES_PATH=/tmp/roles
2. 영구 변경
특정 유저의 .bashrc 파일, 또는 전체 유저에 대해서는 /etc/bashrc 파일에 아래 내용을 포함한다.
ANSIBLE_ROLES_PATH=원하는경로
또는 특정 디렉토리 기준이라면 ansible.cfg에 roles_path 항목을 설정한다.
role 예시 - vhost httpd를 만드는 role 생성
1. 디렉토리 생성
- 아래 경로대로 myvhosts 디렉토리를 생성한다.
mkdir -pv ~/role-create/roles/myvhosts
2. myvhosts 디렉토리 안에 tasks 디렉토리를 만들고, main.yml 파일을 생성한다.
---
- name: ensure httpd is installed
yum:
name: httpd
state: latest
- name: ensure httpd is started and enabled
service:
name: httpd
state: started
enabled: true
- name : vhost file is installed
template:
src: vhost.conf.j2
dest: /etc/httpd/conf.d/vhost.conf
owner: root
group: root
mode: 0644
notify:
- restart httpd
- name: html content is installed
copy:
src: html/
dest: "/var/www/vhosts/{{ansible_hostname}}"
* 설명
- httpd 서비스를 최신으로 설치
- httpd 서비스를 시작하고 활성화
- vhost.conf.j2 템플릿을 /etc/httpd/conf.d/vhost.conf 파일로 복사 및 오너쉽 등 수정
- 템플릿이 수정되면, httpd를 자동으로 재시작
- html/ 디렉토리 안에 있는 모든 내용을 /var/www/vhosts/호스트명 으로 복사
3. myvhosts 디렉토리 안에 handlers 디렉토리를 만들고, main.yml 파일을 생성한다.
---
- name: restart httpd
service:
name: httpd
state: restarted
* 설명
- httpd를 재시작한다.
4. myvhosts 디렉토리 안에 files/html 디렉토리를 만들고, index.html 파일을 원하는 대로 생성해서 넣는다.
5. myvhosts 디렉토리 안에 templates 디렉토리를 만들고, vhosts.conf.j2 파일을 넣는다.
6. myvhosts role을 실행할 yml 파일을 생성한다.
vi use-vhost-role.yml
---
- name: use myvhost role playbook
hosts: web.example.com
pre_tasks:
- name: pre_tasks messages
debug:
msg: 'ensure web server configuration.'
roles:
- myvhost
post_tasks:
- name: post_tasks messages
debug:
msg: 'web service is configured.'
7. 특정 서비스에 대한 방화벽을 허용하는 role을 생성한다.
mkdir -pv ~/role-create/roles/myfirewall
8. myfirewall 디렉토리 안에 tasks 디렉토리를 만들고, main.yml 파일을 생성한다.
---
- name: ensure firewalld is installed
yum:
name: firewalld
state: latest
- name: ensure firewalld is started and enabled
service:
name: firewalld
state: started
enabled: true
- name: ensure firewalld services are enabled
firewalld:
state: enabled
immediate: true
permanent: true
service: "{{ item }}"
loop: "{{ firewall_services }}"
9. 변수 기본값들을 정의하기 위해 myfirewall 디렉토리 안에 defaults라는 디렉토리에 main.yml 파일을 정의한다.
---
# defaults file for myfirewall
# by default, no firewall services are enabled.
# firewall_services: []
#
# to enable, for example, FTP and HTTP service in firewalld
# use the following definition for "firewall_services"
# firewall_services:
# - http
# - ftp
firewall_service: []
이렇게 firweall_services: [] 대괄호로 묶어서 아무것도 없게 하면, 빈 목록이 되며 이로 인해 기본적으로 role이 포트를 열지 않는다. 이제 사람들이 여기 나와있는 주석 예시대로 원하는 서비스를 아래에 명시하면 된다.
10. webservers라는 그룹에서만 조건에 맞는 firewall_service 변수를 넣기 위해 그룹 변수를 명시한다.
- role-create 디렉토리에서 group_vars/webservers 디렉토리를 생성한다.
mkdir -pv group_vars/webservers
- 이 디렉토리 안에 myfirweall.yml 파일을 만들고 아래와 같이 명시한다.
---
firewall_services:
- http
11. myvhosts의 role이 실행되기 전에 myfirewall role을 먼저 실행하도록 dependnecy를 정의한다.
- myvhosts 디렉토리에 meta 디렉토리를 만들고, main.yml을 생성하고 아래와 같이 정의한다.
dependencies:
- role: myfirewall
- 이 meta/main.yml 파일에는 선택적으로 galaxy_info 변수의 author, company, description, license 필드도 업데이트 할 수 있다.
12. 실행
ansible-playbook use-vhost-role.yml
- dependency role은 자동으로 실행되므로 이 메인 yml파일만 실행하면 전체가 실행된다.
참고링크
role 및 ansible 설명서
https://docs.ansible.com/ansible/2.7/user_guide/playbooks_reuse_roles.html#using-roles
변수 및 ansible 설명서
https://docs.ansible.com/ansible/2.7/user_guide/playbooks_variables.html
'Ansible' 카테고리의 다른 글
20장. playbook의 문제 해결하기 (0) | 2021.03.15 |
---|---|
18장. ansible galaxy를 사용하여 role 관리, 배포하기 (0) | 2021.03.15 |
16장. playbook에 파일 포함하기 및 가져오기 (0) | 2021.02.27 |
15장. 병렬 구성 작업 (1) | 2021.02.27 |
14장. ansible에서 jinja2 사용하기 (0) | 2021.02.26 |