• Role은 여러개의 연관된 Task를 체계화하기 위해 사용
  • 예를 들어, Nginx 를 설치하려면 package repository를 등록해야하고, package를 깔고, 설정 변경이 필요
  • Playbook으로 복잡한 작업을 할 수 있지만, 변경해야하는 부분이 복잡하고 어려워 지기 때문에 Role을 사용 → 변수, 파일, 동적 템플릿 등과 같은 추가 데이터 적용
  • 프로젝트를 진행한다고 했을 때 마구잡이로 프로그래밍을 하게되면 유지보수하기도, 미래에 재사용하기도 어려움 → 해당 문제를 해결하기 위해 ansible도 코드의 구성을 체계화하고 모듈화를 시키는 Role을 활용
  • Role은 여러 연관된 Task들을 체계화시키고 여러 playbook에 추가시켜 사용 가능
  • Role을 구성할 때 ansible이 참조할 수 있도록 하나의 디렉토리에 위치해야함

ansible Role(롤)의 구조

1. handlers

  • handlers가 담겨있는 디렉토리
  • tasks에 사용할 handler들이 보관되어서 사용

2. defaults

  • defaults 인자가 담겨있는 디렉토리
  • 각 task에서 사용할 기본값으로, Roles 상에서 기본으로 사용하는 변수
  • role defaults는 우선순위가 가장 낮음
  • 사용자의 목적 또는 시스템의 환경에 따라 변경이 필요하면 defaults 변수보다 우선순위가 높은 변수에 값을 등록

3. vars

  • Role에서 사용할 변수들이 담겨있는 디렉토리
  • 적용 우선순위가 높기에 Role default에서 변경될 필요가 있는경우 해당 디렉토리에 변수 지정

4. files

  • 배포될 정적 파일들이 위치하는 디렉토리
  • tasks에서는 files 디렉토리에서 필요한 파일들을 끌어다가 사용

5. templates

  • 배포에 사용될 템플릿들이 들어가는 디렉토리
  • python의 jinja2 템플릿 엔진에 기반한 템플릿 변수들이 디렉토리에 담겨있음
  • .j2 확장자를 가지고 있으며 파일명은 아무거나 지정하여도 상관 없음
  • template/default.conf.j2 파일을 생성한 뒤, 원하는 대로 커스텀하여 /etc/nginx/conf.d/default.conf 파일로 배포
    1. ip나 이름과 같이 가변적인 변수들은 vars 디렉토리에 있는 변수 참조
    2. task에서 templates 사용 방법
      - name: Add Default config
      template:
       src: templates/default.conf.j2
       dest: '/etc/nginx/conf.d/default.conf'

6. meta

  • 하나의 Role을 사용하는 경우에는 상관없으나, 여러 Role을 사용하는 경우에 필요

  • 여러 Role이 의존성(dependency)을 가질 때, 미리 수행되어야하는 Role을 지정하는 경우 사용

  • meta 디렉토리의 main.yaml은 Role의 메타정보를 담고 있음

  • 자동으로 생성된 main.yml 파일은 아래와 같이 생성 가능

  • 제작자, 회사, 라이센스 정보 등 Role에 필요한 dependency 정보들이 기입되어야함

    galaxy_info:
      author: your name
      description: your role description
      company: your company (optional)
    
      license: license (GPL-2.0-or-later, MIT, etc)
    
      min_ansible_version: 2.1
      galaxy_tags: []
    
    dependencies: []

7. tasks

  • 기본 task를 넣는 공간으로 Role의 핵심 디렉토리
  • Role이 실제로 동작할 내용이 담겨있음
  • 실제적으로 Role의 역할이 구성되는 뼈대


nginx 테스트를 위한 Role 구성 및 사용 방법 (Virtual Box 내부 테스트에서 실행)

  • 테스트 환경
    1. ansible 서버 : 192.168.0.100
    2. ansible 클라이언트 : 192.168.0.201, 192.168.0.202

1. tasks 디렉토리 구성 (main.yml, centos7.yml, centos6.yml, check_nginx_cfg.yml 파일 구성)

  • tasks 실행하기 위한 main.yml 파일

    $ vi ../tasks/main.yml
    ---
    - name: nginx for Linux
      include_tasks: "{{ linux_version_name }}.yml"
    - name: Check nginx config
      include_tasks: check_nginx_cfg.yml
    
    - name: Check nginx service
      debug:
        msg:
          - "{{ lookup('template',' nginx_check.j2').split('\n') }}"
  • Centos 7 버전의 OS에서 nginx 설치하는 centos7.yml 파일
    $ vi ../tasks/centos7.yml
    - name: install epel-release
      action : "{{ ansible_pkg_mgr }} name=epel-release state=latest"
    - name: install nginx web server
      action : "{{ ansible_pkg_mgr }} name=nginx state=present"
    - name: Upload default index.html for web server
      get_url: url=https://www.nginx.com dest=/usr/share/nginx/html/ mode=0644
      notify:
        - Restart nginx web server
  • Centos 6 버전의 OS에서 nginx 설치하는 centos6.yml 파일
    $ vi ../tasks/centos6.yml
    - name: install epel-release
      action : "{{ ansible_pkg_mgr }} name=epel-release state=latest"
    - name: install nginx web server
      action : "{{ ansible_pkg_mgr }} name=nginx state=present"
    - name: Upload default index.html for web server
      get_url: url=https://www.nginx.com dest=/usr/share/nginx/html/ mode=0644
      notify:
        - Restart nginx web server
  • 설정 파일 check하는 check_nginx_cfg.yml 파일 → nginx -t를 통해 nginx 설정 파일 체크
    $ vi ../tasks/check_nginx_cfg.yml
    - command: "nginx -t"
      register: nginx
    - debug: msg="{{ nginx.stderr_lines }}"

