[Ansible] Ansible-Playbook 사용하기 #2

12 분 소요

image.png

테스트 환경

  • ansible212-247 / OS : CentOS 8.3 (controller)
  • ansible-node212-227 / OS : CentOS 6.10 (target)
  • ansible-node212-216 / OS : CentOS 7.9 (target)
  • ansible-node212-245 / OS : CentOS 8.3 (target)


Tips

  • ansible 사용 시, 에러를 보기 편하게 변경 가능
vim /etc/ansible/ansible.cfg
#stdout_callback = skippy
stdout_callback = debug


Ansible-playbook 의 멱등성

Ansible playbook을 이용하면 멱등성을 보장한다는 장점이 있습니다. ( 대부분의 모듈이 보장하며, 보장하지 않는 모듈도 존재 ) 멱등성이란, 명령을 여러번 실행해도 결과가 변하지 않는다는 것을 의미합니다.


  • 아래와 같이, 명령어로 문자 입력 시, 실행한 만큼 입력되는 것을 볼수 있습니다.
[root@ansible212-247 yaml]# echo -e "[test]\n1.1.1.1" >> /etc/hosts
[root@ansible212-247 yaml]# cat /etc/hosts
[test]
1.1.1.1
[root@ansible212-247 yaml]# echo -e "[test]\n1.1.1.1" >> /etc/hosts
[root@ansible212-247 yaml]# cat /etc/hosts
[test]
1.1.1.1
[test]
1.1.1.1


이를 Shell Script로 작성 시, 복잡하고 입력할 문자열이 달라질때마다 새로운 코드를 작성해야되는 번거로움이 있습니다. Ansible Play book 사용 시, 아래와 같이 사용이 가능합니다.

  • Playbook 예제
---
- name : Add_hostfile
  hosts : localhost

  tasks:
  - name : Add hostfile
    blockinfile:
            path: /etc/hosts
            block: |
               [test]
               1.1.1.1


  • 실행 결과
[root@ansible212-247 yaml]# ansible-playbook Add_hostfile.yaml

PLAY [Add_hostfile] ************************************************************************************************************************************************************************************************************************

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

TASK [Add hostfile] ************************************************************************************************************************************************************************************************************************
changed: [localhost]

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

[root@ansible212-247 yaml]# cat /etc/hosts
# BEGIN ANSIBLE MANAGED BLOCK
[test]
1.1.1.1
# END ANSIBLE MANAGED BLOCK
[root@ansible212-247 yaml]# ansible-playbook Add_hostfile.yaml

PLAY [Add_hostfile] ************************************************************************************************************************************************************************************************************************

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

TASK [Add hostfile] ************************************************************************************************************************************************************************************************************************
ok: [localhost]

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

[root@ansible212-247 yaml]# cat /etc/hosts
# BEGIN ANSIBLE MANAGED BLOCK
[test]
1.1.1.1
# END ANSIBLE MANAGED BLOCK


여러번 실행해도 결과가 변하지 않습니다. YAML 파일 내, 내용을 변경하면 입력한 값에 대해서만 내용이 변경 됩니다.

  • 실행 결과
[root@ansible212-247 yaml]# ansible-playbook Add_hostfile.yaml

PLAY [Add_hostfile] ************************************************************************************************************************************************************************************************************************

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

TASK [Add hostfile] ************************************************************************************************************************************************************************************************************************
changed: [localhost]

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

[root@ansible212-247 yaml]# vim /etc/hosts
[root@ansible212-247 yaml]# cat /etc/hosts
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
110.45.212.247 ansible-212-247

# BEGIN ANSIBLE MANAGED BLOCK
[test]
1.1.1.3
# END ANSIBLE MANAGED BLOCK


become

become이란, 특정 사용자로 명령을 수행하는 것을 의미합니다.

  • 기본 설정값은 주석처리 되어있음
vim /etc/ansible/ansible.cfg
[privilege_escalation]
#become=True
#become_method=sudo
#become_user=root
#become_ask_pass=False


  • 아래와 같이 변경 시, 명령어 수행 시 su - root 를 통해 root 로그인 후 명령어 수행
