###### 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 ```