2. handlers 구성(main 파일 구성)

$ vi ../handlers/main.yml
---
- name: Restart nginx web server
  service: name=nginx state=restarted enabled=yes

3. templates(nginx_check.j2 파일 구성)

$ vi ../template/nginx_check.j2
{% if ansible_facts.distribution_major_version == '7' %}
   [ OS : CentOS ver7 ]
    >> yum list installed | grep nginx
    OR
    >> systemctl status nginx
{% elif ansible_facts.distribution_major_version == '6' %}
   [ OS : CentOS ver6 ]
    >> yum list installed | grep nginx
    OR
    >> service nginx status
{% else %}
    >> service nginx status (* Gernally)
{% endif %}

4. vars (main.yml 파일 구성 → role_groups 변수 지정)

$ vi ../vars/main.yml
---
linux_version_name: "{{ 'centos7' if ansible_facts.distribution_major_version == '7'
          else 'centos6' if ansible_facts.distribution_major_version == '6'
          else 'No working Linux version' }}"

5. nginx 테스트를 위해 작성한 Role 디렉토리 하위 tree 구조 확인

$ tree roles/
roles/
└── nginx
    ├── handlers
    │   └── main.yml
    ├── tasks
    │   ├── centos6.yml
    │   ├── centos7.yml
    │   ├── check_nginx_cfg.yml
    │   └── main.yml
    ├── templates
    │   └── nginx_check.j2
    └── vars
        └── main.yml

5 directories, 7 files

6. nginx를 설치하기 위한 Role 파일 구성

  • 위에 작성된 Role 구성을 실행하기 위해서 Playbooks를 작성해야함

  • roles 디렉토리를 실행하기 위해 roles 디렉토리가 위치한 공간에 해당 yaml 파일 생성

  • ./roles/nginx 디렉토리 아래에 있는 구성들을 실행

    $ vi nginx_instal_role.yml
    ---
    - name: Install nginx
      hosts: test
      become: no
    
      roles:
        - { role: ./roles/nginx }

7. ansible Role을 활용하여 nginx 설치

# ansible role 구성 내용이 담긴 roles 디렉토리와 ansible Role을 실행할 nginx_install_role.yml 파일 확인
$ ls
nginx_instal_role.yml  roles

# 실행 명령어
$ ansible-playbook nginx_instal_role.yml -k
SSH password:

PLAY [Install nginx] ************************************************************************

TASK [Gathering Facts] **********************************************************************
ok: [192.168.0.202]
ok: [192.168.0.201]

TASK [./roles/nginx : nginx for Linux] ******************************************************
included: /root/ansible_test/roles/nginx/tasks/centos7.yml for 192.168.0.201, 192.168.0.202

TASK [./roles/nginx : install epel-release] *************************************************
changed: [192.168.0.201]
changed: [192.168.0.202]

TASK [./roles/nginx : install nginx web server] *********************************************
changed: [192.168.0.202]
changed: [192.168.0.201]

TASK [./roles/nginx : Upload default index.html for web server] *****************************
changed: [192.168.0.202]
changed: [192.168.0.201]

TASK [./roles/nginx : Check nginx config] ***************************************************
included: /root/ansible_test/roles/nginx/tasks/check_nginx_cfg.yml for 192.168.0.201, 192.168.0.202

TASK [./roles/nginx : command] **************************************************************
changed: [192.168.0.202]
changed: [192.168.0.201]

TASK [./roles/nginx : debug] ****************************************************************
ok: [192.168.0.201] => {
    "msg": [
        "nginx: the configuration file /etc/nginx/nginx.conf syntax is ok",
        "nginx: configuration file /etc/nginx/nginx.conf test is successful"
    ]
}
ok: [192.168.0.202] => {
    "msg": [
        "nginx: the configuration file /etc/nginx/nginx.conf syntax is ok",
        "nginx: configuration file /etc/nginx/nginx.conf test is successful"
    ]
}

TASK [./roles/nginx : Check nginx service] **************************************************
ok: [192.168.0.201] => {
    "msg": [
        [
            "   [ OS : CentOS ver7 ]",
            "    >> yum list installed | grep nginx",
            "    OR",
            "    >> systemctl status nginx",
            ""
        ]
    ]
}
ok: [192.168.0.202] => {
    "msg": [
        [
            "   [ OS : CentOS ver7 ]",
            "    >> yum list installed | grep nginx",
            "    OR",
            "    >> systemctl status nginx",
            ""
        ]
    ]
}

RUNNING HANDLER [./roles/nginx : Restart nginx web server] **********************************
changed: [192.168.0.202]
changed: [192.168.0.201]

PLAY RECAP **********************************************************************************
192.168.0.201              : ok=10   changed=5    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
192.168.0.202              : ok=10   changed=5    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Playbook run took 0 days, 0 hours, 0 minutes, 46 seconds