vim /etc/ansible/ansible.cfg
[privilege_escalation]
become=True # True / False
become_method=su # su 혹은 sudo 등 선택
become_user=root # su - 계정명
become_ask_pass=True  # 명령 사용 시 su - root 패스워드를 요구하도록 하는 옵션


  • 간단한 nginx 설치 테스트 예제
---
- hosts : verify
  tasks:
    - name: install epel-release
      yum: name=epel-release
    - name: install nginx web server
      yum: name=nginx state=present
    - name: Start nginx web server
      service: name=nginx state=started


  • 실행 결과
[root@ansible212-247 yaml]# ansible-playbook install_nginx.yaml -k
SSH password:
# SSH 패스워드
BECOME password[defaults to SSH password]:
# root 패스워드

PLAY [verify] ******************************************************************************************************************************************************************************************************************************

TASK [Gathering Facts] *********************************************************************************************************************************************************************************************************************
ok: [ansible-node212-216]
ok: [ansible-node212-227]
ok: [ansible-node212-245]

TASK [install epel-release] ****************************************************************************************************************************************************************************************************************
changed: [ansible-node212-216]
changed: [ansible-node212-227]
changed: [ansible-node212-245]

TASK [install nginx web server] ************************************************************************************************************************************************************************************************************
changed: [ansible-node212-227]
changed: [ansible-node212-216]
changed: [ansible-node212-245]

TASK [Start nginx web server] **************************************************************************************************************************************************************************************************************
changed: [ansible-node212-227]
changed: [ansible-node212-216]
changed: [ansible-node212-245]

PLAY RECAP *********************************************************************************************************************************************************************************************************************************
ansible-node212-216        : ok=1    changed=3    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
ansible-node212-227        : ok=1    changed=3    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
ansible-node212-245        : ok=1    changed=3    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0


  • 서버 결과
[root@ansible-node212-227 ansible]# netstat -nltp |grep nginx
tcp        0      0 0.0.0.0:80                  0.0.0.0:*                   LISTEN      9257/nginx
tcp        0      0 :::80                       :::*                        LISTEN      9257/nginx

[root@ansible-node212-245 ~]# netstat -nltp |grep nginx
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      3601/nginx: master
tcp6       0      0 :::80                   :::*                    LISTEN      3601/nginx: master

[root@ansible-node212-216 ansible]# netstat -nltp |grep nginx
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      22922/nginx: master
tcp6       0      0 :::80                   :::*                    LISTEN      22922/nginx: master

FACT

  • Ansible에서는 FACT를 통해 서버의 정보를 수집하고 변수로 저장함
  • 디폴트로 사용하도록 되어있으며, 사용하지 않을 경우 아래와 같이 no 설정 ( yes 일 경우, 전체 정보를 수집하기 때문에, 성능이 저하 됨)
---
- hosts: verify
  gather_facts: no


아래와 같이 실행 시, FACT에서 수집한 값을 확인 가능

ansible verify -m setup -k >> out_facts.txt


FACT로 수집된 값은 아래와 같이 when 을 통해 사용 가능 ( CentOS 7만 nginx 설치)

---
- name: Install nginx on the nodes
  hosts: verify

  tasks:
    - name: install nginx web server
      yum: name=nginx state=present
      when : ansible_distribution == 'CentOS'
      when : ansible_distribution_major_version == '7'


Include_task

  • include_task 통해 코드를 구조화 하여 사용 가능 ( OS 종류 별로 실행되는 커맨드가 다르기 때문에 구조화하여 코드를 보기쉽게 정리 가능 )
---
- name: Install nginx on the nodes
  hosts: verify
  vars:
    lnx_name: "Ubuntu_nginx"

  tasks:
  - name: nginx for Any Linux
    include_tasks: ".yml"


  • CentOS_nginx.yaml
- name: Install epel-release
  action : " name=epel-release state=latest"
- name: Install nginx web server
  action : " name=nginx state=present"
