Ansible Roles and Collections
Данное практическое занятие посвящено знакомству с ролями(roles) и коллекциями(collections) в ansible.
Vagrant
Для работы с ansible воспользуемся следующим Vagrantfile
c тремя машинами:
Vagrant.configure("2") do |config|
config.vm.define "bastion" do |c|
c.vm.box = "ubuntu/lunar64"
c.vm.hostname = "bastion"
c.vm.network "private_network", type: "dhcp"
c.vm.provision "shell", inline: <<-SHELL
apt-get update -q
apt-get install -yq libnss-mdns ansible
SHELL
end
config.vm.define "node1" do |c|
c.vm.box = "ubuntu/lunar64"
c.vm.hostname = "node1"
c.vm.network "private_network", type: "dhcp"
c.vm.provision "shell", inline: <<-SHELL
apt-get update -q
apt-get install -yq libnss-mdns
SHELL
end
config.vm.define "node2" do |c|
c.vm.box = "ubuntu/lunar64"
c.vm.hostname = "node2"
c.vm.network "private_network", type: "dhcp"
c.vm.provision "shell", inline: <<-SHELL
apt-get update -q
apt-get install -yq libnss-mdns
SHELL
end
end
Все управление будем производить с машины bastion
, для взаимодействия с другими машинами
потребуется ssh
ключ, путь до которого можно узнать в выводе команды vagrant ssh-config
:
$ vagrant ssh-config bastion | grep IdentityFile
IdentityFile ~/.vagrant.d/boxes/ubuntu-VAGRANTSLASH-lunar64/0/virtualbox/vagrant_insecure_key
Скопируем данный файл в директорию проекта с Vagrantfile
с именем key
.
Все дальнейшие команды будем вводить находясь на машине bastion
, в первую очередь добавив
ключ пользователю vagrant
и выставив переменную ANSIBLE_HOST_KEY_CHECKING
в значение
False
для отключения проверки ssh
ключей.
$ install -m 600 -o vagrant /vagrant/key /home/vagrant/.ssh/id_rsa
$ export ANSIBLE_HOST_KEY_CHECKING=False
В качестве inventory
можно использовать список хостов через запятую:
$ ansible -m ping -i node1.local,node2.local all
node2.local | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false,
"ping": "pong"
}
node1.local | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false,
"ping": "pong"
}
Roles
Для того чтобы не писать однотипные плейбуки для часто повторяемых операций,
а также для логического разбиения и переиспользования в ansible
существует
концепция ролей.
Init
Роль имеет определенную структуру директорий,
чтобы создать данную структуру можно воспользоваться командой ansible-galaxy
:
$ ansible-galaxy role init --init-path roles nginx
$ ls -1 roles/nginx/
README.md # описание роли в формате markdown
defaults # переменные по-умолчанию с низким приоритетом
files # файлы используемые в задачах, например для копирования
handlers # хендлеры, выполняемы по событиям
meta # мета информация о роли, например об авторе и лицензии
tasks # основные задачи выполняемые ролью
templates # jinja2 шаблоны используемые в задачах
tests # тесты для проверки роли
vars # переменные для этой роли
После выполнения команды создалась структура каталогов для роли с именем nginx
.
Tasks
Добавим в роль установку пакета, для это допишем в файл roles/nginx/tasks/main.yml
:
---
# tasks file for nginx
- name: nginx package
ansible.builtin.package:
name: nginx
Роли можно использовать в плейбуках также как задачи(tasks) с помощью
директив roles
, include_role
и import_role
. Создадим файл playbook.yaml
:
---
- hosts: all
become: True
roles:
- name: nginx
И запустим:
$ ansible-playbook -i node1.local,node2.local playbook.yaml
PLAY [all] ********************************************************************************
TASK [Gathering Facts] ********************************************************************
ok: [node2.local]
ok: [node1.local]
TASK [nginx : nginx package] **************************************************************
changed: [node1.local]
changed: [node2.local]
PLAY RECAP ********************************************************************************
node1.local : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
node2.local : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
$ curl -s node1.local | grep title
<title>Welcome to nginx!</title>
Defaults
Для задания значений по-умолчанию к переменным используемым в роли можно отредактировать
файл roles/nginx/defaults/main.yml
:
---
# defaults file for nginx
nginx_html_dir: /var/www/html
nginx_config_file: default
nginx_template_file: index.html.j2
А также добавим соответствующие файлы
roles/nginx/files/default
:
server {
listen 80;
listen [::]:80;
server_name localhost;
location / {
root /var/www/html;
index index.html;
}
}
roles/nginx/templates/index.html.j2
:
hello from {{ ansible_host }}
Files/Templates
Добавим задачи по копированию конфигурации и index.html
в нашу роль
в файл roles/nginx/tasks/main.yml
:
---
# tasks file for nginx
- name: nginx package
ansible.builtin.package:
name: nginx
- name: nginx config
ansible.builtin.copy:
src: "{{ nginx_config_file }}"
dest: "/etc/nginx/sites-enabled/default"
- name: "index.html to {{ nginx_html_dir }}"
ansible.builtin.template:
src: "{{ nginx_template_file }}"
dest: "{{ nginx_html_dir }}/index.html"
И запустим плейбук:
$ ansible-playbook -i node1.local,node2.local playbook.yaml
PLAY [all] ********************************************************************************
TASK [Gathering Facts] ********************************************************************
ok: [node2.local]
ok: [node1.local]
TASK [nginx : nginx package] **************************************************************
ok: [node2.local]
ok: [node1.local]
TASK [nginx : nginx config] ***************************************************************
changed: [node1.local]
changed: [node2.local]
TASK [nginx : index.html to /var/www/html] ************************************************
changed: [node1.local]
changed: [node2.local]
PLAY RECAP ********************************************************************************
node1.local : ok=4 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
node2.local : ok=4 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
$ curl -s node1.local
hello from node1.local
$ curl -s node2.local
hello from node2.local
Для переопределения значений по-умолчанию расположим в директориях files
и templates
рядом с плейбуком
templates/overrided.html.j2
:
### hello from {{ ansible_host }} !
files/overrided.conf
:
server {
listen 80;
listen [::]:80;
listen 8080;
server_name localhost;
location / {
root /var/www/html;
index index.html;
}
}
И добавим переменные в плейбук:
---
- hosts: all
become: True
roles:
- name: nginx
nginx_template_file: overrided.html.j2
nginx_config_file: overrided.conf
Запустим:
$ ansible-playbook -i node1.local,node2.local playbook.yaml
PLAY [all] ********************************************************************************
TASK [Gathering Facts] ********************************************************************
ok: [node2.local]
ok: [node1.local]
TASK [nginx : nginx package] **************************************************************
ok: [node2.local]
ok: [node1.local]
TASK [nginx : nginx config] ***************************************************************
changed: [node2.local]
changed: [node1.local]
TASK [nginx : index.html to /var/www/html] ************************************************
changed: [node2.local]
changed: [node1.local]
PLAY RECAP ********************************************************************************
node1.local : ok=4 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
node2.local : ok=4 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
$ curl node1.local
### hello from node1.local !
$ curl node2.local
### hello from node2.local !
$ curl node1.local:8080
curl: (7) Failed to connect to node1.local port 8080 after 3 ms: Couldn't connect to server
Как видно мы переопределили значения из роли не меняя саму роль. Но измененная
конфигурация nginx
хоть и скопировалась, но не применилась.
Handlers
Для перезапуска nginx
при изменении конфигурации добавим в файл
roles/nginx/handlers/main.yml
:
---
# handlers file for nginx
- name: nginx restart
ansible.builtin.systemd:
name: nginx
state: restarted
И соответственно в задачу по записи конфигурации отправку события в файле
roles/nginx/tasks/main.yml
:
---
# tasks file for nginx
- name: nginx package
ansible.builtin.package:
name: nginx
- name: nginx config
ansible.builtin.copy:
src: "{{ nginx_config_file }}"
dest: "/etc/nginx/sites-enabled/default"
notify: nginx restart
- name: "index.html to {{ nginx_html_dir }}"
ansible.builtin.template:
src: "{{ nginx_template_file }}"
dest: "{{ nginx_html_dir }}/index.html"
Теперь можем изменить нашу конфигурацию в файле files/overrided.conf
:
server {
listen 80;
listen 8080;
server_name localhost;
location / {
root /var/www/html;
index index.html;
}
}
И запустить плейбук:
$ ansible-playbook -i node1.local,node2.local playbook.yaml
PLAY [all] ********************************************************************************
TASK [Gathering Facts] ********************************************************************
ok: [node1.local]
ok: [node2.local]
TASK [nginx : nginx package] **************************************************************
ok: [node1.local]
ok: [node2.local]
TASK [nginx : nginx config] ***************************************************************
changed: [node1.local]
changed: [node2.local]
TASK [nginx : index.html to /var/www/html] ************************************************
ok: [node1.local]
ok: [node2.local]
RUNNING HANDLER [nginx : nginx restart] ***************************************************
changed: [node2.local]
changed: [node1.local]
PLAY RECAP ********************************************************************************
node1.local : ok=5 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
node2.local : ok=5 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
$ curl node1.local:8080
### hello from node1.local !
$ curl node2.local:8080
### hello from node2.local !
Таким образом мы получили роль, которую можно переиспользовать с другими значениями переменных.
Collections
Коллекции в ansible
- это способ создания переносимых дистрибутивов,
которые могут включать в себя плейбуки, роли, модули и плагины.
Перед работой с коллекциями пересоздадим виртуальные машины командами vagrant destroy -f
и vagrant up
. Примеры для работы с коллекциями также будут созданы с нуля в пустой
директории.
Install
Коллекциями как и ролями можно управлять с помощью утилиты
ansible-galaxy
. Данная утилита по-умолчанию использует публичный
репозиторий galaxy.ansible.com, в котором можно найти
готовые коллекции от комьюнити. Установим из данного репозитория коллекцию
nginxinx.nginx_core:
$ ansible-galaxy collection install nginxinc.nginx_core
Starting galaxy collection install process
Process install dependency map
Starting collection install process
Downloading https://galaxy.ansible.com/api/v3/plugin/ansible/content/published/collections/artifacts/nginxinc-nginx_core-0.8.0.tar.gz to /home/vagrant/.ansible/tmp/ansible-local-31197c0gyeuh/tmpfiqc995o/nginxinc-nginx_core-0.8.0-o05zpo12
Installing 'nginxinc.nginx_core:0.8.0' to '/home/vagrant/.ansible/collections/ansible_collections/nginxinc/nginx_core'
Downloading https://galaxy.ansible.com/api/v3/plugin/ansible/content/published/collections/artifacts/ansible-posix-1.5.4.tar.gz to /home/vagrant/.ansible/tmp/ansible-local-31197c0gyeuh/tmpfiqc995o/ansible-posix-1.5.4-wvjd1tyo
nginxinc.nginx_core:0.8.0 was installed successfully
Installing 'ansible.posix:1.5.4' to '/home/vagrant/.ansible/collections/ansible_collections/ansible/posix'
Downloading https://galaxy.ansible.com/api/v3/plugin/ansible/content/published/collections/artifacts/community-crypto-2.15.1.tar.gz to /home/vagrant/.ansible/tmp/ansible-local-31197c0gyeuh/tmpfiqc995o/community-crypto-2.15.1-h5a72qdu
ansible.posix:1.5.4 was installed successfully
Installing 'community.crypto:2.15.1' to '/home/vagrant/.ansible/collections/ansible_collections/community/crypto'
Downloading https://galaxy.ansible.com/api/v3/plugin/ansible/content/published/collections/artifacts/community-general-7.5.0.tar.gz to /home/vagrant/.ansible/tmp/ansible-local-31197c0gyeuh/tmpfiqc995o/community-general-7.5.0-3w9itazy
community.crypto:2.15.1 was installed successfully
Installing 'community.general:7.5.0' to '/home/vagrant/.ansible/collections/ansible_collections/community/general'
community.general:7.5.0 was installed successfully
Usage
Использовать коллекции в плейбуке можно объявив их в директиве collections
в play
,
либо указывая полное имя непосредственно в месте использования:
---
- hosts: all
become: True
collections:
- nginxinc.nginx_core
roles:
- name: nginxinc.nginx_core.nginx
- name: nginx_config
nginx_config_http_template_enable: true
nginx_config_http_template:
- config:
servers:
- core:
listen:
- port: 80
server_name: localhost
locations:
- location: /
core:
root: /usr/share/nginx/html
index: index.html
Здесь мы используем две роли из коллекции nginxinc.nginx_core
:
nginx
- используя полное имя, данная роль установит самnginx
nginx_config
- использую только имя роли, так как имя коллекции было объявлено ранее, данная роль позволяет сконфигурироватьnginx
Запустим получившийся плейбук:
$ ansible-playbook -i node1.local,node2.local playbook.yaml
PLAY [all] ********************************************************************************
TASK [Gathering Facts] ********************************************************************
ok: [node1.local]
ok: [node2.local]
TASK [nginxinc.nginx_core.nginx : Validate distribution and role variables] ***************
...
RUNNING HANDLER [nginxinc.nginx_core.nginx_config : (Handler - NGINX Config) Start/reload NGINX] ***
changed: [node1.local]
changed: [node2.local]
TASK [nginxinc.nginx_core.nginx_config : Debug output] ************************************
skipping: [node1.local]
skipping: [node2.local]
PLAY RECAP ********************************************************************************
node1.local : ok=22 changed=2 unreachable=0 failed=0 skipped=37 rescued=0 ignored=1
node2.local : ok=22 changed=2 unreachable=0 failed=0 skipped=37 rescued=0 ignored=1
$ curl -s node1.local | grep title
<title>Welcome to nginx!</title>
$ curl -s node2.local | grep title
<title>Welcome to nginx!</title>
Также ознакомившись с документацией к роли nginx_config
, либо же
посмотреть на список определенных переменных, можно узнать что
авторы данной роли позволяют параметризовать. Добавим также шаблон для index.html
,
как мы это делали в собственной роли в файле templates/index.html.j2
рядом с плейбуком:
hello from {{ ansible_host }}
И добавим переменные для роли в плейбук:
---
- hosts: all
become: True
collections:
- nginxinc.nginx_core
roles:
- name: nginxinc.nginx_core.nginx
- name: nginx_config
nginx_config_http_template_enable: true
nginx_config_http_template:
- config:
servers:
- core:
listen:
- port: 80
server_name: localhost
locations:
- location: /
core:
root: /usr/share/nginx/html
index: index.html
nginx_config_html_demo_template_enable: true
nginx_config_html_demo_template:
- template_file: index.html.j2
deployment_location: /usr/share/nginx/html/index.html
Запустим плейбук:
$ ansible-playbook -i node1.local,node2.local playbook.yaml
PLAY [all] ********************************************************************************
TASK [Gathering Facts] ********************************************************************
ok: [node2.local]
ok: [node1.local]
...
PLAY RECAP ********************************************************************************
node1.local : ok=22 changed=1 unreachable=0 failed=0 skipped=34 rescued=0 ignored=1
node2.local : ok=22 changed=1 unreachable=0 failed=0 skipped=34 rescued=0 ignored=1
$ curl node1.local
hello from node1.local
$ curl node2.local
hello from node2.local
Как видно используя роли и коллекции можно переиспользовать код (в том числе подготовленный комьюнити и загруженный в ansible galaxy) для автоматизации развертывания инфраструктурных приложений.