8. nginx 설치 확인

  • 192.168.0.201 서버에 nginx가 설치되고, https://www.nginx.com 웹 페이지가 보일 수 있도록 설정되어있는지 확인

  • 192.168.0.202 서버에 nginx가 설치되고, https://www.nginx.com 웹 페이지가 보일 수 있도록 설정되어있는지 확인

1. 변수 사용

  • Ansible은 변수를 사용하여 시스템 간의 차이점을 관리
  • Ansible을 사용하면 단일 명령으로 여러 시스템에서 tasks와 playbooks을 실행 가능
  • 서로 다른 시스템 간의 변형을 나타내기 위해 lists와 dictionary을 포함한 표준 YAML 구문을 사용하여 변수 생성
  • playbooks, inventory, files, roles 그리고 명령줄에 변수를 정의
  • tasks의 출력값을 새 변수로 등록하여서 playbooks를 실행하는 동안 변수를 생성하여 사용 가능
  • tasks의 출력값을 새 변수로 등록하여, "when" 조건문 구문, templates, loops에서 사용 가능


2. 유효한 변수 이름 만들기

  • 모든 문자열을 Ansible 변수 이름으로 사용하지는 못함 → 변수 이름에는 문자, 숫자 및 밑줄만 포함 가능
  • Python 키워드 또는 playbooks 키워드는 변수 이름으로 사용 불가
  • 변수 이름은 숫자로 시작할 수 없음
  • 변수 이름은 밑줄로 시작할 수 있음
  • 많은 프로그래밍 언어에서 밑줄로 시작하는 변수를 private로 사용하지만, ansible에서는 일반 변수와 동일하게 사용됨
  • 개인 정보 보호 또는 보안으로 ansible에서 밑줄로 시작하는 변수를 사용하여도 효과 X
  • 유효한 변수 이름과 잘못된 변수 이름의 예시

3. 단순 변수

  • 단순 변수는 변수 이름에 하나의 값을 결합
  • 단순 변수는 lists 및 dictionaries 등 다양한 곳에서 사용 가능
  • 단순 변수는 playbooks, inventory, files, roles 그리고 명령줄에서 사용 가능

3.1. 단순 변수 정의

  • 표준 YAML 구문에 단일 변수를 정의
  • 단순 변수 사용 예시
    remote_install_path: /opt/my_app_config

3.2. 단순 변수 참조

  • 단순 변수를 정의한 후 Jinja2 구문을 사용하여 단순 변수 참조 가능
  • Jinja2 변수는 이중 중괄호로 호출 가능
  • playbooks에서 Jinja2 구문을 사용하여 위에 선언한 단순 변수를 참조 → 변수는 시스템마다 다를 수 있는 파일의 위치를 정의
  • 단순 변수 참조 예시
    ansible.builtin.template:
    src: foo.cfg.j2
    dest: '{{ remote_install_path }}/foo.cfg'

※ 참조

  • Ansible은 templates에서 Jinja2 loops와 Jinja2 conditions을 사용 가능
  • Ansible은 playbooks에서Jinja2 loops와 Jinja2 conditions을 사용 불가 → 작업 루프를 생성 불가
  • Ansible의 playbooks은 순수한 기계 구문 분석이 가능한 YAML으로 해당 설정이 적용이 안되는 것


4. Quotes 사용한 변수

  • 문자열에서는 큰 따옴표보다 작은 따옴표를 선호
  • 큰 따옴표는 작은 따옴표 안에 중첩되거나, 문자열에 이스케이프 문자가 필요한 경우에 사용
  • folded block scalar로 불리는 > 을 사용할 때, 문자열에 따옴표를 생략
  • Quotes를 사용하면 안되는 경우
    1. 부울논리(true or false)
    2. 숫자 (42, 17),
    3. Ansible 환경을 참조 (부울 논리 또는 값을 할당할 변수의 이름을 사용하는 경우)
  • 문자열이 YAML의 기본 유형이지만, Quotes를 사용하면 명시적으로 구문을 강조하여 더 좋아 보임
  • 따옴표 없이 변수를 사용하는 경우 변수 호출에 문제 발생 → ERROR! Syntax Error while loading YAML.
    - hosts: app_servers
      vars:
          app_path: {{ base_path }}/22
  • 따옴표를 추가하는 경우, Ansible가 제대로 작동
    - hosts: app_servers
      vars:
           app_path: "{{ base_path }}/22"


5. 변수 나열

  • 목록 변수는 변수 이름을 여러 값과 결합
  • 여러 값을 항목별 목록으로 저장하거나 []쉼표로 구분하여 대괄호 안에 저장 가능

5.1. lists 변수 정의

  • YAML에 lists를 사용하여 여러 값으로 변수를 정의 가능
  • lists 변수 사용 예시
    region:
      - northeast
      - southeast
      - midwest

5.2. lists 변수 참조

  • lists(배열)로 정의된 변수를 사용하는 경우 해당 lists의 개별 특정 필드 사용 가능
  • lists의 첫 번째 항목은 항목 0이고 두 번째 항목은 항목 1임 → 일반 프로그래밍 언어와 같이 0부터 시작
  • 예시 → region 이름을 가진 lists 표현식의 값은 "북동쪽"
    region: "{{ region[0] }}"


