Ansible 플레이북은 반복 가능하고 재사용 가능하며 간단한 구성 관리부터 여러 호스트에 복잡한 애플리케이션을 배포하는데 매우 적합하가.
Ansible 플레이북의 YAML 파일은 항상 목록(List)으로 시작되며, 목록의 각 항목은 해시(Hash) 또는 사전(Dictionary)이라는 키/값 쌍을 가지고 있다.
선택적으로 YAML 파일의 시작은 ---로, 파일의 끝은 ...으로 끝을 나타내고 들여쓰기 수준은 동일해야 하며, 공백문자로만 사용
탭은 구문 오류를 발생함으로 일반적으로 공백 2칸을 사용
vi ~/.vimrc
syntax on
autocmd FileType yaml setlocal ai ts=2 sw=2 sts=2 et autoindent
set cursorcolumn
# 만들어졌나 확인
ls -a ~/
ymal은 기본적으로 사전(딕셔너리)과 목록(리스트) 형태로 되어있다.
💡 ymal 목록 예시
--- # A list of tasty fruits - Apple - Orange - Strawberry - Mango ... #축약 표현 [Apple, Orange,S trawberry. Mango]
💡 사전 예시
--- # An employee record martin: name: Martin D'vloper job: Developer skill: Elite ... # 축약 표현 {martin: {name: Martin D'vloper,job: Developer,skill: Elite}}
💡 사전+목록
--- # Employee records - martin: name: Martin D'vloper job: Developer skills: - python - perl - pascal - tabitha: name: Tabitha Bitumen job: Developer skills: - lisp - fortran - erlang ... #축약 표현 [ martin: {name: Martin D'vloper, job: Developer, skill: ['python', 'perl','pascal']}, tabitha: {name: Tabitha Bitumen, job: Developer, skill: 'lisp', 'fortran','erlang']}]
yes = true = True = TRUE
no = false = False = FALSE
콜론 YAML 구문 오류
foo: somebody said I should put a colon here: so I did 콜론 뒤에 콜론 기호
windows_drive: c: 콜론 뒤에 공백
해결방법: ' 작은 따옴표나, “ 큰 따옴표로 인용부호를 사용
foo: 'somebody said I should put a colon here: so I did'
windows_drive: 'c:'
따옴표의 차이는 큰 따옴표는 안에서 탈출 문자를 사용할 수 있음
foo: "a \t TAB and a \n NEWLINE"
※역슬러쉬(\)를 보여주고 싶을 때에는 역슬러쉬 2개를 사용해야 된다. 역슬러쉬 뒤에 문자는 일반문자로 바뀐다. (ex: \")
문자열
: yes, true, no, false는 부울 값으로 예약되었다. 일반 문자열로 인식 하려면 인용
non_boolean: "yes"
other_string: "False"
부동소수점
: 1.0은 부동 소수점 값으로, 이를 특정 버전을 지정해야 하는 경우 인용
version: "1.0"
플레이북은 하나 이상의 플레이를 가지고 있으며, 플레이는 작업을 실행하기 위한 특정 관리 노드 또는 그룹을 지정한다.
▪ 플레이에는 작업을 선언하며, 작업은 모듈을 호출
▪ 플레이북은 위에서부터 아래방향으로 순차적으로 진행된다.
YAML의 목록은 위에서 아래로 순서를 가짐
- 플레이북: 하나 이상의 플레이를 가짐
- 플레이: 하나 이상의 작업을 가짐
- 작업: 하나의 모듈과 모듈의 옵션/아규먼트를 지정
① Ansible은 기본적으로 호스트의 패턴과 일치하는 모든 시스템에 대해 각 작업을 순서대로 실행
② 각 작업은 지정한 모듈 옵션을 사용하여 모듈을 실행
③ 하나의 작업이 호스트 패턴과 일치하는 모든 시스템에서 완료되면 다음 작업으로 이동
④ 특정 호스트에서 작업이 실패하면 해당 호스트는 작업이 더 남아 있더라도 제외
수학이나 IT에서 연산의 한 성질을 나타내며, 연산을 여러 번 적용하더라도 결과가 달라지지 않는 성질을 의미
Ansible의 대부분 모듈은 원하는 최종 상태가 달성 되었는지 확인하고, 이미 원하는 최종 상태를 달성했다면 작업을 실행하지 않게 되는데 몇 번이고 작업을 반복 실행 하더라도 최종 상태가 변경되지 않기 때문에 멱등성을 가진다고 함.
모듈의 옵션에 따라 이러한 멱등성을 제공하지 않는 것도 있음.
: 멱등성을 제공하지 않는 모듈
플레이북 실행
ansible-playbook playbook.yml
구문 체크
ansible-playbook playbook.yml --syntax-check
체크 모드
ansible-playbook playbook.yml --check
: 체크 모드는 실제 실행이 아닌 모의 테스트(dry run)로 시뮬레이션을 진행
: 체크 모드는 관리 노드를 변경하지 않고 실행하고 모듈은 변경 사항을 보고함
: 검사모드를 지원하지 않는 모듈은 아무것도 보고하지 않으며 실행하지도 않음
차이점 모드
ansible-playbook playbook.yml --check --diff --limit foo.example.com
: 파일을 조작하는 모듈에서 변경 전 내용과 변경 후 내용을 출력
작업 목록 확인
ansible-playbook playbook.yml --list-tasks
: --list-tasks 옵션을 사용하면 플레이북의 플레이 목록 및 작업 목록을 확인할 수 있음
호스트 목록 확인
ansible-playbook playbook.yml --list-hosts
: --list-hosts 옵션을 사용하면 플레이북의 플레이 목록과 해당 플레이에 매칭되는 호스트 또는 그룹의 목록을 확인할 수 있음
Ansible Lint
ansible-lint playbook.yml
: 린트는 코드의 오류, 버그, 스타일 등을 점검하는 도구로 Ansible에
도 린트 도구를 사용할 수 있음
vi test1.yml
---
- name: play1 test => 플레이 이름
hosts: AA => 플레이 대상
tasks: => 플레이 작업
- name: 1st task
debug:
msg: "1st task"
...
# 구문 체크
ansible-playbook playbook.yml --syntax-check
# 체크 모드
ansible-playbook playbook.yml --check
vi test2.yml
# 해당파일 불러오기
:r test1.yml
---
- name: play1 test
hosts: AA
gather_facts: no #0 false False FALSE 중 아무거나 가능, 추가
tasks:
- name: 1st task
debug:
msg: "1st task"
...
작업에 대해서 여러 모듈을 실행한다.
mkdir files
echo "playbook test" > /home/vagrant/ansitest/files/index.html
============================
web-service.yml
---
- name: Install and start Apache HTTPD
hosts: BB
tasks:
- name: httpd package is present
apt:
name: apache2
update_cache: yes
state: present
- name: correct index.html is present
copy:
src: /home/vagrant/ansitest/files/index.html
dest: /var/www/html/index.html
- name: httpd is started
service:
name: apache2
state: started
enabled: true
...
# 구문 체크
ansible-playbook --syntax-check web-service.yml
# 설치, 체크로 진행하면 안됨!
ansible-playbook web-service.yml
# 설치 확인, 미리 만들어두었던 'playbook' 화면이 나오는걸 볼 수 있음
vagrant@ansi-master1:~/ansitest
$ curl ansi-node2
playbook test
vagrant@ansi-master1:~/ansitest
$ curl ansi-node3
playbook test
플레이를 설정함으로써 다중 작업이 가능하다.
vi test4.yml
- name: 1st play
hosts: ansi-node1
gather_facts: no
tasks:
- name: 1st play - 1st task
command: id
register: result1
- name: 1st play - 2nd task
debug:
msg: "{{ result1.stdout }}"
- name: 2nd play
hosts: ansi-node2
gather_facts: no
tasks:
- name: 2nd play - 1st task
command: id
register: result2
- name: 2nd play - 2nd task
debug:
msg: "{{ result2.stdout }}"
- name: 2nd play
hosts: ansi-node2
gather_facts: no
become: no
tasks:
vi intranet.yml
---
- name: Enable intranet service
hosts: ansi-node1
tasks:
- name: latest version of httpd and firewalld installed
apt:
pkg:
- apache2
- firewalld
update_cache: yes
state: latest
- name: test html page is installed
copy:
content: "Welcome to the groupB intranet!\n"
dest: /var/www/html/index.html
- name: firewalld enabled and running
service:
name: firewalld
enabled: true
state: started
- name: firewalld permits http service
firewalld:
service: http
permanent: true
state: enabled
immediate: yes
- name: httpd enabled and running
service:
name: apache2
enabled: true
state: started
- name: Test intranet web server
hosts: ansi-node1
become: no
tasks:
- name: connect to intranet web server
uri:
url: http://192.168.56.21
return_content: yes
status_code: 200
...
# 구문 확인
ansible-playbook --syntax-check intranet.yml
# 실행, -v: 자서한 결과 출력
ansible-playbook -v intranet.yml
# 결과 확인
vagrant@ansi-master1:~/ansitest
$ ssh ansi-node1 cat /var/www/html/index.html
Welcome to the groupB intranet!
Ansible을 이용하여 시스템의 구성 관리를 자동화 할 수 있지만, 모든 시스템이 항상 같은 구성을 가지지 않고 경우에 따라 다른 구성을 가져야 할 수 있다. Ansible은 변수를 사용해 시스템 간 차이를 처리한다.
• 목록 변수 참조시 key값을 이용해서 지정
• 키값 지정은 대괄호 표기법 ([]) 나 점 표기법(.) 를 이용
• 점 표기법 사용시 모듈에 따라 일부 키가 Python 사전의 속성 및 메서드와 충돌할 수 있음
- name: dict vars
hosts: AA
gather_facts: no
vars:
info:
name: Lee
age: 47
area: seoul
tasks:
- debug:
msg: "{{ info }}"
- debug:
msg: "{{ info.name }}"
- debug:
msg: "{{ info['age'] }}, {{ info.area }}"
vi list_var_dir.yml
- name: list & dict vars
hosts: ansi-node1
gather_facts: no
vars:
infos:
- name: Lee
age: 47
area: seoul
- name: Kim
age: 30
area: busan
tasks:
- debug:
msg: "all info {{ infos }}"
- debug:
msg: "Lee info {{ infos[0] }}"
- debug:
msg: "info[0] name is {{ infos[0].name }}"
- debug:
msg: "Kim area is {{ infos[1]['area'] }}"
등록 변수는 모듈의 반환 값을 저장하는 변수이다.
vi register_vars1
- name: resgister vasrs
hosts: ansi-node1
gather_facts: no
tasks:
- name: Input register vars
command: "ls -la /home/vagrant"
register: result
- name: Output register vars
debug:
msg: "{{ result }}"
# lines값만 출력하도록 설정
- name: register vars stdout_lines
debug:
msg: "{{ result.stdout_lines}}"
# 9번쨰 항목만 출력하도록 설정
- name: stdout_lines[9] print
debug:
msg: "{{ result.stdout_lines[9] }}"
#실행
vagrant@ansi-master1:~/ansitest
$ ansible-playbook resgister_vars1.yml
PLAY [resgister vasrs] *********************************************************
TASK [Input register vars] *****************************************************
changed: [ansi-node1]
TASK [Output register vars] ****************************************************
ok: [ansi-node1] => {
"msg": {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": true,
"cmd": [
"ls",
"-la",
"/home/vagrant"
],
"delta": "0:00:00.002319",
"end": "2023-01-31 07:18:15.202598",
"failed": false,
"rc": 0, # 정상적인 결과일 경우 0
"start": "2023-01-31 07:18:15.200279",
"stderr": "",
"stderr_lines": [],
"stdout": "total 36\ndrwxr-xr-x 6 vagrant vagrant 4096 Jan 31 05:17 .\ndrwxr-xr-x 4 root root 4096 Jan 30 02:27 ..\ndrwx------ 3 vagrant vagrant 4096 Jan 31 05:17 .ansible\n-rw-r--r-- 1 vagrant vagrant 220 Jan 24 16:59 .bash_logout\n-rw-r--r-- 1 vagrant vagrant 3771 Jan 24 16:59 .bashrc\ndrwx------ 2 vagrant vagrant 4096 Jan 30 02:27 .cache\ndrwx------ 3 vagrant vagrant 4096 Jan 30 02:27 .gnupg\n-rw-r--r-- 1 vagrant vagrant 807 Jan 24 16:59 .profile\ndrwx------ 2 vagrant vagrant 4096 Jan 30 02:27 .ssh",
"stdout_lines": [
"total 36",
"drwxr-xr-x 6 vagrant vagrant 4096 Jan 31 05:17 .",
"drwxr-xr-x 4 root root 4096 Jan 30 02:27 ..",
"drwx------ 3 vagrant vagrant 4096 Jan 31 05:17 .ansible",
"-rw-r--r-- 1 vagrant vagrant 220 Jan 24 16:59 .bash_logout",
"-rw-r--r-- 1 vagrant vagrant 3771 Jan 24 16:59 .bashrc",
"drwx------ 2 vagrant vagrant 4096 Jan 30 02:27 .cache",
"drwx------ 3 vagrant vagrant 4096 Jan 30 02:27 .gnupg",
"-rw-r--r-- 1 vagrant vagrant 807 Jan 24 16:59 .profile",
"drwx------ 2 vagrant vagrant 4096 Jan 30 02:27 .ssh"
]
}
}
# stdout: 한줄로 결과 출력, stdout_lines: 보기 쉽게 결과 출력
# lines값만 출력하도록 설정
TASK [register vars stdout_lines] **********************************************
ok: [ansi-node1] => {
"msg": [
"total 36",
"drwxr-xr-x 6 vagrant vagrant 4096 Jan 31 05:17 .",
"drwxr-xr-x 4 root root 4096 Jan 30 02:27 ..",
"drwx------ 3 vagrant vagrant 4096 Jan 31 05:17 .ansible",
"-rw-r--r-- 1 vagrant vagrant 220 Jan 24 16:59 .bash_logout",
"-rw-r--r-- 1 vagrant vagrant 3771 Jan 24 16:59 .bashrc",
"drwx------ 2 vagrant vagrant 4096 Jan 30 02:27 .cache",
"drwx------ 3 vagrant vagrant 4096 Jan 30 02:27 .gnupg",
"-rw-r--r-- 1 vagrant vagrant 807 Jan 24 16:59 .profile",
"drwx------ 2 vagrant vagrant 4096 Jan 30 02:27 .ssh"
]
}
TASK [stdout_lines[9] print] ***************************************************
ok: [ansi-node1] => {
"msg": "drwx------ 2 vagrant vagrant 4096 Jan 30 02:27 .ssh"
}
PLAY RECAP *********************************************************************
ansi-node1 : ok=4 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
vim play_vars_example.yml
- name: Deploy and start Apache HTTPD service
hosts: AA
vars:
#options = { pkg : [ apache2 , firewalld ] , states : [ present , latest , started , enabled ] , bools : [ yes , no ] , rule : http } 해당 내용대로 변수 생성할 것
options:
pkg:
- apache2
- firewalld
states:
- present
- latest
- started
- enabled
bools:
- yes
- no
rule: http
#rule:
# - http rule: [http]
tasks:
- name: Required packages are installed and up to date
apt:
name:
- "{{ options.pkg[0] }}" # web package
- "{{ options.pkg[1] }}" # 방화벽 package
state: "{{ options.states[1] }}" # 마지막 버전 지정
- name: The web service is started and enabled
service:
name: "{{ options.pkg[0] }}"
enabled: "{{ options.bools[0] }}" # 활성화 여부 True
state: "{{ options.states[2] }}" # service 시작
- name: The firewalld service is started and enabled
service:
name: "{{ options.pkg[1] }}"
enabled: "{{ options.bools[0] }}" # 활성화 여부 True
state: "{{ options.states[2] }}" # service 시작
- name: Web content is in place # copy 모듈을 이용해서 파일 생성
copy:
content: "Example web content" # content 키워드로 파일 내용 작성
dest: /var/www/html/index.html # content의 내용으로 해당 경로에 파일 생성
- name: "The {{ options.rule }} port for web is open" # name 키워드에도 변수 사용 가능
firewalld:
service: "{{ options.rule }}" # 포트를 열고자 하는 서비스명
permanent: "{{ options.bools[0] }}" # 영구 설정 여부
immediate: "{{ options.bools[0] }}" # 런타임 설정
state: "{{ options.states[3] }}" # 활성화 상태
▪ 인벤토리
▪ 플레이북
▪ 재사용 가능 파일(외부 참조 파일)
▪ 역할
▪ 명령의 -e옵션
atlanta:
host1:
http_port: 80
maxRequestsPerChild: 808
host2:
http_port: 303
maxRequestsPerChild: 90
atlanta:
host:
host1:
host2:
vars:
ntp_server: ntp.atlanta.example.com
proxy: proxy.atlanta.example.com
vars.yml
---
- name: vars test
hosts: all
gather_facts: no
vars:
- username: play
tasks:
- debug:
msg: "{{ username }}"
...
ansible-playbook vars.yml
# 변수파일 생성
vi testvar.yml
username: varsfiles
========================================
vi vars.yml
---
- name: vars test
hosts: AA
gather_facts: no
vars_files:
- testvar.yml
tasks:
- debug:
msg: "{{ username }}"
...
ansible-playbook vars.yml
vi mk.yml
---
- name: make file and directory
hosts: ansi-node1
gather_facts: no
tasks:
- name: make directory
file:
path: "/tmp/testdir1/testdir2"
state: directory
recurse: yes
owner: bin
group: bin
- name: make file
file:
path: "/tmp/testdir1/testdir2/test1.txt"
state: touch
owner: vagrant
group: vagrant
- name: input test.txt data
lineinfile:
dest: "/tmp/testdir1/testdir2/test1.txt"
line: "make file and dir1"
- name: create file
copy:
content: "mask file and dir2"
dest: "/tmp/testdir1/testdir2/test2.txt"
ansible-playbook mk.yml
#실행 결과 확인
ssh ansi-node1 ls -l /tmp/testdir1/testdir2
ssh ansi-node1 cat /tmp/testdir1/testdir2/test1.txt
ssh ansi-node1 cat /tmp/testdir1/testdir2/test2.txt