Ansible ๊ธฐ์ด
Ansible ๊ธฐ์ด¶
ํ์ต ๋ชฉํ¶
์ด ๋ฌธ์๋ฅผ ํตํด ๋ค์์ ํ์ตํฉ๋๋ค:
- Ansible์ ๊ฐ๋ ๊ณผ ์ํคํ ์ฒ
- ์ธ๋ฒคํ ๋ฆฌ์ Ad-hoc ๋ช ๋ น์ด
- Playbook ์์ฑ ๋ฐ ์คํ
- Roles์ ๋ณ์ ๊ด๋ฆฌ
๋์ด๋: โญโญโญ (์ค๊ธ-๊ณ ๊ธ)
๋ชฉ์ฐจ¶
- Ansible ๊ฐ์
- ์ค์น ๋ฐ ์ค์
- ์ธ๋ฒคํ ๋ฆฌ
- Ad-hoc ๋ช ๋ น์ด
- Playbook
- ๋ณ์์ ํฉํธ
- Roles
- Ansible Vault
1. Ansible ๊ฐ์¶
Ansible์ด๋?¶
Ansible์ ์์ด์ ํธ ์์ด SSH๋ก ์๋ํ๋ IT ์๋ํ ๋๊ตฌ์ ๋๋ค.
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Control Node โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Ansible โ โ
โ โ โโโ Playbooks (YAML) โ โ
โ โ โโโ Inventory โ โ
โ โ โโโ Modules โ โ
โ โ โโโ Plugins โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ SSH
โโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโ
โ โ โ
โผ โผ โผ
โโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโ
โ Managed Node โ โ Managed Node โ โ Managed Node โ
โ (web-server1) โ โ (web-server2) โ โ (db-server) โ
โ No agent โ โ No agent โ โ No agent โ
โโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโ
Ansible vs ๋ค๋ฅธ ๋๊ตฌ¶
| ํน์ฑ | Ansible | Puppet | Chef |
|---|---|---|---|
| ์์ด์ ํธ | ๋ถํ์ | ํ์ | ํ์ |
| ์ธ์ด | YAML | Ruby DSL | Ruby |
| Push/Pull | Push | Pull | Pull |
| ํ์ต ๊ณก์ | ๋ฎ์ | ์ค๊ฐ | ๋์ |
2. ์ค์น ๋ฐ ์ค์ ¶
Ansible ์ค์น¶
# Ubuntu/Debian
sudo apt update
sudo apt install ansible
# RHEL/CentOS
sudo yum install epel-release
sudo yum install ansible
# pip๋ก ์ค์น (๊ถ์ฅ)
pip3 install ansible
# ๋ฒ์ ํ์ธ
ansible --version
SSH ํค ์ค์ ¶
# SSH ํค ์์ฑ
ssh-keygen -t ed25519 -f ~/.ssh/ansible_key
# ๋์ ์๋ฒ์ ํค ๋ณต์ฌ
ssh-copy-id -i ~/.ssh/ansible_key.pub user@target-server
# SSH ์ฐ๊ฒฐ ํ
์คํธ
ssh -i ~/.ssh/ansible_key user@target-server
ansible.cfg ์ค์ ¶
# /etc/ansible/ansible.cfg (์ ์ญ)
# ~/.ansible.cfg (์ฌ์ฉ์)
# ./ansible.cfg (ํ๋ก์ ํธ)
# ansible.cfg
[defaults]
inventory = ./inventory
remote_user = ansible
private_key_file = ~/.ssh/ansible_key
host_key_checking = False
retry_files_enabled = False
forks = 20
timeout = 30
[privilege_escalation]
become = True
become_method = sudo
become_user = root
become_ask_pass = False
[ssh_connection]
pipelining = True
ssh_args = -o ControlMaster=auto -o ControlPersist=60s
3. ์ธ๋ฒคํ ๋ฆฌ¶
INI ํ์¶
# inventory/hosts
[webservers]
web1.example.com
web2.example.com ansible_host=192.168.1.11
[dbservers]
db1.example.com ansible_host=192.168.1.21 ansible_port=2222
db2.example.com ansible_user=dbadmin
[loadbalancers]
lb1.example.com
# ๊ทธ๋ฃน์ ๊ทธ๋ฃน
[production:children]
webservers
dbservers
loadbalancers
# ๊ทธ๋ฃน ๋ณ์
[webservers:vars]
http_port=80
max_clients=200
[all:vars]
ansible_python_interpreter=/usr/bin/python3
YAML ํ์¶
# inventory/hosts.yml
all:
children:
webservers:
hosts:
web1.example.com:
web2.example.com:
ansible_host: 192.168.1.11
vars:
http_port: 80
max_clients: 200
dbservers:
hosts:
db1.example.com:
ansible_host: 192.168.1.21
ansible_port: 2222
db2.example.com:
ansible_user: dbadmin
production:
children:
webservers:
dbservers:
vars:
ansible_python_interpreter: /usr/bin/python3
๋์ ์ธ๋ฒคํ ๋ฆฌ¶
# AWS EC2 ๋์ ์ธ๋ฒคํ ๋ฆฌ
pip install boto3 botocore
# ansible.cfg
[defaults]
inventory = aws_ec2.yml
# aws_ec2.yml
plugin: amazon.aws.aws_ec2
regions:
- ap-northeast-2
keyed_groups:
- key: tags.Environment
prefix: env
- key: instance_type
prefix: type
compose:
ansible_host: public_ip_address
์ธ๋ฒคํ ๋ฆฌ ํ์ธ¶
# ํธ์คํธ ๋ชฉ๋ก
ansible-inventory --list
ansible-inventory --graph
# ํน์ ๊ทธ๋ฃน
ansible webservers --list-hosts
# ํธ์คํธ ๋ณ์ ํ์ธ
ansible-inventory --host web1.example.com
4. Ad-hoc ๋ช ๋ น์ด¶
๊ธฐ๋ณธ ์ฌ์ฉ๋ฒ¶
ansible <pattern> -m <module> -a "<arguments>"
# ์์
ansible all -m ping
ansible webservers -m command -a "uptime"
ansible dbservers -m shell -a "df -h | grep /dev/sda"
์์ฃผ ์ฌ์ฉํ๋ ๋ชจ๋¶
# ping (์ฐ๊ฒฐ ํ
์คํธ)
ansible all -m ping
# command (๊ธฐ๋ณธ ๋ช
๋ น ์คํ)
ansible all -m command -a "hostname"
# shell (์ ๊ธฐ๋ฅ ์ฌ์ฉ)
ansible all -m shell -a "cat /etc/os-release | grep VERSION"
# copy (ํ์ผ ๋ณต์ฌ)
ansible all -m copy -a "src=/local/file dest=/remote/path mode=0644"
# file (ํ์ผ/๋๋ ํ ๋ฆฌ ๊ด๋ฆฌ)
ansible all -m file -a "path=/tmp/testdir state=directory mode=0755"
# yum/apt (ํจํค์ง ๊ด๋ฆฌ)
ansible webservers -m yum -a "name=httpd state=present"
ansible webservers -m apt -a "name=nginx state=present update_cache=yes"
# service (์๋น์ค ๊ด๋ฆฌ)
ansible webservers -m service -a "name=nginx state=started enabled=yes"
# user (์ฌ์ฉ์ ๊ด๋ฆฌ)
ansible all -m user -a "name=deploy state=present groups=sudo"
# setup (์์คํ
์ ๋ณด ์์ง)
ansible web1 -m setup
ansible web1 -m setup -a "filter=ansible_distribution*"
๊ถํ ์์น¶
# sudo ์ฌ์ฉ
ansible all -m apt -a "name=vim state=present" --become
# ํน์ ์ฌ์ฉ์๋ก
ansible all -m command -a "whoami" --become --become-user=postgres
๋ณ๋ ฌ ์คํ¶
# ๋์ ์คํ ํธ์คํธ ์ ์กฐ์
ansible all -m ping -f 50 # 50๊ฐ ๋์ ์คํ
# ์์ฐจ ์คํ
ansible all -m ping -f 1
5. Playbook¶
๊ธฐ๋ณธ ๊ตฌ์กฐ¶
# site.yml
---
- name: Configure webservers
hosts: webservers
become: yes
vars:
http_port: 80
tasks:
- name: Install nginx
apt:
name: nginx
state: present
update_cache: yes
- name: Start nginx service
service:
name: nginx
state: started
enabled: yes
- name: Configure databases
hosts: dbservers
become: yes
tasks:
- name: Install PostgreSQL
apt:
name: postgresql
state: present
Playbook ์คํ¶
# ๊ธฐ๋ณธ ์คํ
ansible-playbook site.yml
# ์ธ๋ฒคํ ๋ฆฌ ์ง์
ansible-playbook -i inventory/production site.yml
# ๋๋ผ์ด๋ฐ (๋ณ๊ฒฝ ์์ด ํ์ธ)
ansible-playbook site.yml --check
# ์ฐจ์ด์ ํ์
ansible-playbook site.yml --diff
# ์์ธ ์ถ๋ ฅ
ansible-playbook site.yml -v # ์์ธ
ansible-playbook site.yml -vvv # ๋งค์ฐ ์์ธ
# ํน์ ํธ์คํธ๋ง
ansible-playbook site.yml --limit web1.example.com
# ํ๊ทธ ์ฌ์ฉ
ansible-playbook site.yml --tags "install,configure"
ansible-playbook site.yml --skip-tags "debug"
# ์์ ํ์คํฌ ์ง์
ansible-playbook site.yml --start-at-task "Start nginx service"
์กฐ๊ฑด๋ฌธ (when)¶
tasks:
- name: Install Apache (RHEL)
yum:
name: httpd
state: present
when: ansible_os_family == "RedHat"
- name: Install Apache (Debian)
apt:
name: apache2
state: present
when: ansible_os_family == "Debian"
- name: Check if file exists
stat:
path: /etc/myapp.conf
register: config_file
- name: Create config if not exists
template:
src: myapp.conf.j2
dest: /etc/myapp.conf
when: not config_file.stat.exists
๋ฐ๋ณต๋ฌธ (loop)¶
tasks:
- name: Install multiple packages
apt:
name: "{{ item }}"
state: present
loop:
- nginx
- vim
- git
- curl
- name: Create users
user:
name: "{{ item.name }}"
groups: "{{ item.groups }}"
state: present
loop:
- { name: 'alice', groups: 'sudo' }
- { name: 'bob', groups: 'developers' }
- name: Install packages with apt
apt:
name: "{{ packages }}"
state: present
vars:
packages:
- nginx
- postgresql
- redis
ํธ๋ค๋ฌ (handlers)¶
tasks:
- name: Copy nginx config
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: Restart nginx
- name: Copy site config
template:
src: site.conf.j2
dest: /etc/nginx/sites-available/mysite
notify:
- Reload nginx
- Clear cache
handlers:
- name: Restart nginx
service:
name: nginx
state: restarted
- name: Reload nginx
service:
name: nginx
state: reloaded
- name: Clear cache
file:
path: /var/cache/nginx
state: absent
๋ธ๋ก (block)¶
tasks:
- name: Install and configure app
block:
- name: Install package
apt:
name: myapp
state: present
- name: Configure app
template:
src: myapp.conf.j2
dest: /etc/myapp/config.yml
rescue:
- name: Rollback on failure
apt:
name: myapp
state: absent
always:
- name: Always run cleanup
file:
path: /tmp/install_temp
state: absent
when: install_myapp | default(true)
become: yes
6. ๋ณ์์ ํฉํธ¶
๋ณ์ ์ ์ ์์น¶
์ฐ์ ์์ (๋ฎ์ โ ๋์):
1. role defaults
2. inventory file vars
3. inventory group_vars
4. inventory host_vars
5. playbook group_vars
6. playbook host_vars
7. host facts
8. play vars
9. play vars_prompt
10. play vars_files
11. role vars
12. block vars
13. task vars
14. include_vars
15. set_facts / registered vars
16. role parameters
17. extra vars (-e)
๋ณ์ ํ์ผ ๊ตฌ์กฐ¶
project/
โโโ ansible.cfg
โโโ inventory/
โ โโโ hosts
โ โโโ group_vars/
โ โ โโโ all.yml
โ โ โโโ webservers.yml
โ โ โโโ dbservers.yml
โ โโโ host_vars/
โ โโโ web1.example.com.yml
โ โโโ db1.example.com.yml
โโโ playbooks/
โ โโโ site.yml
โโโ roles/
group_vars/host_vars ์์¶
# inventory/group_vars/all.yml
---
ansible_user: deploy
ntp_servers:
- 0.pool.ntp.org
- 1.pool.ntp.org
# inventory/group_vars/webservers.yml
---
http_port: 80
nginx_worker_processes: auto
# inventory/host_vars/web1.example.com.yml
---
nginx_worker_processes: 4
custom_config: true
Playbook์์ ๋ณ์ ์ฌ์ฉ¶
---
- name: Configure web servers
hosts: webservers
vars:
app_name: myapp
app_version: "1.2.3"
app_env: production
vars_files:
- vars/common.yml
- "vars/{{ ansible_os_family }}.yml"
tasks:
- name: Deploy application
template:
src: app.conf.j2
dest: "/etc/{{ app_name }}/config.yml"
- name: Set runtime variable
set_fact:
runtime_var: "{{ ansible_hostname }}-{{ app_version }}"
ํฉํธ (Facts)¶
tasks:
# ์๋ ์์ง๋ ํฉํธ ์ฌ์ฉ
- name: Print OS info
debug:
msg: "OS: {{ ansible_distribution }} {{ ansible_distribution_version }}"
# ์ปค์คํ
ํฉํธ (๋์ ํธ์คํธ์ ์ ์ฅ)
# /etc/ansible/facts.d/custom.fact
- name: Create custom fact
copy:
content: |
[app]
version=1.2.3
environment=production
dest: /etc/ansible/facts.d/custom.fact
mode: '0644'
# ํฉํธ ์๋ก๊ณ ์นจ
- name: Refresh facts
setup:
filter: ansible_local
- name: Use custom fact
debug:
msg: "App version: {{ ansible_local.custom.app.version }}"
Jinja2 ํ ํ๋ฆฟ¶
{# templates/nginx.conf.j2 #}
worker_processes {{ nginx_worker_processes | default('auto') }};
events {
worker_connections {{ nginx_worker_connections | default(1024) }};
}
http {
server {
listen {{ http_port }};
server_name {{ ansible_fqdn }};
{% for location in nginx_locations | default([]) %}
location {{ location.path }} {
proxy_pass {{ location.proxy }};
}
{% endfor %}
{% if enable_ssl | default(false) %}
ssl_certificate {{ ssl_cert_path }};
ssl_certificate_key {{ ssl_key_path }};
{% endif %}
}
}
7. Roles¶
Role ๊ตฌ์กฐ¶
roles/
โโโ webserver/
โโโ defaults/
โ โโโ main.yml # ๊ธฐ๋ณธ ๋ณ์ (๋ฎ์ ์ฐ์ ์์)
โโโ files/
โ โโโ static.conf # ์ ์ ํ์ผ
โโโ handlers/
โ โโโ main.yml # ํธ๋ค๋ฌ
โโโ meta/
โ โโโ main.yml # ์์กด์ฑ, ๋ฉํ๋ฐ์ดํฐ
โโโ tasks/
โ โโโ main.yml # ํ์คํฌ
โโโ templates/
โ โโโ nginx.conf.j2 # Jinja2 ํ
ํ๋ฆฟ
โโโ vars/
โโโ main.yml # ๋ณ์ (๋์ ์ฐ์ ์์)
Role ์์ฑ¶
# ๊ธฐ๋ณธ ๊ตฌ์กฐ ์์ฑ
ansible-galaxy init roles/webserver
Role ์์¶
# roles/webserver/defaults/main.yml
---
nginx_port: 80
nginx_worker_processes: auto
nginx_sites: []
# roles/webserver/tasks/main.yml
---
- name: Install nginx
apt:
name: nginx
state: present
update_cache: yes
tags: install
- name: Configure nginx
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: Restart nginx
tags: configure
- name: Configure sites
template:
src: site.conf.j2
dest: "/etc/nginx/sites-available/{{ item.name }}"
loop: "{{ nginx_sites }}"
notify: Reload nginx
tags: configure
- name: Enable sites
file:
src: "/etc/nginx/sites-available/{{ item.name }}"
dest: "/etc/nginx/sites-enabled/{{ item.name }}"
state: link
loop: "{{ nginx_sites }}"
notify: Reload nginx
tags: configure
- name: Start nginx
service:
name: nginx
state: started
enabled: yes
tags: service
# roles/webserver/handlers/main.yml
---
- name: Restart nginx
service:
name: nginx
state: restarted
- name: Reload nginx
service:
name: nginx
state: reloaded
# roles/webserver/meta/main.yml
---
dependencies:
- role: common
- role: firewall
vars:
firewall_allowed_ports:
- "{{ nginx_port }}"
Role ์ฌ์ฉ¶
# site.yml
---
- name: Configure web servers
hosts: webservers
become: yes
roles:
- common
- role: webserver
vars:
nginx_port: 8080
nginx_sites:
- name: mysite
domain: example.com
root: /var/www/mysite
- name: Configure databases
hosts: dbservers
become: yes
roles:
- common
- postgresql
Ansible Galaxy¶
# Role ๊ฒ์
ansible-galaxy search nginx
# Role ์ค์น
ansible-galaxy install geerlingguy.nginx
# ์ปฌ๋ ์
์ค์น
ansible-galaxy collection install community.general
# requirements.yml๋ก ์ค์น
ansible-galaxy install -r requirements.yml
# requirements.yml
---
roles:
- name: geerlingguy.nginx
version: "3.1.0"
- name: geerlingguy.postgresql
version: "3.3.1"
collections:
- name: community.general
version: ">=6.0.0"
- name: amazon.aws
version: "5.0.0"
8. Ansible Vault¶
Vault ๊ธฐ๋ณธ ์ฌ์ฉ¶
# ์ ์ํธํ ํ์ผ ์์ฑ
ansible-vault create secrets.yml
# ๊ธฐ์กด ํ์ผ ์ํธํ
ansible-vault encrypt vars/secrets.yml
# ํ์ผ ๋ณตํธํ
ansible-vault decrypt vars/secrets.yml
# ํ์ผ ํธ์ง
ansible-vault edit secrets.yml
# ํ์ผ ๋ด์ฉ ๋ณด๊ธฐ
ansible-vault view secrets.yml
# ๋น๋ฐ๋ฒํธ ๋ณ๊ฒฝ
ansible-vault rekey secrets.yml
Vault ์ฌ์ฉ ์์¶
# vars/secrets.yml (์ํธํ๋จ)
---
db_password: "SuperSecretPassword123"
api_key: "sk-1234567890abcdef"
# playbook.yml
---
- name: Deploy application
hosts: webservers
vars_files:
- vars/secrets.yml
tasks:
- name: Configure database
template:
src: db.conf.j2
dest: /etc/app/db.conf
# {{ db_password }} ์ฌ์ฉ ๊ฐ๋ฅ
Playbook ์คํ¶
# ๋น๋ฐ๋ฒํธ ํ๋กฌํํธ
ansible-playbook site.yml --ask-vault-pass
# ๋น๋ฐ๋ฒํธ ํ์ผ ์ฌ์ฉ
ansible-playbook site.yml --vault-password-file ~/.vault_pass
# ํ๊ฒฝ ๋ณ์
export ANSIBLE_VAULT_PASSWORD_FILE=~/.vault_pass
ansible-playbook site.yml
๋ฌธ์์ด ์ํธํ¶
# ๋ฌธ์์ด๋ง ์ํธํ
ansible-vault encrypt_string 'MySecretPassword' --name 'db_password'
# ์ถ๋ ฅ (YAML์ ์ง์ ์ฌ์ฉ)
db_password: !vault |
$ANSIBLE_VAULT;1.1;AES256
66386439653236...
์ฐ์ต ๋ฌธ์ ¶
๋ฌธ์ 1: ์ธ๋ฒคํ ๋ฆฌ ์์ฑ¶
๋ค์ ์๋ฒ๋ค์ ์ธ๋ฒคํ ๋ฆฌ๋ฅผ YAML ํ์์ผ๋ก ์์ฑํ์ธ์: - app1, app2 (webservers ๊ทธ๋ฃน) - db1 (dbservers ๊ทธ๋ฃน) - ๋ชจ๋ ์๋ฒ: ansible_user=deploy - webservers: http_port=8080
๋ฌธ์ 2: Playbook ์์ฑ¶
nginx๋ฅผ ์ค์นํ๊ณ ์์ํ๋ Playbook์ ์์ฑํ์ธ์: - RedHat๊ณผ Debian ๊ณ์ด ๋ชจ๋ ์ง์ - ํธ๋ค๋ฌ๋ก ์๋น์ค ์ฌ์์ - ํ๊ทธ ์ฌ์ฉ (install, configure)
๋ฌธ์ 3: Role ์์ฑ¶
PostgreSQL์ ์ค์นํ๋ Role์ tasks/main.yml์ ์์ฑํ์ธ์: - ํจํค์ง ์ค์น - ์๋น์ค ์์ - ์ด๊ธฐ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์์ฑ
์ ๋ต¶
๋ฌธ์ 1 ์ ๋ต¶
# inventory/hosts.yml
all:
vars:
ansible_user: deploy
children:
webservers:
hosts:
app1:
app2:
vars:
http_port: 8080
dbservers:
hosts:
db1:
๋ฌธ์ 2 ์ ๋ต¶
---
- name: Install and configure nginx
hosts: webservers
become: yes
tasks:
- name: Install nginx (RedHat)
yum:
name: nginx
state: present
when: ansible_os_family == "RedHat"
tags: install
- name: Install nginx (Debian)
apt:
name: nginx
state: present
update_cache: yes
when: ansible_os_family == "Debian"
tags: install
- name: Copy nginx config
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: Restart nginx
tags: configure
- name: Start nginx
service:
name: nginx
state: started
enabled: yes
tags: install
handlers:
- name: Restart nginx
service:
name: nginx
state: restarted
๋ฌธ์ 3 ์ ๋ต¶
# roles/postgresql/tasks/main.yml
---
- name: Install PostgreSQL
apt:
name:
- postgresql
- postgresql-contrib
- python3-psycopg2
state: present
update_cache: yes
- name: Start PostgreSQL service
service:
name: postgresql
state: started
enabled: yes
- name: Create application database
become_user: postgres
postgresql_db:
name: "{{ pg_database | default('appdb') }}"
state: present
- name: Create database user
become_user: postgres
postgresql_user:
name: "{{ pg_user | default('appuser') }}"
password: "{{ pg_password }}"
db: "{{ pg_database | default('appdb') }}"
priv: ALL
state: present
๋ค์ ๋จ๊ณ¶
- 23_Advanced_Networking.md - VLAN, bonding, iptables/nftables
์ฐธ๊ณ ์๋ฃ¶
- Ansible Documentation
- Ansible Galaxy
- Ansible Best Practices
ansible-doc -l,ansible-doc <module>