6. dictionary 변수

  • dictionary 변수는 키-값 쌍으로 데이터를 저장
  • 일반적으로 dictionary 변수는 ID 또는 사용자 프로필에 포함된 정보와 관련 데이터를 저장하는 데 사용

6.1. 변수를 키:값 사전으로 정의

  • YAML의 dictionary 변수는 복잡한 변수를 정의 가능
  • YAML의 dictionary 변수는 key와 value로 매핑
  • 예시
    foo:
      field1: one
      field2: two

6.2. 키:값 dictionary 변수 참조

  • 키:값 dictionary(해시)으로 정의된 변수를 사용할 때 대괄호 표기법이나 점 표기법을 사용하여 dictionary의 특정 필드를 사용 가능
  • 대괄호 표기법은 항상 작동
  • 점 표기법은 일부 키가 파이썬 dictionary의 속성 및 메소드와 충돌하기 때문에 문제 발생 가능 → 점 표기법보다는 대괄호 표기법 사용하는게 좋음
  • 예시 → 아래 두 예는 모두 동일한 값("하나")을 참조
    foo['field1']
    foo.field1

7. 변수 등록

  • Ansible의 task의 출력 결과를 저장하는 register 변수 생성

  • register에 저장된 변수는 when 키워드를 사용하여 조건문에 사용 가능

  • register 변수는 많은 영역에서 사용 가능

    1. 단순 변수(simple variables)
    2. lists 변수
    3. dictionary 변수
    4. 중첩 데이터 변수
  • register 변수는 메모리에 저장

  • register 변수는 playbooks의 실행기간 동안에만 사용 가능

  • 루프가 있는 작업에 변수를 등록하면 register 변수에는 루프의 각 항목에 대한 값이 저장 → 루프가 동작하는 동안 변수에 저장된 데이터는 모든 응답 목록에 사용 가능

  • 예시

    - hosts: web_servers
    
      tasks:
         - name: Run a shell command and register its output as a variable
           ansible.builtin.shell: /usr/bin/foo
           register: foo_result
           ignore_errors: true
    
         - name: Run a shell command using output of the previous task
           ansible.builtin.shell: /usr/bin/bar
           when: foo_result.rc == 5

※ 참조

  • task가 실패하거나 건너뛴 경우 Ansible은 실패 또는 건너뜀


8. 중첩 변수 참조

  • 많은 register 변수는 중첩된 YAML 또는 JSON 데이터 구조를 가짐
  • {{ foo }}와 같이 간단한 구조로는 중첩 데이터에 접근 불가능 → 대괄호 표기법이나 점 표기법을 사용해야 함
  • 대괄호 표기법을 사용하여 ansible_facts에서 IP 주소를 참조
    {{ ansible_facts["eth0"]["ipv4"]["address"] }}
  • 점 표기법을 사용하여 ansible_facts에서 IP 주소를 참조
    {{ ansible_facts.eth0.ipv4.address }}


9. multi line 사용방법

  • JSON으로 표현할 때, 데이터 내에 \n을 넣어주는 것은 힘듦
  • 하지만, YAML 파일에는 데이터 내에 \n을 넣어주는 것은 쉬움 → YAML에 넣어서 JSON으로 치환해주면 됨
  • YAML파일에 \n와 같이 개행문자을 넣는 2가지 방법 → > (folded block scalar) 와 | (literal block scalar)

