[Ansible] 플레이북 Role 과 Include

[Ansible] 플레이북 Role 과 Include

안녕하세요? 정리하는 개발자 워니즈입니다. 이번시간에는 Ansible 의 플레북에 대한 이야기를 해보려고 합니다. 필자가 속한 프로젝트에서는 Ansible을 주로 다수의 서버의 configuration 을 수정할 때 사용하고 있습니다. 즉, 1개의 master 파일을 수정하고 각 서버의 경로에 해당 파일로 copy 해주는 단순한 구조입니다.

ansible playbook

필자가 있는 프로젝트에서는 다음과 같이 사용합니다 .

  • 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 설치를 한번 해보기 위해서 예제를 다운받았습니다.

  1. https://galaxy.ansible.com/geerlingguy/docker 접속
  2. ansible 이 설치된 서버에서 다음을 실행
$ cd /etc/ansible/
$ ansible-galaxy install geerlingguy.docker
  1. 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
  1. 실행

/etc/ansible 경로에 playbook_docker_test.yml 파일을 생성한뒤, playbook을 실행합니다.

ansible-playbook playbook_docker_test.yml
- hosts: test
  roles:
    - geerlingguy.docker
  1. 실행결과 해석
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의 멱등성에 의거하여 변경점이 없으면, 업데이트를 안하게 됩니다.

  1. 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 파일 이상유무 테스트
  1. 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 체크

두 개의 파일로 분할해 두었는데, roletask를 쪼개고 하나의 job으로 만드는 것도 좋을 것 같습니다.

5. 마치며..

오늘은 ansible의 roleinclude에 대해서 알아봤습니다. ansible을 필자도 사용은 하지만, 아주 기초적인 수준으로 사용하고 있었다라는 것을 느꼈습니다. 이번기회에 role관리를 좀더 잘해보고, 특히 ansible-galaxy를 통해서 수준높은 role 관리에 대해서 학습을 좀 더 해보도록 하겠습니다.

다음 시간에는 ansible jinja2 에 대해서 알아보는 시간을 갖도록 하겠습니다.

워니즈 블로그
워니즈 깃헙

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다