play와 playbook 기본 개요

playbook은 사용하는 명령을 ad-hoc 명령으로 실행하지 않고 스크립트로 만든 것이다. 그 안에서 play는 지정된 작업을 모은 집합을 지칭하며, playbook은 1개 이상의 play를 모은것이다. playbook안에서 각 play는 순서대로 실행된다. 그래서 기본적인 용어는 playbook이라고 통칭한다. 이러한 playbook은 사용자가 구성한 inventory에서 선택한 호스트에 대해 실행된다.

 

 

플레이북 형태 예시

ansible -m user -a "name=newbie uid=4000 state=present" servera.lab.example.com

위 ad-hoc 명령을 플레이북으로 바꾸면 아래와 같다.

---
- name: Configure important user consistently
  hosts: web1.example.com
  tasks:
    - name: newbie exists with UID 4000
      user:
        name: newbie
        uid: 4000
        state: present

 

 

yaml이란? (Yet Another Markup Language, YAML Ain't Markup Language)

사람이 쉽게 읽을 수 있는 데이터 직렬화 양식이다. 프로그래밍에서 사용하는 데이터 포맷은 여러가지가 있다. 이러한 포맷은 타 시스템 간에 데이터를 주고받을 때 쓰는 약속이다. 또한 데이터를 리스트화 하고, array, dictionary(key-value형태) 등의 형태들도 모두 표현할 수 있다. 전통적으로 xml, json을 사용했고 현재 좀 더 발전되어 yaml이라는 포맷이 생겼다. 현재 많은 오픈소스에서 xml, json 등을 많이 쓰지만, yaml로 많이 변경되는 추세이다.

 

ansible playbook 작성시 유의할 yaml 규칙

* 기본 규칙

- ansible playbook은 스크립트를 작성할 때 yaml 포맷을 사용한다. 또한 playbook의 확장자는 yml, yaml이다.

- playbook 자체 구조는 키-밸류 구조의 컬렉션 기반이다.

- yaml에서는 -(하이픈) 이 array를 의미하고, :(콜론)이 dictiorary를 의미한다.

- 플레이의 첫 줄은 해당 플레이가 목록의 첫 항목임을 나타내는 대시와 공백으로 시작하고, 그 다음에 첫 번째 키가 들어간다.

- 가독성을 위해 빈 줄을 추가할 수 있다.

- yaml 에서 빈 줄(엔터값)은 아무런 의미가 없으므로 가독성을 위해 써도 상관없다.

 

* 들여쓰기 규칙

- 들여쓰기 할 때, 계층 구조상 동일한 수준에 있는 데이터 요소의 들여쓰기는 동일해야 한다.

- 들여쓰기 할 때, 하위 항목은 상위 항목보다 들여써야 한다.

- 들여쓰기는 스페이스만 가능하며 탭은 불가능하다.

- 보통 들여쓰기 스페이스는 2칸이 권장된다.

 

* 문서 규칙

- (---) 는 플레이북의 문서 시작 마커이다.

- (...)는 플레이북의 문서 종료 마커이다. (일반적으로 자주 생략된다)

 

* 주석 규칙

- # 기호 뒤는 주석이다. 즉 라인 맨 앞에 두면 전체 라인 주석이고, 글자 뒤에 #를 놓으면 # 뒤부터 주석이다.

- (some date # this is al a yaml content) 파란부분만 주석이다.

  

# 참고 : vi 편집기에서 들여쓰기 편하게 하기

$HOME/.vimrc 를 새로 만들거나 원래 있는 파일 내용 맨 아래 내용을 추가하면 tab키를 누르면 두 칸 들여쓰고, 다음 줄을 자동으로 들여쓴다. 이것이 잘 적용되려면, vim이 설치되어있어야 하고, 또한 파일의 확장자가 yaml/yml 이어야 한다.

autocmd FileType yaml setlocal ai ts=2 sw=2 et

 

 

yaml 고급 문법 - 문자열 묶기

가독성을 위해 문자열을 작성하는 방법이 2가지 있다.

 

* 기호 |

- 원래 include_newlines: Example Comany (엔터) 123 Main Street (엔터) Atlanta, GA 30303 이런식으로 해야 할 것이다. (정확하게는 어떻게 해야할 지 모르겠지만)

- 하지만 그렇게 하지 않고 아래처럼 할 수 있다. 

include_newlines: |
	Example Company
        123 Main Street
        Atlanta, GA 30303

- 이것은 각각 줄을 한줄한줄로 처리한다. 즉 나뉘어져 있는 것임.

 

* 기호 >

- 원래 fold_newlines: This is a very long, long, long, long, long, long sentence. 이런식으로 쓰면 되는데 너무 길다.

- 너무길어서 가독성도 안좋고, 막 잘리고 그런다면 이렇게 할 수 있다.

include_newlines: >
	This is a very long, long,
        long, long, long, long
        sentence.

- 이것은 사실 전체 한줄인데, 보기에만 나눠 놓는 것임. 한마디로 전체가 한줄이다.

 

 

리스트 값과 딕셔너리 값의 형태

리스트와 딕셔너리는 둘 다 배열의 한 형태이며, yaml에서 데이터를 나타내는데 사용된다.

 

* 딕셔너리(key-value) 형태 변경

일반적으로 key-value 형태는 따옴표가 포함된 아래와 같은  배열 형태이다.

name: svcrole
svcservice: httpd
svcport: 80

그것을 아래와 같이 중괄호로 묶인 인라인 블록 형식으로도 구현할 수 있다. 이 모양은 python 같은 개발 언어에서 dictionary의 형태와 비슷하다.

{name: svcrole, svcservice: httpd, svcport: 80}

 

* 리스트 형태 변경

일반적으로 list 형태는 -(하이픈) 으로 시작하는 구문으로 작성된 형태이다. 아래 예시는 hosts: 키값과 [servera, serverb, serverc] 리스트로 구성된 밸류값이 된다.

hosts:
  - servera
  - serverb
  - serverc

이것을 대괄호로 묶인 인라인 형식으로 쓸 수도 있다. 이 모양도 마찬가지로 python 같은 개발 언어에서 list 형태와 비슷하다. 

hosts: [servera, serverb, serverc]

일반적으로 인라인 블록 형식은 가독성이 떨어지므로 쓰지는 않지만 특정 상황에서 이러한 형식을 사용할 때가 있다. ((플레이북에 roles 목록이 포함되는 경우 플레이에 포함되는 역할과 역할로 전달되는 변수를 더 쉽게 구분하도록 이 구문을 사용하는게 더 일반적이다. role은 이후 강의에서 설명한다.)

 

 

* 리스트와 딕셔너리가 섞인 형태 변경

---
- hosts: localhost
  tasks:
    - name: test
      yum:
        name:
          - httpd
          - vsftpd
        state: latest

이것은 아래와 같이 변경될 수 있다. 

일반적으로 이렇게 사용되지 않으며, hosts, tasks 부분은 대괄호로 묶을 수 없다. hosts, tasks는 ansible의 기본 틀이기 때문이다.

 

 

* key-value 빠르게 쓰는 방법

일반 방법과 빠르게 쓰는 방법 모두 결과는 동일한데, 원서에서는 빠르게 쓰는 법은 피하도록 권고한다.

일반방법
tasks:
  - name: normal form
    service:
        name: httpd
        enabled: true
        state: started
        
빠르게 쓰는 방법 (=을 사용하여 나열)
tasks:
  - name: shorthand form
    service: name=httpd enabled=true state=started

 

 

* playbook에서 하이픈은 언제 넣는 것인가?

지시문의 위치에 따라 달라지는데, 맨 처음 시작할 때 하이픈이 들어가고, 그 밑에서부터는 특정 개체의 내용을 명시할 때 같은 그룹인 경우 하이픈으로 시작하고 그 안에서는 하이픈을 넣지 않는다. 예를들어 위 예시에서 tasks 밑에 name부터 service는 같은 한 범주이다. 하지만 service 밑에 name, enabled, state는 하이픈이 들어가지 않는다. 그냥 헷갈리면 ansible-doc -s 명령을 사용하자.

 

 

playbook의 기본 구조

하나의 play는 아래처럼 크게 3가지의 항목으로 구분된다. 이러한 play가 1개 이상 모은 것을 (즉, 하나만 있어도) playbook이라고 한다. 물론 name, hosts, tasks 외에 다른 항목들도 있다. 아래 3개는 최소 있어야 하는 play의 요소이다.

name
hosts
tasks

* name

play의 용도를 알려주며 필수는 아닌 옵션이다. 설명하는데 도움이 되므로 사용하는게 좋다. 즉 play의 name, tasks의 name, 각 모듈의 name도 있다. 아래 예시를 참고할 것.  또한 name은 플레이북 실행 시 이 내용이 나오므로, 해당 작업의 설명을 넣으면 된다. 주석과 비슷하다고 볼 수 있는데 주석은 실행시 보이지 않고, name은 실행시 보인다는 것이다.

 

* hosts

플레이의 작업을 실행할 대상 호스트를 지정한다. 이 hosts 속성은 관리 호스트 또는 그룹의 이름과 같은 호스트 "패턴"을 값으로 사용한다.

 

* tasks

실질적인 명령어들이 들어가는 가장 핵심적인 부분. 실행할 작업들을 지정한다. 여기에 모듈들이 명시된다. 아래 예시를 참고해보면, tasks 밑에 각각의 - name 아래 부분들이 하나하나의 모듈이다(user, service, service 각각 모두 모듈). 이 - name은 모듈의 용도를 알려준다. 이것도 필수는 아닌 옵션이다. 

 

# 예시

---
- name: play to setup webserver, ntpserver, newuser
  hosts: all
  tasks:
    - name: newbie exists with UID 4000
      user:
        name: newbie
        uid: 4000
        state: present
    
    - name: web server is enabled
      service:
        name: httpd
        enabled: true
        
    - name: ntp server is enabled
      service:
        name: chronyd
        enabled: true

 

정리하면, name, hosts, tasks 한 세트가 하나의 play이다. 이러한 세트가 1개 이상이면 playbook이 된다. 또한 각각의 플레이는 위에서 아래로 차례대로 실행되고, 플레이 안에 있는 tasks에 있는 각각의 모듈들도 위에서 아래로 차례로 실행된다.

 

 

playbook 실행  구문

playbook은 ansible host, 즉 제어 노드에 저장되며, 제어 노드에서 실행된다. ad-hoc 명령처럼, 이전 단원에서 설명한 ansible.cfg 파일에 설정에 맞춰서 playbook을 실행할 수 있다. (ansible.cfg 가 있는 디렉토리에서 실행하거나, global ansible.cfg 설정을 하고 실행하는 등)

 

* 실행 구문

# ansible-playbook 파일명(.yaml/.yml)

 

* 예시

# ansible-playbook test_html.yml

---
- name: play to setup web server
  hosts: all
  tasks:
  - name: latest httpd version installed
    yum:
      name: httpd
      state: latest

 

플레이북 실행 결과 상세 보기

첫 실행 예시 결과를 기준으로 스크립트 구조를 비교하여 살펴보자.

 

* 실행결과 (첫 번째 실행)

* 사용 스크립트

---

- name: play to setup web server

  hosts: all

  tasks:

  - name: latest httpd version installed

    yum:

      name: httpd

      state: latest

 

 

* 각각 색깔에 맞춰 상세 설명

PLAY [play to setup web server] ******************************************************

이 부분은 하나의 PLAY의 시작을 의미하며, 위에서 명시한 name의 내용이 함께 포함된다. 만약 PLAY가 여러개라면, 이 부분이 아래에 순서대로 뜰 것이다.

 

TASK [Gathering Facts] **************************************************************

ok: [web2.example.com]

ok: [web1.example.com]

ok: [db1.example.com]

ok: [db2.example.com]

이 부분은 GETHERING FACTS 라고 해서, 모든 task중 가장 처음 시작되는 부분이다. 실제로 스크립트에 명시되지 않았지만 자동으로 수행된다.  SETUP 이라는 모듈에서 일반적으로 플레이 시작 시 자동으로 실행하는 특수 작업이다. 나중에 자세히 설명한다.

 

TASK [latest httpd version installed] **********************************************

changed: [web1.example.com]

changed: [db2.example.com]

changed: [web2.example.com]

changed: [db1.example.com]

여기서부터 실제로 스크립트에 명시된 첫번째 TASK가 시작된다. 오른쪽에 대괄호에는 name에 명시한 내용이 포함된다.

만약 추가로 TASK가 더 있다면, 아래에 추가로 TASK가 명시될 것이다.

 

PLAY RECAP *************************************************

db1.example.com            : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

db2.example.com            : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

web1.example.com           : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

web2.example.com           : ok=2    changed=1    unreachable=0    failed=0 

PLAYBOOK 전체의 최종 결과이다. ok, changed, unreachable, failed, skippied, rescued, ignored 등 여러가지 요소가 있으며, 각각의 요소 뒤의 숫자는 개수이다. 즉 ok가 2개고, changed가 1개라는 의미. ok 2개는 위의 gathering facts를 포함한 task의 수가 2개라는 의미이고, changed는 task 1개가 changed가 된 것이다.

 

 

* 실행결과 (두 번째 실행)

두 번째 결과에서는 ok=2 이고, 그 외 모든것들은 changed를 포함하여 0이다. 바뀐 것이 없고 다른 특이사항도 없기 때문이다. 즉 멱등성이 있어서 다시 해도 아무 문제가 없고 변화도 없다.

 

 

 

playbook 실행시 문제가 발생했을 때 

문제가 발생하는 경우, 크게 2가지 방법을 사용해서 문제를 확인할 수 있다.

 

* 실행하기 전에 구문 오류 체크

ansible-playbook --syntax-check webserver.yaml

--syntax-check 옵션은 yaml 파일의 구문 검사를 수행한다. 모든 playbook 명령 실행시 먼저 실행하는 것이 좋다. 문제가 없으면 아래와 같이 결과가 나온다. 문제가 있다면, 다른 추가 내용을 보여준다.

 

* 실행 시 verbose 모드를 사용하여 더 자세하게 결과를 확인

ansible-playbook -vvv webserver.yaml

-v 옵션을 사용하면 결과를 더 자세하게 확인할 수 있다.

-v

작업 결과가 표시된다.  (예를들어, cal을 실행하도록 ansible에서 수행하면 결과는 안나온다. 근데 이옵션을 쓰면 나온다)

-vv

작업 결과와 작업 구성이 모두 표시된다.

-vvv

관리 호스트 연결에 대한 정보를 포함한다.

-vvvv

스크립트를 실행하기 위해 관리 호스트에서 사용중인 사용자 및 실행된 스크립트를 포함하여 추가적인 플러그인 옵션을 연결 플러그인에 추가한다.

 

 

정상적인 playbook 실행과 문제가 있는 playbook 실행의 구분

ansible은 명령을 사용한 후 return code를 평가해서 작업 성공 여부를 판단한다. 리눅스에서는 return code가 있는데, 어떤 명령이든 제대로 잘 실행되었으면 return code는 0, 잘 안되었으면 0이 아닌 다른 값이 나온다. ansible뿐만 아니라 pacemaker 같은 클러스터나, 다른 프로그램들, 스크립트 등에서 명령어의 성공 여부를 이 return code를 평가해서 정상/비정상 여부를 판단한다.

 

adhoc 명령을 쓰다 보면 간혹가다 rc=0 이라는 것을 볼 수 있는데, rc=0은 return code = 0, 즉 정상적으로 실행되었다는 의미가 된다.

 

playbook에 여러개의 task가 있고, 각각의 task가 실행되다가 중간 task에서 return code가 0이 나오지 않는다면, 즉 작업을 실펴한다면 나머지 task들은 즉시 중단하고 playbook이 중단된다. 중단된 시점 이후 task는 아무 일도 하지 않은 것이며, 중단된 task의 전 task들은 잘 작업이 실행된 것이다.

 

 

 

 

 

 

play에서 유저 변경과 권한 에스컬레이션 수행하기

유저 설정과 권한 에스컬레이션은 ansible.cfg에서 설정하지만, ad-hoc 명령에서 옵션을 통해 변경할 수 있듯이 playbook에서도 유저 설정과 권한 에스컬레이션을 따로 추가할 수 있다. 물론 여기서 추가한 값들은 ansible.cfg보다 높은 우선순위를 갖는다. 이러한 설정은 playbook안에서 각 play 별로 설정할 수 있다.

 

* 자주 쓰는 구문

become: true 모두 4장의 내용과 동일한 내용이다. 추가 다른 구문들은 도움말에서 확인할 것.
become_method: sudo
become_user: 유저명

* 예시

---
- name: /etc/hosts is up to date
  hosts: datacenter-west
  remote_user: devops
  become: yes
  tasks:

 

다중 플레이 구현

여러가지 플레이를 수행하는 시나리오가 있을 수 있다. 예를들어, 특정 호스트 2개에 대해 하나의 플레이를 실행하고, 해당 플레이가 완료되면 전체 호스트 집합에 대해 하나의 플레이를 실행하는 등 이런식으로 다중 플레이를 사용할 수 있다. 사용은 간단하다. 플레이북 안에 해당 내용을 나열하면 된다. 아래와 같은 형태가 된다.

---
- name:
  hosts:
  tasks:
  
- name:
  hosts:
  become: false
  tasks:
    - name: 
...

 

 

 

실습

 

1. 단일 플레이 실습

httpd 설치, index.html 파일을 옮기고, 서비스를 시작하는 플레이

---
- name: play to setup web server
  hosts: all

  tasks:

  - name: latest httpd version installed
    yum:
      name: httpd
      state: latest

  - name: copy index.html file from ansible_host
    copy:
      src: files/index.html
      dest: /var/www/html/

  - name: startup httpd service
    service:
      name: httpd
      enabled: yes
      state: started
...

 

2. 다중 플레이 실습

 

* 개요

 

플레이1

- web.example.com 서버에 아래와 같이 작업한다.

- 권한 에스컬레이션이 필요

- yum으로 최신버전 httpd, firwealld 패키지 설치되었는지 확인

- firewalld 서비스가 enable, start 되었는지 확인

- firewalld가 httpd에 연결할 수 있게 구성되었는지 확인

- httpd 서비스가 enable, start 되었는지 확인

- 관리 호스트의 /var/www/html/index.html 파일이 "welcome to the example.com internet!" 이라는 내용 확인

 

플레이2

- localhost 서버에 아래와 같이 작업한다.

- 권한 에스컬레이션 필요없음.

- url http://servera.lab.example.com 에서 http 상태코드 200을 반환하는지 확인하기 위해 uri 모듈 사용.

- 웹 서버의 내용 확인

 

 

* 구문

---
# 첫번째 플레이
- name: intranet
  hosts: web.example.com
  become: yes
  tasks:
    - name: check httpd,firewalld / and install
      yum:
        name:
          - httpd
          - firewalld
        state: present
    - name: html page data
      copy:
        content: "welcome to the example.com intranet!\n"
        dest: /var/www/html/index.html
    - name: service firewalld
      service:
        name: firewalld
        enabled: true
        state: started
    - name: firewalld startup, and authorize http port
      firewalld:
        service: http
        state: enabled
        permanent: true
        immediate: yes
    - name: service httpd
      service:
        name: httpd
        enabled: true
        state: started

# 두번째 플레이
- name: test server
  hosts: localhost
  become: no
  tasks:
    - name: check url
      uri:
        status_code: 200
        url: http://web.example.com
        return_content: yes  # -> 서버 응답이 task 결과에 출력된다.
...

이 플레이는 -v옵션을 줘서 실행하면, content의 내용이나 상세사항을 함께 확인하여 이 플레이가 제대로 실행되었는지 확인할 수 있다.

 

* 결과

[devops@ansible-host ansible_test]$ ansible-playbook -v test_multi.yml
Using /home/devops/ansible_test/ansible.cfg as config file

PLAY [intranet] *************************************************************************************************************************

TASK [Gathering Facts] ******************************************************************************************************************
ok: [web.example.com]

TASK [check httpd,firewalld / and install] **********************************************************************************************
ok: [web.example.com] => {"changed": false, "msg": "", "rc": 0, "results": ["httpd-2.4.6-97.el7.centos.x86_64 providing httpd is already installed", "firewalld-0.5.3-5.el7.noarch providing firewalld is already installed"]}

TASK [html page data] *******************************************************************************************************************
changed: [web.example.com] => {"changed": true, "checksum": "5a4e04b17f900a1dde5e34e1ed9a401561848691", "dest": "/var/www/html/index.html", "gid": 0, "group": "root", "md5sum": "38b4f61a99340b08b3e39d115d320f30", "mode": "0644", "owner": "root", "secontext": "system_u:object_r:httpd_sys_content_t:s0", "size": 37, "src": "/home/jason/.ansible/tmp/ansible-tmp-1613751488.04-9272-251732413321698/source", "state": "file", "uid": 0}

TASK [service firewalld] ****************************************************************************************************************
ok: [web.example.com] => {"changed": false, "enabled": true, "name": "firewalld", "state": "started", "status": {"ActiveEnterTimestamp": "Sat 2021-02-20 10:41:53 KST", "ActiveEnterTimestampMonotonic": "9614472", "ActiveExitTimestampMonotonic": "0", "ActiveState": "active", "After": "dbus.service basic.target polkit.service system.slice", "AllowIsolate": "no", "AmbientCapabilities": "0", "AssertResult": "yes", "AssertTimestamp": "Sat 2021-02-20 10:41:52 KST", "AssertTimestampMonotonic": "8339522", "Before": "shutdown.target multi-user.target network-pre.target", "BlockIOAccounting": "no", "BlockIOWeight": "18446744073709551615", "BusName": "org.fedoraproject.FirewallD1", "CPUAccounting": "no", "CPUQuotaPerSecUSec": "infinity", "CPUSchedulingPolicy": "0", "CPUSchedulingPriority": "0", "CPUSchedulingResetOnFork": "no", "CPUShares": "18446744073709551615", "CanIsolate": "no", "CanReload": "yes", "CanStart": "yes", "CanStop": "yes", "CapabilityBoundingSet": "18446744073709551615", "ConditionResult": "yes", "ConditionTimestamp": "Sat 2021-02-20 10:41:52 KST", "ConditionTimestampMonotonic": "8339522", "Conflicts": "ebtables.service ip6tables.service shutdown.target ipset.service iptables.service", "ControlGroup": "/system.slice/firewalld.service", "ControlPID": "0", "DefaultDependencies": "yes", "Delegate": "no", "Description": "firewalld - dynamic firewall daemon", "DevicePolicy": "auto", "Documentation": "man:firewalld(1)", "EnvironmentFile": "/etc/sysconfig/firewalld (ignore_errors=yes)", "ExecMainCode": "0", "ExecMainExitTimestampMonotonic": "0", "ExecMainPID": "2814", "ExecMainStartTimestamp": "Sat 2021-02-20 10:41:52 KST", "ExecMainStartTimestampMonotonic": "8367736", "ExecMainStatus": "0", "ExecReload": "{ path=/bin/kill ; argv[]=/bin/kill -HUP $MAINPID ; ignore_errors=no ; start_time=[n/a] ; stop_time=[n/a] ; pid=0 ; code=(null) ; status=0/0 }", "ExecStart": "{ path=/usr/sbin/firewalld ; argv[]=/usr/sbin/firewalld --nofork --nopid $FIREWALLD_ARGS ; ignore_errors=no ; start_time=[n/a] ; stop_time=[n/a] ; pid=0 ; code=(null) ; status=0/0 }", "FailureAction": "none", "FileDescriptorStoreMax": "0", "FragmentPath": "/usr/lib/systemd/system/firewalld.service", "GuessMainPID": "yes", "IOScheduling": "0", "Id": "firewalld.service", "IgnoreOnIsolate": "no", "IgnoreOnSnapshot": "no", "IgnoreSIGPIPE": "yes", "InactiveEnterTimestampMonotonic": "0", "InactiveExitTimestamp": "Sat 2021-02-20 10:41:52 KST", "InactiveExitTimestampMonotonic": "8368316", "JobTimeoutAction": "none", "JobTimeoutUSec": "0", "KillMode": "mixed", "KillSignal": "15", "LimitAS": "18446744073709551615", "LimitCORE": "18446744073709551615", "LimitCPU": "18446744073709551615", "LimitDATA": "18446744073709551615", "LimitFSIZE": "18446744073709551615", "LimitLOCKS": "18446744073709551615", "LimitMEMLOCK": "65536", "LimitMSGQUEUE": "819200", "LimitNICE": "0", "LimitNOFILE": "4096", "LimitNPROC": "7120", "LimitRSS": "18446744073709551615", "LimitRTPRIO": "0", "LimitRTTIME": "18446744073709551615", "LimitSIGPENDING": "7120", "LimitSTACK": "18446744073709551615", "LoadState": "loaded", "MainPID": "2814", "MemoryAccounting": "no", "MemoryCurrent": "18446744073709551615", "MemoryLimit": "18446744073709551615", "MountFlags": "0", "Names": "firewalld.service", "NeedDaemonReload": "no", "Nice": "0", "NoNewPrivileges": "no", "NonBlocking": "no", "NotifyAccess": "none", "OOMScoreAdjust": "0", "OnFailureJobMode": "replace", "PermissionsStartOnly": "no", "PrivateDevices": "no", "PrivateNetwork": "no", "PrivateTmp": "no", "ProtectHome": "no", "ProtectSystem": "no", "RefuseManualStart": "no", "RefuseManualStop": "no", "RemainAfterExit": "no", "Requires": "basic.target", "Restart": "no", "RestartUSec": "100ms", "Result": "success", "RootDirectoryStartOnly": "no", "RuntimeDirectoryMode": "0755", "SameProcessGroup": "no", "SecureBits": "0", "SendSIGHUP": "no", "SendSIGKILL": "yes", "Slice": "system.slice", "StandardError": "null", "StandardInput": "null", "StandardOutput": "null", "StartLimitAction": "none", "StartLimitBurst": "5", "StartLimitInterval": "10000000", "StartupBlockIOWeight": "18446744073709551615", "StartupCPUShares": "18446744073709551615", "StatusErrno": "0", "StopWhenUnneeded": "no", "SubState": "running", "SyslogLevelPrefix": "yes", "SyslogPriority": "30", "SystemCallErrorNumber": "0", "TTYReset": "no", "TTYVHangup": "no", "TTYVTDisallocate": "no", "TasksAccounting": "no", "TasksCurrent": "18446744073709551615", "TasksMax": "18446744073709551615", "TimeoutStartUSec": "1min 30s", "TimeoutStopUSec": "1min 30s", "TimerSlackNSec": "50000", "Transient": "no", "Type": "dbus", "UMask": "0022", "UnitFilePreset": "enabled", "UnitFileState": "enabled", "WantedBy": "multi-user.target", "Wants": "system.slice network-pre.target", "WatchdogTimestamp": "Sat 2021-02-20 10:41:53 KST", "WatchdogTimestampMonotonic": "9614435", "WatchdogUSec": "0"}}

TASK [firewalld startup, and authorize http port] ***************************************************************************************
changed: [web.example.com] => {"changed": true, "msg": "Permanent and Non-Permanent(immediate) operation, Changed service http to enabled"}

TASK [service httpd] ********************************************************************************************************************
changed: [web.example.com] => {"changed": true, "enabled": true, "name": "httpd", "state": "started", "status": {"ActiveEnterTimestampMonotonic": "0", "ActiveExitTimestampMonotonic": "0", "ActiveState": "inactive", "After": "remote-fs.target -.mount network.target systemd-journald.socket system.slice tmp.mount nss-lookup.target basic.target", "AllowIsolate": "no", "AmbientCapabilities": "0", "AssertResult": "no", "AssertTimestampMonotonic": "0", "Before": "shutdown.target", "BlockIOAccounting": "no", "BlockIOWeight": "18446744073709551615", "CPUAccounting": "no", "CPUQuotaPerSecUSec": "infinity", "CPUSchedulingPolicy": "0", "CPUSchedulingPriority": "0", "CPUSchedulingResetOnFork": "no", "CPUShares": "18446744073709551615", "CanIsolate": "no", "CanReload": "yes", "CanStart": "yes", "CanStop": "yes", "CapabilityBoundingSet": "18446744073709551615", "ConditionResult": "no", "ConditionTimestampMonotonic": "0", "Conflicts": "shutdown.target", "ControlPID": "0", "DefaultDependencies": "yes", "Delegate": "no", "Description": "The Apache HTTP Server", "DevicePolicy": "auto", "Documentation": "man:httpd(8) man:apachectl(8)", "EnvironmentFile": "/etc/sysconfig/httpd (ignore_errors=no)", "ExecMainCode": "0", "ExecMainExitTimestampMonotonic": "0", "ExecMainPID": "0", "ExecMainStartTimestampMonotonic": "0", "ExecMainStatus": "0", "ExecReload": "{ path=/usr/sbin/httpd ; argv[]=/usr/sbin/httpd $OPTIONS -k graceful ; ignore_errors=no ; start_time=[n/a] ; stop_time=[n/a] ; pid=0 ; code=(null) ; status=0/0 }", "ExecStart": "{ path=/usr/sbin/httpd ; argv[]=/usr/sbin/httpd $OPTIONS -DFOREGROUND ; ignore_errors=no ; start_time=[n/a] ; stop_time=[n/a] ; pid=0 ; code=(null) ; status=0/0 }", "ExecStop": "{ path=/bin/kill ; argv[]=/bin/kill -WINCH ${MAINPID} ; ignore_errors=no ; start_time=[n/a] ; stop_time=[n/a] ; pid=0 ; code=(null) ; status=0/0 }", "FailureAction": "none", "FileDescriptorStoreMax": "0", "FragmentPath": "/usr/lib/systemd/system/httpd.service", "GuessMainPID": "yes", "IOScheduling": "0", "Id": "httpd.service", "IgnoreOnIsolate": "no", "IgnoreOnSnapshot": "no", "IgnoreSIGPIPE": "yes", "InactiveEnterTimestampMonotonic": "0", "InactiveExitTimestampMonotonic": "0", "JobTimeoutAction": "none", "JobTimeoutUSec": "0", "KillMode": "control-group", "KillSignal": "18", "LimitAS": "18446744073709551615", "LimitCORE": "18446744073709551615", "LimitCPU": "18446744073709551615", "LimitDATA": "18446744073709551615", "LimitFSIZE": "18446744073709551615", "LimitLOCKS": "18446744073709551615", "LimitMEMLOCK": "65536", "LimitMSGQUEUE": "819200", "LimitNICE": "0", "LimitNOFILE": "4096", "LimitNPROC": "7120", "LimitRSS": "18446744073709551615", "LimitRTPRIO": "0", "LimitRTTIME": "18446744073709551615", "LimitSIGPENDING": "7120", "LimitSTACK": "18446744073709551615", "LoadState": "loaded", "MainPID": "0", "MemoryAccounting": "no", "MemoryCurrent": "18446744073709551615", "MemoryLimit": "18446744073709551615", "MountFlags": "0", "Names": "httpd.service", "NeedDaemonReload": "no", "Nice": "0", "NoNewPrivileges": "no", "NonBlocking": "no", "NotifyAccess": "main", "OOMScoreAdjust": "0", "OnFailureJobMode": "replace", "PermissionsStartOnly": "no", "PrivateDevices": "no", "PrivateNetwork": "no", "PrivateTmp": "yes", "ProtectHome": "no", "ProtectSystem": "no", "RefuseManualStart": "no", "RefuseManualStop": "no", "RemainAfterExit": "no", "Requires": "basic.target -.mount", "RequiresMountsFor": "/var/tmp", "Restart": "no", "RestartUSec": "100ms", "Result": "success", "RootDirectoryStartOnly": "no", "RuntimeDirectoryMode": "0755", "SameProcessGroup": "no", "SecureBits": "0", "SendSIGHUP": "no", "SendSIGKILL": "yes", "Slice": "system.slice", "StandardError": "inherit", "StandardInput": "null", "StandardOutput": "journal", "StartLimitAction": "none", "StartLimitBurst": "5", "StartLimitInterval": "10000000", "StartupBlockIOWeight": "18446744073709551615", "StartupCPUShares": "18446744073709551615", "StatusErrno": "0", "StopWhenUnneeded": "no", "SubState": "dead", "SyslogLevelPrefix": "yes", "SyslogPriority": "30", "SystemCallErrorNumber": "0", "TTYReset": "no", "TTYVHangup": "no", "TTYVTDisallocate": "no", "TasksAccounting": "no", "TasksCurrent": "18446744073709551615", "TasksMax": "18446744073709551615", "TimeoutStartUSec": "1min 30s", "TimeoutStopUSec": "1min 30s", "TimerSlackNSec": "50000", "Transient": "no", "Type": "notify", "UMask": "0022", "UnitFilePreset": "disabled", "UnitFileState": "disabled", "Wants": "system.slice", "WatchdogTimestampMonotonic": "0", "WatchdogUSec": "0"}}

PLAY [test server] **********************************************************************************************************************

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

TASK [check url] ************************************************************************************************************************
ok: [localhost] => {"accept_ranges": "bytes", "changed": false, "connection": "close", "content": "welcome to the example.com intranet!\n", "content_length": "37", "content_type": "text/html; charset=UTF-8", "cookies": {}, "cookies_string": "", "date": "Sat, 20 Feb 2021 02:23:02 GMT", "elapsed": 0, "etag": "\"25-5bbbb3f553b19\"", "last_modified": "Sat, 20 Feb 2021 02:22:59 GMT", "msg": "OK (37 bytes)", "redirected": false, "server": "Apache/2.4.6 (CentOS)", "status": 200, "url": "http://web.example.com"}

PLAY RECAP ******************************************************************************************************************************
localhost                  : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
web.example.com            : ok=6    changed=3    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

[devops@ansible-host ansible_test]$

 

아래처럼 직접 확인할 수도 있다.

참고

ansible-playbook(1)

 

플레이북 소개 , ansible 설명서

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

 

Intro to Playbooks — Ansible Documentation

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

docs.ansible.com

플레이북 ansible 설명서

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

 

Working With Playbooks — Ansible Documentation

Docs » User Guide » Working With Playbooks 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 Ansi

docs.ansible.com

YAML 구문 ansible 설명서

docs.ansible.com/ansible/2.7/reference_appendices/YAMLSyntax.html

 

YAML Syntax — Ansible Documentation

Docs » YAML Syntax 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. YAML S

docs.ansible.com

 

'Ansible' 카테고리의 다른 글

9장. fact 변수 관리  (0) 2021.02.20
8장. 변수 사용하기  (0) 2021.02.20
부록1 - 자주 사용되는 모듈들  (0) 2021.02.19
6장. ansible에서 모듈 사용하기  (0) 2021.02.19
5장. ad-hoc 명령  (0) 2021.02.19

자주쓰는 모듈 리스트

파일모듈

copy : 로컬/원격 시스템의 파일을 관리 호스트의 위치로 복사한다. selinux 속성 설정 가능
fetch : copy 처럼 작동하지만 반대로 작동한다. 이 모듈은 원격 시스템에서 제어 노드로 파일을 가져온다.
file : 권한, 소유권, selinux 컨텍스트, 일반파일의 타임스탬프, 심볼릭링크, 하드링크 및 디렉토리 같은
         속성을 설정한다.
이 모듈은 일반 파일, 심링크, 하드링크 및 디렉토리를 생성하거나 제거할수도 있다.
lineinfile : 특정 행이 파일에 있는지 확인하거나 역참조 정규식을 사용하여 기존 행을 바꾼다.
                     이 모듈은 파일에서 한 줄을 변경할 때 유용하다.

blockinfile :  사용자 지정 가능한 마커 선으로 둘러싸인 여러 줄의 텍스트 블록을 삽입, 업데이트, 제거.
synchronize : rsync를 사용하여 컨텐츠 동기화. rsync의 모든 기능을 쓰지는 못한다.
stat : linux stat 명령과 유사하게 파일의 상태 정보를 검색한다.

소프트웨어 패키지 모듈

package : 운영체제 고유의 자동 감지 패키지 관리를 사용하여 패키지 관리
yum : yum 패키지 관리자 사용하여 패키지 관리
apt : apt 패키지 관리자를 사용하여 패키지 관리
dnf : dnf 패키지 관리자를 사용하여 패키지 관리
gem : ruby gem 관리
pip : pypi에서 python 패키지 관리

시스템 모듈

firewalld : firewalld를 사용하여 임의의 포트/서비스 관리
reboot : 시스템 재부팅
service : 서비스 관리
user :  사용자 계정 추가, 제거 및 관리

net tools 모듈

get_url:  http,https 또는 ftp 를 통해 파일 다운로드
nmcli : 네트워킹 관리
url: 웹 서비스와 상호작용

만약 원하는 모듈이 없다면, 사용자가 직접 고유의 모듈을 만들수도 있고, 타사의 모듈을 가져와서 사용할 수도 있다.

 

ansible은 ANSIBLE_LIBRARY 환경변수에 지정한 위치에서 사용자 지정 모듈을 찾으며, 위치가 설정되지 않은 경우 현재 ansible 구성 파일의 library 키워드로 찾는다. 또한 ansible은 현재 실행되고 있는 플레이북과 관련하여, ./library 디렉토리에서 사용자 지정 모듈을 검색한다. ansible.cfg 파일에서 이런식으로 명시할 수 있다. (library = /usr/share/my_modules)

 

 

file 모듈

- 파일의 권한, 속성생성, 복사, 편집, 제거, 수정같은 리눅스 파일관리와 관련된 대부분의 작업을 수행할 수 있는 모듈이다. 여기서는 file 모듈로 자주 사용하는 기능을 확인한다.

 

 

* 파일 생성 / 소유자, 그룹, 사용권한 제어

- torch 명령처럼 작동하여 파일이 존재하지 않으면 빈 파일을 만들고, 파일이 존재하면 수정 시간을 업데이트한다.

- 파일 소유자, 그룹, 사용 권한 등도 제어할 수 있다.

- name: files test
  hosts: web1.example.com
  tasks:
    - name: Touch a file and set permissions
      file:
        path: /test6
        owner: student
        group: student
        mode: 0640
        state: touch

- 결과로, 해당 호스트의 /에 test6 이라는 파일이 생성된다.

 

 

* 파일의 selinux 속성 수정하기

---
- name: selinux setting
  hosts: web.example.com
  tasks:
    - name: changes elinux type
      file:
        path: /test1
        setype: samba_share_t

- 결과로, 아래와 같이 변경전, 변경 후를 확인할 수 있다.

- 파일 속성 매개변수는 여러 파일 관리 모듈에서 사용할 수 있으므로, 추가정보는 ansible-doc로 확인한다. (ansible-doc copy, ansible-doc file 등) 

 

# 참고 : 영구적으로 selinux 속성 수정하기

- 위 selinux 속성 수정 예시에서 file모듈은 chcon과 같이 작동한다. 

- restorecon 명령으로 쉽게 원복된다. 또한 리스타트로도 원복될거라고 생각했는데 아니었음. selinux 좀 더 공부

파일 이름이 test1이 아닌 것은 무시해주세요..ㅠ

- 아무튼, 영구적으로 변경된 selinux을 적용하려면, 아까 file 모듈로 samba_share_t 설정을 수행한 후에, 추가로 아래 새로운 예시를 실행한다.

---
- name: selinux setting
  hosts: web.example.com
  tasks:
    - name: changes selinux type permently
      sefcontext:
        target: /test4
        setype: samba_share_t
        state: present

- 이렇게 하면 해당 test1 파일은 samba_share_t 속성이 확정되어 restorecon으로도 원복되지 않는다.

이것도 파일 이름은 무시해 주세요 ㅠ

# 참고 : sefcontext 모듈은 selinux 정책에서 대상에 대해 기본 컨텍스트를 업데이트 하지만 기존 파일의 컨텍스트는 변경하지 않는다.

 

# 참고 : sefcontext 모듈 사용할 때 에러 발생 시

만약 이러한 에러가 발생한다면, 해당 ansible client에서 policycoreutils-python 패키지를 설치해야 한다. (centos 기준, yum install policycoreutils-python)

 

* 파일 제거하기

- file 모듈의 state: absent 지시문을 사용하여 파일을 삭제할 수 있다.

---
- name: remove file
  hosts: web.example.com
  tasks:
    - name: remove file
      file:
        dest: /tmp/hello_ansible_world
        state: absent

- 이렇게 하면 /tmp의 hello_ansible_world 파일은 삭제된다.

 

copy 모듈

- 이 모듈은 ansible host에 있는 파일을 ansible client로 복사한다.

- 또한 이 모듈은 defult 로 force: yes가 적용되어 있어서 ansible client로 파일을 복사할 때 해당 파일이 존재하지만 지금 복사 명령을 한 내용과 다른 내용을 포함하는 경우 덮어씌운다.

- 만약 이것을 해제하고 싶으면 force: no를 설정하면 된다. 이 옵션은 ansible client에 해당 파일이 존재하지 않을 경우에만 복사를 수행한다.

---
- name: test copy module
  hosts: web.example.com
  tasks:
    - name: test copy module
      copy:
        src: hello_world
        dest: /tmp/hello_ansible_world

- 이렇게 하면, ansible host에 있는 hello_world 라는 파일이 ansible client인 web.example.com의 /tmp/hello_ansible_world로 복사된다.

 

- content 지시문을 사용하여 파일을 새로 만들거나 있는 파일의 내용을 변경할 수 있다. 원래 /tmp/hello에 내용이 있었다면 다 없어지고 Managed by Ansible 이 저장된다. 없는 파일명이라면 해당 내용으로 새 파일이 생성된다. (잘 안씀 다른 모듈을 쓰지)

ansible all -m copy -a 'content="Managed by Ansible\n" dest=/tmp/hello'

 

lineinfile 모듈

- 기존 텍스트로 된 파일의 맨 아래에 라인을 추가하는 모듈이다.

---
- name: line test
  hosts: web.example.com
  tasks:
    - name: line test
      lineinfile:
        path: /tmp/hello_ansible_world
        line: 'happy happy'
        state: present

- 결과는 다음과 같다.

 

# 참고

- 파일 안에 line 으로 명시한 부분이 있다면 (맨 밑이 아니더라도) 더 추가하지 않는다. 

- 대소문자를 구분하므로 대소문자 하나만 달라도 다시 작동한다.

 

blockinfile 모듈

- 기존 텍스트로 된 파일의 맨 아래에 한 라인이 아닌 여러 라인으로 이루어진 텍스트 블록을 추가하는 모듈이다.

- 또한 blockinfile을 사용하면, 멱등을 위해 주석처리된 블록마커가 블록의 앞뒤에 삽입된다. (아래 결과처럼 BEGIN ANSIBLE MANAGED BLOCK, END ANSIBLE MANAGED BLOCK 이라고 나온 주석)

- 이러한 마커는 marker: 지시문을 사용해서 변경할 수 있다. ansible-doc -s blockinfile 참고

---
- name: block test
  hosts: web.example.com
  tasks:
    - name: block test1
      blockinfile:
        path: /tmp/hello_ansible_world
        block: >
          welcome to the mettugi world
          welcome to the mettugi world
          I'm i'm dancing mettugi
        state: present

# 참고로, 위 구문에서 block: | 로 하면 아래와 같이 나온다.

 

stat 모듈

- ansible client에 있는 특정 파일의 상세정보를 확인한다. 리눅스의 stat 명령과 유사하다.

- 또한 stat 모듈은 파일 상태 데이터가 포함된 값의 해시/딕셔너리 값을 반환하여 별도의 변수를 사용한 개별 정보를 참조하게 할 수 있다.

- 아래 예시는, stat 모듈의 결과를 보여주며, 결과에 md5 checksum 값도 포함한다.

---
- name: stat module
  hosts: web.example.com
  tasks:
    - name: check stat module
      stat:
        path: /tmp/message-log
        checksum_algorithm: md5
      register: result

    - name: check result
      debug:
        var: result

- 만약 checksum만 보고 싶다면, 아래와 같이 하면 된다.

---
- name: stat module
  hosts: web.example.com
  tasks:
    - name: check stat module
      stat:
        path: /tmp/message-log
        checksum_algorithm: md5
      register: result

    - name: check result
      debug:
        msg: "the file's checksum is {{ result.stat.checksum }}"

 

 

synchronize 모듈

- 이 모듈은 rsync 도구와 관련된 레퍼런스이다. 즉 ansible host와 ansible client 간의 파일을 동기화 한다. 이렇게 하여 playbook에서 일반적인 파일 관리 작업을 간소화한다.

- 이 모듈을 사용하기 위해 rsync를 ansible host, ansible client 양쪽에 설치해야 한다.

- 기본적으로 이 모듈을 쓸 때 동기화 작업을 시작하는 것은 ansible host이고, 동기화 당하는 것은 ansible client 이다.

---
- name: sync file
  hosts: web.example.com
  tasks:
    - name : sync file
      synchronize:
        src: /home/devops/ansible_test
        dest: /tmp

- 이 예시는, ansible host의 /home/devops/ansible_test 디렉토리 자체를 ansible client의 /tmp로 복사한다.(동기화한다)

 

 

 

fetch 모듈

- fetch는 각 ansible client에서 특정 경로의 파일을 검색하고, 이 파일들을 모두 ansible host로 각 호스트별로 정리해서 저장한다.

- flat: no 매개변수는 호스트이름, 경로 및 파일 이름을 대상에 추가하는 기본 동작을 수행한다. 이걸 하지 않으면, 모든 내용이 한 텍스트파일에 저장되므로 보기 어렵다.

---
- name: Use the fetch module to retrieve secure log files
  hosts: all

  tasks:
    - name: Fetch the /var/log/secure log file from managed hosts
      fetch:
        src: /var/log/messages
        dest: /home/devops/ansible_test/secure-backups
        flat: no

secure-backups 라는 디렉토리가 생겼고, 아래와 같은 트리구조가 생긴다. (flat: no 옵션)

 

 

연습문제

* 파일 복사하기 심화

- ansible host에서 관리 호스트들로 파일 사본을 뿌리고, 오너쉽, 퍼미션, selinux까지 설정한다.

---
- name: Using the copy module
  hosts: web.example.com
  tasks:
    - name: copy a file to manage hosts and set attributes
      copy:
        src: users.txt
        dest: /home/devops/users.txt
        owner: devops
        group: devops
        mode: u+rw,g=wx,o-rwx
        setype: samba_share_t

- 다음과 같이 확인할 수 있다.

 

* selinux 유형 설정을 기본값으로 돌리기

- user, role, type, level 필드에 대해 기본 selinux 컨텍스트를 적용한다.

---
- name: using the file module to ensure selinux file context
  hosts: web1.example.com
  remote_user: root
  tasks:
    - name: selinux file context is set to defaults
      file:
        path: /home/devops/users.txt
        seuser: _default
        serole: _default
        setype: _default
        selevel: _default

- 다음과 같이 결과를 확인할 수 있다.

 

 

 

참조

ansible-doc(1)

chmod(1)

chown(1)

rsync(1)

stat(1)

touch(1)

 

 

files 모듈 ansible 설명서

https://docs.ansible.com/ansible/2.7/modules/list_of_files_modules.html

 

Files modules — Ansible Documentation

 

docs.ansible.com

 

'Ansible' 카테고리의 다른 글

8장. 변수 사용하기  (0) 2021.02.20
7장. ansible playbook 기본 구현  (0) 2021.02.20
6장. ansible에서 모듈 사용하기  (0) 2021.02.19
5장. ad-hoc 명령  (0) 2021.02.19
4장. ansible.cfg 파일 톺아보기  (0) 2021.02.06

모듈의 기본 개념

ansible에서 모듈이란, 1장에서 소개한 것처럼 간단하게 ansible에서 사용하는 "명령어" 라고 이해할 수 있다. 이러한 모듈은 하나의 작은 프로그램이므로, 모두 사용법을 배워야 한다. 이러한 사용법은 아주 자세히 확인할 수 있고, 친절하게 사용하는 예시 등도 잘 나와 있다.

 

더자세한 설명은 http://docs.ansible.com/ansible/2.7/modules/modules_by_category.html 에서 확인한다.

 

Module Index — Ansible Documentation

Docs » User Guide » Working With Modules » Module Index 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 t

docs.ansible.com

 

모듈 관련 가이드와 문서 사용 명령

* ansible-doc -l

- ansible host에서 사용할 수 있는 모든 모듈을 나열한다.

- 아래와 같이 엄청난 양의 모듈들의 리스트와 간단한 설명이 나온다.

 

* ansible-doc 모듈명

- 해당 모듈의 상세 메뉴얼을 확인한다.

- 만약 어떤 키워드가 모듈인지 아닌지 판단하려면 ansible-doc 키워드를 썼을 때 뭔가 나오지 않는다면 해당 키워드는 모듈이 아니다.

- 아래와 같이 해당 모듈의 상세 설명들을 확인한다. 더 자세한 사항은 아래에서 설명한다.

- ansible-doc yum, ansible-doc ping, ansible-doc parted 등등 한번 입력해보자.

 

* ansible-doc -s 모듈명

- 해당 모듈을 사용할 플레이북 구문의 템플릿 역할도 할 수 있고, 한눈에 알아보기 쉽게 상세 설명을 해주는 방식으로 보여준다.

- 아래 캡쳐에서 - name: , ping: data 이런 부분이 템플릿 처럼 사용할 수 있는 부분이다.

 

ansible-doc 명령 상세하게 띁어보기

ansible-doc 명령은 일반적으로 모듈이름, 옵션, 노트, 요구사항, 저자, 예시 로 이루어진다. 물론 일부가 빠질 수도 있다. 이러한 doc는 전적으로 개발자가 개인의 재량으로 만드는 것이다. ansible-doc yum 명령 예시를 확인해보자.

 

모듈 이름

- 관련 기본 설명과 개요

 

OPTIONS

- 해당 모듈에서 쓰는 옵션들

 

NOTES

- 기타 알아야 할 것들.

약간 구구절절하지만 개발자 입장에서 팁 등을 명시한다. 위 내용의 번역은 아래와 같다.

 

* 'loop'와 함께 사용하는 경우 : 각 패키지는 개별적으로 처리되므로 목록을`name` 옵션에 직접 전달하는 것이 훨씬 더 효율적입니다.

* 1.9.2 이전 버전에서이 모듈은 yum 모듈에 제공된 각 패키지를 별도로 설치하고 제거했습니다. 이로 인해 파일 이름 또는 URL로 지정된 패키지를 함께 설치하거나 제거해야 할 때 문제가 발생했습니다. 1.9.2에서는 패키지가 하나의 yum 트랜잭션에 설치되도록 수정되었습니다. 그러나 패키지 중 하나가 다른 패키지 (예 : epel-release)에서 가져온 새 yum 저장소를 추가하는 경우 해당 패키지를 별도의 작업에 설치해야합니다. 이것은 yum의 명령 줄 동작을 모방합니다.

* Yum 자체에는 두 가지 유형의 그룹이 있습니다. "패키지 그룹"은 rpm 자체에 지정되고 "환경 그룹"은 별도의 파일 (일반적으로 배포판)에 지정됩니다. 안타깝게도 ansible은 단일 트랜잭션으로 패키지 그룹에서 작동해야하고 yum에서는 그런 방식으로 사용할 때 다른 방식으로 그룹을 지정해야하므로 ansible 사용자에게는이 구분이 분명해집니다. 패키지 그룹은 "@ development-tools"로 지정되고 환경 그룹은 "@ ^ gnome-desktop-environment"로 지정됩니다. "yum group list hidden ids"명령을 사용하여 설치하려는 그룹이 속하는 그룹 범주를 확인하십시오.

* yum 모듈은 멱등성 방식으로 yum 캐시 지우기를 지원하지 않으므로 구현하지 않기로 결정했습니다. 유일한 방법은 명령을 사용하고 yum 명령을 직접 호출하는 것입니다. "command : yum clean all"  https://github.com/ansible/ansible/pull/31450#issuecomment-352889579

 

 

REQUIREMENTS

- 해당 모듈을 사용하기 위해 필요사항

 

AUTHOR

- 해당 모듈 제작자, 그리고 METADATA에 status가 있는데 해당 모듈의 개발 상태를 말함.

 

EXAMPLES

- 플레이북 사용 시 예시들. ansible-doc -s 옵션과 함께 같이 보면서 플레이북을 만들 수 있다.

 

모듈 유지 관리

업스트림에서는 매우 활동적으로 모듈을 개발하고 있다. ansible-doc 령의 AUTHOR 섹션에는 업스트림 ansible 커뮤니티에서 누가 해당 모듈을 유지관리하는지, 현재 개발 상태는 어떤지 등의 상세 정보를 확인할 수 있다.

 

* status 항목 - 모듈의 개발 상태를 기록

stableinterface

모듈의 키워드가 안정적이고키워드를 제거하거나 의미를 변경하지 않도록 모든 노력을 기울인다. (모듈의 인터페이스가 안정적임으 나타내는 것이지모듈의 코드 품질을 평가한 것은 아님)

preview 

모듈이 기술 프리뷰에 있고, 불안정할  있고키워드가 변경되는 등의 문제가 발생할  있다. 즉 베타버전이라고 볼 수 있다.

deprecated 

현재 이 모듈은 사용되지 않으며일부는 향후 릴리스에서  이상 사용할  없다.

removed 

모듈이 릴리즈에서 제거되었으나, 이전 사용자가 같은 기능의 다른 새 모듈을 사용할 때 도움이 되도록 설명서 용도로 존재한다.

 

* supported_by 항목 - 업스트림 ansible 커뮤니티에서 모듈을 유지관리하는 단체(또는 사람)을 기록

core 

"핵심" ansible 개발자 업스트림에서 유지 관리하며 항상 앤서블에 포함된다. 즉 ansible official 모듈이다.

curated 

커뮤니티의 파트너 또는 회사에서 만들고 유지관리하는 모듈 모듈의 유지관리자는 보고된 모든 문제를 관찰하거나 모듈에 대해 제기된 요청을 가져와야 한다커뮤니티 유지 관리자가 변경 사항을 승인한 후에는 업스트림 core 개발자가 큐레이션된 모듈에 제안된 변경 사항을 검토한다또한 핵심 commiter ansible 엔진의 변경으로 인해 이러한 모듈의 모든 문제가 해결되었는지를 확인한다이러한 모듈은 현재 앤서블에 포함되어 있지만 향후 특정 시점에 별도의 패키지로 제공될  있다.

community

core 업스트림 개발자파트너 또는 회사에서 지원하는것은 아니다. 그냥 일반 오픈소스 커뮤니티에서 전적으로 유지관리하는 모듈이다사용 자체는    있으나문제 발생시 feedback은 전적으로 해당 커뮤니티에 달려있다이러한 모듈은 현재 ansible 포함되어 있지만 향후 특정 시점에 별도의 패키지로 제공될 가능성이 있다.

 

멱등성 (冪等性, idempotent)

module은 기본적으로 멱등성을 가진다. 또한 가져야만 한다. 멱등성이란 수학에서 연산의 한 성질을 의미하는 것으로, 연산을 여러번 적용해도 결과가 달라지지 않는 성질을 의미한다. 즉 ansible에서 수행하는 task, ad-hoc, playbook 등에 있는 모듈은 멱등성을 가지므로, 이러한 작업들은 안전하게 실행된다.

 

다시말해 ansible의 모든 작업은, 예를들어 A -> B 의 작업을 했으면 현재 B일텐데, 이 상태에서 또 작업을 해도 이미 상태가 B이므로 작업을 하지 않는다. 이는 ad-hoc, playbook 등을 안전하게 여러 번 실행할 수 있고, 시스템이 이미 올바른 상태에 있으면 아무 작업도 하지 않는다는 것이다.

 

Ansible은 "상태"를 만드는 것이므로, 패키지를 설치하는 것을 install이라고 표현하지 않고 present 라는 식으로 표현한다. 반대는 물론 delete가 아니라 absent 이런식으로 된다.

 

 

대부분의 모듈들은 멱등성을 가지지만, 예외적으로 멱등성을 가지지 않는 모듈이 있다. 이는 아래에 몇가지를 소개할 것이다.

 

멱등성 확인 예시

 

최초 실행하는 ad-hoc 명령은, 아래와 같이 chagned가 true인 것을 볼 수 있다. 이것은 변경이 되었다는 의미이다.

동일한 명령을 다시 실행하면, 아래와 같이 changed가 false가 나오는 것을 볼 수 있다. 동일한 명령을 수행했지만 변경은 되지 않았다는 의미이다.

 

멱등성이 제대로 작동하지 않는 모듈 3가지

대부분의 모듈은 멱등성을 가진다. 시스템의 상태를 확인하고 시스템이 올바르면 아무것도 하지 않는다. 하지만 아래에서 소개하는 command, shell, raw 3가지의 "run command" 모듈들은 멱등성이 제대로 작동하지 않는다. 아예 안 되는 것이 아니고, 좀 애매하다. 아무튼 이 명령어들은 매우 간편해서 자주 쓸 수 있는데, 이에 의존하면 애드혹 명령이나 플레이북 재실행으로 예기치 않은 오류가 발생할 수 있다.  또한, 멱등성이 없는 이 3가지 명령은 실행할 때 마다 무조건 changed를 반환한다.

 

그렇다고 해서 이러한 명령어들을 완전히 피해야 한다는 것은 아니다. 이러한 "run command" 모듈들은 중요한 도구이자 문제에 대한 좋은 해결책이 될 수도 있다. 따라서 상황과 환경에 맞게 조심해서 사용해야 한다. 참고로 가능하면 playbook에서는 사용하지 않는 것을 권고한다.

 

* command 모듈

- ansible 클라이언트들의 명령줄에서 임의의 명령을 실행한다. 

- 이 모듈은 ansible 클라이언트에 python이 설치되어 있어야 사용이 가능하다.

- 예시 : ansible all -m command -a '/usr/bin/hostname'

 

 

* shell 모듈

- command 모듈은 클라이언트의 쉘에서 실행되는게 아니다. 따라서 파이프도 못쓰고, 리디렉션도 할 수 없다. 즉 쉘작업은 할 수 없다.

     - ansible all -m command -a 'ps -e | grep rsyslog' : 작동하지 않음

     - ansible all -m shell -a 'ps -e | grep rsyslog' : 잘 작동함

- shell 모듈은 쉘을 통해 명령어가 처리된다. 쉘 환경변수에도 액세스할수 있고, 리디렉션, 파이프 등도 다 쓸 수 있다.

- 이 모듈은 ansible 클라이언트에 python이 설치되어 있어야 사용이 가능하다.

- 예시 : ansible all -m shell -a 'set | grep PWD' ( set 명령은 환경변수를 보는 명령어이다)

 

# shell 모듈의 사용을 다른 모듈로 대체하기

- 예를 들어, 다음 명령은 멱등성이 없어서 다시 반복하면 /etc/resolv.conf에 nameserver 192.0.2.1 이라는 내용이 계속 아래에 추가되게 된다.

ansible web.example.com -m shell -a 'echo "nameserver 192.0.2.1" >> /etc/resolv.conf'

- 이것 대신 아래처럼 copy 명령을 쓰면, 멱등성이 있고 여러번 반복해도 상관없다.

ansible web.example.com -m copy -a 'dest=/etc/resolv.conf content="nameserver 192.0.2.1\n"'

 

* raw 모듈

- 모듈 하위 시스템을 거치지 않고 원격 쉘을 사용하여 직접 명령을 실행할 수 있다.

- 이는 파이썬을 설치할 수 없는 시스템 (예 : 네트워크 라우터 등)을 관리할 때 유용하다. 즉 윈도우, 네트워크 장치 등의 리눅스가 아닌 시스템에서 자주 사용할 수 있다.

- 또한 호스트에 파이썬을 설치하는 데에도 사용할 수 있다.

- 보통으로 리눅스의 기본적인 조건을 만족하지 못할 때나, 아니면 ssh가 없는 등의 환경에서 사용된다.

- 이 강의에서는 사용하지 않으므로 더 이상 자세한 설명은 생략한다.

 

 

# 참고

당연한 이야기지만, 멱등성이 없는 모듈은 반복하면 문제가 발생할 수 있다. 예를들어, 유저 생성하는 명령을 command 모듈로 하고, 다시한번 하면 "이미 있는 유저이다" 이런식으로 에러가 발생할 것이다. 이것은 리턴코드가 0이 아니므로 playbook상에서는 error로 인식된다.

 

# 참고 : 변경되지 않았는데 왜 CHANGED?

shell 또는 command 모듈이 실행될 때 일반적으로 쉘이 시스템 상태에 영향을 미쳤다고 판단하는지 여부에 따라 CHANGED라고상태를 보고한다. 실제로 변경되지 않았지만 CHANGED 라고 결과가 나오는 것이다. 오해할 수 있으므로 참고할 것.

 

 

 

 

참고사항

ansible(1)

 

도움이 되는 링크

 

모듈 색인: ansible 문서

http://docs.ansible.com/ansible/2.7/modules/modules_by_category.html

 

Module Index — Ansible Documentation

Docs » User Guide » Working With Modules » Module Index 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 t

docs.ansible.com

명령 - 원격 노드에서 명령 실행 모듈: ansible 문서

http://docs.ansible.com/ansible/2.7/modules/command_module.html

 

command – Executes a command on a remote node — Ansible Documentation

Docs » command – Executes a command on a remote node 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

docs.ansible.com

 쉘 - 노드에서 명령 실행 모듈: ansible 문서

http://docs.ansible.com/ansible/2.7/modules/shell_module.html

 

shell – Execute commands in nodes. — Ansible Documentation

Docs » shell – Execute commands in nodes. 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 Ansi

docs.ansible.com

모듈 개발 ansible 설명서

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

모듈 지원 ansible 설명서

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

 

Module Maintenance & Support — Ansible Documentation

To help identify maintainers and understand how the included modules are officially supported, each module now has associated metadata that provides additional clarity for maintenance and support. Core Maintained modules are maintained by the Ansible Engin

docs.ansible.com

 

업스트림 ansible 커뮤니티에는 앤서블 및 해당 통합 모듈에 대한 이슈 트래커가 있다. 아래 링크를 참고한다.

https://github.com/ansible/ansible/issue

 

ansible/ansible

Ansible is a radically simple IT automation platform that makes your applications and systems easier to deploy and maintain. Automate everything from code deployment to network configuration to clo...

github.com

 

 

 

 

 

 

 

 

 

 

'Ansible' 카테고리의 다른 글

7장. ansible playbook 기본 구현  (0) 2021.02.20
부록1 - 자주 사용되는 모듈들  (0) 2021.02.19
5장. ad-hoc 명령  (0) 2021.02.19
4장. ansible.cfg 파일 톺아보기  (0) 2021.02.06
3장. Inventory 파일  (2) 2021.02.06

ad-hoc 명령이란?

ansible은 일반적으로 playbook을 사용하도록 설계되어 있다. 하지만 한번만 딱 쓰거나 간단한 명령, 상태를 확인하는 정도의 명령은 굳이 플레이북을 사용하지 않고 간단한 명령 구문으로 신속하게 수행할 수 있다. 이 방식이 ad-hoc  명령이라고 부른다. 예를 들어, ad-hoc 명령 한줄로 인벤토리에 있는 모든 서버의 /etc/hosts 파일의 특정 줄을 확인할 수 있다.

 

 

ad-hoc 명령 구문

ansible 호스트명패턴 -m 모듈명 [-a 'module argunets'] [-i inventory]

* 설명

- 대괄호는 필수가 아닌 선택사항이다.

- 호스트명 패턴은 web[1:9].example.com 이런식으로 사용하거나, 그룹명 또는 그룹의 그룹명을 명시하는 방법을 쓸 수 있다.

 

ad-hoc 명령 옵션

* 일반 옵션

-m 모듈을 명시하는 옵션이다.
-i  특정 인벤토리 파일(경로포함)을 명시해서 사용하는 옵션이다.
-a

여러 모듈은 추가 명시해야 할 인수가 있을 수도 있고 없을수도 있다. -a 옵션은 추가 인수를 넣을 때 명시한다. 대부분의 모듈은 인수를 받는다. 모듈에서 사용하는 인수들은 ansible-doc 명령으로 확인 가능하다. 여러 인수를 넣을 때는 다음과 같이 사용한다. -a 'name=newbie uid=400 state=present'

-o 결과가 보통 여러줄로 나오는데, 결과를 한줄로 나오게 한다.
-v 어떤 구성파일을 쓰고 있는지 명시해준다.

* 권한 및 유저 관련 옵션

--become-method BECOME_METHOD
--become-user BECOME_USER
--ask-become-pass, -K
--become, -b
--ask-pass, -k
--user REMOTE_USER, -u REMOTE_USER
이 옵션들 어디서 많이 본 옵션들이다. 4장에서 다룬 ansible.cfg 구성파일에 있는 내용들이다. 즉 ansible.cfg에서 기본 설정을 해 두었더라도, ad-hoc 명령 구문에서 바꿀 수 있다는 것이다. ansible.cfg에서 설정한 값보다, ad-hoc 명령에서 옵션을 넣은 값이 더 우선순위가 높다.

예를들어 ansible.cfg 파일에서 become = false , true값으로 변경하고 싶은데 이번 한번만 그렇게 하고 싶다면, 명령어 라인에서 --become 옵션만 넣어주면 된다는 것이다. (예시 : ansible all -m copy -a 'content="This server is managed by Ansible!.\n" dest=/etc/motd' --become)

상세 내용은 아래 4장에서, ansible.cfg 구성파일 내용 부분을 확인할 수 있다. 2021/02/06 - [Ansible] - 4장. ansible.cfg 파일 톺아보기

# 추가 이해를 위한 참고 예시

ansible localhost -m command -a 'id'

ansible localhost -m command -a 'id' -u devops

- 두 명령의 결과는 다를 것이다.

 

* 관리 옵션

--version ansible의 버전 및 기타 환경 정보를 확인한다.
--list-hosts 인벤토리를 확인할 때 사용한다. 상세 내용은 아래 3장을 참고한다.
2021/02/06 - [Ansible] - 3장. Inventory 파일
-v, --verbose verbose 모드로, -vvv은 더 상세하게, -vvvv는 디버그 모드이다. troubleshooting을 할 때 사용한다.

여러 옵션이 더 있는데, 더 자세한 것은 ansible --help에서 확인할 수 있다.

 

 

 

참고 링크

 

패턴 사용 : ansible 설명서

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

 

Working with Patterns — Ansible Documentation

Docs » User Guide » Working with Patterns 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 Ansib

docs.ansible.com

애드혹 명령 소개: ansible 문서

http://docs.ansible.com/ansible/2.7/user_guide/intro_adhoc.html

 

Introduction To Ad-Hoc Commands — Ansible Documentation

Docs » User Guide » Introduction To Ad-Hoc Commands 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 la

docs.ansible.com

 

'Ansible' 카테고리의 다른 글

부록1 - 자주 사용되는 모듈들  (0) 2021.02.19
6장. ansible에서 모듈 사용하기  (0) 2021.02.19
4장. ansible.cfg 파일 톺아보기  (0) 2021.02.06
3장. Inventory 파일  (2) 2021.02.06
2장. Ansible 설치하기  (0) 2021.02.06

ansible.cfg 파일이란?

ansible 엔진은 ansible 명령을 실행할 때 모든 환경설정 및 세팅이 들어있는 ansible.cfg를 참고하여 명령을 실행한다. 

 

 

ansible.cfg 파일 사용하기

ansible 엔진은 ansible 명령을 실행할 때 모든 환경설정 및 세팅이 들어있는 ansible.cfg를 참고하여 명령을 실행한다. 이 ansible.cfg 파일은 여러 위치에 있을 수 있고, ansible 엔진은 아래 명시된 순서대로 구성파일을 확인한다. 그리고 가장 먼저 확인된 파일을 사용한다.  즉, 여러개가 있어도 우선순위 가장 높은걸 쓰고 다른 것은 무시된다.

 

1. ansible 명령이 실행되는 디렉토리에 있는 ansible.cfg 파일 (보통 이걸 많이 쓰는 듯 하다)

2. 홈디렉토리/.ansible.cfg : ansible은 사용자의 홈디렉토리에서 ansible.cfg를 찾는다. 이게 없으면 default 구성파일을 사용한다. (홈디렉토리의 특성상 파일의 맨 앞에 . 이 붙는다. 이렇게 숨김파일로 하는 것은 관례적으로 하는 것이며 계정의 설정파일은 대부분 히든파일로 되어 있는것도 같은 맥락이다. 특정 유저 홈디렉토리의 bashrc는 숨김파일이지만, /etc/bashrc는 그냥 파일이다.)

3. /etc/ansible/ansible.cfg : default 구성 파일. 다른 구성 파일이 없는 경우 사용된다.

 

이를 통해 관리자는 다양한 환경이나 프로젝트가 별도의 디렉토리에 저장되는 디렉토리 구조를 생성할 수 있고, 각 디렉토리에는 고유 설정 집합으로 맞춤화된 구성파일이 포함된다. 

 

또한 ansible.cfg 파일은 변경한 후 따로 서비스 등을 재시작할 필요가 없으며 변경하고 저장하면 바로 적용된다.

 

 

 

# 참고 : 설정의 default값은?

- 일반적으로, conf 파일에서 주석처리된 기본 내용이 default 값으로 보면 된다. (ansible.cfg 뿐만 아니고 다른 conf도)

- 아래처럼 /etc/ansible/ansible.cfg 은 ansible 설치시 생성되는 가장 근본이 되는 cfg파일이며, 여기에 많은 예시와 default 값들이 명시되어 있다.

 

현재 사용하고 있는 인벤토리 파일, ansible.cfg파일 확인하기

ansible --version 명령을 사용하여 현재 사용하고 있는 ansible.cfg가 어떤 파일인지 확인할 수 있다. 아래 config file = 부분을 확인하면 된다.

 

명령어에서 -v 옵션을 쓰면, 어떤 구성파일을 쓰고 있는지 확인할 수 있다.

예시 : ansible all -i /ansible_test/inventory -v --list-hosts

 

 

 

ansible.cfg 구성파일 내용

ansible.cfg에는 크게 2가지 섹션이 있다. 

 

- [default]  : ansible 작업의 기본 환경설정 값을 구성
- [privilege_escalation] : 관리 호스트에서 ansible이 권한 에스컬레이션을 수행하는 방법을 구성

 

[default]

inventory = ./inventory

사용할 인벤토리 파일의 경로
- 정적 인벤토리 파일 또는 여러개의 정적 인벤토리 파일이 있는 디렉토리를 명시
- 동적 인벤토리 스크립트를 포함하는 디렉토리 명시

remote_user = user

관리 호스트에 연결할 사용자의 이름. 관리 호스트에서 사용하므로 관리 호스트에 해당 유저가 존재해야 한다.
이 구문을 따로 명시하지 않으면 ansible은 ansible 명령을 실행하는 로컬 사용자명을 사용하여 관리 호스트에 연결한다. 따라서 다른 원격 사용자를 지정하려면 여기에 해당 유저명을 넣는다.
또한 여기서 명시된 유저를 사용하여 [privilege_escalation]을 적용한다. (물론 명시하지 않으면 ansible 명령을 실행하는 로컬 사용자 이름을 사용한다)

ask_pass = false

ssh암호를 요청하는 메시지 표시 여부를 결정
제어노드에서 관리 호스트에 연결할 사용자에 대해 인증할 수 있는 키가 구성된 경우 따로 비밀번호를 물어보지 않고 자동으로 로그인된다. 이런 경우에는 ssh 암호를 요청할 필요가 없으므로 false로 설정한다. default는 false 이다.
위 경우가 아닌, 비밀번호를 입력해야 하도록 설정된경우, true로 설정하여 로컬 사용자에게 원격 사용자가 사용하는 암호를 요청하는 메시지가 표시하도록 해야 한다. 만약 비밀번호를 써야하는데 false로 되어있으면, 아예 비밀번호를 입력할 기회 자체가 없어진다.

 

[privilege_escalation] 

become = true

remote_user로 연결한 유저를 sudo 또는 su를 사용하여 특정 상급 유저로 전환할지 여부
사용하는 모듈에 따라 원래 유저의 권한만으로 충분할 수도 있고, 상급유저(root)의 권한이 필요할 수도 있다. 이에 따라 true/false가 달라질 것이다. 상급 유저로 전환하려면, true를 설정한다. default 값은 false이다.
다만, 이렇게 설정해도 ansible 명령 또는 ansible-playbook 실행 시 다양한 방법으로 이를 재정의할 수 있다. 예를 들어 특정 ansible-playbook은 권한 상승이 필요없는 경우 false로 재정의할 수 있다. 이 방법은 이후에 다시 설명한다.

become_method = sudo

상급 유저로 전환하는 방법을 명시한다.
default는 sudo이고, su는 옵션이다. 일반적으로 sudo 그대로 사용한다.

become_user = root

상급 유저로 전환할 때, 어떤 유저로 전환할지 명시한다.
default는 root 이다.

become_ask_pass = false

become_method의 암호를 요청하는 메시지 표시 여부
default는 false 이다. 상급 유저로 전환할 때 sudoers 설정을 따로 하지 않아 상급유저의 암호를 입력해야 한다면, 이 값을 true로 해야 한다. 위 [inventory] 항목에서 remote_user 로 설정한 유저가 sudo 권한이 있다면 이 값은 false로 하면 된다.

* 구성파일 내에서 주석 사용하기

- ansible .cfg 구성파일은 #와 ; 로 주석을 사용할 수 있다.

- # 는 줄 맨앞에 넣으며 전체 줄을 주석처리한다.

- ; 는 해당 줄에서 ; 의 오른쪽에 있는 모든 내용을 주석처리한다.

 

 

제어노드에서 관리호스트로 연결하고 task를 실행하는 과정

* ansible.cfg 예시파일

[defaults]
remote_user = jason
inventory = /home/jason/ansible/inventory
roles_path = /home/jason/ansible/roles:/usr/share/ansible/roles
ask_pass = false

[privilege_escalation]
become = true
become_method = sudo
become_user = root
become_ask_pass = false

 

* 절차

1. 제어노드에서 jason라는 유저명으로 관리호스트에 ssh 접속을 수행한다.

2. 키가 설정되어있다면, 비밀번호를 입력하지 않고 바로 로그인되고, 키가 설정되어 있지 않다면 비밀번호를 물어본다. (비밀번호 입력 부분)

3. 로그인을 완료했으면, ansible에서 명시한 task를 실행할 때 root 권한의 sudo를 사용하여 실행한다.

4. 관리호스트의 sudoers에서 jason 유저에 대해 비번 없이 sudo를 사용할 수 있도록 설정했다면 바로 task 가 실행된다.

5. 따로 sudoers의 설정을 하지 않았다면, sudo 실행시 jason의 비밀번호를 물어본다. (비밀번호 입력 부분)

 

위 순서대로 연결하여 task를 실행하므로, 비밀번호를 2번 물어볼 수 있는 상황이 있다. 일반적으로 운영에서 사용할 때, 편의를 위해 ansible 명령어를 쳤을 때 어떤 비밀번호도 입력하지 않도록 하기를 원할 것이다. 따라서 둘다 비밀번호를 물어보지 않게 하기 위해 2가지 설정을 한다.

 

1. 제어노드에서 ssh 키설정으로 비밀번호를 물어보지 않도록 한다.

2. 관리호스트에서 sudoers를 적절히 설정하여 비밀번호를 물어보지 않도록 한다.

 

이후 실제 운영에서는 필요에 따라 위 설정을 수행할수도 있고 하지 않을수도 있다. 이 강의의 예제에서는 이 설정들을 기본적으로 적용한 것으로 간주하고 예제를 수행한다.

 

 

비밀번호를 묻지 않도록 설정하기 - ssh 키 설정

관리 호스트에서 리눅스 제어노드와 openssh를 사용한다면, ssh 키 기반 인증을 설정할 수 있다. 이렇게 하여 ask_pass = false 설정을 하고, 비밀번호를 따로 입력하지 않고 넘어갈 수 있다.

 

 

1. ansible을 사용할 유저로 로그인한 후, 현재 ~/.ssh 에 값이 있는지 확인한다.

- devops 계정을 사용하며, ~/.ssh 디렉토리는 없다.

 

2. ssh-keygen 명령을 실행한다.

- 실행 후 그냥 엔터 엔터 엔터로 진행하면 된다. 아래와 같이 결과를 확인할 수 있다.

- 또한 아래 경로에 두개의 파일이 생성되는 것을 확인할 수 있다.

id_rsa : private key (열쇠로 생각하면 된다. 접속을 시도하는 호스트가 가지고 있다)

id_rsa.pub : public key (열쇠구멍이라고 생각하면 된다. 접속을 당할 클라이언트에 해당 파일이 있어야 한다)

 

 

3. public key를 접속할 서버에 복사하기

- # ssh-copy-id 유저@서버주소

- 여기서 유저는 ansible에서 사용당할 서버의 유저, 즉 remote_user = 에 명시되는 유저이다.

- 아래 예제에서는 해당 유저를 jason으로 설정한다. (이 유저는 ansible host가 아닌, ansible client에 있는 유저이다)

- 명령어 입력 후 yes를 입력하고, jason의 비밀번호를 입력한다.

- 위와 같이 완료 후, ansible-client1의 jason 계정 홈디렉토리에 들어가면, .ssh가 새로 생긴 것을 확인할 수 있다.

- .ssh에 들어가면, authorized_keys 라는 파일이 있는 것을 확인할 수 있는데, 이 파일은 ssh-keygen으로 만든 id_rsa.pub와 동일한 내용이다. 즉 public key가 ansible-client1 서버 안에 있는 jason 유저에 저장된 것이다. 

 

4. 접속 테스트

- 이제 ansible host에서 ansible-client1에 접속할 때 아래와 같이 jason계정으로 접속하면, 비밀번호 입력 없이 그대로 접속되는 것을 확인할 수 있다.

- 이렇게 다 되었으면, ansible.cfg에서 remote_user = jason 으로 설정하여 ansible에서 해당 유저 사용 시 비밀번호를 입력하지 않고 명령을 수행할 수 있다.

 

# 참고

localhost 즉 앤서블호스트인 자기자신도 ansible에서 사용하기 위해서는 ansible 호스트에 해당 유저를 생성하고, 그 유저에 대해서 키를 동일하게 복사해주어야 한다. 그리고 ssh jason@localhost 이런식으로 접속 테스트를 수행하면 된다. 

 

 

비밀번호를 묻지 않도록 설정하기 - sudo 설정

RHEL에서는 기본적으로 /etc/sudoers에서 wheel 그룹의 모든 사용자에게 자신의 암호로 sudo를 사용하여 root 권한으로 명령을 실행할 수 있는 기능이 있다. 여기서는 특정 유저가 아예 비밀번호를 쓰지 않고 sudo 명령을 써서 root 권한으로 명령을 실행하도록 설정한다. 이 부분은 ansible.cfg파일의 become = true 부분과 연관이 있다.

 

또한 이러한 권한 상승 방법은 보안상의 영향을 충분히 고려해야 한다. 조직 및 배포 방식에 따라 고려해야 할 장단점이 서로 다를 수 있으므로 운영 장비에서는 심도있게 고려해야 한다.

 

 

1. ansible로 관리 당할 호스트의 root 유저로 아래와 같이 수행한다.

- # vi /etc/sudoers.d/jason(유저명)

- 다음과 같이 입력한다. jason ALL=(ALL) NOPASSWD:ALL (아래 캡쳐 참조)

- 이렇게 하면, 해당 유저는 암호를 입력하지 않고 sudo를 사용하여 root권한으로 명령을 실행할 수 있다.

 

2. sudo 명령으로 테스트해본다.

- /proc/vmallocinfo 는 루트만 읽을 수 있다. 이 파일로 테스트해보자. (아래 캡쳐 참조)

- 이렇게 다 되었으면, ansible.cfg 파일의 become = true 를 설정하여 권한상승을 할 때 비밀번호를 물어보지 않는다.

 

실습 : 테스트 수행

<인벤토리>

 

[servers]

web.example.com

db.example.com

was.example.com

localhost

 

<ansible.cfg>

 

[defaults]

inventory = inventory

remote_user = jason

ask_pass = false

 

[privilege_escalation]

become = true

become_method = sudo

become_user = root

become_ask_pass = false

 

# 참고 : 접속할 모든 클라이언트는 최초로 한번은 직접 ssh로 접속을 해서 yes를 입력해줘야 한다.

 

명령어 : ansible all -m ping

위의 모든 설정을 완료하고 방화벽에 문제가 없다면 이렇게 잘 나올 것이다. 참고로, 이 ping 모듈은 icmp ping이 아니다.

 

 

 

 

# 참고 : 패스워드를 입력하도록 설정하는 경우, ansible 클라이언트의 유저들은 패스워드를 모두 통힐해야 하는 것으로 보인다.

클라이언트 4대에 대해 각각 패스워드를 다르게 하고, 명령을 실행하면 패스워드를 물어보는데, 한번만 입력하며 그 패스워드 하나를 전체 클라이언트에 적용한다. 첫번째 것 맞는것만 들어가고 아래는 다 incorrect password가 뜬다.

 

참고 도움말 페이지

ansible(1)

ansible-config(1)

ssh-keygen(1)

ssh-copy-id(1)

 

구성파일 : ansible 설명서

https://docs.ansible.com/ansible/2.7/installation_guide/intro_configuration.html

 

Configuring Ansible — Ansible Documentation

Docs » » Configuring Ansible 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 documentat

docs.ansible.com

 

'Ansible' 카테고리의 다른 글

6장. ansible에서 모듈 사용하기  (0) 2021.02.19
5장. ad-hoc 명령  (0) 2021.02.19
3장. Inventory 파일  (2) 2021.02.06
2장. Ansible 설치하기  (0) 2021.02.06
1장. Ansible 소개  (0) 2021.02.06

인벤토리

- Ansible에서 관리할 호스트의 모음을 정의한 것이다.

- 호스트명만 정의하는 것을 넘어서, 그룹도 정의할 수 있고 여러가지 정보를 추가로 넣을 수도 있다. (호스트나 그룹에 적용되는 변수 설정 등)

- 인벤토리는 2가지 종류로 나뉜다.

    - 정적 인벤토리 : 단순하게 사용자가 직접 텍스트 파일로 정의한다.

    - 동적 인벤토리 :필요에 따라 외부 정보 provider를 사용해 스크립트나 기타 프로그램으로 인벤토리를 생성한다.

- 이러한 인벤토리 파일에 기록되는 호스트명은 각 서버의 /etc/hosts에 있는 내용에 의존된다.

 

정적 인벤토리란?

- 이 파일은 기본적으로 ini 형식을 사용하며, yaml 형식도 사용할 수 있다. 

- 기본 경로는 /etc/ansible/hosts 이지만 일반적으로 이 기본경로는 사용하지 않으며 직접 따로 만들어서 사용한다.

- 한줄에 하나씩 호스트 또는 ip주소를 나열하면 된다. 그룹은 대괄호를 사용해서 그룹명을 명시하고, 그 아래에 구성원을 명시한다.

- 아래 예시 참고

test1
test2
192.168.1.201
localhost

[webservers]
web1.example.com
web2.example.com
localhost
192.168.1.201

[dbservers]
db1.example.com
db2.example.com

[east_datacenter]
web1.example.com
db1.example.com

[west_datacenter]
web2.example.com
db2.example.com

[production]
web1.example.com
web2.example.com
db1.example.com
db2.example.com

[development]
localhost
192.168.1.201

[all_datacenters:children]
east_datacenter
west_datacenter

 

 

호스트 그룹

- 위 예시와 같이 대괄호로 그룹을 지정할 수 있다.

- 여러개의 그룹에 동일한 호스트가 각각 들어갈 수 있다.

- 실제로 호스트를 여러 그룹으로 구성하면, 호스트의 역할, 물리적 위치, 운영/테스트 장비 여부 등의 따라 다양한 방법으로 구성할 수 있다.

- 그룹 이름에서는 특수문자는 _(언더바) 만 허용한다. 다른 특수문자를 쓰면 WARNING이 뜰 수 있다.

- 따로 명시하지 않아도 기본적으로 항상 존재하는 호스트 그룹이 2가지 있다.

    - all : 인벤토리에 명시된 모든 호스트를 포함한다.

    - ungrouped : 인벤토리에 명시되었으나 그룹에 속하지는 않은 모든 호스트를 포함한다.

  

localhost

- ansible을 실행하는 제어노드 본인을 의미한다.

- 이 말은, 제어노드도 ansible의 클라이언트로서 ansible의 컨트롤을 받게 할 수 있다는 것이다.

- localhost는 all 호스트그룹에는 포함되지 않는다. 

 

중첩 그룹

그룹 안에 그룹을 포함할 수 있다. :children 접미사를 사용한다. 위 예시에 명시된 all-datacenters를 선택하면, 실제로는 총 4개의 호스트가 선택되는 것이다. (web1.example.com, db1.example.com, web2.example.com, db2.example.com)

 

 

범위를 사용하여 호스트 간소화하기

- [start:end] 형식을 사용하여, 많은 수의 호스트를 간단하게 명시할 수 있다. 아래 예제를 보자.

192.168.[4:6].[0.255]
: 192.168.4.1~255, 192.168.5.1~255, 192.168.6.1~255 모두의 아이피를 포함한다.

server[01:20].example.com
: server01.example.com ~ server20.example.com 까지 총 20개를 모두 포함한다.

[a:c].dns.example.com
: a, b, c 총 3개의 a.dns.example.com, b.dns.example.com ... 을 명시한다.

2001:db8::[a:f]
: a~f까지를 각각 명시하여 2001:db9::a, 2001:db9::b, 2001:db9::c ... 총 6개가 나온다. 

- 숫자 범위 앞에 0이 포함된 경우 0이 패턴에 그대로 사용된다. 두번째 예제는 01, 02, 03 등은 패턴이지만 그냥 1,2,3은 안되는 것이다.

 

 

정적 인벤토리 파일 사용

- 정적 인벤토리를 사용할 때는 3가지 방법으로 사용된다.

1. 아무데서나 ansible 명령을 입력하는경우 :  기본 경로인 /etc/ansible/hosts가 사용된다.

2. ansible.cfg 파일에 inventory 이름을 명시하고, ansible.cfg 파일이 있는 경로에서 ansible 명령을 입력하는경우 : 명시한 inventory가 사용된다.

3. ansible 명령에 특정 인벤토리를 명시하는 경우 : 명령에 명시한 특정 인벤토리 파일이 사용된다. 

   명령 실행 시 --inventory 경로/파일명 또는 -i 경로/파일명 으로 지정할 수 있다.

 

2번, 3번의 경우 inventory 파일은 사용자가 원하는 위치에 자유롭게 둘 수 있고 파일명도 마음대로 할 수 있다. 또한 기본 경로인 /etc/ansible/hosts에는 인벤토리를 사용하는 구문이나 방식이 상세히 명시되어 있으므로 참고할 수 있다.

 

 

인벤토리에 변수 정의하기

- ansible 에서 사용하는 변수 값을 호스트 인벤토리에 정의할 수 있다. 이러한 변수는 특정 호스트, 또는 호스트 그룹에만 적용된다.

- 일반적으로 이러한 인벤토리 변수를 인벤토리 파일에 직접 정의하는 것 보다는 특수 디렉토리에 정의하는게 좋다. 변수 쪽 챕터에서 해당 내용을 상세하게 다룬다.
- 가능하면 권장하지 않는다고 한다.

 

 

명령어 : 인벤토리 확인하기

- 인벤토리에 명시된 서버가 통신이 되지 않아도 문제없다. 즉 상태에 상관없이 인벤토리 파일에 있기만 하면 결과는 잘 나온다.

 

* 형식

ansible.cfg에 인벤토리 파일을 명시하고 ansible.cfg 파일이 있는 위치에서 실행하는 경우

ansible 호스트명(그룹명) --list-hosts

특정 인벤토리를 지정하는 경우

ansible 호스트명(그룹명) -i 인벤토리파일경로 --list-hosts

 

* 예시

ansible web1.example.com -i /ansible_test/inventory --list-hosts

ansible webservers -i /ansible_test/inventory --list-hosts

ansible all -i /ansible_test/inventory --list-hosts

ansible localhost -i /ansible_test/inventory --list-hosts

ansible all --list-hosts

 

# 참고

- -i옵션 뒤에 파일이 아닌 디렉토리를 명시해도 그 디렉토리에서 파일을 찾아서 읽는다. 디렉토리 안에 인벤토리 파일이 여러개면 다 가져온다.

- 가져올 때 파일 이름은 상관 없다.

 

 

인벤토리의 호스트 패턴

- 호스트 패턴은 playbook 또는 adhoc 명령의 대상이 될 호스트를 지정하는데 사용한다.

- 호스트의 개수가 많은 경우 일일이 다 명시할 수 없으므로, 이러한 패턴을 사용하게 된다.

- 일반적으로 플레이의 작업에 복잡한 조건문을 설정하는 대신, 호스트 패턴을 주의깊게 사용하고 적절한 인벤토리 그룹을 설정하여 플레이로 호스트를 제어하는게 더 쉽고 간편하다.

 

* 이 섹션의 예시에서 참조하는 인벤토리 내용

web.example.com
data.example.com

[lab]
labhost1.example.com
labhost2.example.com

[test]
test1.example.com
test2.example.com

[datacenter1]
labhost1.example.com
test1.example.com

[datacenter2]
labhost2.example.com
test2.example.com

[datacenter:children]
datacenter1
datacenter2

[new]
192.168.1.245
192.168.1.231

data.example.com
labhost2.example.com
test2.example.com
labhost1.example.com
test1.example.com

labhost2.example.com
test2.example.com
labhost1.example.com
test1.example.com

 

* 호스트 패턴 사용하는 위치

- ansible-playbook 에서는, hosts 지시문에 호스트 패턴을 명시한다.

- adhoc 명령어 에서는, ansible 명령 바로 뒤에 인수로 호스트 패턴을 명시한다.

 

 

* 호스트 패턴으로 사용되는 요소

 

클라이언트 호스트명 (관리 호스트)

- 가장 기본적인 호스트 패턴으로, 인벤토리에 나열된 각 클라이언트의 호스트 이름들이다.

- 이 클라이언트 호스트명은 인벤토리 안에서 unique해야 한다.

- 클라이언트 호스트명 대신 IP를 명시적으로 나열할 수도 있다. 다른 사용 방법은 모두 동일하다. 그러나 IP는 용도를 기억하기 어려우므로 일반적으로 호스트명을 사용하는 것이 권고된다.

 

그룹

- 그룹의 그룹도 있으며, 인벤토리에 따로 명시적으로 적혀있지 않은 all, ungrouped라는 특수그룹도 존재한다.

- 이러한 그룹들은 모두 호스트 패턴으로 사용할 수 있다.

 

와일드카드

- 리눅스에서 사용하는 와일드카드 별표와 동일하게 사용한다.

- 호스트 패턴이 작은따옴표로 묶인 별표의 경우, 인벤토리의 모든 호스트가 일치한다.  ( - hosts : '*'  이런식으로 사용)

- 패턴 예시는 다음과 같다.

 

'*.example.com' 은 .example.com 으로 끝나는 모든 호스트들과 일치한다.
web.example.com
data.example.com
labhost1.example.com
labhost2.example.com
test1.example.com
test2.example.com

'192.168.1.*' 은 192.168.1. 이후 모든 호스트들을 의미한다.
192.168.1.245
192.168.1.231

'datacenter*' 은 datacenter로 시작하는 모든 호스트을 의미한다. (여기서 datacenter*는 그룹)
labhost1.example.com
test1.example.com
labhost2.example.com
test2.example.com

- 기타 추가 예시들

ansible '*.example.com, !*.lab.example.com' -i inventory1 --list-hosts

ansible 'prod,172*,*lab*' -i inventory1 --list-hosts

 

 

 

앰퍼샌드

- 논리 AND와 비슷하게 작동한다.

- 아래 예시는, lab 그룹의 호스트들을 가져오는데 그 중에서 datacenter1 그룹과의 교집합만 가져오게 한다.

hosts: lab,&datacenter1

or 

ansible 'lab,&datacenter1' -i inventory1 --list-hosts

- 따라서 결과는  labhost1.example.com  이것 하나만 나온다.

 

 

 

느낌표 (부정을 의미하는 !)

- 아래 예시는 datacenter 그룹을 가져오는데, 그 중 test2.example.com을 빼고 가져오는 것임.

hosts: datacenter, !test2.example.com
(!test2.example.com,datacenter 로 해도 동일)

- 따라서 결과는 아래 3개가 나온다.

labhost1.example.com

test1.example.com

labhost2.example.com

 

- 추가 예시 

   - hosts: all,!datacenter1    이러면, datacenter1 그룹만 제외하고 모두이다.

   - hosts: '!webservers'   이러면, webservers를 뺀다. 이게 그룹이면 해당 그룹에 있는 모든 호스트들이 다 빠진다.

 

 

# 참고

- *별표라던지 !느낌표 등의 문자를 사용할 때, 이런 문자들은 shell에서 사용되는 의미도 있으므로 adhoc 명령에서 문제가 발생할 수 있다.

- 마찬가지로, ansible-playbook에서도 특수 와일드카드 등을 사용할 때 호스트 패턴을 올바르게 구문분석하도록 작은 따옴표로 묶도록 한다. (hosts: '!test1.example.com, development' / 이런식으로 )

 

# 참고

- 와일드카드 호스트 패턴은 호스트뿐만 아니라 그룹, 그룹의 그룹 등 모든 패턴에 일치하게 된다. 

- dns이름에 해당하는 이름, ip주소 또는 그룹을 구분하지 않는다.

- 따라서 아래 예시처럼 결과가 다르게 나오므로 조심해야 한다.

data*
data.example.com
labhost2.example.com
test2.example.com
labhost1.example.com
test1.example.com

datacenter*
labhost2.example.com
test2.example.com
labhost1.example.com
test1.example.com

 

 

* playbook에서 사용 예시

 

호스트지정

- hosts : labhost1.example.com, test2.example.com, 192.168.1.2

 

그룹지정

- host : lab, datacenter1

 

# 참고

- 그룹지정에서는아래처럼 test1.example.com이중복인데, 두번이 아닌 한번만 참조하게 된다.

[lab]
labhost1.example.com
labhost2.example.com

[datacenter1]
labhost1.example.com
test1.example.com

 

와일드카드 혼합

- hosts: lab, data*, 192.168.1.245

 

아래처럼 총 6개가 지정될 것이다.

data.example.com

labhost1.example.com

test1.example.com

labhost2.example.com

test2.example.com

192.168.1.245

 

 

 

 

 

# 참고

- 호스트명을 나열할 때 일반적으로 콤마로 하지만, 콜론: 도 가능하다. 사실 안쓰는게 나은데, 옛날에 사용하던 방법이므로 알아두면 참고가 된다.

 

동적 인벤토리

- 정적 인벤토리처럼 직접 입력하는 방식이 아닌, 외부 제공하는 정보를 사용하여 동적으로도 생성할 수 있다.

- 예를들어 /etc/hosts에 100대를 등록해야 한다면, 또한 등록한 100대 중 몇대를 수정해야 한다면.. 이런식으로 인벤토리 등록 작업 자체가 복잡하고 양이 많거나, 아니면 지속적으로 인벤토리가 변경되는 경우, 동적 인벤토리가 매우 편리하다.

- 오픈소스 커뮤니티에서는 업스트림 앤서블 프로젝트에서 사용할 수 있는 수많은 동적 인벤토리 스크립트를 만들었다. 또한 직접 작성할수도 있다.

- 동적 인벤토리는 정적 인벤토리와 비교하여 매우 복잡하므로 다른 섹션에서 다룬다.

 

 

* 동적 인벤토리 예시

-  핑으로 장비를 찾아서 응답이 오면 인벤토리에 등록을 하거나, 아니면 ssh 접속을 해서 되는지 여부를 확인한다는지 등 여러가지 방식을 사용해 인벤토리를 스캔하고 등록까지 할 수 있다.

- 동적 인벤토리 프로그램에서 amazon ec2 계정에 접속하여 ec2에 있는 가상머신들의 정보를 사용하여 ansible 인벤토리를 자동으로 생성할 수 있다. ansible 실행 시 동적 인벤토리 프로그램에서 이 작업을 수행하므로, 호스트 추가/제거 변경을 항상 최신정보로 적용할 수 있다.

 

이러한 구문을 파이썬 등의 언어로 만들 수 있다.

 

 

참조 링크

 

인벤토리 설명서

http://docs.ansible.com/ansible/2.7/user_guide/intro_inventory.html

 

Working with Inventory — Ansible Documentation

Docs » User Guide » Working with Inventory 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 Ansi

docs.ansible.com

패턴 사용 ansible 설명서

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

 

Working with Patterns — Ansible Documentation

Docs » User Guide » Working with Patterns 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 Ansib

docs.ansible.com

 

'Ansible' 카테고리의 다른 글

6장. ansible에서 모듈 사용하기  (0) 2021.02.19
5장. ad-hoc 명령  (0) 2021.02.19
4장. ansible.cfg 파일 톺아보기  (0) 2021.02.06
2장. Ansible 설치하기  (0) 2021.02.06
1장. Ansible 소개  (0) 2021.02.06

+ Recent posts