9.1. > (folded block scalar)

  • 개행 문자(\n)를 스페이스로 치환

  • 빈 줄 하나가 단독으로 있는 경우 줄바꿈(\n)으로 변경

  • > 뒤에 -을 붙이면 맨 끝의 줄바꿈 문자를 포함시키지 않음

  • > 만 사용 → YAML 파일을 \n을 스페이스로 치환하고, 맨끝 개행(\n) 문자 포함한 JSON 파일로 변경

    # YAML
    paragraph1: >
      abc
      def
      ghi
    
      aab
      ccc
    
    # JSON
    {
      "paragraph1": "abc def ghi\naab ccc\n"
    }
  • > 와 -를 사용 → YAML 파일을 \n을 스페이스로 치환하고, 맨끝 개행(\n) 문자는 제외한 JSON 파일로 변경

    # YAML
    paragraph2: >-
      abc
      def
      ghi
    
      aab
      ccc
    
    # JSON
    {
      "paragraph2": "abc def ghi\naab ccc"

9.2. | (literal block scalar)

  • 기록한 그대로 줄을 변경

  • 라인의 맨 마지막은 \n으로 표시

  • 빈줄은 \n을 표시

  • | 뒤에 -을 붙이면 맨 끝의 줄바꿈 문자를 포함시키지 않음

  • | 만 사용 → YAML 파일에 표시된 형식을 JSON 파일 그대로 변경, 맨 끝 개행문자(\n) 포함

    # YAML
    paragraph3: |
      abc
      def
      ghi
    
      aab
      ccc
    
    # JSON
    {
      "paragraph3": "abc\ndef\nghi\n\naab\nccc\n"
    }
  • | 와 -를 사용 → YAML 파일에 표시된 형식을 JSON 파일 그대로 변경, 맨 끝 개행문자(\n) 제외

    # YAML
    paragraph4: |-
      abc
      def
      ghi
    
      aab
      ccc
    
    # JSON
    {
      "paragraph4": "abc\ndef\nghi\n\naab\nccc"
    }



10. 변수 설정 위치

  • playbooks, inventory, files, roles 그리고 명령줄은 다양한 위치에서 변수를 정의 가능
  • Ansible은 사용 가능한 변수를 모두 로드한 다음 변수 우선 순위 규칙에 따라 적용할 변수를 선택

10.1. inventory의 변수 정의

  • 각 개별 호스트에 대해 서로 다른 변수를 정의하거나 inventory의 호스트 그룹에 대해 공유 변수를 설정 가능
  • inventory는 호스트 변수 및 그룹 변수를 저장

10.2. playbook에서 변수 정의하기

  • playbooks에서 직접 변수를 정의 가능
  • playbook에서 변수를 정의하면 해당 playbook에서 실행되는 tasks에만 사용 가능.
    - hosts: webservers
      vars:
        http_port: 80

10.3. include files 및 roles에서 변수 정의

  • include files 또는 roles에서 변수를 정의

  • 민감한 변수는 playbooks에 제외하고, include files 또는 roles에 변수를 정의 필요 → 암호나 기타 민감한 개인 데이터가 노출될 위험 없이 playbook을 소스 제어 소프트웨어에 저장하고 공유 가능

  • 외부 파일에 정의된 변수를 포함하는 방법

    ---
    - hosts: all
      remote_user: root
      vars:
        favcolor: blue
      vars_files:
        - /vars/external_vars.yml
    
      tasks:
      - name: This is just a placeholder
        ansible.builtin.command: /bin/echo foo
  • external_vars.yml 파일은 변수들을 저장하는 YAML파일로 playbooks에 선언하여 사용
    $ vi /vars/external_vars.yml
    ---
    # in the above example, this would be vars/external_vars.yml
    somevar: somevalue
    password: magic

※ 참조

  • 호스트별 및 그룹별 변수를 유사한 파일에 보관
  • 변수 구성에 대해 배우려면 호스트 및 그룹 변수 구성을 참조


11. 런타임에 변수 정의

  • --extra-vars 또는 -e 옵션을 사용하여 명령줄에 변수를 정의하여 playbook 실행에 사용
  • vars_prompt(대화협 입력)을 사용하여 사용자 입력을 요청 가능
  • 명령줄에서 변수를 전달할 때 아래 형식 중 하나로 하나 이상의 변수를 포함하는 작은 따옴표로 묶인 문자열을 사용
  • --extra-vars 옵션을 사용하는 예시 → 12. ansible-playbook을 사용하여 netbox 설치 -> extra-vars 변수 2개 사용

11.1. 키=값 형식

  • key=value 구문을 사용하여 값을 전달
  • 전달된 값은 문자열로 해석
    ansible-playbook release.yml --extra-vars "version=1.23.45 other_variable=foo"

11.2. JSON 문자열 형식

  • 부울, 정수, 부동 소수점, 목록 등과 같은 문자열이 아닌 값을 전달해야 하는 경우 JSON 형식을 사용해야함
  • 변수를 전달할 때 --extra-vars 마크업(예: JSON)과 셸 모두에 적절하게 따옴표 및 기타 특수 문자를 이스케이프해야 함
    ansible-playbook release.yml --extra-vars '{"version":"1.23.45","other_variable":"foo"}'
    ansible-playbook arcade.yml --extra-vars '{"pacman":"mrs","ghosts":["inky","pinky","clyde","sue"]}'
  • 특수 문자가 많은 경우 변수 정의가 포함된 JSON 또는 YAML 파일을 사용
    ansible-playbook arcade.yml --extra-vars "{\"name\":\"Conan O\'Brien\"}"
    ansible-playbook arcade.yml --extra-vars '{"name":"Conan O'\\\''Brien"}'
    ansible-playbook script.yml --extra-vars "{\"dialog\":\"He said \\\"I just can\'t get enough of those single and double-quotes"\!"\\\"\"}"

11.3. JSON 또는 YAML 파일에 vars를 정의하여서 --extra-vars를 사용

  • JSON이나, YAML파일을 사용할 때 --extra-vars를 사용 예시
    ansible-playbook release.yml --extra-vars "@some_file.json"


12. 변수 우선 순위: 변수를 어디에 넣어야 할까??

  • 여러 위치에서 동일한 이름으로 여러 변수를 설정 가능 → Ansible은 사용 가능한 변수를 모두 로드한 다음 변수 우선 순위 규칙에 따라 적용할 변수를 선택
  • 다른 변수가 특정 순서로 서로를 무시 → 각 변수를 한 곳에서 정의하는 것이 좋음(변수를 정의할 위치를 단순하게 하는 것이 효율적)
  • 설정의 우선 순위에 따라 의도하지 않은 결과가 나올 수 있기에 주의하여서 변수를 정의하는 것이 중요
  • ansible 변수의 우선 순위(1번이 우선 순위가 낮음이고, 21번이 우선순위가 높음임)


  • task의 맨 뒤에 notify action 설정(handler의 이름으로 호출) → 함수의 기능과 유사
  • 여러 번 조건을 만족해도 단 한번만 실행
  • handler는 변경이 있을 때 작업을 수행(Handler 는 특정 이벤트가 일어났을 때 실행)
  • handler는 특별한 task의 list이며 이름으로 참조 → handlers로 선언한 이름을 기반으로 task의 notify에서 호출(Task에서 notify로 handler를 call할 때만 실행됨)
  • 대개 service restart 등에 사용 → 예시) rc-local.services를 restart할때 handler 사용
  • 'notify' action은 play의 각 task들이 끝날때 시작
  • Handler는 Task와 기능적으로 많이 유사 → Task가 하는 모든 일을 할 수 있음

