오픈소스이며 IT 자동화 도구
코드 기반으로 작성하여 여러 환경에 동일하게 적용하도록 돕는 역할
리눅스, MacOS, BSC 계열 유닉스, WSL을 지원하는 원도우에 파이썬과 앤서블 코어만 설치하면 어디에서나 플레이북(YAML 형식의 작업들은 순서대로 작성해 놓은 파일)을 작성하고 이를 실행할 수 있습니다.
앤서블은 앤서블 코어가 설치되고 플레이북을 작성하여 실행할 수 있는 제어 노드 (Control Node)와 플레이북이 실행되어 애플리케이션 설치나 클라우드 시스템의 가상 서버 생성과 같은 작업이 수행되는 관리 노드 (Managed Node)로 구성되며, 앤서블은 제어노드에만 설치됩니다.
간단하게 사용자가 정의한 플레이북과 관리할 대상의 노드들을 정의해놓은 파일인 인벤토리파일에 의해 SSH 프로토콜을 사용하여 다양한 환경의 대상 노드에 자동화를 수행시킬 수 있습니다.
특징
에이전트가 필요없다는 장점 : SSH 로 접속하여 대상서버들에게 접속하기 때문입니다!
멱등성을 보장한다는 장점 : 시스템을 원하는 상태로 표현하여 유지하도록 설계되어 있어, 동일한 운영 작업을 여러 번 실행해도 같은 결과를 냅니다!
Easy : YAML 문법을 사용하기 때문에 에디터만 있으면 걱정 없으며, 시스템 관리 모듈 부터 다양한 환경의 퍼블릭 클라우드 관련 모듈 및 컬렉션 까지 제공하기 때문에 쉽게 예제를 많이 찾아볼 수 있습니다!
커뮤니티 앤서블 아키텍처
제어 노드 Control Node
관리 노드 Managed Node
인벤토리 Inventory
$ cat inventory
192.168.10.101
[WebServer]
web1.example.com
web2.example.com
[DBServer]
db1.example.com
db2.example.com
모듈 Modules
앤서블은 관리 노드의 작업을 수행할 때 SSH를 통해 연결한 후 앤서블 모듈이라는 스크립트를 PUSH 하여 작동합니다.
대부분의 모듈은 원하는 시스템 상태를 설명하는 매개 변수를 허용하며, 모듈 실행이 완료되면 제거되는 프로세스입니다.
플러그인 Plugins
모듈이 대상 시스템에서 별도의 프로세스로 실행되는 동안 플러그인은 제어 노드에서 실행됩니다.
앤서블의 핵심 기능인 데이터변환, 로그 출력, 인벤토리 연결 등에 대한 옵션 및 확장 기능을 제공합니다.
플레이북 Playbook
관리 노드에서 수행할 작업들을 YAML 문법을 이용해 순서대로 작업해 놓은 파일입니다.
작성된 플레이북을 활용하여 관리 노드에 SSH 로 접근해 작업을 수행합니다.
사용자가 직접 작성해야하는 가장 중요한 파일이라고 할 수 있습니다.
# Read more about SSH config files: https://linux.die.net/man/5/ssh_config
Host ansible-server
HostName {Your ansible-server Pub IP}
User root
작업디렉토리 생성
mkdir my-ansible
pwd
/root/my-ansible
# 파이썬 버전 확인
python3 --version
Python 3.10.12
# 설치
apt install software-properties-common -y
add-apt-repository --yes --update ppa:ansible/ansible
apt install ansible -y
# 설치확인
ansible --version
ansible [core 2.15.8]
config file = /etc/ansible/ansible.cfg
configured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /usr/lib/python3/dist-packages/ansible
ansible collection location = /root/.ansible/collections:/usr/share/ansible/collections
executable location = /usr/bin/ansible
python version = 3.10.12 (main, Nov 20 2023, 15:14:05) [GCC 11.4.0] (/usr/bin/python3)
jinja version = 3.0.3
libyaml = True
앤서블은 로컬 사용자에게 개인 SSH 키가 있거나 관리 호스트에서 원격 사용자임을 인증 가능한 키가 구성될 경우 자동으로 로그인이 됩니다.
먼저, ssh-keygen 명령어를 이용하여 SSH 키를 생성합니다.
ssh-keygen -t rsa -N "" -f /root/.ssh/id_rsa
Generating public/private rsa key pair.
...
# 공개 키를 관리 노드에 복사
for i in {1..3}; do ssh-copy-id root@tnode$i; done
# 복사 확인
for i in {1..3}; do echo ">> tnode$i <<"; ssh tnode$i cat ~/.ssh/authorized_keys; echo; done
# ssh 접속 테스트 / 관리노드에 하나씩 접근해봅니다.
ssh tnode1
ssh tnode2
ssh tnode3
web1.example.com
web2.example.com
db1.example.com
db2.example.com
192.0.2.42
# inventory 파일 생성
cat <<EOT > inventory
10.10.1.11
10.10.1.12
10.10.1.13
EOT
root@server:~/my-ansible# ls
Easy-Ansible inventory
## 인벤토리 검증
root@server:~/my-ansible# ansible-inventory -i ./inventory --list | jq
{
"_meta": {
"hostvars": {}
},
"all": {
"children": [
"ungrouped"
]
},
"ungrouped": {
"hosts": [
"10.10.1.11",
"10.10.1.12",
"10.10.1.13"
]
}
}
cat /etc/hosts
127.0.0.1 localhost
# The following lines are desirable for IPv6 capable hosts
::1 ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
ff02::3 ip6-allhosts
10.10.1.10 server
10.10.1.11 tnode1
10.10.1.12 tnode2
10.10.1.13 tnode3
root@server:~/my-ansible# cat <<EOT > inventory
tnode1
tnode2
tnode3
EOT
## 인벤토리 검증
root@server:~/my-ansible# ansible-inventory -i ./inventory --list | jq
{
"_meta": {
"hostvars": {}
},
"all": {
"children": [
"ungrouped"
]
},
"ungrouped": {
"hosts": [
"tnode1",
"tnode2",
"tnode3"
]
}
}
그룹별 호스트 설정
그룹별로 호스트를 설정하여 사용하면 앤서블 플레이북 실행 시 그룹별로 작업을 처리할 수 있어서 좀 더 효과적입니다.
이 경우 다음과 같이 그룹명을 대괄호 ([]) 내에 작성하고 해당 그룹에 속하는 호스트명이나 IP를 한 줄에 하나씩 나열합니다.
아래 인벤토리는 두 개의 호스트 그룹인 webservers와 db-servers를 정의한 것입니다.
[webservers]
web1.example.com
web2.example.com
[db-servers]
db01.example.com
db02.example.com
또는 호스트는 여러 그룹에 있을 수 있으며, 실제 호스트를 여러 그룹으로 구성하면 호스트의 역할, 실제 위치, 프로덕션 여부 등에 따라 다양한 방식으로 구성할 수 있습니다.
그러면 특성, 용도 또는 위치에 따라 특정 호스트 집합에 앤서블 플레이북을 쉽게 적용할 수 있습니다.
[webservers]
web1.example.com
web2.example.com
192.0.2.42
[db-servers]
db01.example.com
db02.example.com
[east-datacenter]
web1.example.com
db01.example.com
[west-datacenter]
web2.example.com
db02.example.com
[production]
web1.example.com
web2.example.com
db01.example.com
db02.example.com
[development]
192.168.0.42
중첩 그룹 정의
앤서블 인벤토리는 호스트 그룹에 기존에 정의한 호스트 그룹을 포함할 수도 있습니다.
이 경우 호스트 그룹 이름 생성 시 :children 이라는 접미사를 추가하면 됩니다.
다음은 webservers 및 db-servers 그룹의 모든 호스트를 포함하는 datacenter 그룹을 생성하는 예시입니다.
[webservers]
web1.example.com
web2.example.com
[db-servers]
db01.example.com
db02.example.com
[datacenter:children] ## 호스트 그룹인 webservers 와 dbservers 를 포함하고 있습니다.
webservers
dbservers
범위를 사용한 호스트 사양 간소화
인벤토리의 호스트 이름 또는 IP 주소를 설정할 때 범위를 지정하여 호스트 인벤토리를 간소화 할 수 있습니다.
숫자 또는 영문자로 범위를 지정할 수 있으며, 대괄호 사이에 시작 구문과 종료 구문을 포함합니다.
[webservers]
web[1:2].example.com
[db-servers]
db[01:02].example.com
# IP 범위 설정 : 192.168.4.0 ~ 192.168.4.255 사이의 IP 범위를 표현
[defaults]
192.168.4.[0:255]
# 호스트명 범위 설정 : com01.example.com ~ com20.example.com 의 범위를 표현
[compute]
com[01:20].example.com
# DNS 범위 설정 : a.dns.example.com , b.dns.example.com , c.dns.example.com 을 의미함
[dns]
[a:c].dns.example.com
# IPv6 범위 설정 : 2001:db08::a ~ 2001:db08::f 사이의 IPv6 범위를 표현
[ipv6]
2001:db8::[a:f]
cat <<EOT > inventory
[web]
tnode1
tnode2
[db]
tnode3
[all:children]
web
db
EOT
## 검증
ansible-inventory -i ./inventory --list | jq
{
"_meta": {
"hostvars": {}
},
"all": {
"children": [
"ungrouped",
"web",
"db"
]
},
"db": {
"hosts": [
"tnode3"
]
},
"web": {
"hosts": [
"tnode1",
"tnode2"
]
}
}
cat <<EOT > ansible.cfg
[defaults]
inventory = ./inventory
EOT
root@server:~/my-ansible# ls
Easy-Ansible ansible.cfg inventory
root@server:~/my-ansible# ansible-inventory --list | jq
{
"_meta": {
"hostvars": {}
},
"all": {
"children": [
"ungrouped",
"web",
"db"
]
},
"db": {
"hosts": [
"tnode3"
]
},
"web": {
"hosts": [
"tnode1",
"tnode2"
]
}
}
# cat ansible.cfg
[defaults]
inventory = ./inventory
remote_user = root
ask_pass = false
[privilege_escalation]
become = true
become_method = sudo
become_user = root
become_ask_pass = false
[defaults] 섹션 : 앤서블 작업을 위한 기본값 설정
매개 변수 | 설명 |
---|---|
inventory | 인벤토리 파일의 경로를 지정함. |
remote_user | 앤서블이 관리 호스트에 연결할 때 사용하는 사용자 이름을 지정함. 이때, 사용자 이름을 지정하지 않으면 현재 사용자 이름으로 지정됨. |
ask_pass | SSH 암호를 묻는 메시지 표시 여부를 지정함. SSH 공개 키 인증을 사용하는 경우 기본값은 false임. |
[privilege_escalation] 섹션 : 보안/감사로 인해 원격 호스트에 권한 없는 사용자 연결 후 관리 액세스 권한을 에스컬레이션하여 루트 사용자로 가져올 때 사용합니다.
매개 변수 | 설명 |
---|---|
become | 기본적으로 권한 에스컬레이션을 활성화할 때 사용하며, 연결 후 관리 호스트에서 자동으로 사용자를 전환할지 여부를 지정함. |
일반적으로 root로 전환되며, 플레이북에서도 지정할 수 있음. | |
become_method | 권한을 에스컬레이션하는 사용자 전환 방식을 의미함. 일반적으로 기본값은 sudo를 사용하며, su는 옵션으로 설정할 수 있음. |
become_user | 관리 호스트에서 전환할 사용자를 지정함. 일반적으로 기본값은 root임. |
become_ask_pass | become_method 매개 변수에 대한 암호를 묻는 메시지 표시 여부를 지정함. 기본값은 false임. |
권한을 에스컬레이션하기 위해 사용자가 암호를 입력해야 하는 경우, 구성 파일에 become_ask_pass = true 매개 변수를 설정하면 됨. |
ansible -m ping web
# ansible -m ping web
tnode2 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false,
"ping": "pong"
}
tnode1 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false,
"ping": "pong"
}
ansible -m ping --ask-pass web
SSH password:
tnode2 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false,
"ping": "pong"
}
tnode1 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false,
"ping": "pong"
}
ansible -m ping web -u ubuntu
tnode1 | UNREACHABLE! => {
"changed": false,
"msg": "Failed to connect to the host via ssh: ubuntu@tnode1: Permission denied (publickey,password).",
"unreachable": true
}
tnode2 | UNREACHABLE! => {
"changed": false,
"msg": "Failed to connect to the host via ssh: ubuntu@tnode2: Permission denied (publickey,password).",
"unreachable": true
}
---
ansible -m ping web -u ubuntu --ask-pass
SSH password:
tnode1 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false,
"ping": "pong"
}
tnode2 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false,
"ping": "pong"
}
# ansible -m shell -a "free -h" web
tnode2 | CHANGED | rc=0 >>
total used free shared buff/cache available
Mem: 3.8Gi 195Mi 2.6Gi 0.0Ki 994Mi 3.3Gi
Swap: 0B 0B 0B
tnode1 | CHANGED | rc=0 >>
total used free shared buff/cache available
Mem: 3.8Gi 200Mi 2.6Gi 0.0Ki 995Mi 3.3Gi
Swap: 0B 0B 0B
플레이북은 YAML 포맷으로 작성된 텍스트 파일이며, 일반적으로 .yml 이라는 확장자를 사용하여 저장됩니다.
플레이북은 대상 호스트나 호스트 집합에 수행할 작업을 정의하고 이를 실행합니다. 이때 특정 작업 단위를 수행하기 위해 모듈을 적용합니다.
debug 모듈을 사용하여 문자열을 출력해보겠습니다.
first-playbook.yml
---
- hosts: all
tasks:
- name: Print message
debug:
msg: Hello CloudNet@ Ansible Study
---
first-playbook-error.yml
---
- hosts: all
tasks:
- name: Print message
debug:
msg: Hello CloudNet@ Ansible Study
ansible-playbook --syntax-check first-playbook.yml
playbook: first-playbook.yml
ansible-playbook --syntax-check first-playbook-with-error.yml
ERROR! the playbook: first-playbook-with-error.yml could not be found
ansible-playbook first-playbook.yml
# (신규터미널) 모니터링 : 서비스 재시작 실행 여부 확인
ssh tnode1 tail -f /var/log/syslog
# 실행 전 check 옵션으로 플레이북의 실행 상태를 미리 점검 가능 : 출력 중 changed=1 확인
ansible-playbook --check restart-service.yml
# 플레이북 실제 실행
ansible-playbook restart-service.yml
# 작성한 yml 파일들 삭제
rm -r *.yml
앤서블은 변수를 사용하여 사용자, 설치하고자 하는 패키지, 재시작할 서비스, 생성 또는 삭제할 파일명 등 시스템 작업 시 사용되는 다양한 값을 저장할 수 있습니다.
이런 변수를 활용하면 얼마든지 플레이북을 재사용할 수 있으며, 사용자로부터 받은 값도 쉽게 적용할 수 있습니다.
앤서블에서 사용되는 변수는 그룹 변수, 호스트 변수, 플레이 변수, 추가 변수가 있으며 플레이 결과를 저장하기 위한 작업 변수도 있습니다.
그룹 변수 : 인벤토리에 정의된 호스트 그룹에 적용하는 변수
:vars
라는 문자열을 추가해 변수를 선언한다는 것을 알려줍니다. 예제를 통해 알아보겠습니다.[ansible-server]의 my-ansible 디렉터리에 있는 inventory 파일 하단에 [all:vars] 섹션을 선언하고 해당 섹션 아래에 user=ansible이라는 변수와 값을 선언합니다.
cat inventory
[web]
tnode1
tnode2
[db]
tnode3
[all:children]
web
db
[all:vars]
user=ansible
이번에는 create-user.yml 이라는 파일을 생성합니다. 해당 파일은 사용자를 생성하는 태스크를 포함합니다.
그리고 인벤토리에서 선언한 user라는 변수를 겹중괄호 사이에 넣어주면, 해당 변수를 플레이북에서 사용할 수 있습니다.
cat create-user.yml
---
- hosts: all
tasks:
- name: Create User {{ user }}
ansible.builtin.user:
name: "{{ user }}"
state: present
# (터미널2) 모니터링
watch -d "ssh tnode1 tail -n 3 /etc/passwd"
# 실행
ansible-playbook create-user.yml
...
TASK [Create User ansible] *********
...
# 한번 더 실행 : 멱등성 확인
ansible-playbook create-user.yml
# 대상 호스트 ansible 사용자 생성 확인
for i in {1..3}; do echo ">> tnode$i <<"; ssh tnode$i tail -n 3 /etc/passwd; echo; done
ssh tnode1 userdel -r ansible
ssh tnode1 tail -n 2 /etc/passwd
ansible-playbook create-user.yml
cat inventory
[web]
tnode1
tnode2
[db]
tnode3 user=ansible1
[all:children]
web
db
[all:vars]
user=ansible
---
- hosts: db ## hosts를 all -> db
tasks:
- name: Create User {{ user }}
ansible.builtin.user:
name: "{{ user }}"
state: present
# 실행
ansible-playbook create-user1.yml
# 확인
for i in {1..3}; do echo ">> tnode$i <<"; ssh tnode$i tail -n 3 /etc/passwd; echo; done
cat create-user2.yml
---
- hosts: all
vars:
user: ansible2
tasks:
- name: Create User {{ user }}
ansible.builtin.user:
name: "{{ user }}"
state: present
# 터미널 모니터링
watch -d "ssh tnode3 tail -n 3 /etc/passwd"
# 실행
ansible-playbook create-user2.yml
# 3개의 노드 확인
for i in {1..3}; do echo ">> tnode$i <<"; ssh tnode$i tail -n 3 /etc/passwd; echo; done
mkdir vars
echo "user: ansible3" > vars/users.yml
---
- hosts: all
vars_files:
- vars/users.yml
tasks:
- name: Create User {{ user }}
ansible.builtin.user:
name: "{{ user }}"
state: present
# 실행
ansible-playbook create-user3.yml
# 확인
for i in {1..3}; do echo ">> tnode$i <<"; ssh tnode$i tail -n 4 /etc/passwd; echo; done
# 실행
ansible-playbook -e user=ansible4 create-user3.yml
# 확인
for i in {1..3}; do echo ">> tnode$i <<"; ssh tnode$i tail -n 5 /etc/passwd; echo; done
변수 우선 순위 : 추가변수(실행 시 파라미터) > 플레이 변수 > 호스트 변수 > 그룹 변수
작업 변수 : 플레이북의 태스트 수행 결과를 저장. 특정 작업 수행 후 그 결과를 후속 작업에서 사용할 때 주로 사용됩니다.
파일 복사 후 수정하여 Create User 태스크에 register: result 라는 문구를 추가하겠습니다.
create-user4.yml
---
- hosts: db
tasks:
- name: Create User {{ user }}
ansible.builtin.user:
name: "{{ user }}"
state: present
register: result
- ansible.builtin.debug:
var: result
# 터미널 열어 확인
watch -d "ssh tnode3 tail -n 3 /etc/passwd"
# 실행
ansible-playbook -e user=ansible5 create-user4.yml
---
- hosts: db
tasks:
- name: Create User {{ user }}
ansible.builtin.user:
name: "{{ user }}"
state: absent
remove: yes
register: result
- ansible.builtin.debug:
var: result
---
# 터미널 확인
watch -d "ssh tnode3 tail -n 3 /etc/passwd"
# 플레이북 실행
ansible-playbook -e user=ansible5 remove-user.yml
---
- hosts: all
tasks:
- name: watch uptime
ansible.builtin.shell: /usr/bin/uptime
register: result
- ansible.builtin.debug:
var: result
---
# 실행
ansible-playbook uptime.yml
PLAY [all] *********************************************************************
TASK [Gathering Facts] *********************************************************
ok: [tnode2]
ok: [tnode3]
ok: [tnode1]
TASK [watch uptime] ************************************************************
changed: [tnode1]
changed: [tnode3]
changed: [tnode2]
TASK [ansible.builtin.debug] ***************************************************
ok: [tnode1] => {
"result": {
"changed": true,
"cmd": "/usr/bin/uptime",
"delta": "0:00:00.006178",
"end": "2024-01-10 23:24:10.556688",
"failed": false,
"msg": "",
"rc": 0,
"start": "2024-01-10 23:24:10.550510",
"stderr": "",
"stderr_lines": [],
"stdout": " 23:24:10 up 2 days, 2:39, 1 user, load average: 0.08, 0.02, 0.01",
"stdout_lines": [
" 23:24:10 up 2 days, 2:39, 1 user, load average: 0.08, 0.02, 0.01"
]
}
}
ok: [tnode2] => {
"result": {
"changed": true,
"cmd": "/usr/bin/uptime",
"delta": "0:00:00.007231",
"end": "2024-01-10 23:24:10.580110",
"failed": false,
"msg": "",
"rc": 0,
"start": "2024-01-10 23:24:10.572879",
"stderr": "",
"stderr_lines": [],
"stdout": " 23:24:10 up 2 days, 2:39, 1 user, load average: 0.00, 0.00, 0.00",
"stdout_lines": [
" 23:24:10 up 2 days, 2:39, 1 user, load average: 0.00, 0.00, 0.00"
]
}
}
ok: [tnode3] => {
"result": {
"changed": true,
"cmd": "/usr/bin/uptime",
"delta": "0:00:00.006615",
"end": "2024-01-10 23:24:10.586592",
"failed": false,
"msg": "",
"rc": 0,
"start": "2024-01-10 23:24:10.579977",
"stderr": "",
"stderr_lines": [],
"stdout": " 23:24:10 up 2 days, 2:39, 1 user, load average: 0.00, 0.04, 0.03",
"stdout_lines": [
" 23:24:10 up 2 days, 2:39, 1 user, load average: 0.00, 0.04, 0.03"
]
}
}
PLAY RECAP *********************************************************************
tnode1 : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
tnode2 : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
tnode3 : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=
ansible-vault create mysecret.yml
New Vault password:
Confirm New Vault password:
vi 편집기로 편집
su - ubuntu -c 'cat /root/my-ansible/mysecret.yml'
cat: /root/my-ansible/mysecret.yml: Permission denied
cat mysecret.yml
$ANSIBLE_VAULT;1.1;AES256
62346266653762633361616632356166636161343138643065343762666639636233353230346533
3532366165613437636434653761633764346530326630620a323465366362316437313835643737
39346237373461393537356537346233623433303835396634386338373639626133353139623639
3564653732353264630a343566656538393766313039373338663363373630643130306332626332
39343665353037376134303337353662363235363739663031666564656533623932
ansible-vault view mysecret.yml
를 실행시켜주면 됩니다.ansible-vault view mysecret.yml
Vault password:
user: ansible
password: dmlwn
ansible-vault create --vault-pass-file ./vault-pass mysecret1.yml
cat mysecret1.yml
$ANSIBLE_VAULT;1.1;AES256
37633337613965613938366631396231613434316232383666663439363533623239623964343861
3934616430393933643761313461623363313962353062390a373564626437653261666634396466
64303930633534373833363132636636613663323531643032393035353033343331666265353565
3538363437643364300a663461363232373965613136663937633261383665633665373936636637
6432
ansible-vault view --vault-pass-file ./vault-pass mysecret1.yml
Hello!
ansible-vault encrypt create-user.yml
New Vault password:
Confirm New Vault password:
Encryption successful
root@server:~/my-ansible# cat create-user.yml
$ANSIBLE_VAULT;1.1;AES256
63383731336464623964316261396565323234343864356134323432633236343832353730383631
3538303065623563353936653234373734393533663935360a353632316130623065646233366566
33383633323136323130633230616539363034643639363735363539313330313532393564656262
3466383366623462350a333331396663313639623963333237393164653537326634333066333665
65623637653832383931323834323639613236356134303933356530376135653232373938646634
66646433333362366631343462343434626137333337326133663330356265366465333066386665
35653432313430666337393161623131376438353035303236323766336562306631336335326536
31323532343435326133353661343133373866623737313331363133336135633238333566316366
38646530663339653030356635356538616139666463303438623766646561333635636362303238
61613337663962613537303735346337343036396462643033383466313664346136386561386562
65373239366463306435316338303661623932303664613565323032386635366364666562393636
30313036303538626134
root@server:~/my-ansible# ansible-vault decrypt create-user.yml --output=create-user-decrypted.yml
Vault password:
Decryption successful
root@server:~/my-ansible# cat create-user-decrypted.yml
---
- hosts: all
tasks:
- name: Create User {{ user }}
ansible.builtin.user:
name: "{{ user }}"
state: present
ansible-vault rekey mysecret.yml
Vault password:
New Vault password:
Confirm New Vault password:
Rekey successful
cat vault-pass
dmlwn
## 패스워드 파일을 이용해 패스워드 변경
ansible-vault rekey --new-vault-password-file=./vault-pass mysecret.yml
Vault password:
Rekey successful
mv ./mysecret.yml ./vars/
root@server:~/my-ansible# ansible-vault view vars/mysecret.yml
Vault password:
user: ansible
password: dmlwn
## 플레이북 작성
create-user5.yml
---
- hosts: db
vars_files:
- vars/mysecret.yml
tasks:
- name: Create User {{ user }}
ansible.builtin.user:
name: "{{ user }}"
state: present
## 실행확인
ansible-playbook create-user5.yml
ERROR! Attempting to decrypt but no vault secrets found
ansible-playbook --vault-id @prompt create-user5.yml
# 패스워드 입력 없이 사용
ansible-playbook --vault-password-file=./vault-pass create-user5.yml
팩트 Facts는 앤서블이 관리 호스트에서 자동으로 검색한 변수(자동 예약 변수)입니다.
팩트에는 플레이, 조건문, 반복문 또는 관리 호스트에서 수집한 값에 의존하는 기타 명령문의 일반 변수처럼 사용 가능한 호스트별 정보가 포함되어 있습니다.
관리 호스트에서 수집된 일부 팩트에는 다음 내용들이 포함될 수 있습니다.
---
- hosts: db
tasks:
- name: Print all facts
ansible.builtin.debug:
var: ansible_facts
## 플레이북 실행
ansible-playbook facts.yml
PLAY [db] *********************************************************************************************
TASK [Gathering Facts] ********************************************************************************
ok: [tnode3]
TASK [Print all facts] ********************************************************************************
ok: [tnode3] => {
"ansible_facts": {
"all_ipv4_addresses": [
"10.10.1.13"
],
"all_ipv6_addresses": [
"fe80::9a:acff:fee7:a0c"
],
"ansible_local": {},
"apparmor": {
"status": "enabled"
},
"architecture": "x86_64",
"bios_date": "10/16/2017",
"bios_vendor": "Amazon EC2",
"bios_version": "1.0",
"board_asset_tag": "i-061e4fcc389f84510",
"board_name": "NA",
"board_serial": "NA",
"board_vendor": "Amazon EC2",
"board_version": "NA",
"chassis_asset_tag": "Amazon EC2",
"chassis_serial": "NA",
"chassis_vendor": "Amazon EC2",
"chassis_version": "NA",
"cmdline": {
"BOOT_IMAGE": "/boot/vmlinuz-6.2.0-1017-aws",
"console": "ttyS0",
"nvme_core.io_timeout": "4294967295",
"panic": "-1",
"ro": true,
"root": "PARTUUID=d05003df-bf97-4623-bee3-09c11b95e3d4"
},
"date_time": {
"date": "2024-01-12",
"day": "12",
"epoch": "1705069355",
"epoch_int": "1705069355",
"hour": "23",
"iso8601": "2024-01-12T14:22:35Z",
"iso8601_basic": "20240112T232235456155",
"iso8601_basic_short": "20240112T232235",
"iso8601_micro": "2024-01-12T14:22:35.456155Z",
"minute": "22",
"month": "01",
"second": "35",
"time": "23:22:35",
"tz": "KST",
"tz_dst": "KST",
"tz_offset": "+0900",
"weekday": "Friday",
"weekday_number": "5",
"weeknumber": "02",
"year": "2024"
},
"default_ipv4": {
"address": "10.10.1.13",
"alias": "ens5",
"broadcast": "",
"gateway": "10.10.1.1",
"interface": "ens5",
"macaddress": "02:9a:ac:e7:0a:0c",
"mtu": 9001,
"netmask": "255.255.255.0",
"network": "10.10.1.0",
"prefix": "24",
"type": "ether"
},
"default_ipv6": {},
"device_links": {
"ids": {
"nvme0n1": [
"nvme-Amazon_Elastic_Block_Store_vol0bf6c9a74009c9b28",
"nvme-Amazon_Elastic_Block_Store_vol0bf6c9a74009c9b28_1",
"nvme-nvme.1d0f-766f6c3062663663396137343030396339623238-416d617a6f6e20456c617374696320426c6f636b2053746f7265-00000001"
],
...
---
- hosts: db
tasks:
- name: Print all facts
ansible.builtin.debug:
msg: >
The default IPv4 address of {{ ansible_facts.hostname }}
is {{ ansible_facts.default_ipv4.address }}
## 플레이북 실행
ansible-playbook facts1.yml
PLAY [db] *********************************************************************************************
TASK [Gathering Facts] ********************************************************************************
ok: [tnode3]
TASK [Print all facts] ********************************************************************************
ok: [tnode3] => {
"msg": "The default IPv4 address of tnode3 is 10.10.1.13"
}
PLAY RECAP ********************************************************************************************
tnode3 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
팩트 | ansible_facts.* 표기법 |
---|---|
호스트명 | ansible_facts.hostname |
도메인 기반 호스트명 | ansible_facts.fqdn |
기본 IPv4 주소 | ansible_facts.default_ipv4.address |
네트워크 인터페이스 이름 목록 | ansible_facts.interfaces |
/dev/vda1 디스크 파티션 크기 | ansible_facts.device.vda.partitions.vda1.size |
DNS 서버 목록 | ansible_facts.dns.nameservers |
현재 실행 중인 커널 버전 | ansible_facts.kernel |
운영체제 종류 | ansible_facts.distribution |
ansible_* 표기법 | ansible_facts.* 표기법 |
---|---|
ansible_hostname | ansible_facts.hostname |
ansible_fqdn | ansible_facts.fqdn |
ansible_default_ipv4.address | ansible_facts.default_ipv4.address |
ansible_interfaces | ansible_facts.interfaces |
ansible_device.vda.partitions.vda1.size | ansible_facts.device.vda.partitions.vda1.size |
ansible_dns.nameservers | ansible_facts.dns.nameservers |
ansible_kernel | ansible_facts.kernel |
ansible_distribution | ansible_facts.distribution |
현재 위 두 개의 표기법 모두 인지. 이는 앤서블 환경 설정 파일인 ansible.cfg [defaults] 섹션에 있는 inject_facts_as_vars 매개 변수 기본 설정 값이 true이기 때문이며, false로 설정하면 ansible_* 표기법을 비활성화 할 수 있습니다.
다만, 대부분의 플레이북에서 이전 표기법인 ansible_* 표기법을 사용하므로 기본 설정 값인 true를 그대로 사용하는 것이 좋습니다.
파일을 생성해줍니다.
---
- hosts: db
tasks:
- name: Print all facts
ansible.builtin.debug:
msg: >
The node's host name is {{ ansible_hostname }}
and the ip is {{ ansible_default_ipv4.address }}
## 플레이북 실행
ansible-playbook facts2.yml
ansible-playbook facts2.yml
PLAY [db] *********************************************************************************************
TASK [Gathering Facts] ********************************************************************************
ok: [tnode3]
TASK [Print all facts] ********************************************************************************
ok: [tnode3] => {
"msg": "The node's host name is tnode3 and the ip is 10.10.1.13"
}
PLAY RECAP ********************************************************************************************
tnode3 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
# ansible.cfg 파일 편집
[defaults]
inventory = ./inventory
remote_user = root
ask_pass = false
inject_facts_as_vars = false
[privilege_escalation]
become = true
become_method = sudo
become_user = root
become_ask_pass = false
# 실행
ansible-playbook facts2.yml
ansible-playbook facts2.yml
PLAY [db] *********************************************************************************************
TASK [Gathering Facts] ********************************************************************************
ok: [tnode3]
TASK [Print all facts] ********************************************************************************
fatal: [tnode3]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'ansible_hostname' is undefined. 'ansible_hostname' is undefined\n\nThe error appears to be in '/root/my-ansible/facts2.yml': line 6, column 5, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n tasks:\n - name: Print all facts\n ^ here\n"}
PLAY RECAP ********************************************************************************************
tnode3 : ok=1 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
팩트 수집을 위해 해당 호스트에 특정 패키지를 설치해야만 하는 경우가 있습니다.
팩트 수집 실행 시 관리 호스트에 프로세스 확인
## (터미널2) tnode3에 SSH 접속 후 아래 모니터링
ssh tnode3
watch -d -n 1 pstree
## 플레이북 실행
ansible-playbook facts.yml
---
- hosts: db
gather_facts: no
tasks:
- name: Print all facts
ansible.builtin.debug:
msg: >
The default IPv4 address of {{ ansible_facts.hostname }}
is {{ ansible_facts.default_ipv4.address }}
## 실행
ansible-playbook facts3.yml
PLAY [db] **********************************************************************
TASK [Print all facts] *********************************************************
fatal: [tnode3]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'dict object' has no attribute 'hostname'. 'dict object' has no attribute 'hostname'\n\nThe error appears to be in '/root/my-ansible/facts3.yml': line 7, column 5, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n tasks:\n - name: Print all facts\n ^ here\n"}
PLAY RECAP *********************************************************************
tnode3 : ok=0 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
## 파일 복사
cp facts3.yml facts3-2.yml
## facts3-2.yml 파일 편집
---
- hosts: db
gather_facts: no
tasks:
- name: Print message
debug:
msg: Hello!!!!!!!!!!!
ansible-playbook facts3-2.yml
PLAY [db] **********************************************************************
TASK [Print message] ***********************************************************
ok: [tnode3] => {
"msg": "Hello!!!!!!!!!!!"
}
PLAY RECAP *********************************************************************
tnode3 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
cp facts3.yml facts4.yml
ansible-playbook facts4.yml
PLAY [db] **********************************************************************
TASK [Manually gather facts] ***************************************************
ok: [tnode3]
TASK [Print all facts] *********************************************************
ok: [tnode3] => {
"msg": "The default IPv4 address of tnode3 is 10.10.1.13"
}
PLAY RECAP *********************************************************************
tnode3 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
mkdir /etc/ansible/facts.d
## my-ustom.fact 파일 생성
cat <<EOT > /etc/ansible/facts.d/my-custom.fact
[packages]
web_package = httpd
db_package = mariadb-server
[users]
user1 = ansible
user2 = gasida
EOT
cat /etc/ansible/facts.d/my-custom.fact
[packages]
web_package = httpd
db_package = mariadb-server
[users]
user1 = ansible
user2 = gasida
cp facts4.yml facts5.yml
## facts5.yml 파일 편집
cat facts5.yml
---
- hosts: localhost
gather_facts: no
tasks:
- name: Manually gather facts
ansible.builtin.debug:
var: ansible_local
## 실행
ansible-playbook facts5.yml
PLAY [localhost] ***************************************************************
TASK [Manually gather facts] ***************************************************
ok: [localhost] => {
"ansible_local": {}
}
PLAY RECAP *********************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
rm -r *.yml
cat kernal-os.yml
---
- hosts: all
tasks:
- name: Print all node's kernell & OS
ansible.builtin.debug:
msg: >
kernel : {{ ansible_facts.kernel }} ,
OS : {{ ansible_facts.distribution }}
## 실행
ansible-playbook kernal-os.yml
PLAY [all] *********************************************************************
TASK [Gathering Facts] *********************************************************
ok: [tnode1]
ok: [tnode2]
ok: [tnode3]
TASK [Print all node's kernell & OS] *******************************************
ok: [tnode1] => {
"msg": "kernel : 6.2.0-1017-aws , OS : Ubuntu\n"
}
ok: [tnode2] => {
"msg": "kernel : 6.2.0-1017-aws , OS : Ubuntu\n"
}
ok: [tnode3] => {
"msg": "kernel : 6.2.0-1017-aws , OS : Ubuntu\n"
}
PLAY RECAP *********************************************************************
tnode1 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
tnode2 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
tnode3 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
aws cloudformation delete-stack --stack-name mylab --region ap-northeast-2