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

 

프로비저닝(Provisioning)이란? 종류, 설정, 배포, 자동화 방법

프로비저닝은 IT 인프라 설정 프로세스를 뜻하며, 데이터와 리소스에 대한 액세스 관리에 필요한 단계입니다. 서버, 네트워크, 사용자, 서비스 유형이 있습니다.

www.redhat.com

 

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 변수의 기본값이 포함되어 있다. 이러한 변수는 우선 순위가 낮으며 플레이에서 변경하고 사용자 지정할 수 있다.

이러한 변수는 role을 사용하는 플레이를 작성하는 사람에게 할 일을 정확하게 사용자 지정하거나 제어할 방법을 제공하기 위한 것이다. default 변수는 해당 값을 재정의하려는 경우에만 사용해야 한다.

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

 

Roles — Ansible Documentation

You are reading an unmaintained version of the Ansible documentation. Unmaintained Ansible versions can contain unfixed security vulnerabilities (CVE). Please upgrade to a maintained version. See the latest Ansible documentation. Roles are ways of automati

docs.ansible.com

 

변수 및 ansible 설명서

https://docs.ansible.com/ansible/2.7/user_guide/playbooks_variables.html

 

Using Variables — Ansible Documentation

Docs » User Guide » Working With Playbooks » Using Variables You are reading an unmaintained version of the Ansible documentation. Unmaintained Ansible versions can contain unfixed security vulnerabilities (CVE). Please upgrade to a maintained version.

docs.ansible.com

 

 

 

 

+ Recent posts