Ansible Basic¶
Inventory¶
- Group 및 Nested Group 사용 방법입니다.
- Simplifying Host Specifications with Ranges
[usa] washington1.example.com washington2.example.com [canada] ontario01.example.com ontario02.example.com [north-america:children] canada usa [usa] washington[1:2].example.com [canada] ontario[01:02].example.com
- Host가 속한 Group, Group에 속한 Host 출력합니다.
$ ansible [host|group] --list-hosts $ ansible [host|group] -i [inventory] --list-hosts
Ansible Configuration File¶
[defaults]
inventory = ./inventory
remote_user = user
ask_pass = false
[privilege_escalation]
become = true
become_method = sudo
become_user = root
become_ask_pass = false
Ad Hoc Commands¶
ansible host-pattern -m module [-a 'module arguments'] [-i inventory]
ansible -m user -a 'name=newbie uid=4000 state=present' servera.lab.example.com
Variables¶
-
Playbook 에서 변수 선언 방법입니다.
- hosts: all vars: user: joe home: /home/joe
- hosts: all vars_files: - vars/users.yml #cat vars/users.yml user: joe home: /home/joe
-
ansible.cfg 파일에서 변수 선언 방법입니다.
[servers] demo.example.com ansible_user=joe
[servers] demo1.example.com demo2.example.com [servers:vars] user=joe
[servers1] demo1.example.com demo2.example.com [servers2] demo3.example.com demo4.example.com [servers:children] servers1 servers2 [servers:vars] user=joe
-
host_vars, group_vars 디렉토리를 이용한 변수 선언 방법입니다. (Inventory에 속한 group/host 이름으로 yaml 파일 생성)
cat ~/project/inventory [datacenter1] demo1.example.com demo2.example.com [datacenter2] demo3.example.com demo4.example.com [datacenters:children] datacenter1 datacenter2
cat ~/project/group_vars/datacenters package: httpd
$ cat ~/project/group_vars/datacenter1 package: httpd $ cat ~/project/group_vars/datacenter2 package: apache
project ├── ansible.cfg ├── group_vars │ ├── datacenters │ ├── datacenters1 │ └── datacenters2 ├── host_vars │ ├── demo1.example.com │ ├── demo2.example.com │ ├── demo3.example.com │ └── demo4.example.com ├── inventory └── playbook.yml
-
배열 형태로 사용하는 경우 입니다.
user1_first_name: Bob user1_last_name: Jones user1_home_dir: /users/bjones user2_first_name: Anne user2_last_name: Cook user3_home_dir: /users/acook
users: bjones: first_name: Bob last_name: Jones home_dir: /users/bjones acook: first_name: Anne last_name: Cook home_dir: /users/acook
# Returns 'Bob' users.bjones.first_name #해당 값에 접근하는 방법 # Returns '/users/acook' users.acook.home_dir
-
register 변수 사용, 명령의 출력을 변수에 등록(register)할 수 있습니다.
--- - name: Installs a package and prints the result hosts: all tasks: - name: Install the package yum: name: httpd state: installed register: install_result - debug: var=install_result # var: 로 대체 가능
TASK [debug] *************************************************************** ok: [demo.example.com] => { "install_result": { "changed": false, "msg": "", "rc": 0, "results": [ "httpd-2.4.6-40.el7.x86_64 providing httpd is already installed" ] } }
-
예제로 사용할 수 있는 Playbook 입니다.
- name: Deploy and start Apache HTTPD service hosts: webserver vars: web_pkg: httpd firewall_pkg: firewalld web_service: httpd firewall_service: firewalld python_pkg: python-httplib2 rule: http tasks: - name: Required packages are installed and up to date yum: name: - "{{ web_pkg }}" - "{{ firewall_pkg }}" - "{{ python_pkg }}" state: latest - name: The {{ firewall_service }} service is started and enabled service: name: "{{ firewall_service }}" enabled: true state: started - name: The {{ web_service }} service is started and enabled service: name: "{{ web_service }}" enabled: true state: started - name: Web content is in place copy: content: "Example web content" dest: /var/www/html/index.html - name: The firewall port for {{ rule }} is open firewalld: service: "{{ rule }}" permanent: true immediate: true state: enabled - name: Verify the Apache service hosts: localhost become: false tasks: - name: Ensure the webserver is reachable uri: url: http://servera.lab.example.com status_code: 200
Vault¶
-
암호화된 파일 만드는 방법입니다.
$ ansible-vault create secret.yml New Vault password: redhat Confirm New Vault password: redhat
-
암호가 저장된 파일을 이용하는 방법입니다.
$ ansible-vault create --vault-password-file=vault-pass secret.yml
Managing Facts¶
- Facts 접근 방법입니다.
ansible_facts['default_ipv4']['address'] can also be written ansible_facts.default_ipv4.address ansible_facts['dns']['nameservers'] can also be written ansible_facts.dns.nameservers
-
커스텀 Facts를 만들고
/etc/ansible/facts.d/[name].fact
파일을 이용하는 방법 입니다.[packages] web_package = httpd db_package = mariadb-server [users] user1 = joe user2 = jane
-
예제로 사용할 수 있는 Playbook 입니다.
--- - name: Install and configure webserver with basic auth hosts: webserver vars: firewall_pkg: firewalld web_pkg: httpd web_svc: httpd ssl_pkg: mod_ssl python_pkg: python-passlib httpdconf_src: files/httpd.conf httpdconf_file: /etc/httpd/conf/httpd.conf htaccess_src: files/.htaccess secrets_dir: /etc/httpd/secrets secrets_file: "{{ secrets_dir }}/htpasswd" web_root: /var/www/html web_user: guest vars_files: - vars/secret.yml tasks: - name: Latest version of necessary packages installed yum: name: - "{{ firewall_pkg }}" - "{{ web_pkg }}" - "{{ ssl_pkg }}" - "{{ python_pkg }}" state: latest - name: Configure web service copy: src: "{{ httpdconf_src }}" dest: "{{ httpdconf_file }}" owner: root group: root mode: 0644 - name: Secrets directory exists file: path: "{{ secrets_dir }}" state: directory owner: apache group: apache mode: 0500 - name: Web user exists in secrets file htpasswd: path: "{{ secrets_file }}" name: "{{ web_user }}" password: "{{ web_pass }}" owner: apache group: apache mode: 0400 - name: .htaccess file installed in docroot copy: src: "{{ htaccess_src }}" dest: "{{ web_root }}/.htaccess" owner: apache group: apache mode: 0400 - name: Create index.html copy: content: "{{ ansible_facts['fqdn'] }} ({{ ansible_facts['default_ipv4']['address'] }}) has been customized by Ansible.\n" dest: "{{ web_root }}/index.html" - name: Open the port for the web server firewalld: service: https state: enabled immediate: true permanent: true - name: Web service enabled and restarted service: name: "{{ web_svc }}" state: restarted enabled: true - name: Firewall service enable and restarted service: name: firewalld state: restar
Loop & Condition¶
- 조건문 사용 예시 입니다.
when: > ( ansible_distribution == "RedHat" and ansible_distribution_major_version == "7" ) or ( ansible_distribution == "Fedora" and ansible_distribution_major_version == "28" )
--- - name: Restart HTTPD if Postfix is Running hosts: all tasks: - name: Get Postfix server status command: /usr/bin/systemctl is-active postfix ignore_errors: yes register: result - name: Restart Apache HTTPD based on Postfix status service: name: httpd state: restarted when: result.rc == 0
--- - name: Database Setup play hosts: database_servers vars: min_ram_size_bytes: 2000000000 supported_distros: - RedHat #- Centos tasks: - name: Setup Database tasks on supported hosts w/ Min. RAM include_tasks: "{{ ansible_distribution }}_database_tasks.yml" #Add a conditional here - name: Print a message for unsupported Distros debug: msg: > {{ inventory_hostname }} is a {{ ansible_distribution }}-based host, which is not one of the supported distributions ({{ supported_distros }}) #Add a conditional here - name: Print a message for systems with insufficient RAM debug: msg: > {{ inventory_hostname }} does not meet the minimum RAM requirements of {{ min_ram_size_bytes }} bytes. #Add a conditional here
- name: Ensure Database Users exist user: name: "{{ item.username }}" groups: "{{ item.role }}" append: yes state: present loop: "{{ user_list }}" when: item.role in host_permission_groups
Handlers¶
tasks:
- name: copy demo.example.conf configuration template
template:
src: /var/lib/templates/demo.example.conf.template
dest: /etc/httpd/conf.d/demo.example.conf
notify:
- restart mysql
- restart apache
handlers:
- name: restart mysql
service:
name: mariadb
state: restarted
- name: restart apache
service:
name: httpd
state: restarte
---
- name: MariaDB server is installed
hosts: databases
vars:
db_packages:
- mariadb-server
- MySQL-python
db_service: mariadb
resources_url: http://materials.example.com/labs/control-handlers
config_file_url: "{{ resources_url }}/my.cnf.standard"
config_file_dst: /etc/my.cnf
tasks:
- name: "{{ db_packages }} packages are installed"
yum:
name: "{{ db_packages }}"
state: present
notify:
- set db password
- name: Make sure the database service is running
service:
name: "{{ db_service }}"
state: started
enabled: true
- name: The {{ config_file_dst }} file has been installed
get_url:
url: "{{ config_file_url }}"
dest: "{{ config_file_dst }}"
owner: mysql
group: mysql
force: yes
notify:
- restart db service
handlers:
- name: restart db service
service:
name: "{{ db_service }}"
state: restarted
- name: set db password
mysql_user:
name: root
password: redhat
- name: Latest version of notapkg is installed
yum:
name: notapkg
state: latest
ignore_errors: yes
---
- hosts: all
force_handlers: yes
tasks:
- name: a task which always notifies its handler
command: /bin/true
notify: restart the database
- name: a task which fails because the package doesn't exist
yum:
name: notapkg
state: latest
#오류가 발생하는 태스크, 하지만 force_handlers 키워드로 핸들러가 실행된다.
handlers:
- name: restart the database
service:
name: mariadb
state: restarted
changed가
보고 되면,handler
가 동작 하기때문에ok
또는failed
만 보고하도록 강제합니다.
- name: get Kerberos credentials as "admin"
shell: echo "{{ krb_admin_pass }}" | kinit -f admin
changed_when: false
tasks:
- shell:
cmd: /usr/local/bin/upgrade-database
register: command_result
changed_when: "'Success' in command_result.stdout"
notify:
- restart_database
handlers:
- name: restart_database
service:
name: mariadb
state: restarted
block/rescue/alywas¶
tasks:
- block:
- name: upgrade the database
shell:
cmd: /usr/local/lib/upgrade-database
rescue:
- name: revert the database upgrade
shell:
cmd: /usr/local/lib/revert-database
always:
- name: always restart the database
service:
name: mariadb
state: restarted
---
- name: Playbook Control Lab
hosts: webservers
vars_files: vars.yml
tasks:
#Fail Fast Message
- name: Show Failed System Requirements Message
fail:
msg: "The {{ inventory_hostname }} did not meet minimum reqs."
when: >
ansible_memtotal_mb*1024*1024 < min_ram_megabytes*1000000 or
ansible_distribution != "RedHat"
#Install all Packages
- name: Ensure required packages are present
package:
name: "{{ item }}"
state: present
loop: "{{ packages }}"
#Enable and start services
- name: Ensure services are started and enabled
service:
name: "{{ item }}"
state: started
enabled: yes
loop: "{{ services }}"
#Block of config tasks
- block:
- name: Create SSL cert directory
file:
path: "{{ ssl_cert_dir }}"
state: directory
- name: Copy Config Files
copy:
src: "{{ item.src }}"
dest: "{{ item.dest }}"
loop: "{{ web_config_files }}"
notify: restart web service
rescue:
- name: Configuration Error Message
debug:
msg: >
One or more of the configuration
changes failed, but the web service
is still active.
#Configure the firewall
- name: ensure web server ports are open
firewalld:
service: "{{ item }}"
immediate: true
permanent: true
state: enabled
loop:
- http
- https
#Add handlers
handlers:
- name: restart web service
service:
name: "{{ web_service }}"
state: restarted
File Module¶
- name: SELinux type is set to samba_share_t
file:
path: /path/to/samba_file
setype: samba_share_t
- name: SELinux type is persistently set to samba_share_t
sefcontext:
target: /path/to/samba_file
setype: samba_share_t
state: present
tasks:
- name: Fetch the /var/log/secure log file from managed hosts
fetch:
src: /var/log/secure
dest: secure-backups
flat: no
Template¶
tasks:
- name: template render
template:
src: /tmp/j2-template.j2
dest: /tmp/dest-config-file.txt
---
- hosts: all
remote_user: devops
become: true
vars:
system_owner: clyde@example.com
tasks:
- template:
src: motd.j2
dest: /etc/motd
owner: root
group: root
mode: 0644
Manage Large Project¶
# 특정 Host의 IP주소를 명시
ansible_host: 192.168.2.1
# 와일드 카드 이용
$ cat playbook.yml
---
- hosts: '*'
...output omitted...
# 작은 따옴표를 이용하여 호스트를 묶는다.
---
hosts: '!test1.example.com,development'
---
- hosts: '*.example.com'
---
- hosts: all,!datacenter1
[student@workstation projects-host]$ ansible '*.example.com, !*.lab.example.com' \
> -i inventory1 --list-hosts
hosts (7):
saturn.example.com
db1.example.com
db2.example.com
db3.example.com
file2.example.com
srv1.example.com
srv2.example.com
Dynamic Inventory¶
inventorya.py
chmod 755 inventorya.py
./inventorya.py --list
ansible -i inventorya.py all --list-hosts
Serial¶
[student@demo ~]$ grep forks ansible.cfg
forks = 5
#동시에 5대 씩 실행
# 두대씩 순차적으로 진행
---
- name: Rolling update
hosts: webservers
serial: 2
tasks:
2. 플레이북 실행 중에 콘텐츠가 포함되는 대로 처리합니다.
3. 콘텐츠를 가져오면(import) 정적(static)으로 처리합니다.
4. 플레이북 구문을 처음 분석할때 가져온 콘텐츠를 가져와 먼저 실행합니다.
5.
import_playbook
은 Master Playbook에 사용합니다. - name: Prepare the web server
import_playbook: web.yml
- name: Prepare the database server
import_playbook: db.yml
import_tasks
는 task에 다른 작업을 추가할때 사용 합니다.7. webserver_tasks.yml 에는 단순 task만 명시 합니다.
---
- name: Install web server
hosts: webservers
tasks:
- import_tasks: webserver_tasks.yml
---
- name: Install web server
hosts: webservers
tasks:
- include_tasks: webserver_tasks.yml
9. import_playbook은 플레이북이기때문에 동등한 위치에 정의, 플레이북에 대한 내용이 모두 포함되어있는 파일 추가 합니다.
---
- name: Configure web server
hosts: servera.lab.example.com
tasks:
- name: Import the environment task file and set the variables
import_tasks: tasks/environment.yml
vars:
package: httpd
service: httpd
- name: Import the firewall task file and set the variables
import_tasks: tasks/firewall.yml
vars:
firewall_pkg: firewalld
firewall_svc: firewalld
rule: http
- name: Import the placeholder task file and set the variable
import_tasks: tasks/placeholder.yml
vars:
file: /var/www/html/index.html
- name: Import test play file and set the variable
import_playbook: plays/test.yml
vars:
url: 'http://servera.lab.example.com'
Role¶
rhel-system-roles
패키지에서 시스템 Role을 받을 수 있습니다.(/usr/share/ansible/roles/
)- Role은 디렉토리 구조가 중요하며, 각 용도에 맞게 사용할 수 있습니다.
- 암호나 개인키는 절대 포함되어 있으면 안됩니다.
- Ansible은 현재 경로에서 roles라는 디렉토리를 찾으며, 찾지 못할 경우 ansible.cfg에서 roles_path 값을 참고합니다.
DIRECTORY | DESCRIPTION |
---|---|
defaults | Role 변수의 기본값이 포함되어 있습니다. |
files | TASK에서 사용할 파일이 포함되어 있습니다. |
handlers | handler가 포함되어 있습니다. |
meta | Role에 대한 작성자, 라이센스, 옵션 등 역할의 종속성이 포함되어 있습니다. |
tasks | TASK가 있는 main.yml 파일이 포함되어 있습니다. |
templates | jinja2 템플릿이 포함되어 있습니다. |
tests | 테스트를 할 수 있는 inventory와 test.yml 파일이 포함되어 있습니다. |
vars | 변수를 정의하고 있는 yaml파일이 포함되어 있습니다. |
- defaults 에 선언된 변수는 우선 순위가 가장 낮으며, vars 변수는 우선 순위가 높습니다.
- 대부분의 경우 Role이 먼저 수행되고 이후 TASK가 동작하지만,
pre_tasks
로 먼저 실행할 TASK를 정의할 수 있습니다. post_tasks
를 사용하면 일반 TASK와 handlers가 모두 실행 된 후 실행 됩니다.- Role-Skeleton 은
ansible-galaxy init [role_name]
명령어로 만들 수 있습니다. requirement
파일이 있는 경우ansible-galaxy install -r [requirement] -p [role path]
형식으로 role을 install 할 수 있습니다.
$ tree user.example
user.example/
├── defaults
│ └── main.yml
├── files
├── handlers
│ └── main.yml
├── meta
│ └── main.yml
├── README.md
├── tasks
│ └── main.yml
├── templates
├── tests
│ ├── inventory
│ └── test.yml
└── vars
└── main.yml
---
- hosts: remote.example.com
roles:
- role1
- role2
- name: Play to illustrate order of execution
hosts: remote.example.com
pre_tasks:
- debug:
msg: 'pre-task'
notify: my handler
roles:
- role1
tasks:
- debug:
msg: 'first task'
notify: my handler
post_tasks:
- debug:
msg: 'post-task'
notify: my handler
handlers:
- name: my handler
debug:
msg: Running my handler
- name: Play to illustrate order of execution
hosts: remote.example.com
pre_tasks:
- debug:
msg: 'pre-task'
notify: my handler
roles:
- role1
tasks:
- debug:
msg: 'first task'
notify: my handler
post_tasks:
- debug:
msg: 'post-task'
notify: my handler
handlers:
- name: my handler
debug:
msg: Running my handler
---
- name: Configure Dev Web Server
hosts: dev_webserver
force_handlers: yes
roles:
- apache.developer_configs
pre_tasks:
- name: Check SELinux configuration
block:
- include_role:
name: rhel-system-roles.selinux
rescue:
# Fail if failed for a different reason than selinux_reboot_required.
- name: Check for general failure
fail:
msg: "SELinux role failed."
when: not selinux_reboot_required
- name: Restart managed host
reboot:
msg: "Ansible rebooting system for updates."
- name: Reapply SELinux role to complete changes
include_role:
name: rhel-system-roles.selinux