notes/ansible.md

863 lines
32 KiB
Markdown
Raw Normal View History

2022-05-07 22:12:11 +03:00
###### top
## Ansible
[Официальная документация](https://docs.ansible.com/ansible/latest/user_guide/)
***
[Базовые понятия Ansible](#part1)
- [inventory](#chapter1)
- [Модули](#chapter2)
- [Ad-hoc команды](#chapter3)
[Ansible playbook](#part2)
- [Простой playbook](#chapter4)
- [Переменные](#chapter5)
- [Отладка](#chapter6)
- [Блоки и обработка ошибок](#chapter7)
- [Асинхронные задачи](#chapter8)
- [Пример playbook - установка Docker + Docker-compose](#chapter9)
- [](#chapter10)
- [](#chapter11)
- [](#chapter12)
***
###### part1
## Базовые понятия Ansible
[вверх](#top)
Установить ansible можно с помощью пакетного менеджера, будет установлена версия, которая есть в репозиториях дистрибутива, скорее всего, эта версия не будет являться последней.
При установке через пакетный менеджер будет создана папка с параметрами по-умолчанию по пути `/etc/ansible/`.
Также, ansible можно установить с помощью pip. Будет установлена последняя версия, но папка с параметрами по-умолчанию создана не будет.
###### Chapter1
### inventory
https://docs.ansible.com/ansible/latest/user_guide/intro_inventory.html#intro-inventory
Описание хостов, на которых необходимо выполнять какие-то действия производится в файле inventory.
Синтаксис файла может быть в виде `ini` или `yaml` форматов.
Пример описания inventory с использованием ini
```dosini
my.domain.com
[mygroup]
domain.com
app.domain.com
[dbservers]
db.domain.com
```
Пример описания inventory с использованием yaml
```yaml
all:
hosts:
my.domain.com:
children:
mygroup:
hosts:
domain.com:
app.domain.com:
dbservers:
hosts:
db.domain.com:
```
Указывать хосты можно в виде диапазона
```dosini
[webservers]
www[01:50].example.com
```
Для удобства хостам можно задавать свои имена и передавать переменные которые будут доступны на хостах
```dosini
[db]
mydb env=production replicas=2
```
Можно указать параметры подключения
```dosini
[db]
myhost ansible_port=222 ansible_host=192.168.1.23
```
Переменные можно указать для группы хостов
```dosini
[db]
db1.com
db2.com
[db:vars]
env=production
```
В inventory можно указать параметры подключения
```
ansible_connection
ansible_host
ansible_port
ansible_user
ansible_password
```
Параметры подключения ssh/sftp/scp
```
ansible_ssh_private_key_file
ansible_ssh_common_args
ansible_ssh_extra_args
ansible_ssh_pipelining
ansible_ssh_executable
ansible_scp_extra_args
ansible_sftp_extra_args
```
Привилегии
```
ansible_become
ansible_become_method
ansible_become_user
ansible_become_password
ansible_become_exe
ansible_become_flags
```
Настройки shell
```
ansible_shell_type
ansible_python_interpreter
ansible_shell_executable
```
Пример файла inventory
```bash
user@host$ cat hosts.ini
```
```dosini
[deploy]
us1 ansible_host=192.168.10.151
[managers]
us2 ansible_host=192.168.10.152
us3 ansible_host=192.168.10.153
[workers]
us154 ansible_host=192.168.10.154
us155 ansible_host=192.168.10.155
[all:vars]
ansible_user=da2001
ansible_port=22
ansible_ssh_private_key_file=~/.ssh/id_rsa.pub
```
Пользовательский файл инвентаря
*Файл инвентаря по-умолчанию обычно находится в /etc/ansible/hosts, но можно использовать опцию `-i` для указания пользовательских файлов при запуске команд и плейбуков Ansible. Это удобный способ настройки индивидуального инвентаря для каждого проекта, который можно включить в системы контроля версий, такие как Git:*
```bash
ansible all -m ping -i my_custom_inventory
```
Такая опция действительна и для ansible-playbook:
```bash
ansible-playbook myplaybook.yml -i my_custom_inventory
```
Динамический файл инвентаря
*Ansible поддерживает сценарии инвентаризации для создания динамических файлов. Это полезно если инвентарь часто меняется, когда серверы создаются и уничтожаются.*
*Существуют готовые скрипты с открытым исходным кодом в официальном репозитории [Ansible GitHub](https://github.com/ansible/ansible). После загрузки требуемого сценария на Ansible control machine и настройки необходимых параметров (например, учетных данных API) можно запустить исполняемый файл в качестве пользовательского инвентаря с любой командой Ansible, которая поддерживает эту опцию.*
*Следующая команда использует скрипт инвентаря my_inventory.py с командой ping для проверки подключения ко всем текущим активным серверам:*
```bash
ansible all -m ping -i my_inventory.py
```
*Как использовать динамические файлы инвентаризации описано в [официальной документации Ansible](https://docs.ansible.com/ansible/latest/user_guide/intro_dynamic_inventory.html).*
Инвентарь можно описывать в отдельной папке и группировать хосты в отдельных файлах.
*например, можно создать папку `hosts`, в ней папку `bd` и уже в этой папке создавать файлы с описанием хостов*
Запуск в этом случае будет выглядеть так:
```bash
ansible -i hosts -m ping
```
или
```bash
ansible -i bd -m ping
```
[вверх](#top)
***
###### Chapter2
### Модули
https://docs.ansible.com/ansible/latest/user_guide/modules.html
Пример использования модулей
```bash
ansible webservers -m service -a "name=httpd state=started"
```
```bash
ansible webservers -i /home/user/ansible/hosts.ini -m command -a "/sbin/reboot -t now"
```
[Список доступных модулей на сайте Ansible](https://docs.ansible.com/ansible/2.8/modules/list_of_all_modules.html)
*описание модулей можно получить в cli, для этого используется команда*
```bash
ansible-doc -l
```
*будет выведен список доступных модулей в less подобном отображении*
Документация по конкретному модулю `ansible-doc <название модуля>`
```bash
ansible-doc user
```
[вверх](#top)
***
###### Chapter3
### Ad-hoc команды
https://docs.ansible.com/ansible/latest/user_guide/intro_adhoc.html
Структура команды
```bash
ansible -i hosts -m user -a "name=student state=present" all
^ ^ ^ ^
| | | |
инвентарь | аргументы используемые хосты
модуль
```
Создание пользователя на хостах
*если пользователь существует, вернётся сообщение зеленого цвета*
*если пользователь не существует, ansible попытается его создать. Если команда была запущена не от root, вернётся сообщение красного цвета типа __Pemission denied__*
```bash
ansible -i hosts.ini -m user -a "name=da2001 state=present" all
```
*создать нового пользователя с повышением привилегий - sudo*
```bash
ansible -i hosts.ini -m user -a "name=student state=present" -b -K all
```
`-b` - команда должна выполняться с привилегиями sudo
`-K` - запрашивать пароль sudo перед выполнением команды
*удалить пользователя*
```bash
ansible -i hosts.ini -m user -a "name=student state=absent" -b -K all
```
*передать sudo пароль в виде аргументов - extra args*
```bash
ansible -i hosts.ini -m user -a "name=student state=present" -e "ansible_become=true ansible_become_password=USER-PASS" all
```
`USER-PASS` - пароль пользователя для выполнения команды с привилегиями sudo
[вверх](#top)
***
###### part2
## Ansible playbook
[вверх](#top)
https://docs.ansible.com/ansible/latest/user_guide/playbooks_intro.html
###### Chapter4
### Простой playbook
```bash
cat user.yml
```
```bash
---
- name: user - имя playbook, произвольное
hosts: deploy - группа хостов из файла inventory
tasks: - задачи, выполняемые в этом плейбуке
- name: Create user - имя задачи, произвольное
user: - название модуля, который используется в этой задаче
name: da2001 - аргументы модуля
state: present - аргументы модуля
become: true
```
```bash
ansible-playbook -i hosts.ini user.yml -K
```
[вверх](#top)
***
###### Chapter5
### Переменные
https://docs.ansible.com/ansible/latest/user_guide/playbooks_variables.html
Переменные можно задавать в:
- playbook
- block
- tasks
- group_vars
- host_vars
- inventory
- extra_vars
- var_files
***переменная задаётся в фигурных скобках, обязательно в кавычках***
```bash
cat user.yml
```
```yaml
---
- name: user
hosts: deploy
tasks:
- name: Create user
vars:
user: da2001
user:
name: "{{ user }}"
state: present
become: true
```
Переменная может быть задана во внешнем файле
```bash
cat myvars.yml
```
```yaml
user: da2001
```
*в плейбуке необходимо указать файл с переменными*
```yaml
---
- name: user
hosts: deploy
vars_files:
- ./myvars.yml
tasks:
- name: Create user
user:
name: "{{ user }}"
state: present
become: true
```
Вместе с ansible устанавливается плагин, который позволяет считывать переменные из определённых файлов и папок без явного указания этих файлов и папок в плейбуке
- `group_vars` - папка в которой нужно создать папку с названием группы, для которой будут задаваться переменные. Например, для группы хостов `deploy` необходимо создать такой путь/файл - `./group_vars/deploy/vars.yml`
```bash
cat ./group_vars/deploy/vars.yml
```
```yaml
user: da2001
```
*в самом плейбуке нет необходимости где-то указывать ссылки на файл с переменными*
```bash
cat user.yml
```
```yaml
---
- name: user
hosts: deploy
tasks:
- name: Create user
user:
name: "{{ user }}"
state: present
become: true
```
Если переменные могут использоваться во всех группах, можно создать папку `all` - `./group_vars/all/vars.yml`
- `host_vars` - папка, в которой создаётся файл с именем хоста - `./host_vars/us1.yml`. Если для хоста имя не задано - `./host_vars/192.168.10.151.yml`
- создание переменных в файле инвентаря
*для хоста us1 добавлена переменная `user`*
```bash
cat hosts.ini
```
```
[deploy]
us1 ansible_host=192.168.10.151 user=da2001
[managers]
us2 ansible_host=192.168.10.152
us3 ansible_host=192.168.10.153
[workers]
us154 ansible_host=192.168.10.154
us155 ansible_host=192.168.10.155
[all:vars]
ansible_user=da2001
ansible_port=22
ansible_ssh_private_key_file=~/.ssh/id_rsa.pub
```
- `--extra-vars` - передача переменных в команде запуска
```bash
ansible-playbook -i hosts.ini user.yml -K --extra-vars "user=da2001"
```
- `vars-prompt` - ввод переменных в интерактивном режиме при выполнении playbook
```bash
cat user.yml
```
```yaml
---
- name: user
hosts: deploy
vars_prompt:
- name: user
prompt: "Введите имя пользователя"
private: no
tasks:
- name: Create user
user:
name: "{{ user }}"
state: present
become: true
```
`private: no` - отображать ли вводимые символы
[вверх](#top)
***
###### Chapter6
### Отладка
Очень полезно получать в консоль результат выполнения плейбука.
1. Один из способов это создание в плейбуке поля `register` с какой-либо переменной для вывода. Далее этот вывод получаем в таске `debug`. Например:
```yaml
---
- name: user
hosts: deploy
tasks:
- name: Create user
vars:
user: da2001
user:
name: "{{ user }}"
state: present
become: true
register: output
- debug:
var: output
```
После запуска плейбука
```bash
ansible-playbook -i hosts.ini user.yml -K
```
к выводу добавится значение таск `debug` с выводом значения созданной нами переменной `output`
```json
TASK [debug] ***********************************************************************************************************
ok: [us1] => {
"output": {
"append": false,
"changed": false,
"comment": "da2001",
"failed": false,
"group": 1000,
"home": "/home/da2001",
"move_home": false,
"name": "da2001",
"shell": "/bin/bash",
"state": "present",
"uid": 1000
}
}
```
2. Debugger
https://docs.ansible.com/ansible/latest/user_guide/playbooks_debugger.html#playbook-debugger
| Значение | Результат |
|----------------|-----------|
| always | всегда вызывать отладчик, независимо от результата |
| never | никогда не запускать отладчик, независимо от результата |
| on_failed | вызывать отладчик только в случае сбоя задачи |
| on_unreachable | вызывать отладчик только в том случае, если хост недоступен |
| on_skipped | вызывать отладчик только в том случае, если задача пропущена |
```yaml
---
- name: user
hosts: deploy
tasks:
- name: Create user
vars:
user: da2001
user:
name: "{{ user }}"
state: present
become: true
debugger: always
```
При наступлении условия, при котором вызывается отладчик, выполнение задачи приостанавливается и отладчик ожидает действий от пользователя
```bash
user@host:~$ ansible-playbook -i hosts.ini user.yml -K
BECOME password:
PLAY [user] ************************************************************************************************************
TASK [Gathering Facts] *************************************************************************************************
ok: [us1]
TASK [Create user] *****************************************************************************************************
ok: [us1]
[us1] TASK: Create user (debug)>
```
Отладчик принимает команды:
| Команда | Сокр. вызов | Действие |
| -------------------------- | ----------- | ------------------------------------------------------------ |
| print | p | напечатать информацию о задаче |
| task.args[*key*] = *value* | no shortcut | изменить значение аргумента |
| task_vars[*key*] = *value* | no shortcut | изменить значение переменной (далее необходимо выполнить `update_task`) |
| update_task | u | повторно создать задачу с обновленными переменными |
| redo | r | заново запустить задачу |
| continue | c | продолжить выполнение, запуск следующей задачи |
| quit | q | выход из отладчика |
*Примеры:*
```
[us1] TASK: Create user (debug)> p task
TASK: Create user
```
```
[us1] TASK: Create user (debug)> p task.args
{'_ansible_check_mode': False,
...
'_ansible_version': '2.11.6',
'name': 'da2001',
'state': 'present'}
```
```
[us1] TASK: Create user (debug)> p task.vars
{'user': 'da2001'}
```
```
[us1] TASK: Create user (debug)> p task_vars
{'ansible_all_ipv4_addresses': ['192.168.10.151'],
...
'playbook_dir': '/home/da2001/ansible/udemy/ansible',
'role_names': [],
'user': 'da2001'}}
```
```
[us1] TASK: Create user (debug)> p task_vars['user']
'da2001'
```
```
[us1] TASK: Create user (debug)> p host
us1
```
```
[us1] TASK: Create user (debug)> task_vars['user'] = 'user'
[us1] TASK: Create user (debug)> p task_vars['user']
'user'
[us1] TASK: Create user (debug)> update_task
[us1] TASK: Create user (debug)> redo
```
[вверх](#top)
***
###### Chapter7
### Блоки и обработка ошибок
Пример блока
```yaml
---
- name: user
hosts: deploy
tasks:
- name: Preconfig block
block:
- name: Create user
vars:
user: da2001
user:
name: "{{ user }}"
state: present
- name: Install curl
apt:
name: curl
update-cashe: yes
become: true
```
Условие выполнения блока
*в начале выполнения плейбука, ansible собирает факты о хостах, на основе полученных фактов можно описывать условия выполнения блоков.*
Например: добавим условие, при котором блок будет выполняться только если в качестве хоста используется Ubuntu. Для этого в блок нужно добавить:
```yaml
when: ansible_facts['distribution' == 'Ubuntu']
```
Обработка ошибок
```yaml
---
- name: user
hosts: deploy
tasks:
- name: Preconfig block
block:
- name: Create user
vars:
user: da2001
user:
name: "{{ user }}"
state: present
register: error
- name: Install curl
apt:
name: curl
update-cashe: yes
register: error
become: true
rescue:
- name: Some error print
debug:
var: error
always:
- name: Reboot
debug:
msg: "Rebooooooting"
```
Обработка ошибок происходит в блоке `rescue`. В каждом задании мы определили `register: error` - переменную, которая будет отображаться при возникновении ошибки. Т.к. в блоке `rescue` определена единственная переменная `var: error` - она будет выводиться при ошибке в любом задании. Для каждого задания можно определить свою переменную.
Блок `always` будет выполняться всегда, вне зависимости от того, были ошибки в заданиях или нет.
**Другие возможности управления ошибками**
- `any_errors_fatal` - true/false. Если установлено значение `true` - появление любой ошибки будет приводить к остановке плейбука, `false` - имеет обратное значение, плейбук попытается продолжить своё выполнение при любых ошибках.
- `ignore_errors` - true/false. В некоторых задачах можно установить параметр, чтобы игнорировать возникающие ошибки и перейти к выполнению следующей задачи.
- принудительный вызов ошибки. Можно создать отдельную задачу, в которой будет перехвачен вывод и будет сымитирована ошибка, которая приведёт к остановке выполнения плейбука. В нужное место плейбука добавить подобную задачу:
```yaml
- name: Fail on FAILED
command: echo "FAILED"
register: command_result
failed_when: "'FAILED' in command_result.stdout"
```
*при выполнении этой задачи, команда echo выведет в консоль слово `FAILED`, следующей строкой происходит перехват вывода и проверяется на соответствие условию - `'FAILED' in command_result.stdout`, если условие выполняется, сформируется ошибка и дальнейшее выполнение будет остановлено.*
[вверх](#top)
***
###### Chapter8
### Асинхронные задачи
Ansible выполняет задачи плейбука последовательно, иногда возникают ситуации, когда есть независимые друг от друга задачи, которые могут выполняться параллельно. Именно для этого используется асинхронный запуск длинных задач.
Т.е. задача запускается и ansible отключается от неё, чтобы подождать выполнение или переключиться на другую задачу.
*Пример - 1*
`async: 1000` - максимальное время в секундах, в течение которого задача должна выполниться
`poll: 5` - интервал в секундах, в течение которого ansible будет проверять состояние задачи
```bash
cat async.yml
```
```yaml
---
- name: user
hosts: deploy
tasks:
- name: Preconfig block
block:
- name: Sleep
command: sleep 15
async: 1000
poll: 5
become: true
```
```bash
user@host:~$ ansible-playbook -i hosts.ini async.yml -K
BECOME password:
PLAY [user] ************************************************************************************************************
TASK [Gathering Facts] *************************************************************************************************
ok: [us1]
TASK [Sleep] ***********************************************************************************************************
ASYNC POLL on us1: jid=290251027418.31066 started=1 finished=0
ASYNC POLL on us1: jid=290251027418.31066 started=1 finished=0
changed: [us1]
PLAY RECAP *************************************************************************************************************
us1 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
```
`ASYNC POLL on us1: jid=290251027418.31066 started=1 finished=0` - каждые 5 секунд (значение poll) ansible подключается к задаче по id (`jid=290251027418.31066`) и проверяет её статус. При этом, переключение на следующую задачу не произойдёт до завершения текущей задачи.
*Пример - 2*
*Задача 1 - спим 15 секунд, не дожидаясь окончания задачи переходим к следующей*
*Задача 2 - выполняем команду echo*
*Зачада 3 - проверяем статус выполнения первой задачи*
*`jid: "{{ sleep.ansible_job_id }}"` - получаем статус задачи __(выполнение задачи и получение её статуса важно делать от одного и того же пользователя, иначе ansible не увидит задачу. Т.е. если основная задача выполняется от sudo (`become: true`), то и проверять статус также нужно от sudo)__.*
*ждём выполнения задачи `until: job_result.finished`, при этом задаём необязательные параметры -*
*`retries: 100` - проверяем выполнение отслеживаемой задачи 100 раз*
*`delay: 1` - задержка между повторами - 1 секунда.*
```bash
cat async.yml
```
```yaml
---
- name: user
hosts: deploy
tasks:
- name: Preconfig block
block:
- name: Sleep
command: sleep 10
async: 1000
poll: 0
register: sleep
- name: Echo
command: echo "Задача выполнена"
become: true
- name: Check sleep status
async_status:
jid: "{{ sleep.ansible_job_id }}"
register: job_result
until: job_result.finished
retries: 100
delay: 1
become: true
```
```bash
user@host:~$ ansible-playbook -i hosts.ini async.yml -K
BECOME password:
PLAY [user] ************************************************************************************************************
TASK [Gathering Facts] *************************************************************************************************
ok: [us1]
TASK [Sleep] ***********************************************************************************************************
changed: [us1]
TASK [Echo] ************************************************************************************************************
changed: [us1]
TASK [Check sleep status] **********************************************************************************************
FAILED - RETRYING: Check sleep status (100 retries left).
FAILED - RETRYING: Check sleep status (99 retries left).
FAILED - RETRYING: Check sleep status (98 retries left).
FAILED - RETRYING: Check sleep status (97 retries left).
FAILED - RETRYING: Check sleep status (96 retries left).
FAILED - RETRYING: Check sleep status (95 retries left).
changed: [us1]
PLAY RECAP *************************************************************************************************************
us1 : ok=4 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
```
[вверх](#top)
***
###### Chapter9
### Пример playbook - установка Docker + Docker-compose
```yaml
---
- name: Preconfig
hosts: deploy
tasks:
- name: Установка Docker
block:
- name: Добавление репо Ububntu universe
apt_repository:
repo: "deb http://archive.ubuntu.com/ubuntu {{ ansible_distribution_release }} main universe restricted multiverse"
state: present
- name: Установка дополнительных пакетов
apt:
name:
- ca-certificates
- curl
- gnupg
- lsb-release
update_cache: true
- name: Добавление ключа Docker
apt_key:
url: https://download.docker.com/linux/ubuntu/gpg
keyring: /usr/share/keyrings/docker-archive-keyring.gpg
state: present
- name: Установка репозитория Docker
apt_repository:
repo: >
deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg]
https://download.docker.com/linux/ubuntu
{{ ansible_distribution_release }} stable
state: present
update_cache: true
filename: docker
- name: Установка Docker
apt:
name:
- docker-ce
- docker-ce-cli
- containerd.io
update_cache: true
- name: Проверка корректности установки Docker
service:
name: docker
state: restarted
enabled: true
become: true
- name: Установка Docker-compose
block:
- name: Получение последней версии Docker-compose
uri:
url: "https://api.github.com/repos/docker/compose/releases/latest"
body_format: json
register: page
- name: Установка Docker-compose
get_url:
url: "https://github.com/docker/compose/releases/download/{{ page.json.tag_name }}/docker-compose-Linux-x86_64"
dest: /usr/local/bin/docker-compose
mode: 0755
become: true
- name: Завершение установки
block:
- name: Добавление пользователя в группу Docker
user:
name: "{{ ansible_user }}"
groups: docker
append: true
- name: Перезагрузка
reboot:
become: true
```
*перечень необходимых пакетов, ссылки на репозитории и прочие данные, необходимые для установки взяты из официальной документации*
*https://docs.docker.com/engine/install/ubuntu/*
*https://docs.docker.com/compose/install/*
[вверх](#top)
***
```bash
git add . && git commit -m 'mod ansible' && git push
```