[Ansible] 플레이북 Role 과 Include
[Ansible] 플레이북 Role 과 Include
안녕하세요? 정리하는 개발자 워니즈입니다. 이번시간에는 Ansible
의 플레북에 대한 이야기를 해보려고 합니다. 필자가 속한 프로젝트에서는 Ansible을 주로 다수의 서버의 configuration 을 수정할 때 사용하고 있습니다. 즉, 1개의 master 파일을 수정하고 각 서버의 경로에 해당 파일로 copy 해주는 단순한 구조입니다.
필자가 있는 프로젝트에서는 다음과 같이 사용합니다 .
- nginx conf 파일 변경
- nginx map 파일 변경
- nginx location 파일 변경
다음시간에 nginx
conf 구조에 대해서 설명을 하겠지만, 우선 ansible의 가장 효과적인 부분이 다수의 host에서 작업이 가능한 것입니다.
플레이북을 매우 큰 하나의 파일로 작성하는 것도 가능하지만 (초기에 플레이북을 배우면서는 이렇게 하기도 합니다) 점차 플레이북을 재사용하거나 계층적으로 관리할 필요도 생깁니다.
1. Ansible include
만약 플레이북의 플레이에 있는 일련의 태스크를 재사용한다고 가정해봅니다. include 를 이용하여 이 작업을 하면 됩니다. 가져온 태스크 목록은 특정 역할(role)을 수행하기에 알맞습니다. 다시한번 강조하면 플레이북은 관리 대상 시스템 그룹에 다양한 역할을 매핑하는 작업입니다.
---
# possibly saved as tasks/foo.yml
- name: placeholder foo
command: /bin/foo
- name: placeholder bar
command: /bin/bar
tasks:
- include: tasks/foo.yml
분명 위와 아래는 확연히 달라집니다. 같은 내용이더라도, 파일을 include
하는 구조가 훨씬 간단해 보입니다.
include 할 때 변수도 포함해서 가져올 수 있습니다. (parameterized include 라고 합니다)
tasks:
- include: wordpress.yml wp_user=timmy
- include: wordpress.yml wp_user=alice
- include: wordpress.yml wp_user=bob
버전 1.0 상위부터는 아래와 같이 key, value pair로도 지정이 가능합니다.
tasks:
- include: wordpress.yml
vars:
wp_user: timmy
ssh_keys:
- keys/one.txt
- keys/two.txt
include에 작접 패러미터를 전달하던 아니면 vars
를 이용하던 상관없습니다.
include 되는 파일에서는
{{ wp_user }}
여러개의 하위 플레이북도 include
가 가능합니다.
- name: this is a play at the top level of a file
hosts: all
remote_user: root
tasks:
- name: say hi
tags: foo
shell: echo "hi..."
- include: load_balancers.yml
- include: webservers.yml
- include: dbservers.yml
2. Ansible Role
버전 1.2 이후
태스크와 핸들러에 관하여 알았으므로 이제는 플레이북을 어떻게 잘 구성하는가를 알아낼 차례입니다. Role을 이용 하면 됩니다. Role은 파일 구조에 따라 vars_files, tasks 와 핸들러 등을 자동으로 로딩하는 것입니다.
처음에 필자는 Role
이라는 것 자체가 계정과 역할 이런것일줄 알았는데, 말그대로 어떤 업무 수행단위를 하는지를 Component화 해서 재사용할 수 있도록 하는것으로 보면 될 것 같습니다.
다음과 같은 프로젝트 구조가 되어 있다고 합시다.
site.yml
webservers.yml
fooservers.yml
roles/
common/
files/
templates/
tasks/
handlers/
vars/
defaults/
meta/
webservers/
files/
templates/
tasks/
handlers/
vars/
defaults/
meta/
플레이북에는 다음과 같이 사용합니다.
---
- hosts: webservers
roles:
- common
- webservers
다음과 같이 role x
에 대하여 작업이 진행됩니다.
- 만약 roles/x/tasks/main.yml 이 존재하면, 그곳에 있는 태스크 들이 플레이에 추가됩니다
- 만약 roles/x/handlers/main.yml 이 존재하면, 그곳에 있는 핸들러 들이 플레이에 추가됩니다
- 만약 roles/x/vars/main.yml 이 존재하면, 그곳에 있는 변수 들이 플레이에 추가됩니다
- 만약 roles/x/defaults/main.yml 이 존재하면, 그곳에 있는 변수 들이 플레이에 추가됩니다
- 만약 roles/x/meta/main.yml 이 존재하면, 그곳에 있는 role 의존성이 role 목록에 추가됩니다 (role 의존성은 아래에 따로 설명합니다)
- Role에 있는 다른 어떤 copy, script, template 또는 include task 들은 role/x/{files,templates,tasks}/ 에 있는 것을 참조합니다.
버전 1.4 이후에는 role을 찾기위한 roles_path
설정 변수를 사용할 수 있습니다.
만약 해당 파일이 존재하지 않으면 해당 내용은 무시됩니다. 예를 들어 role을 위한 vars/ 하위 디렉터리는 없을 수도 있습니다.
- 파라미터 방식
---
- hosts: webservers
roles:
- common
- { role: foo_app_instance, dir: '/opt/a', app_port: 5000 }
- { role: foo_app_instance, dir: '/opt/b', app_port: 5001 }
- 조건부 방식
---
- hosts: webservers
roles:
- { role: some_role, when: "ansible_os_family == 'RedHat'" }
- 태그방식
---
- hosts: webservers
roles:
- { role: foo, tags: ["bar", "baz"] }
role의 태스크에 있는 이런 tag는 role 안에 단순 정의된 tag를 우선합니다. 만약 만약 특정 role에 있는 여러 task를 부분적으로 사용할 필요가 생긴다면 해당 role을 더 작은 단위로 나눌 필요가 있습니다.
- 순서 지정
---
- hosts: webservers
pre_tasks:
- shell: echo 'hello'
roles:
- { role: some_role }
tasks:
- shell: echo 'still busy'
post_tasks:
- shell: echo 'goodbye'
3. Ansible Role 예제 분석
ansible galaxy
라는 것을 알게 되어, 처음으로 docker
설치를 한번 해보기 위해서 예제를 다운받았습니다.
- https://galaxy.ansible.com/geerlingguy/docker 접속
- ansible 이 설치된 서버에서 다음을 실행
$ cd /etc/ansible/
$ ansible-galaxy install geerlingguy.docker
- roles 폴더 확인
$ cd /etc/ansible/roles
defaults/main.yml
handlers/main.yml
LICENSE
meta/main.yml
molecule
README.md
tasks/main.yml
/docker-1809-shim.yml
/docker-compose.yml
/docker-users.yml
/setup-Debian.yml
/setup-RedHat.yml
templates/override.conf.j2
- 실행
/etc/ansible 경로에 playbook_docker_test.yml 파일을 생성한뒤, playbook을 실행합니다.
ansible-playbook playbook_docker_test.yml
- hosts: test
roles:
- geerlingguy.docker
- 실행결과 해석
PLAY [test] ************************************************************************************************************************************************************************************************************************
TASK [Gathering Facts] ************************************************************************************************************************************************************************************************************
ok: [https_ids_a01]
TASK [geerlingguy.docker : include_tasks] *****************************************************************************************************************************************************************************************
included: /etc/ansible/roles/geerlingguy.docker/tasks/setup-RedHat.yml for https_ids_a01
TASK [geerlingguy.docker : Ensure old versions of Docker are not installed.] ******************************************************************************************************************************************************
ok: [https_ids_a01]
TASK [geerlingguy.docker : Add Docker GPG key.] ***********************************************************************************************************************************************************************************
ok: [https_ids_a01]
TASK [geerlingguy.docker : Add Docker repository.] ********************************************************************************************************************************************************************************
ok: [https_ids_a01]
TASK [geerlingguy.docker : Configure Docker Edge repo.] ***************************************************************************************************************************************************************************
[WARNING]: The value 0 (type int) in a string field was converted to u'0' (type string). If this does not look like what you expect, quote the entire value to ensure it does not change.
ok: [https_ids_a01]
TASK [geerlingguy.docker : Configure Docker Test repo.] ***************************************************************************************************************************************************************************
ok: [https_ids_a01]
TASK [geerlingguy.docker : include_tasks] *****************************************************************************************************************************************************************************************
skipping: [https_ids_a01]
TASK [geerlingguy.docker : install the container-selinux rpm from a remote repo] **************************************************************************************************************************************************
ok: [https_ids_a01]
TASK [geerlingguy.docker : Install Docker.] ***************************************************************************************************************************************************************************************
changed: [https_ids_a01]
TASK [geerlingguy.docker : Ensure containerd service dir exists.] **************************************************************************************************************************************************************************
ok: [https_ids_a01]
TASK [geerlingguy.docker : Add shim to ensure Docker can start in all environments.] *******************************************************************************************************************************************************
ok: [https_ids_a01]
TASK [geerlingguy.docker : Reload systemd daemon if template is changed.] ******************************************************************************************************************************************************************
skipping: [https_ids_a01]
TASK [geerlingguy.docker : Ensure Docker is started and enabled at boot.] ******************************************************************************************************************************************************************
changed: [https_ids_a01]
RUNNING HANDLER [geerlingguy.docker : restart docker] **************************************************************************************************************************************************************************************
changed: [https_ids_a01]
TASK [geerlingguy.docker : include_tasks] **************************************************************************************************************************************************************************************************
included: /etc/ansible/roles/geerlingguy.docker/tasks/docker-compose.yml for https_ids_a01
TASK [geerlingguy.docker : Check current docker-compose version.] **************************************************************************************************************************************************************************
ok: [https_ids_a01]
TASK [geerlingguy.docker : Delete existing docker-compose version if it's different.] ******************************************************************************************************************************************************
skipping: [https_ids_a01]
TASK [geerlingguy.docker : Install Docker Compose (if configured).] ************************************************************************************************************************************************************************
ok: [https_ids_a01]
TASK [geerlingguy.docker : include_tasks] **************************************************************************************************************************************************************************************************
[DEPRECATION WARNING]: evaluating [] as a bare variable, this behaviour will go away and you might need to add |bool to the expression in the future. Also see CONDITIONAL_BARE_VARS configuration toggle.. This feature will be removed in
version 2.12. Deprecation warnings can be disabled by setting deprecation_warnings=False in ansible.cfg.
skipping: [https_ids_a01]
PLAY RECAP *********************************************************************************************************************************************************************************************************************************
https_ids_a01 : ok=16 changed=3 unreachable=0 failed=0 skipped=4 rescued=0 ignored=0
제일 먼저, 위의 플레이북 실행 결과를 보면, task/main.yml
이 실행 됩니다.
- setup-RedHat.yml
- 도커 설치
- docker-1809-shim.yml
- 도커 정상시작 체크
- 도커 방화벽 충돌체크
- docker-compose.yml
- docker-users.yml
Task의 구조는 위와 같고, 각 yml 파일들이 include
되면서 수행이 됩니다.
4. 필자 Ansible 사용예시
포스트 서두에 말씀드렸듯이, 필자가 사용하는 방법은 nginx configuration 파일들에 대해서 변경해주고 재시작해주는 방식으로 사용하고 있습니다. 여기서 변경할때는 copy
를 하게 되는데 nginx의 멱등성에 의거하여 변경점이 없으면, 업데이트를 안하게 됩니다.
- playbook_nginx_conf.yml
- hosts: "{{ server }}"
remote_user: idsuser
tasks:
- name: Change Nginx Config file
become: yes
become_method: sudo
template:
src: /etc/ansible/config/{{server}}/nginx.conf
dest: /etc/nginx/nginx.conf
- name: Validate Config file
become: yes
become_method: sudo
command: "nginx -t"
register: shell_result
- debug:
var: shell_result.stderr_lines
따로 role
관리를 안하고 상위에서 보듯이 하나의 playbook으로 작성했습니다.
- config 파일 변경
- config 파일 이상유무 테스트
- playbook_nginx_restart.yml
- hosts: idsqa_a01
remote_user: idsuser
tasks:
- name: Syntax Test Nginx.conf
become: yes
become_method: sudo
command: "nginx -t"
register: shell_result
- debug:
var: shell_result.stderr_lines
- name: Restart Nginx
become: yes
become_method: sudo
service: name=nginx state=restarted
when: '"nginx: configuration file /etc/nginx/nginx.conf test is successful" in shell_result.stderr_lines'
- name: Check Nginx Status
become: yes
become_method: sudo
shell: service nginx status
args:
warn: false
register: service_status
- debug:
var: service_status.stdout_lines
- name: Check Process Nginx
become: yes
become_method: sudo
shell: 'ps -ef | grep nginx'
register: process_result
- debug:
var: process_result.stdout_lines
- config 파일 이상유무 테스트
- nginx 재시작
- nginx status 체크
- nginx process 체크
두 개의 파일로 분할해 두었는데, role
로 task
를 쪼개고 하나의 job으로 만드는 것도 좋을 것 같습니다.
5. 마치며..
오늘은 ansible의 role
과 include
에 대해서 알아봤습니다. ansible을 필자도 사용은 하지만, 아주 기초적인 수준으로 사용하고 있었다라는 것을 느꼈습니다. 이번기회에 role
관리를 좀더 잘해보고, 특히 ansible-galaxy
를 통해서 수준높은 role
관리에 대해서 학습을 좀 더 해보도록 하겠습니다.
다음 시간에는 ansible jinja2
에 대해서 알아보는 시간을 갖도록 하겠습니다.