Nginx 서비스 설치 및 실행 과정


Nginx 서비스 설치 및 nginx의 html 파일 다운하는 yaml 파일

  • handler를 사용하여 task 작업 후에 Nginx 서비스가 실행 될 수 있게 설정

    $ vi nginx_install.yml
    ---
    - hosts: test
      remote_user: hippo                                                          # 사용자 id -> hippo
      become_method: su                                                         # sudo로 하면 아래 비밀번호 입력할 때 까지 root 권한을 가질 수 없어 문제 발생
      gather_facts: no
      tasks:
        - name: install epel-release
          yum: name=epel-release state=latest
    
        - name: install nginx web server
          yum: name=nginx state=present
    
        - name: Upload basic index.html for web server
          get_url:
            url : https://www.nginx.com
            dest : /usr/share/nginx/html/index.html mode=0644   # get_url 모듈을 사용할 수 없다면 shell 모듈로 "wget 명령어" 사용하여 해당 파일 다운
          notify:                                                                        # handler를 호출하기 위해 handler 이름 지정
            - Restart nginx web server
            - Print nginx service status
    
        - name: confirm to change index.html
          shell: "cat /usr/share/nginx/html/index.html"
          register: change_result
    
        - name: print change file result
          debug:
            msg:
              - "{{ change_result.stdout.split('\n') }}"
    
      handlers:
        - name: Restart nginx web server
          service: name=nginx state=restarted enabled=yes
    
        - name: Print nginx service status
          shell: "/bin/systemctl status nginx"
          register: nginx_status
          debug:
            msg:
              - "{{ nginx_status.stdout.split('\n') }}"

  • get_url 모듈을 사용하려면 libselinux-python이 기본으로 설치되어 있어야함
  • 기본적으로 wget보다는 get_url 모듈을 사용 권장하지만 libselinux-python이 설치 되어 있지 않아 사용 불가능 할 경우에는 wget을 사용해야함
  • 서버에 libselinux-python 설치 진행하면 get_url 모듈 사용 가능
  • wget은 ansible에서는 기본적으로는 권장 X → wget은 모듈이 아니라 shell 모듈에 wget 명령어를 사용하기에 get_url 모듈 사용 권장

get_url 모듈을 사용하였을 때 문제가 발생하는 문구 → Aborting, target uses selinux but python bindings (libselinux-python) aren't installed!"


get_url 모듈을 사용하는 yml 파일

$ vi login_change.yml
---
- hosts: test
  gather_facts: no
  become: no

  tasks:
  - name: download login
    get_url:
      url : http://setting.hippo.com/install/os/login_policy_change.sh
      dest : /root/login_policy_change.sh

  - name: execute login policy change
    shell: "source /root/login_policy_change.sh"
    register: change_result

  - name: print change result
    debug:
      msg:
        - "{{ change_result.stdout.split('\n') }}"

shell 모듈에 wget 명령어를 사용하는 yml 파일

$ vi login_change_update.yml
---
- hosts: test
  gather_facts: no
  become: no

  tasks:
  - name: download login
    shell: "wget -P /root/ http://setting.hippo.com/install/os/login_policy_change.sh"

  - name: execute login policy change
    shell: "source /root/login_policy_change.sh"
    register: change_result

  - name: print change result
    debug:
      msg:
        - "{{ change_result.stdout.split('\n') }}"

  • include_tasks를 사용하여 각각 실행시키고자 하는 yaml 파일을 포함
  • 간단하게 또는 소규모로 작성할 경우 하나의 yaml 파일에다가 다 작성해도 괜찮음
  • 규모가 커지면 관리가 어려워지고 가독성이 떨어지기에 분리해서 yaml 파일을 작성

1. Centos7인지 Cenos6인지, Centos 아닌지 따라 ip 출력하는 playbook 코드(Include_tasks 사용)

  • centos_check.yml 파일은 메인 yaml 파일

  • include_tasks를 사용해서 조건에 맞게 centos7.yml, centos6.yml, not_centos.yml 파일을 가지고 올 수 있게 함

    $ vi centos_check.yml
    ---
    - name: print ipv4 address for ansible client
      hosts: all
      #gather_facts: no
    
      tasks:
        - name: debug by msg in Centos7
          include_tasks: centos7.yml
          when:
            - ansible_distribution == 'CentOS'
            - ansible_distribution_major_version == '7'
    
        - name: debug by msg in Centos6
          include_tasks: centos6.yml
          when:
            - ansible_distribution == 'CentOS'
            - ansible_distribution_major_version == '6'
    
        - name: debug by msg not in Cenos
          include_tasks: not_centos.yml
          when:
            - ansible_distribution != 'CentOS'

2. include_tasks를 통해 불러오는 보조용 yaml 파일들

2.1. centos7.yml 파일 내용