- name: Start nginx web server
  service: name=nginx state=started


  • Ubuntu_nginx.yaml
- name: Install nginx web server
  action : " name=nginx state=present update_cache=yes"
- name: Upload default index.html for web server
  get_url: url=https://www.nginx.com dest=/usr/share/nginx/html/ mode=0644 validate_certs=no


handler

  • handler 는 변경된 사항이 있을 때만 실행
  • nginx를 설치하고 handler 를 통해 nginx 재 시작하는 예제, (첫 실행 시에만 핸들러가 동작하고, 그 이후에는 변경사항이 없어 핸들러가 시작되지 않음 )
---
- name: Install nginx on the nodes
  hosts: nodes
  become: yes
  vars:
    lnx_name: "Ubuntu"

  tasks:
  - name: nginx for Any Linux
    include_tasks: ".yml"

  handlers:
  - name: Restart nginx web server
    service: name=nginx state=restarted


variable

  • ansible의 변수는 우선순위에 따라 실행 된다.
  1. command line values (eg “-u user”)
  2. role defaults
  3. inventory file or script group vars
  4. inventory group_vars/all
  5. playbook group_vars/all
  6. inventory group_vars/*
  7. playbook group_vars/*
  8. inventory file or script host vars
  9. inventory host_vars/*
  10. playbook host_vars/*
  11. host facts / cached set_facts
  12. play vars
  13. play vars_prompt
  14. play vars_files
  15. role vars (defined in role/vars/main.yml)
  16. block vars (only for tasks in block)
  17. task vars (only for the task)
  18. include_vars
  19. set_facts / registered vars
  20. role (and include_role) params
  21. include params
  22. extra vars (always win precedence)

group_var / host_vars

  • group, hosts 별로 변수, 환경 설정 가능
[root@ansible212-247 Install]# tree
.
├── CentOS.yml
├── group_vars
│   └── verify
├── host_vars
│   └── 110.45.212.216
├── nginx_install_wo_vars.yml
└── Ubuntu.yml

template

  • ansible 만으로 구현하기 어려운 작업을 python jinja2 를 통해 사용 가능


[root@ansible212-247 install]# tree
├── CentOS.yml
├── group_vars
│   └── verify
├── host_vars
│   └── 110.45.212
├── ins_chk.j2
├── nginx_install_w_template.yml
└── Ubuntu.yml


  • nginx_install_w_template.yml
---
- name: Install nginx on the nodes
  hosts: verify
  become: yes

  tasks:
  - name: nginx for Any Linux
    include_tasks: ".yml"
  - name: Check nginx config
    command: "nginx -t"
    register: nginx
  - debug: msg=""

  - name: Check nginx service
    debug: msg=""

  handlers:
  - name: Restart nginx web server
    service: name=nginx state=restarted


  • ins_chk.j2
{% if ansible_distribution == 'Ubuntu' %}
   [ OS : Ubuntu ]
    >> dpkg -l | grep nginx
    OR
    >> service nginx status
{% elif ansible_distribution == 'CentOS' and ansible_distribution_major_version == '7' %}
   [ OS : CentOS ver7 ]
    >> yum list installed | grep nginx
    OR
    >> systemctl status nginx
{% elif ansible_distribution == 'CentOS' and ansible_distribution_major_version < '7' %}
   [ OS : CentOS ver6 ]
    >> yum list installed | grep nginx
    OR
    >> service nginx status
{% else %}
    >> service nginx status (* Gernally)
{% endif %}


Role

  • 위에서 설명한 내용들을 하나로 합쳐 role 이라는 단위로 모듈화 하여 사용
  • 재 사용성, 유지보수 등이 편리하고 구조를 통해 어떤 내용인 지 쉽게 파악 가능


Role은 아래 7개의 디렉토리 구조를 가짐

  • handlers = 핸들러가 담기는 디렉터리
  • defaults = 디폴트 인자가 들어가는 디렉터리
  • vars = 인자가 정의되는 디렉터리
  • files = 배포될 파일들이 위치하는 디렉터리
  • templates = 배포에 사용될 템플릿들이 들어있는 디렉터리
  • meta = 다른 롤과 의존성이 있는 경우에 해당 롤을 명시
  • tasks = 기본 task를 넣는 공간


[root@ansible212-247 Solution]# tree
.
├── nginx_install_w_roles.yml
└── roles
    └── nginx
        ├── handlers
        │   └── main.yml
        ├── tasks
        │   ├── CentOS.yml
        │   ├── chk_ngx_cfg.yml
        │   ├── main.yml
        │   └── Ubuntu.yml
        ├── templates
        │   └── ins_chk.j2
        └── vars
            └── main.yml


  • nginx_install_w_roles.yml
---
- name: Install nginx on the nodes
  hosts: verify
  become: yes

  roles:
    - { role: ./roles/nginx }


  • roles/nginx/tasks/main.yml
---
- name: nginx for Any Linux
  include_tasks: ".yml"
- name: Check nginx config
  include_tasks: chk_ngx_cfg.yml

- name: Check nginx service
  debug: msg=""


  • roles/nginx/handlers/main.yml
---
- name: Restart nginx web server
  service: name=nginx state=restarted


  • roles/nginx/vars/main.yml
---
lnx_name: "Ubuntu"


ansible galaxy


ansible-galaxy <install or remove> <role 제작자>.<role 이름>


[root@ansible212-247 roles]# ansible-galaxy install jdauphant.nginx
- downloading role 'nginx', owned by jdauphant
- downloading role from [https://github.com/jdauphant/ansible-role-nginx/archive/v2.21.2.tar.gz](https://github.com/jdauphant/ansible-role-nginx/archive/v2.21.2.tar.gz)
- extracting jdauphant.nginx to /root/.ansible/roles/jdauphant.nginx
- jdauphant.nginx (v2.21.2) was installed successfully
[root@ansible212-247 roles]# cd /root/.ansible/roles/jdauphant.nginx


[root@ansible212-247 jdauphant.nginx]# tree
.
├── ansible.cfg
├── defaults
│   └── main.yml
├── handlers
│   └── main.yml
├── meta
│   └── main.yml
├── README.md
├── tasks
│   ├── amplify.yml
│   ├── cloudflare_configuration.yml
│   ├── configuration.yml
│   ├── ensure-dirs.yml
│   ├── installation.packages.yml
│   ├── main.yml
│   ├── nginx-official-repo.yml
│   ├── remove-defaults.yml
│   ├── remove-extras.yml
│   ├── remove-unwanted.yml
│   └── selinux.yml
├── templates
│   ├── auth_basic.j2
│   ├── config_cloudflare.conf.j2
│   ├── config.conf.j2
│   ├── config_stream.conf.j2
│   ├── module.conf.j2
│   ├── nginx.conf.j2
│   ├── nginx.repo.j2
│   └── site.conf.j2
├── test
│   ├── custom_bar.conf.j2
│   ├── example-vars.yml
│   └── test.yml
├── Vagrantfile
└── vars
    ├── Debian.yml
    ├── empty.yml
    ├── FreeBSD.yml
    ├── main.yml
    ├── RedHat.yml
    └── Solaris.yml


Ansible-vault

  • 민감한 정보 ( /etc/ansible/hosts 등 ) 의 파일을 암호화
[root@ansible212-247 install]# ansible-vault encrypt group_vars/nodes
New Vault password:
Confirm New Vault password:
Encryption successful


cat group_vars/nodes
$ANSIBLE_VAULT;1.1;AES256
38666665646432386133383634306361346666643035633630383835356230353336643465666232
6431323336383166343938346131396537343732636364340a633734383934363834396334353234
32383562346161616666326335346164313762613431633830376164343832343633366539313165
6331333538623437390a313764333364356462333861643030623737653262366432363237313731
36333566613662356361313736386231313564306361316137353265396535323232633038643666
32613761616631303665623233613965383534376563653730303962353964386131366564383436
31333061316363656464323639366234336337373862306633386465626162383462316465633134
32393263653765653665646235343231383537316339653063333866383562323564366361326561
33653330393561663163313765643166643862663066653535623362313235396133323533646132
63306463616665636462643631326538316638363332666636623161363430613164366163613134
36363666346234373963383939363466623836653538643761656566346332343133363933383336
35623430316533623733323037643532623931386433383238356334393662303761396335646430
35336438646462356231633633666538633139356630623137373836656662633938366530306363
66373438346661393931336662336436323938366565333135626365303164636634323032316630
37643530653432353033363538326536313264653062383838643363646635356430626631323638
31633432336135633330


  • 실행 ( 볼트 패스워드 추가로 입력 )
[root@ansible212-247 install]# ansible-playbook nginx_install_w_template.yml -k --ask-vault-pass
SSH password:
BECOME password[defaults to SSH password]:
Vault password:


  • 복호화
[root@ansible212-247 install]# ansible-vault decrypt group_vars/nodes
Vault password:
Decryption successful


  • 패스워드가 아닌 볼트 키를 이용해서 실행
[root@ansible212-247 install]# ansible-vault create ~/.vault_key
New Vault password:
Confirm New Vault password:


  • .vault_key 키의 $ 기호 삭제
  • 삭제하는 이유는 2.5.x 버전 이후로 ansible-vault를 통해서 암호화되어진 파일은 ansible-vault 키로 사용할수 없기 때문에 $ 삭제하여 평문처럼 인식
[root@ansible212-247 ~]# vim .vault_key
$ANSIBLE_VAULT;1.1;AES256
33363431303363383135326337343232376234333331303266653230373333393839316663653366
6563326637653230616630323363656565666265616132330a353132333633666336613538393764
64653837386266383866353536643732616536323932393935633664393031623834666334366238
3138616438643832620a666462633036666339383334653932383666323966313565356563623033
3338


  • 암호화 키를 통해 실행
[root@ansible212-247 install]# ansible-playbook nginx_install_w_template.yml -k --vault-password-file /root/.vault_key
SSH password:
BECOME password[defaults to SSH password]:


  • 암호화 된 파일 수정
[root@ansible212-247 install]# ansible-vault edit ./group_vars/nodes --vault-password-file /root/.vault_key


  • 암호화된 파일 확인
[root@ansible212-247 install]# ansible-vault view ./group_vars/nodes --vault-password-file /root/.vault_key

no_log

파일 암호화를 하여도, 실행 시 -vvv 옵션 사용 시, 많은 정보들이 노출되기 때문에 no_log 옵션을 통해 자세한 내용이 출력되지 않도록 함


---
- name: Install nginx on the nodes
  hosts: nodes
  become: yes
  no_log: yes


다만, 해당 옵션을 사용해도 노출되는 정보가 있음 ( 유저 정보, 포트 ) 해당 옵션은 최소한으로 노출하지 않도록 방지 정도로만 사용 가능

pipelining

기존의 ansible의 동작 방식은 대상 서버로 SSH 접속 후, 임시 공간 생성, 파일 전송, 실행의 순서로 진행 됨

pipelining 사용 시, 동시에 실행하기 때문에 성능 향상에 도움이 됨

---
- name: Install nginx on the nodes
  hosts: nodes
  become: yes
  vars:
    ansible_ssh_pipelining: true


forks

ansible의 디폴트 forks 값은 5 ( 한번에 처리할 수 있는 값 )

아래와 같이 -f 옵션을 통해, ansible 서버에서 한번에 처리할 수 있는 수치를 찾아서 최적화한다면 성능 향상에 도움이 됨

ansible-playbook nginx_install_w_roles.yml -k -f 10

async

디폴트는 sync ( 여러 테스크를 실행 시, 각 대상 서버가 모두 성공해야 다음 테스크로 이동 ) ansible의 async는 특정 초 마다 실행 가능한지 체크하거나, 아예 체크하지 않고 실행하도록 설정이 가능

아래와 같이 poll 값을 0으로 주면, 특정 대상 서버가 지연으로 작업이 끝나지 않더라도, 다른 서버들은 실행되도록 하여 실행 속도 향상 다만, 종료 결과를 체크하지 않기 때문에 작업에 대한 결과를 보장할 수 없음

  - name: install ntp
    yum: name=ntp state=present
    async: 100 
    poll: 0  # 해당 초마다, 체크 0인 경우 체크하지 않음


Strategy free

strategy free의 경우, async와 비슷하지만, 각 노드들의 속도 별로 작업을 실행하는 개념으로 async와 다르게 종료 결과를 체크하며, 각 테스크 별로 추가해야되는것이 아닌, 아래와 같이 하나만 등록하면 되기 때문에 코드가 간결해지는 장점이 있음

다만 async보다는 속도가 느림


---
- hosts: nodes
  become: yes
  strategy: free


Blocks

Task를 실행 가능한 최소의 개수로 묶어서 요청하는 것


  • 아래와 같이 Task를 여러개 사용 시, 그만큼 속도가 느려짐
---
- hosts: nodes
  become: yes

  tasks:
  - name: update_cache for CentOS
    action : " update_cache=yes"
    when : ansible_distribution == 'CentOS'

  - name: install samba
    yum: name=samba state=present
  - name: install ntp
    yum: name=ntp state=present
  - name: install git
    yum: name=git state=present
  - name: install golang
    yum: name=golang state=present
  - name: install clang
    yum: name=clang state=present
  - name: install freeipmi


  • 아래와 같이 변수 사용으로, Task 수를 줄이는 것이 성능 향상에 도움이 됨
---
- hosts: nodes
  become: yes

  vars:
    svc_yum:
      - ['samba','ntp']
    dev_yum:
      - git
      - golang
      - clang
    mnt_yum:
      - ['tftp'],'psutils','rsyslog','tree']

  tasks:
  - name: update_cache for CentOS
    action : " update_cache=yes"
    when : ansible_distribution == 'CentOS'
  - name: add yum thru block method
    package:
      name: ""
      state: present
    with_flattened:
     - ""
     - ""
     - ""


Cache

ansible은 fact값을 캐시하여 성능향상에 도움을 줄 수 있음

기본적으로 fact 값은 memory에 저장되지만, json  파일로 변경

  • 아래 playbook 실행하여 fact_caching 값을 memory -> jsonfile로 변경
  • json 파일은 fact_caching_connection 값에 저장되며, 파일은 1일동안 보관됨 (디폴트 값)
---
- name: fact_caching Plugin
  hosts: localhost
  become: yes
  gather_facts: no

  tasks:
   - name: Modify fact_caching
     replace:
       path: /etc/ansible/ansible.cfg
       regexp: '#fact_caching = memory'
       replace: 'fact_caching = jsonfile'
       backup: yes
   - name: Add fact_caching
     lineinfile:
       path: /etc/ansible/ansible.cfg
       insertafter: 'fact_caching = jsonfile'
       line: ""
     with_items:
       - "fact_caching_connection = /tmp/ansible_facts_cache"
       - "gathering = smart"
       - "#fact_caching_timeout = 3600"


자동으로 Known_hosts에 등록

/usr/bin/ssh-keyscan -t ecdsa 192.168.0.2


---
- hosts: nodes
  connection: local
  serial: 1
  gather_facts: no

  tasks:
  - command: /usr/bin/ssh-keyscan -t ecdsa 
    register: keyscan

  - lineinfile:
      name=~/.ssh/known_hosts
      create=yes
      line=
    with_items:
      - ""


자동으로 authorized_keys 등록하기

---
- hosts: nodes
  gather_facts: no

  tasks:
  - name: ssh-keygen
    connection: local
    command: "ssh-keygen -b 2048 -t rsa -f ~/.ssh/id_rsa -q -N ''"
    ignore_errors: yes
    run_once: true

  - name: read id_rsa.pub
    connection: local
    command: "cat ~/.ssh/id_rsa.pub"
    register: id_pub
    run_once: true

  - name: remote lineinfile for authorized_keys
    lineinfile:
      dest: /home/vagrant/.ssh/authorized_keys
      line: ""

댓글남기기