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
플레이북 ansible 설명서
https://docs.ansible.com/ansible/2.7/user_guide/playbooks.html
YAML 구문 ansible 설명서
docs.ansible.com/ansible/2.7/reference_appendices/YAMLSyntax.html
'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 |