$ vi centos7.yml
- name: "Print IP in Centos7"
  debug:
    msg:
      - "Centos7 ip is {{ ansible_all_ipv4_addresses[0] }}"

2.2. centos6.yml 파일 내용

$ vi centos6.yml
- name: "Print IP in Centos6"
  debug:
    msg:
      - "Centos6 ip is {{ ansible_all_ipv4_addresses[0] }}"

2.3. not_centos.yml 파일 내용

$ vi not_centos.yml
- name: "Print IP not in Centos"
  debug:
    msg:
      - "Not Centos ip is {{ ansible_all_ipv4_addresses[0] }}"



3. centos_check.yml 파일 실행 결과

$ ansible-playbook centos_check.yml

PLAY [print ipv4 address for ansible client] *************************************************************************

TASK [Gathering Facts] *****************************************************************************************
ok: [192.168.1.13]

TASK [debug by msg in Centos7] **********************************************************************************
included: /root/centos7.yml for 192.168.1.13

TASK [Print IP in Centos7] ****************************************************************************************
ok: [192.168.1.13] => {
    "msg": [
        "Centos7 ip is 192.168.1.13"
    ]
}

TASK [debug by msg in Centos6] **********************************************************************************
skipping: [192.168.1.13]

TASK [debug by msg not in Cenos] *********************************************************************************
skipping: [192.168.1.13]

PLAY RECAP ****************************************************************************************************
192.168.1.13             : ok=3    changed=0    unreachable=0    failed=0    skipped=2    rescued=0    ignored=0

  • when 조건절을 사용하면 task 구문이 조건의 수에 따라 필요하게 됨
  • 조건이 많이 필요해서 when을 많이 구성하면 가독성이 떨어짐
  • if 조건절을 사용하면 task 구문을 한개만 사용 가능

1. Centos7인지 Cenos6인지, Centos 아닌지 따라 ip 출력하는 playbook 코드(if 사용)

  • if 문은 평소 파이썬이나 다른 언어에서 사용하는 문법과 큰 차이는 존재하지 않음
  • 변수를 사용하여 상황에 맞게 작성하면 효율적으로 ansible 사용 가능
  • vars: 를 이용해서 linux_version_name 변수에 조건문 값 적용
    $ vi centos_check.yml
    ---
    - name: print ipv4 address for ansible client
      hosts: all
      #gather_facts: no
      vars:
        linux_version_name: "{{ 'centos7' if ansible_distribution == 'CentOS' and ansible_distribution_major_version == '7'
                                 else 'centos6' if ansible_distribution == 'CentOS' and ansible_distribution_major_version == '6'
                                 else 'not_centos' }}"
      tasks:
        - name: debug by msg in OS
          include_tasks: "{{ linux_version_name }}.yml"

2. include_tasks를 통해 불러오는 보조용 yaml 파일 내용

2.1. centos7.yml 파일 내용

$ vi centos7.yml

- name: "Print IP in Centos7"
  debug:
    msg:
      - "Centos7 ip is {{ ansible_all_ipv4_addresses[0] }}"

2.2. centos6.yml 파일 내용

$ vi centos6.yml

- name: "Print IP in Centos6"
  debug:
    msg:
      - "Centos6 ip is {{ ansible_all_ipv4_addresses[0] }}"

2.3. not_centos.yml 파일 내용

$ vi not_centos.yml

- name: "Print IP not in Centos"
  debug:
    msg:
      - "Not Centos ip is {{ ansible_all_ipv4_addresses[0] }}"



3. centos_check.yml 파일 실행 결과

$ ansible-playbook centos_check.yml

PLAY [print ipv4 address for ansible client] ************************************************************

TASK [Gathering Facts] ****************************************************************************
ok: [192.168.1.13]

TASK [debug by msg in OS] ************************************************************************
included: /root/centos7.yml for192.168.1.13

TASK [Print IP in Centos7] *************************************************************************
ok: [192.168.1.13] => {
    "msg": [
        "Centos7 ip is 192.168.1.13"
    ]
}

PLAY RECAP *************************************************************************************
192.168.1.13             : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

  • ansible playbook을 작성할 때 when을 사용해서 특정 조건,상황을 만족해야만 실행될 수 있도록 설정
  • 특정 조건 해당여부를 확인하고 Task 수행
    1. 논리 조건 사용 가능 : and/or, 부등호
    2. 부등호 조건 사용 가능 : ==, !=, >=, <=
    3. Defined 여부 사용 가능: defined, not defined

1. Centos7인지 Cenos6인지, Centos가 아닌지에 따라 ip 출력하는 playbook 코드

# Centos7인지 Cenos6인지, Centos가 아닌지에 따라 ip 출력하는 playbook 코드
$ vi centos_check.yml

---
- name: print ipv4 address for ansible client
  hosts: all
  #gather_facts: no

  tasks:
    - name: debug by msg in Centos
      debug:
        msg:
          - "Centos7 ip is {{ ansible_all_ipv4_addresses[0] }}"
      when:
        - ansible_distribution == 'CentOS'
        - ansible_distribution_major_version == '7'

    - name: debug by msg in Centos
      debug:
        msg:
          - "Centos6 ip is {{ ansible_all_ipv4_addresses[0] }}"
      when:
        - ansible_distribution == 'CentOS'
        - ansible_distribution_major_version == '6'

    - name: debug by msg not in Cenos
      debug:
        msg:
          - "not Centos ip is {{ ansible_all_ipv4_addresses[0] }}"
      when:
        - ansible_distribution != 'CentOS'

