본 포스팅은 Cloud@net 스터디 참여로 만든 내용임을 밝힌다.
Ansible is a suite of software tools that enables infrastructure as code. It is open-source and the suite includes software provisioning, configuration management, and application deployment functionality.
Ansible은 오픈소스 IaC 도구 중 하나다. Terraform으로 Cloud Infra를 구성한다면, Ansible로는 여러 VM에 동일한 환경을 적용할때 주로 쓰인다.
예를 들면, 여러 대의 VM에 배보된 tomcat을 업그레이드 해야하는 작업이 있을 수 있다.
위 그림과 같이 Control node에서 Managed nodes에 접근하여 같은 작업을 실행하게 되는데, 각 노드에는 Python이 설치되어 있어야 동작하게 된다.
Ansible Server Node: 1대
Ansible Managed Node: 3대
스터디에서 제공해준 CloudFormation을 통해 환경을 구성한다. KeyName과 SgIngressSshCidr를 자신의 환경에 맡게 세팅해준다.
아래와 같이 CLI를 통해서 세팅도 가능하니, 참고하도록 하자.
# YAML 파일 다운로드
curl -O https://s3.ap-northeast-2.amazonaws.com/cloudformation.cloudneta.net/Ansible/a101-1w.yaml
# CloudFormation 스택 배포
# aws cloudformation deploy --template-file a101-1w.yaml --stack-name mylab --parameter-overrides KeyName=<My SSH Keyname> SgIngressSshCidr=<My Home Public IP Address>/32 --region ap-northeast-2
예시) aws cloudformation deploy --template-file a101-1w.yaml --stack-name mylab --parameter-overrides KeyName=kp-gasida SgIngressSshCidr=$(curl -s ipinfo.io/ip)/32 --region ap-northeast-2
## Tip. 인스턴스 타입 변경 : MyInstanceType=t3.xlarge
예시) aws cloudformation deploy --template-file a101-1w.yaml --stack-name mylab --parameter-overrides KeyName=kp-gasida SgIngressSshCidr=$(curl -s ipinfo.io/ip)/32 --region ap-northeast-2 MyInstanceType=t3.xlarge
# CloudFormation 스택 배포 완료 후 작업용 EC2 IP 출력
aws cloudformation describe-stacks --stack-name mylab --query 'Stacks[*].Outputs[0].OutputValue' --output text --region ap-northeast-2
# Ansible Server EC2 SSH 접속
ssh -i ~/.ssh/kp-gasida.pem ubuntu@$(aws cloudformation describe-stacks --stack-name mylab --query 'Stacks[*].Outputs[0].OutputValue' --output text --region ap-northeast-2)
환경세팅이 완료되면, SSH로 EC2에 접근해보자.
# SSH 접속 : ubuntu 계정
ssh -i ~/.ssh/kp-gasida.pem ubuntu@$(aws cloudformation describe-stacks --stack-name mylab --query 'Stacks[*].Outputs[0].OutputValue' --output text --region ap-northeast-2)
------------------------
# 계정 정보 확인
whoami
id
# CPU, Mem, Disk 확인
htop
free -h
df -hT /
# /etc/hosts 확인
cat /etc/hosts
# 노드간 통신 확인
for i in {1..3}; do ping -c 1 tnode$i; done
배포된 EC2는 모두 root/qwe123 로 접속이 가능하다. 실습이니 root계정으로 실습을 하고, 아래와 같이 VSCode를 연동하도록 하자.
Host ansible-server
HostName 50.1.1.1 <- 각자 자신의 ansible-server 의 유동 공인 IP
User root
/root/my-ansible 폴더에 실습코드가 있으니 참고하도록 하자.
# 작업 기본 디렉터리 확인
whoami
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
cat /etc/ansible/ansible.cfg
which ansible
Ansible Server Node에서 Managed Node 접근을 위해 ssh 구성을 한다.
# 모니터링
tree ~/.ssh
watch -d 'tree ~/.ssh'
# Create SSH Keypair
ssh-keygen -t rsa -N "" -f /root/.ssh/id_rsa
다음으로 각 Managed Node에 Public Key를 복사해준다.
# 공개 키를 관리 노드에 복사
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 접속 테스트
# 공개 키를 관리 노드에 복사
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
whoami
exit
ssh tnode2
exit
ssh tnode3
exit
Ansible Server Node가 관리할 Managed Node를 inventory 파일에 작성해준다. 이 파일을 참조하여, 어떤 서버에 배포할지 미리 정할 수 있다.
# my-ansible/inventory
# inventory 파일 생성
# inventory 그룹 구성
cat <<EOT > inventory
[web]
tnode1
tnode2
[db]
tnode3
[all:children]
web
db
EOT
# inventory 검증
ansible-inventory -i ./inventory --list | jq
ansible-inventory -i ./inventory --graph
위 inventory처럼 ip가 아니라 host명으로도 세팅이 가능하다. 아래와 같이 다양하게 각 종류에 맞는 세팅도 가능하니 참고하자.
[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
# 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]
디렉터리 내에 ansible.cfg 파일을 작성하면, -i 옵션으로 inventory 파일의 경로를 지정하지 않고 ansible.cfg 파일에 저장된 경로값을 가져올 수 있다.
# ansible.cfg 파일 생성
cat <<EOT > 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
EOT
우선순위: 1. ansible.cfg
(in the current directory) > 2.~/.ansible.cfg
(in the home directory)
다음으로 실제 Managed Node와 통신을 하는지 알아보기 위해 ping module 테스트를 해보자.
ansible -m ping web
ansible -m ping db
# 암호 입력 후 실행
ansible -m ping --ask-pass web
--ask-pass 옵션을 주면, password 방식으로 접근도 가능하고, -u 옵션으로 다른 계정으로 접근 테스트를 할 수 있다.
위 테스트에서 ping은 ICMP 프로토콜을 사용한 것이 아니라, Ansible의 ping module 기능을 사용한 것이니 참고하자.
위 스샷은 Ansible로 ping을 날릴 때, 해당 Node에서 watch pstree를 한 화면이다. ICMP가 아닌 python3 모듈이 실행되는 것이 확인된다!
그럼 가장 기본적인 기능을 실행해보기 위해, playbook을 만들어보자. playbook은 ansible 세팅값을 기반으로 특정 작업을 수행하는 스크립트 파일이다.
---
- hosts: all
tasks:
- name: Print message
debug:
msg: Hello CloudNet@ Ansible Study
validation check & execution
ansible-playbook --syntax-check first-playbook.yml
ansible-playbook first-playbook.yml
Ansible에서 변수는 파라미터 변수, 플레이 변수, 호스트 변수, 그룹변수 총 4가지가 있다.
우선순위 : 추가변수 > 플레이 변수 > 호스트 변수 > 그룹 변수
인벤토리 파일의 호스트 그룹에 세팅
[web]
tnode1
tnode2
[db]
tnode3
[all:children]
web
db
[all:vars]
user=ansible
# playbook.yml;
---
- hosts: all
tasks:
- name: Create User {{ user }}
ansible.builtin.user:
name: "{{ user }}"
state: present
인벤토리 파일의 특정 호스트 그룹에 세팅
[web]
tnode1
tnode2
[db]
tnode3 user=ansible1
[all:children]
web
db
[all:vars]
user=ansible
# playbook.yml
---
- hosts: db
tasks:
- name: Create User {{ user }}
ansible.builtin.user:
name: "{{ user }}"
state: present
playbook 파일에 hosts 아래 작성
---
- hosts: all
vars:
user: ansible2
tasks:
- name: Create User {{ user }}
ansible.builtin.user:
name: "{{ user }}"
state: present
playbook 파일 실행 시 변수 선언
ansible-playbook -e user=ansible4 create-user3.yml
Ansible 사용 시 민감정보를 암호화 하는 기능이다. 보안상 텍스트로 민감정보가 노출되는 것은 좋지 않으므로 Ansible Vault를 통해 데이터 파일을 암호화/복호화 할 수 있다.
그럼 바로 실습을 진행해보자.
# ansible-vault create 로 생성하려는 플레이북 파일 생성
ansible-vault -h
ansible-vault create mysecret.yml
New Vault password: P@ssw0rd!
Confirm New Vault password: P@ssw0rd!
user: ansible
password: P@ssword!
:wq
ansible-vault로 파일을 만들면, 바로 vim으로 작성을 하게 된다. 값을 입력 후 저장하면, 아래와 같이 암호화된 파일이 생성된다.
복호화도 간단하다. 처음 입력해준 Password를 입력하면 파일이 복호화된 파일을 볼 수 있다.
# 원래 파일 내용 확인(복호화)
ansible-vault view mysecret.yml
Vault password: P@ssword!
별도로 암호를 파일에 만들어서 사용할 수 도 있다.
echo 'P@ssw0rd!' > vault-pass
# ansible-vault create 로 생성하려는 플레이북 파일 생성
ansible-vault create --vault-pass-file ./vault-pass mysecret1.yml
이미 만들어져 있는 파일도 아래와 같이 암호화 할 수 있다.
# 기존 평문 파일 확인
ll create-user.yml
-rw-r--r-- 1 root root 131 Dec 26 14:16 create-user.yml
# 기존 평문 파일 암호화 설정
ansible-vault encrypt create-user.yml
New Vault password: P@ssw0rd!
Confirm New Vault password: P@ssw0rd!
# 암호화 설정 후 확인 : 파일소유자만 읽고 쓸수 있음
ll create-user.yml
-rw------- 1 root root 873 Dec 28 04:18 create-user.yml
cat create-user.yml
# 패스워드 입력 파일을 이용해 패스워드 변경
cat vault-pass
P@ssw0rd!
ansible-vault rekey --new-vault-password-file=./vault-pass mysecret.yml
playbook에서 암호화된 변수 파일을 사용하는 방법도 있다.
# mysecret.yml 파일을 vars 디렉터리로 이동
mv ./mysecret.yml ./vars/
# view로 파일 내용 확인
ansible-vault view vars/mysecret.yml
Vault password: P@ssw0rd!
user: ansible
password: P@ssword!
---
- hosts: db
vars_files:
- vars/mysecret.yml
tasks:
- name: Create User {{ user }}
ansible.builtin.user:
name: "{{ user }}"
state: present
Facts를 통해서 Ansibe이 관리 호스트에서 자동으로 수집된 정보를 긁어오는 작업이다.
# my-ansible/facts.yml
---
- hosts: db
tasks:
- name: Print all facts
ansible.builtin.debug:
var: ansible_facts
tnode3 host의 모든 정보를 긁어온다. 이후 조건문 등을 활용하여, 특정 호스트, OS에 작업을 진행할 수 있다.
---
- 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 }}
특정 값만 가지고 올 수 도 있다.
facts를 사용할 수 없는 경우나, 호스트 부하 방지를 위해 facts 수집을 비활성화 할 수 있다.
---
- 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 }}