2. centos_check.yml 파일 실행 결과

# 실행
$ ansible_playbook centos_check.yml

PLAY [print ipv4 address for ansible client] ************************************************************

TASK [Gathering Facts] ****************************************************************************
ok: [192.168.1.13]

TASK [debug by msg in Centos] *********************************************************************
ok: [192.168.1.13] => {
    "msg": [
        "Centos7 ip is 192.168.1.13"
    ]
}

TASK [debug by msg in Centos] **********************************************************************
skipping: [192.168.1.13]

TASK [debug by msg not in Cenos] ********************************************************************
skipping: [192.168.1.13]

PLAY RECAP ***************************************************************************************
192.168.1.13             : ok=2    changed=0    unreachable=0    failed=0    skipped=2    rescued=0    ignored=0

  • ansible 서버에서 ansible 클라이언트에 따라 동적으로 할당한 변수를 의미
  • ansible 클라이언트의 OS, IP, Hostname 등 다양한 정보를 변수로 저장

1. FACT(s) 확인 방법

  • ansible 클라이언트가 제공하는 동적 변수를 확인 가능
    $ ansible all -m setup
    192.168.1.13 | SUCCESS => {
        "ansible_facts": {
            "ansible_all_ipv4_addresses": [
                "192.168.1.13"
            ],
            "ansible_all_ipv6_addresses": [
                "fe00:1429:2222:31f5:fcf3"
            ],
            "ansible_apparmor": {
                "status": "disabled"
            },
            "ansible_architecture": "x86_64",
            "ansible_bios_date": "03/07/2013",
            "ansible_bios_version": "1.6.0",
            "ansible_cmdline": {
                "BOOT_IMAGE": "/vmlinuz-3.10.0-1160.11.1.el7.x86_64",
                "LANG": "en_US.UTF-8",
                "crashkernel": "auto",
                "quiet": true,
                "rhgb": true,
                "ro": true,
                "root": "UUID=c82653ea-81c1-4ed6-b566-e02cfe1f6ad4"
            },
    [... 생략]

2. FACT(s) 동적 변수를 사용 방법

  • Ansible Playbook YML 파일을 작성할 때 기본이 되는 "gather_facts:no" 부분을 주석 처리 → 기본 설정이 "gather_facts=yes"
  • "gather_facts:yes"로 표시하여 gather_facts 동적 변수 사용 명시
  • gather_fact:no 가 YML 파일에 정의되어있으면 FACT(s)를 수집하지 않도록 하여 ansible의 성능을 향상시킬 수 있음

3. FACT(s)를 사용한 예

  1. debug → 제대로 실행되었는지 알고싶을 때 사용하는 옵션

    • debug 없이 playbook을 실행하였을 때 성공했으면 ok, 실패했으면 failed, 변화가 발생하면 change 출력 → 정상적으로 실행되었는지 여부만 확인 가능
    • debug는 더 자세한 실행과정들을 알고 싶을 때 사용하는 모듈 → 표기된 메세지나 변수를 콘솔에 출력하는 역할
  2. msg → 변수들을 msg형태로 출력

  3. var → Ansible의 playbook은 작성 시 일반 프로그래밍 언어와 같은 변수(var)입력 가능

  4. {{ items }} → with_items: 에서 설정한 변수들을 하나씩 사용 가능

  5. inventory_hostname → /etc/ansible/hosts 에 정의된 hostname을 사용 가능

    # ansible 클라이언트의 ip를 출력하는 playbook 코드
    $ vi ip_address.yml
    ---
    - name: print ipv4 address for ansible client
     hosts: all
     #gather_facts: no
    
     tasks:
       - name: debug by msg
         debug:
           msg:
             - "ip is {{ ansible_all_ipv4_addresses[0] }}"
    
       - name: debug by var
         debug:
           var: "{{ item }}"
         with_items:
           - hostvars[inventory_hostname]['ansible_default_ipv4']['address']

Ansible에서 배열과 같은 리스트를 주어 주고 반복할 때 with_items를 사용하면 편함

  • with_items에 리스트로 추가한 항목들이 item이라는 변수에 하나씩 들어와서 사용 가능
  • 반복할 항목은 with_items에 key: value 형태로 배열로 추가
  • 각 항목의 값은 ``으로 사용

4. FACT(s) 실행 출력 결과

$ ansible-playbook ip_address.yml

PLAY [print ipv4 address for ansible client] **************************************************************

TASK [Gathering Facts] ******************************************************************************
ok: [192.168.1.13]

TASK [debug by msg] *******************************************************************************
ok: [192.168.1.13] => {
    "msg": [
        "ip is 192.168.1.13"
    ]
}

TASK [debug by var] ********************************************************************************
ok: [192.168.1.13] => (item=hostvars[inventory_hostname]['ansible_default_ipv4']['address']) => {
    "ansible_loop_var": "item",
    "hostvars[inventory_hostname]['ansible_default_ipv4']['address']": "192.168.1.13",
    "item": "hostvars[inventory_hostname]['ansible_default_ipv4']['address']"
}

PLAY RECAP ****************************************************************************************
192.168.1.13              : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

+ Recent posts