Thank you for reading this post, don't forget to subscribe!
Инструкция представляет из себя шпаргалку по работе с Ansible
Получение информации
Сюда войдут примеры, которые позволят собирать информацию, выводить ее на экран, помогать в отладке и всякое такое.
1. Сбор информации о системе.
Чтобы собрать информацию о системе, на которой выполняется сценарий ansible, нужно использовать модуль setup, например:
1 2 |
- name: Get service facts setup: |
Или можно применить фильтр, чтобы сбор выполнялся быстрее:
1 2 3 |
- name: Get service facts setup: filter: 'ansible_os_family' |
О setup: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/setup_module.html.
2. Показать информацию об удаленной системе, на которой запускается ansible.
Выполняется с помощью модуля debug, который должен показать содержимое ansible_facts:
1 2 3 |
- name: Print all available facts debug: var: ansible_facts |
* ansible_facts содержит массив данных с информацией о системе. Однако, функция сборки информации может быть отключена (так как на ее работу тратится, относительно, много времени) в настройках плейбука с помощью опции gather_facts: false — в этом случае, значение нужно изменить на true.
Также мы можем обратиться к конкретному элементу массива ansible_facts, получив информацию о конкретной настройке или опции:
1 2 |
... ansible_facts.hostname |
* например, имя компьютера.
Ссылка на документацию: https://docs.ansible.com/ansible/latest/user_guide/playbooks_vars_facts.html.
3. Отображение на экран переменной.
Выше мы уже использовали debug для отображения переменной — принцип тот же:
1 2 3 |
- name: Show Value of Variable debug: msg: "{{ variable }}" |
* при выполнении задачи на экране мы увидим значение переменной variable. Обратите внимание, что запись ansible.builtin.debug и debug — это одно и то же, то есть, ansible.builtin можно не писать.
О debug: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/debug_module.html.
4. Получить список сервисов.
Для этого существует service_facts:
1 2 3 4 5 6 |
- name: Populate service facts ansible.builtin.service_facts: - name: Print all available facts debug: var: ansible_facts.services |
* цель достигается двумя задачами. В первой мы собираем информацию о сервисах с помощью service_facts, второй — выводим на экран содержимое.
О service_facts: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/service_facts_module.html.
5. Сохранение результата при выполнении задания.
Создать и использовать переменную можно в процессе выполнения работы с помощью директивы register:
1 2 3 |
- name: Run a shell command and register its output as a variable shell: command register: command_result |
Также нам может понадобиться сохранить в переменную результат обработки переменной:
1 2 3 |
- name: Set variable set_fact: my_hosts: "{{ command_result.stdout }}" |
* в данном случае мы взяли переменную, которую получили в примере выше и разбили строку по знаку ":". Таким образом, мы получим массив данных, который будет сохранен в переменную my_hosts.
Работа с переменными
В сценариях нам постоянно нужно будет использовать переменные. Их можно передавать при запуске, получать в процессе выполнения задач или описать в самом сценарии заранее. Рассмотрим эти действия на некоторых примерах.
1. Переменные в ролях.
Для описания переменных в ролях существуют два каталога — vars и defaults. Последние имеют самый низкий приоритет, это значит, что их можно переопределить.
Независимо от каталога, в котором идет описание переменной, синтаксис один:
1 |
hostname: mail.test.local |
* в данном примере определена переменная hostname со значением mail.test.local.
2. Переменные в файле inventory.
Инвентарный файл используется для описания хостов, которые могут быть задействованы при работе сценариев ansible. Переменная может быть определена на уровне всех хостов или на уровне конкретного хоста:
1 2 3 4 5 6 7 8 |
all: vars: tower_user_name: master hosts: 192.168.0.10: human_name: mail 192.168.0.11: human_name: www |
* в нашем примере будут определены 3 переменные:
- tower_user_name для всех машин.
- human_name для 192.168.0.10.
- human_name для 192.168.0.11.
3. Переменные в плейбуке.
Задаются в файле плейбука:
1 2 3 4 |
- hosts: all ... vars: vm_name: test-vm |
* в данном примере это переменная vm_name.
4. При запуске плейбука.
Когда мы запускаем на выполнение наш плейбук, мы можем передать переменные с помощью опции extra-vars:
1 |
ansible-playbook ... --extra-vars "{ 'address':'test.local' }" |
* передаем переменную address.
Проверки, условия и действия на основе этих проверок
В данную группу войдут действия, которые помогут нам ограничить выполнение задач или изменить ход выполнения.
1. Проверка на пустую папку.
Задачи сводится к двум операциям:
- получении списка файлов в целевом каталоге (например, с помощью команды ls) и регистрации полученного значения в переменную с помощью register.
- проверка содержимого переменной, которую мы получили на шаге 1 с помощью when.
Пример будет таким:
1 2 3 4 5 6 7 8 9 |
- name: Register Contents of PGDATA Folder shell: ls /var/lib/postgresql/11/main register: pg_contents - name: Init PostgreSQL DB shell: /opt/pgpro/std-11/bin/pg-setup initdb environment: PGDATA: "/var/lib/postgresql/11/main" when: pg_contents["stdout_lines"] | length == 0 |
* в данном примере мы в первой задаче выводим содержимое каталога /var/lib/postgresql/11/main и помещаем его в переменную pg_contents. Во второй задаче мы уже проверяем с помощью when количество строк — если их 0, то тогда выполняем команду initdb. На практике, это важно, так как инициализация базы PostgreSQL при непустом каталоге выводит сообщение об ошибке.
О when: https://docs.ansible.com/ansible/latest/user_guide/playbooks_conditionals.html.
2. Проверить, определена ли переменная.
Для этого используется опция is defined (определена) или is not defined (не определена):
1 2 3 |
when: pgpro is defined when: pgpro is not defined |
* в данном примере мы проверим наличие переменной pgpro. На практике такая проверка имеет значение, так как если мы попробуем выполнить действия с несуществующей переменной, Ansible нам вернет ошибку.
В официальной документации про это сказано в статье о when (ссылка выше).
3. Выполнение команды, если сервис в рабочем состоянии.
Нам необходимо получить информацию о службах с помощью service_facts, после чего можно уже делать проверку с помощью when:
1 2 3 4 5 6 |
- name: Populate service facts ansible.builtin.service_facts: - name: Stop Service If Running One shell: systemctl stop apache2 when: ansible_facts.services["apache2.service"] is defined and ansible_facts.services["apache2.service"].state == "running" |
* в данном примере мы проверим, есть ли служба apache2 и запущена ли она. Если это так, то мы ее останавливаем.
Подробнее о service_facts можно прочитать в документации (ссылка выше в разделе 4. Получить список сервисов).
4. Существует ли файл.
Проверка может быть выполнена с помощью stat:
1 2 3 4 5 6 7 8 |
- name: Register File Stat stat: path: /etc/nginx/nginx.conf register: stat_result - name: Cat file if exists shell: cat /etc/nginx/nginx.conf when: stat_result.stat.exists |
* в данном примере будет выполнено чтение файла /etc/nginx/nginx.conf, если он существует.
О stat: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/stat_module.html.
5. Операционная система.
С помощью переменных ansible_os_family и ansible_distribution_major_version мы можем получить информацию об операционной системы, которая работает на целевом компьютере. Это можно использовать для выполнения проверок и задания условий.
а) Проверка по семейству:
1 2 3 4 5 6 7 |
- name: Run task1 if OS is Debian ... when: ansible_os_family == "Debian" - name: Run task2 if OS is Red Hat ... when: ansible_os_family == "RedHat" |
* в данном примере мы запустим задачу task1 в системах на основе Debian и task2 — на основе RedHat.
б) Проверка по версии системы:
1 2 3 4 5 6 7 |
- name: Run task1 if OS is Debian 9 ... when: ansible_os_family == "Debian" and ansible_distribution_major_version == "9" - name: Run task2 if OS is Red Hat 8 ... when: ansible_os_family == "RedHat" and ansible_distribution_major_version == "8" |
* в данном примере мы запустим задачу task1 в системах на основе Debian версии 9 и task2 — на основе RedHat версии 8.
6. Выдать ошибку и остановить выполнение.
Для этого предусмотрен модуль fail или опция задания failed_when. Рассмотрим примеры использования обоих.
а) Модуль fail. Является отдельным заданием. Его можно выполнить просто так, но без условия его применение нелогично. Пример:
1 2 3 4 |
- name: Stop the executing if variable is empty fail: msg: The variable srv_ip is not defined or its empty. when: srv_ip is not defined or srv_ip == "" |
* в нашем примере, если переменная srv_ip окажется пустой или неопределенной, мы получим ошибку с сообщением, определенным в опции msg.
О fail: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/fail_module.html.
б) Опция failed_when. Задает условие, при соблюдении которого задание выполняется с ошибкой, например:
1 2 3 4 |
- name: Fail task when the command error output prints FAILED ansible.builtin.command: /usr/bin/example-command -x -y -z register: command_result failed_when: "'FAILED' in command_result.stderr" |
* данный пример с официального сайта ansible (ссылка ниже). Задание выдаст ошибку, если в результате выполнения скрипта /usr/bin/example-command будет текст FAILED.
О failed_when: https://docs.ansible.com/ansible/latest/user_guide/playbooks_error_handling.html.
7. Ожидания с помощью wait_for.
Мы можем создать задание, которое остановит выполнение плейбука, пока не будет выполнено определенное условие:
- Просто ждать определенное время.
- Удачное соединение по сети с узлом.
- Определенное содержимое в файле.
- Пока не появится или удалится файл или папка.
Например:
1 2 3 |
- name: Wait rpm file wait_for: path: /tmp/build/my_package.rpm |
* в данном примере выполнение плейбука остановится, пока по пути /tmp/build/my_package.rpm не появится файл.
О wait_for https://docs.ansible.com/ansible/2.3/wait_for_module.html.
Установки пакетов, модулей и расширений
В данном разделе мы коснемся всего, что приводит к установке чего бы то ни было. А именно:
- Установки пакетов в систему.
- Загрузки исходников.
- Установке дополнительных модулей.
- Распаковке архивов.
Рассмотрим это подробнее.
1. Установка/удаление пакетов в систему.
Выполняется с помощью универсального модуля package или с помощью специализированных yum и apt. Последние имеют больше полезных опций.
а) package:
1 2 3 4 |
- name: Install NTP-client package: name: chrony state: latest |
* по данной инструкции в нашей системе должен быть установлен пакет chrony последней версии.
Для установки пакета конкретной версии, ее нужно указать в названии пакета:
1 2 3 4 |
- name: Install NTP-client package: name: chrony-3.4 state: present |
О package: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/package_module.html.
б) yum:
1 2 3 4 |
- name: Install NTP-client yum: name: chrony state: present |
1 2 3 4 |
- name: Install NTP-client yum: name: chrony state: present |
О yum: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/yum_module.html.
в) apt:
1 2 3 4 |
- name: Install NTP-client apt: name: chrony state: present |
Если нам нужно установить пакет из файла deb, синтаксис будет следующий:
1 2 3 4 |
- name: "Install lib for postgresql" apt: deb: http://ftp.ru.debian.org/debian/pool/main/l/llvm-toolchain-7/libllvm7_7.0.1-8+deb10u2_amd64.deb state: present |
* в данном примере мы устанавливаем пакет libllvm7 из файла deb, который доступен по url http://ftp.ru.debian.org/debian/pool/main/l/llvm-toolchain-7/libllvm7_7.0.1-8+deb10u2_amd64.deb.
Об apt: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/apt_module.html.
С помощью опции state мы можем управлять поведением установки:
- present — пакет должен быть установлен.
- latest — должна быть установлена последняя версия.
- absent — пакет должен быть удален.
2. Работа с репозиториями RPM.
а) Добавление репозитория:
1 2 3 4 5 6 7 8 9 |
- name: "Add YUM repository" yum_repository: name: new_yum_repo description: "New Yum Repo Description" file: new_repo_file baseurl: https://download.fedoraproject.org/pub/epel/$releasever/$basearch/ enabled: yes gpgcheck: no validate_certs: no |
О yum_repository: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/yum_repository_module.html.
б) Для удаления репозитория используем модуль yum:
1 2 3 4 |
- name: "Remove YUM repositories" yum: name: pgsql95 state: absent |
* в нашем примере будет удален репозиторий с названием pgsql95.
О yum: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/yum_module.html.
3. Работа с репозиториями DEB.
а) Добавление репозитория:
1 2 3 4 |
- name: "Add DEB repository" apt_repository: repo: deb http://dl.google.com/linux/chrome/deb/ stable main filename: new_deb_repo |
О apt_repository: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/apt_repository_module.html.
б) Для удаления репозитория нужно воспользоваться директивой state со значением absent, например:
1 2 3 4 |
- name: "Remove Debian default repository" apt_repository: repo: deb https://ftp.debian.org/debian stretch main contrib state: absent |
* в данном примере будет удален репозиторий, который идет по умолчанию для Debian Stretch.
в) Импорт ключа.
Рассмотрим примеры с добавлением gpg-ключа по URL и с сервера ключей:
1 2 3 4 |
- name: Import postgresql repo key apt_key: url: https://www.postgresql.org/media/keys/ACCC4CF8.asc state: present |
* импорт ключа из файла https://www.postgresql.org/media/keys/ACCC4CF8.asc.
1 2 3 4 5 |
- name: Import keyserver.ubuntu.com repo keys apt_key: keyserver: keyserver.ubuntu.com id: 648ACFD622F3D138 state: present |
* импорт ключа с идентификатором 648ACFD622F3D138 из сервера ключей keyserver.ubuntu.com.
О apt_key: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/apt_key_module.html.
4. Установка модуля в nodejs.
Установка модулей в nodejs выполняется с помощью npm. Для него в ansible есть отдельная функция:
1 2 3 4 |
- name: Install nodejs modules. npm: name: newman global: yes |
* в данном примере будет выполнена установка newman, которая будет доступна всем проектам (опция global).
О nodejs npm: https://docs.ansible.com/ansible/latest/collections/community/general/npm_module.html.
5. Установка расширений для python.
Используем модуль pip. Рассмотрим несколько примеров.
а) установка пакета python:
1 2 3 4 |
- name: Pip install psycopg2 pip: name: psycopg2 state: present |
* в данном примере будет установлен psycopg2.
б) обновление пакетов:
1 2 3 4 5 6 7 8 |
- name: Upgrade pip and wheel pip: name: "{{ item }}" extra_args: --upgrade executable: pip3 loop: - pip - wheel |
* в нашем примере будут обновлены пакеты pip и wheel.
в) использовать определенную версию pip:
1 2 3 4 5 |
- name: Install python modules with pip3 pip: name: patroni[consul] executable: pip3 state: present |
О pip: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/pip_module.html.
6. Распаковка архива.
Выполняется с помощью unarchive:
1 2 3 4 5 6 |
- name: Unpacking Nginx Source unarchive: src: "http://nginx.org/download/nginx-{{ nginx_ver }}.tar.gz" dest: /tmp/ remote_src: yes creates: /tmp/nginx-{{ nginx_ver }}.tar.gz |
* в данном примере мы распакуем исходник для nginx в каталог /tmp. Обратите внимание на две вещи:
- Мы используем переменную nginx_ver. Данная переменная должна быть определена при запуске плейбука, или в инвентарном файле, или в var, или в default. Подробнее в соответствующем разделе выше.
- Опция creates позволит не выполнять операцию, если существует файл /tmp/nginx-{{ nginx_ver }}.tar.gz.
Об unarchive: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/unarchive_module.html.
Настройка системы
В данном разделе мы рассмотрим процессы, которые больше подходят для категории настройки системы.
1. Добавить задание в cron.
Выполняется с помощью модуля cron:
1 2 3 4 5 6 7 8 9 10 |
- name: Add Job for Run Command cron: name: Start Script job: "/scripts/command.sh" user: root minute: "0" hour: "*/6" day: "*" month: "*" weekday: "*" |
* в данном примере мы создадим задание для запуска команды /scripts/command.sh каждый день, каждые 6 часов.
О cron: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/cron_module.html.
2. Добавить публичный ключ хоста в known_hosts.
Делается с помощью known_hosts. Пример из официальной документации:
1 2 3 4 5 |
- name: Tell the host about our servers it might want to ssh to known_hosts: path: /etc/ssh/ssh_known_hosts name: foo.com.invalid key: "{{ lookup('file', 'pubkeys/foo.com.invalid') }}" |
* в данном примере мы добавим ключ из файла pubkeys/foo.com.invalid в /etc/ssh/ssh_known_hosts.
О known_hosts: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/known_hosts_module.html.
3. Создание новых SSH-ключей для сервера.
Создание ключей реализуется с помощью модуля openssh_keypair:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
- name: Generate New SSH Host Keys openssh_keypair: path: "/etc/ssh/ssh_host_{{ item.type }}_key" owner: root state: present type: "{{ item.type }}" size: "{{ item.size }}" force: yes loop: - { type: dsa, size: 1024 } - { type: ecdsa, size: 521 } - { type: ed25519, size: 2048 } - { type: rsa, size: 2048 } |
* в данном примере мы создадим 4 ключа разных типов: dsa, ecdsa, ed25519, rsa. Так как у каждого из них свои требования к размеру, перечень представлен в виде двумерного массива. Ключи будут созданы в каталоге /etc/ssh/.
О openssh_keypair: https://docs.ansible.com/ansible/latest/collections/community/crypto/openssh_keypair_module.html.
4. Работа с SSH authorized_key.
Данный файл содержит публичный ключ для подключения по SSH. Работа с ним через Ansible выполняется с помощью модуля authorized_key.
Пример добавления ключа:
1 2 3 4 5 6 7 |
- name: Set authorized key took from file authorized_key: user: root state: present key: '{{ item }}' with_file: - files/key.pub |
* в данном примере мы берем содержимое файла files/key.pub и устанавливаем его для пользователя root.
Об authorized_key: https://docs.ansible.com/ansible/2.4/authorized_key_module.html.
5. Создание учетной записи.
Для этого есть модуль user. У него много опций, рассмотрим некоторые из них.
а) Простая учетная запись:
1 2 3 4 5 |
- name: Create User1 user: name: user1 shell: /bin/bash create_home: yes |
* в данном примере мы создадим пользователя user1 с домашней директорией. Также мы указали для использования командную оболочку /bin/bash.
б) Для создания системной учетной записи нам достаточно:
1 2 3 4 5 |
- name: Create User Consul user: name: consul system: yes comment: "Consul Agent" |
* в данном примере будет создана учетная запись consul.
О user: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/user_module.html.
6. Работа с systemd.
Для данной настройки есть одноименный модуль systemd. Рассмотрим варианты его использования.
а) перечитать конфигурацию (необходимо делать каждый раз, когда мы меняем настройки юнита):
1 2 3 |
- name: systemd reload systemd: daemon_reload: yes |
б) разрешить сервис (автозапуск):
1 2 3 4 |
- name: mysql enable systemd: name: mysql enabled: yes |
* для сервиса mysql.
в) перезапустить сервис:
1 2 3 4 |
- name: mysql reload systemd: name: mysql state: restarted |
г) остановить сервис:
1 2 3 4 |
- name: mysql stoped systemd: name: mysql state: stopped |
О systemd: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/systemd_module.html.
7. Настройка брандмауэра.
Выполняется разными модулями в зависимости от используемой системы управления netfilter:
- firewalld
- iptables
- ufw
Рассмотрим небольшие примеры.
а) firewalld:
1 2 3 4 5 |
- name: permit traffic in default zone for https service firewalld: service: https permanent: yes state: enabled |
Подробнее: https://docs.ansible.com/ansible/latest/collections/ansible/posix/firewalld_module.html.
б) iptables:
1 2 3 4 5 |
- name: Block specific IP iptables: chain: INPUT source: 8.8.8.8 jump: DROP |
Подробнее: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/iptables_module.html.
в) UFW.
Добавить 80 порт:
1 2 3 4 5 |
- name: Allow all access to tcp port 80 ufw: rule: allow port: '80' proto: tcp |
Добавить порты с циклом:
1 2 3 4 5 6 7 8 |
- name: Allow Ports in Firewall ufw: rule: allow port: "{{ item.port }}" proto: "{{ item.proto }}" comment: "{{ item.comment }}" loop: - { port: 5432, proto: tcp, comment: 'PostgreSQL' } |
Подробнее: https://docs.ansible.com/ansible/latest/collections/community/general/ufw_module.html.
Работа с папками и файлами
Рассмотрим задачи, которые помогут нам создавать, копировать и работать с файлами.
1. Создание каталогов и файлов.
Создание файлов и каталогов выполняется с помощью модуля file.
а) для каталога в качестве state указываем directory:
1 2 3 4 5 6 7 8 9 10 |
- name: Create Directories file: path: "{{ item }}" state: directory owner: www-data group: www-data mode: 0755 loop: - '/var/www/site1' - '/var/www/site2' |
* в данном примере мы создадим 2 каталога: site1 и site2 в каталоге /var/www.
б) для создания файла убираем опцию state (или даем ей значение touch):
1 2 3 4 5 6 7 |
- name: Create File file: path: "/var/www/site1/index.php" state: touch owner: www-data group: www-data mode: 0644 |
* в данном примере мы созданим файл index.php в каталоге /var/www/site1.
в) для создания симлинка используем state со значением link:
1 2 3 4 5 |
- name: Create a symbolic link from foo to bar file: src: /usr/bin/foo dest: /usr/sbin/bar state: link |
О file: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/file_module.html.
2. Задать права.
Это можно выполнить с помощью модуля создания файла или каталога:
1 2 3 4 5 6 |
- name: Set File Rights file: path: "/var/www/site1/index.php" owner: www-data group: www-data mode: 0664 |
* обратите внимание, это пример из предыдущено раздела. Для созданного файла мы просто немного изменили права.
3. Копирование файлов из каталога.
Для копирования данных мы используем модуль copy:
1 2 3 4 5 6 7 8 9 10 |
- name: Copy Cert File If Different copy: src: "{{ item }}" dest: /etc/ssl/test remote_src: no mode: 0644 owner: root group: root with_fileglob: - files/* |
* в данном примере мы прочитаем все содержимое каталога files на компьютере с ansible, и скопируем его в каталог /etc/ssl/test на целевом компьютере.
О copy: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/copy_module.html.
4. Используем шаблон.
Копирование из шаблона отличается от копирования из файла тем, что в шаблоне могут использоваться переменные, которые будет заменяться их значениями в момент копирования. Для самого процесса копирования из шаблона используется модуль template:
1 2 3 4 |
- name: Create Config for Consul Agent template: src: templates/consul/config.json.j2 dest: /etc/consul.d/config.json |
* в данном примере мы возьмом шаблон templates/consul/config.json.j2 на компьютере ansible и разместим его в по пути /etc/consul.d/config.json на целевом компьютере.
Мы можем вывести в консоль результат обработки шаблона следующим образом:
1 2 3 |
- name: Show Templating Results debug: msg: "{{ lookup('template', './config.json.j2') }}" |
О template: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/template_module.html.
5. Архивирование.
Создать архив из файла или каталога можно с помощью модуля archive:
1 2 3 4 5 |
- name: "Use gzip to compress folder" archive: path: /etc/raddb dest: "/tmp/raddb.gz" format: gz |
* в данном примере мы создадим архив из каталога /etc/raddb и сохраним его в файл /tmp/raddb.gz.
О archive: https://docs.ansible.com/ansible/latest/collections/community/general/archive_module.html.
Для распаковки архивов используется модуль unarchive, о котором мы говорили выше.
6. Поиск файлов и папок.
Выполняется с помощью модуля find. Особый интерес представляет в контексте поиска файлов и выполнение над ними определенных действий. Разберем несколько примеров.
а) Удалить последние 30 файлов. Задача решается в два этапа:
- ищем содержимое целевого каталога.
- сотритуем список найденных по времени изменения файлов и удаляем все, что идут после определенного числа объектов.
Поиск выполняем с помощью модуля find, удаление — file:
1 2 3 4 5 6 7 8 9 10 11 |
- name: "Get list of backup files" find: paths: "/backup" file_type: file register: founds - name: "Delete last 30 Copies" file: path: "{{ item }}" state: absent loop: "{{ (founds.files | sort(attribute='mtime', reverse=True) | map(attribute='path') | list )[30:] }}" |
* в данном примере мы ищем файлы в каталоге /backup, после чего сортируем найденное и удаляем по списку все файлы, которые идут после 30-го.
б) Удаление архивов для логов. Также выполняем в два этапа — более сложный поиск и удаление с помощью file:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
- name: "Get a list of logs to be deleted" find: paths: "/var/log" file_type: file patterns: '*.gz,*.log-*,*.old,*.[0-9].log,*.log.[0-9],*-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]' recurse: yes register: logs_to_delete - name: "Delete logs" file: path: "{{ item }}" state: absent loop: "{{ logs_to_delete.files | map(attribute='path') | list }}" |
* в данном примере мы применили регулярные выражения для поиска различных вариантов логов, которые являются архивными. После мы сохраняем результат поиска в переменную logs_to_delete, которую используем для получения списка путей до найденных файлов.
О find: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/find_module.html.
7. Скачать файл с помощью curl.
Для этого используется модуль uri. Простой пример:
1 2 3 4 |
- name: CURL simple download file uri: url: https://www.test.ru/files/winsetupfromusb.zip dest: /tmp |
* в данном примере мы загрузим файл https://www.test.ru/files/winsetupfromusb.zip в каталог /tmp.
Пример посложнее:
1 2 3 4 5 6 7 8 9 |
- name: CURL download file with token auth uri: url: https://gitlab.test.ru/api/v4/projects/555/repository/files/folder%2Fpath%2Fdata.sql/raw?ref=master dest: /tmp/data.sql owner: test group: test mode: 640 headers: PRIVATE-TOKEN: access-token |
* в данном примере мы скачаем файл с ресурса, где требуется аутентификация по токену, который передается в заголовке. Заголовки мы передаем с помощью параметра headers. Также мы задаем права на загруженный файл и делаем в качестве владельца пользователя и группу test.
Об uri: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/uri_module.html.
8. Создание временных файлов и папок.
Иногда, для работы нужно временное хранилище файлов, которое после работы можно будет удалить. Для работы с данным хранилишем в ansible можно использовать модуль tempfile.
Пример создания каталога:
1 2 3 4 5 |
- name: Create temporary ansible directory tempfile: state: directory suffix: ansible register: tmp |
Путь до созданного каталога будет записан в переменную tmp.path.
После не забываем удалить созданную папку:
1 2 3 4 5 |
- name: Remove temporary ansible directory file: path: "{{ tmp.path }}" state: absent when: tmp.path is defined |
О tempfile: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/tempfile_module.html.
9. Работа с GIT.
О Git: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/git_module.html.
а) Клонирование проекта из гита.
Выполняется с помощью модуля git.
1 2 3 4 |
- name: Clone docker-compose git: repo: "https://github.com/docker/compose.git" dest: /tmp/docker-compose |
* в данном примере мы сделаем клон репозитория в каталог /tmp/docker-compose.
б) Изменение содержимого для файла.
Модуль ansible не поддерживает отпавку изменений в git, но можно воспользоваться API. Например, для gitlab обновить контент файла можно с помощью модуля uri:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
- name: uri: url: "https://gitlab.test.ru/api/v4/projects/666/repository/files/folder%2Ffilename" method: PUT return_content: false body_format: json body: branch: "main" author_email: "master@test.ru" author_name: "NAme Test" content: "Text for file" commit_message: "update filename" headers: PRIVATE-TOKEN: 00000_1111111_33333 Content-Type: application/json validate_certs: no |
* где:
- url — полный путь до файла. Обратите внимание на:
- gitlab.test.ru — адрес нашего сервера gitlab.
- 666 — идентификатор проекта. Его можно посмотреть в настройках самого проекта.
- folder%2Ffilename — путь до файла. В нормальном формате, это folder/filename.
- body — содержит данные, которые будут отправлены на сервер для смены контента.
- headers PRIVATE-TOKEN — токен доступа к API. Его можно создать в настройках профиля учетной записи Gitlab.
Подробнее о работе API в гиблабе: https://docs.gitlab.com/ee/api/repository_files.html.
Содержимое файла
С помощью Ansible мы можем менять содержимое строк как в файлах — вставлять, удалять и редактировать, так и полученных результатах. Рассмотрим несколько примеров.
1. Просмотр содержимого файла.
Содержимое файлов на удаленном сервере и на стороне ansible просматривается по-разному. Рассмотрим оба варианта.
а) На ansible.
Чтобы увидеть содержимое файла, который лежит на стороне ansible, используем плагин lookup:
1 2 3 |
- name: Show File Content debug: msg: "{{ lookup('file', '/etc/ntp.conf') }}" |
* в данном примере мы просто покажем содержимое файла /etc/ntp.conf.
О lookup: https://docs.ansible.com/ansible/latest/plugins/lookup.html.
б) На удаленном сервере.
Выполняется в два этапа — просмотр содержимого с сохранением результата в переменную, и вывод содержимого переменной:
1 2 3 4 5 6 7 |
- name: Read file content shell: cat /path/to/file register: file_content - name: Show file content debug: var: file_content.stdout |
2. Замены строки в файлах.
Замены выполняются с помощью модуля replace. Рассмотрим несколько примеров.
а) Простой поиск и замена строки:
1 2 3 4 5 |
- name: Check configs (comment server address 127.0.0.1) replace: path: "/etc/nginx/nginx.conf" regexp: '^server.address=127.0.0.1$' replace: '#server.address=127.0.0.1' |
* в данном примере мы добавляем комментарий к строке server.address=127.0.0.1.
б) Замена с подстановкой найденного содержимого:
1 2 3 4 5 |
- name: Check configs (comment server address 127.0.0.1) replace: path: "/etc/nginx/nginx.conf" regexp: '^server.address=(.*)$' replace: '# commented for \1' |
* в данном примере мы находим строку server.address с любым значением и меняем ее на строку, в которой будет прописано это значение.
О replace: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/replace_module.html.
3. Добавление и удаление строк в файле.
Мы можем менять содержимое файла с помощью модуля lineinfile. Рассмотрим некоторые примеры работы с ним.
а) Удалить строку:
1 2 3 4 5 |
- name: Remove server strings lineinfile: path: "/etc/chrony.conf" regexp: '^server .*' state: absent |
* в данном примере мы удалим все строки из файла /etc/chrony.conf, которые начинаются на server.
б) Добавить строку:
1 2 3 4 |
- name: Add server strings lineinfile: path: "/etc/chrony.conf" line: 'server ntp.server.local' |
* в данном примере мы добавим строку server ntp.server.local в файл /etc/chrony.conf. Если данная запись уже есть в файле, ansible ничего не станет менять.
в) Добавить строку с использованием регулярного выражения:
1 2 3 4 5 |
- name: Ensure SELinux is set to enforcing mode lineinfile: path: /etc/selinux/config regexp: '^SELINUX=' line: SELINUX=enforcing |
* пример взят с официального сайта. В данном случае мы гарантируем наличие строки SELINUX=enforcing — либо для директивы SELINUX будет задано определенное значение, либо строка будет полностью вставлена в конфигурационный файл /etc/selinux/config.
О lineinfile: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/lineinfile_module.html.
4. Разбить строку.
Выполняется с помощью метода split. Рассмотрим пример, когда мы регистрируем результат при выполнении команды, после чего мы разобьем его по предложениям:
1 2 3 4 5 6 7 |
- name: Run a shell command and register its output as a variable shell: command register: command_result - name: Parse string for dot and show results debug: var: command_result.stdout.split(".") |
* в данном примере мы получим массив данных из предложений.
5. Добавить блок текста.
Можно сделать с помощью модуля blockinfile:
1 2 3 4 5 6 7 8 9 |
- name: Simple config for PDF converter blockinfile: path: /opt/app/conf/app.yml block: | # Simple config option: key1: value1 key2: value2 key3: value3 |
О blockinfile: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/blockinfile_module.html.
6. Сохранить содержимое переменной в файл.
Выше мы рассматривали модуль copy для копирования файлов. Мы также можем его использовать для помещения содержимого переменной в файл:
1 2 3 4 |
- name: Save my_variable to data.txt file copy: content: "{{ my_variable }}" dest: '/tmp/data.txt' |
* в данном примере содержимое переменной my_variable будет сохранено в файле /tmp/data.txt.
Виртуализация VMware
Работа с виртуальными машинами на платформе VMware выполняется с помощью большого количества различных модулей vmware. С полным их списком можно ознакомиться на странице https://docs.ansible.com/ansible/latest/collections/community/vmware/index.html.
Мы рассмотрим несколько примеров. В них мы будем использовать следующие переменные:
- vcenter_hostname — имя сервера vcenter или его IP-адрес.
- vcenter_username — логин для подключения к vcenter.
- vcenter_password — пароль для подключения. Лучше хранить с использованием vailt.
- vcenter_datacenter — имя дата центра, к которому нужно подключаться.
Данные переменные необходимо определить заранее. Это можно сделать при вызове плейбука, описать в самом сценарии или роли. Подробнее о работе с переменными смотрите раздел выше.
1. Модуль vmware_guest
С его помощью мы можем взаимодействовать с гостевой операционной системой виртуальной машины. Необходимо позаботиться, чтобы на последней были установлены VMware Tools.
Подробнее о vmware_guest: https://docs.ansible.com/ansible/latest/collections/community/vmware/vmware_guest_module.html.
а) Базовое подключение.
Для выполнения действий над виртуальными машинами мы должны подключиться к хосту VMware. Для этого используем данные строки:
1 2 3 4 5 6 |
- name: Connect to ESX Host vmware_guest: hostname: "{{ vcenter_hostname }}" username: "{{ vcenter_username }}" password: "{{ vcenter_password }}" validate_certs: no |
* параметр validate_certs, выставленный в no, позволит избежать ошибки, если у нас на хосте используется самоподписанный сертификат (как правило, так и есть).
б) Переименовать виртуальную машину.
Для выполнения действия нам нужно знать идентификатор виртуальной машины:
1 2 3 4 5 6 7 8 9 |
- name: Rename a virtual machine vmware_guest: hostname: "{{ vcenter_hostname }}" username: "{{ vcenter_username }}" password: "{{ vcenter_password }}" validate_certs: no uuid: "{{ hw_product_uuid }}" name: "Template-{{ vm_name }}" state: present |
* где uuid — идентификатор виртуальной машины; name — новое имя виртуальной машины.
в) Конвертировать виртуальную машину в шаблон.
Для этого нужно просто задать признак is_template:
1 2 3 4 5 6 7 8 9 |
- name: Convert virtual machine to Template vmware_guest: hostname: "{{ vcenter_hostname }}" username: "{{ vcenter_username }}" password: "{{ vcenter_password }}" validate_certs: no uuid: "{{ vm_info.instance.hw_product_uuid }}" is_template: true state: present |
2. Модуль vmware_vm_info
Позволяет собирать информацию о виртуальных машинах на хосте VMware.
О модуле vmware_vm_info: https://docs.ansible.com/ansible/latest/collections/community/vmware/vmware_vm_info_module.html.
а) Получение информации по всем виртуальным машинам на хосте:
1 2 3 4 5 6 7 8 9 10 |
- name: Gather all registered virtual machines vmware_vm_info: hostname: '{{ vcenter_hostname }}' username: '{{ vcenter_username }}' password: '{{ vcenter_password }}' validate_certs: no register: vminfo - debug: var: vminfo.virtual_machines |
* всю информацию мы запишем в переменную vminfo и выведем ее на экран.
б) Чтобы получить информацию не о всех машинах, а о конкретной, то добавляем опцию vm_name:
1 2 3 4 5 6 7 8 |
- name: Gather all registered virtual machines vmware_vm_info: hostname: '{{ vcenter_hostname }}' username: '{{ vcenter_username }}' password: '{{ vcenter_password }}' validate_certs: no vm_name: mail-vm register: vminfo |
3. Модуль vmware_guest_info
Позволяет получить подробную информацию о виртуальной машине.
О модуле vmware_guest_info: https://docs.ansible.com/ansible/latest/collections/community/vmware/vmware_guest_info_module.html.
а) Получение информации о конкретной виртуальной машине:
1 2 3 4 5 6 7 8 9 10 11 12 |
- name: Gather virtual machine vmware_guest_info: hostname: '{{ vcenter_hostname }}' username: '{{ vcenter_username }}' password: '{{ vcenter_password }}' datacenter: '{{ vcenter_datacenter }}' validate_certs: no name: '{{ vm_name }}' register: vminfo - debug: var: vminfo.instance |
* информация будет собрана для виртуальной машины с именем, которое определено в переменной vm_name.
4. Модуль vmware_guest_powerstate
Позволяет включать, выключать и перезагружать виртуальные машины.
О модуле vmware_guest_powerstate: https://docs.ansible.com/ansible/latest/collections/community/vmware/vmware_guest_powerstate_module.html.
а) Включение виртуальной машины:
1 2 3 4 5 6 7 8 |
- name: 'Start {{ vm_name }}' vmware_guest_powerstate: hostname: "{{ vcenter_hostname }}" username: "{{ vcenter_username }}" password: "{{ vcenter_password }}" name: "{{ vm_name }}" state: powered-on validate_certs: no |
б) Выключение виртуальной машины:
1 2 3 4 5 6 7 8 9 |
- name: 'Shutdown {{ vm_name }}' vmware_guest_powerstate: hostname: "{{ vcenter_hostname }}" username: "{{ vcenter_username }}" password: "{{ vcenter_password }}" name: "{{ vm_name }}" state: shutdown-guest state_change_timeout: 200 validate_certs: no |
Или грубо:
1 2 3 4 5 6 7 8 |
- name: Set the state of a virtual machine to poweroff vmware_guest_powerstate: hostname: "{{ vcenter_hostname }}" username: "{{ vcenter_username }}" password: "{{ vcenter_password }}" name: "{{ vm_name }}" state: powered-off validate_certs: no |
в) Перезагрузка:
1 2 3 4 5 6 7 8 9 |
- name: 'Restart {{ vm_name }}' vmware_guest_powerstate: hostname: "{{ vcenter_hostname }}" username: "{{ vcenter_username }}" password: "{{ vcenter_password }}" name: "{{ vm_name }}" state: reboot-guest state_change_timeout: 200 validate_certs: no |
Или грубо:
1 2 3 4 5 6 7 8 |
- name: Set the state of a virtual machine to restarted vmware_guest_powerstate: hostname: "{{ vcenter_hostname }}" username: "{{ vcenter_username }}" password: "{{ vcenter_password }}" name: "{{ vm_name }}" state: restarted validate_certs: no |
Виртуализация Proxmox
Рассмотрим немного примеров по работе с системой виртуализации proxmox.
Для работы с данной системой виртуализации есть в Ansible специализированные модули, но, пока, их мало. Поэтому часть задач решается с помощью API.
1. Сбор информации
В данном разделе мы рассмотрим примеры для получения информации с Proxmox.
а) Список виртуальных машин на хосте. Для получения информации о виртуальных машинах, которые находятся на хосте виртуализации нужно использовать API Proxmox. Для этого в ansible мы будем применять модуль URI:
1 2 3 4 5 6 7 |
- name: Get vms from pve uri: url: "https://pve.test.local:8006/api2/json/cluster/resources?type=vm" headers: Authorization: PVEAPIToken=ansible@pve!Ansible=e94d5627-1f8d-36a7-37e2-7ad6fad65ab7 follow_redirects: all register: vm_list |
* где:
- pve.test.local — адрес веб-интерфейса сервера виртуализации.
- 8006 — порт для подключения в веб-интерфейсу.
- ansible@pve — имя учетной записи, для которой создан токен доступа. Он создается в консоле управления Proxmox (Датацентр - Разрешения - API Tokens).
- Ansible=e94d5627-1f8d-36a7-37e2-7ad6fad65ab7 — имя токена и сам токен.
В результате мы сохраним список виртуальных машин в переменной vm_list.
б) Подробная информация о виртуальной машине или хосте виртуализации. Если мы хотим собрать побольше информации, нам также понадобится API:
1 2 3 4 5 6 7 |
- name: Get vm info uri: url: "https://pve.test.local:8006/api2/json/<запрос>" headers: Authorization: PVEAPIToken=ansible@pve!Ansible=e94d5627-1f8d-36a7-37e2-7ad6fad65ab7 follow_redirects: all register: vm_info |
Для получения различной информации о виртуальной машине или сервере мы используем различные запросы (отмечено как <запрос>). С полным списком того, что мы можем получить предлагаю ознакомиться на странице pve.proxmox.com/pve-docs/api-viewer.
Например, для получения конфигурации виртуальной машины, используем запрос:
1 |
nodes/<имя ноды>/qemu/<VMID>/config |
Чтобы узнать hostname:
1 |
nodes/<имя ноды>/qemu/<VMID>/agent/get-host-name |
И так далее
Работа с Docker
В данном разделе мы рассмотрим работу с контейнерами Docker.
1. Работа с контейнером.
С помощью модуля docker_container мы можем работать с контейнерами. Создавать их, удалять, перезапускать и так далее. Рассмотрим несколько примеров.
а) Создать новый контейнер можно с помощью сценария:
1 2 3 4 |
- name: Create a new container docker_container: name: new_container image: nginx |
* в данном примере будет создан контейнер с именем new_container из образа nginx.
Создание контейнера с большим количиством опций:
1 2 3 4 5 6 7 8 9 |
- name: Create a new container docker_container: name: new_container image: nginx auto_remove: yes volumes: - "/tmp:/tmp" ports: - "80:80" |
* где:
- auto_remove — удалить контейнер после завершения его работы.
- volumes — создаем volume. В данном случае, проброс каталога /tmp внутрь контейнера тоже в /tmp.
- ports — на каком порту должна слушать хостовая система и на какой порт пробрасывать запросы внутрь контейнера.
б) Для удаления используем:
1 2 3 4 |
- name: Delete container docker_container: name: new_container state: absent |
* важно указать имя контейнера и state со значением absent.
О docker_container: https://docs.ansible.com/ansible/2.9/modules/docker_container_module.html.
2. Запуск команды внутри контейнера.
Данное действие эквивалентно команде docker exec. В ansible можно выполнить с помощью модуля docker_container_exec или ранее рассмотренного docker_container. Рассмотрим оба варианта.
а) docker_container_exec.
Мы можем использовать данный модуль, когда нужно выполнить команду внутри запущенного контейнера. По умолчанию, его нет в системе и требуется отдельная установка командой:
1 |
ansible-galaxy collection install community.docker |
Пример использования:
1 2 3 4 5 |
- name: Download wp-cli utility community.docker.docker_container_exec: container: "wordpress" command: curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar chdir: /root |
* в нашем примере мы скачаем утилиту wp-cli в контейнере wordpress. Команда будет запущена в директории chdir.
О docker_container_exec: https://docs.ansible.com/ansible/latest/collections/community/docker/docker_container_exec_module.html.
б) docker_container.
Данный способ подойдет в случае, когда нам нужно создать контейнер, запустить его и выполнить команду. Например:
1 2 3 4 5 |
- name: Create a new container docker_container: name: new_container image: nginx command: whoami |
Работа с базами данных
В данном блоке рассмотрим примеры работы с различными базами данных.
MySQL
Работа с базой данных возможна с помощью коллекции mysql.mysql_db. Она не идет в комплекте к ansible и нам необходимо ее установить командой:
1 |
ansible-galaxy collection install community.mysql |
О mysql_db: https://docs.ansible.com/ansible/latest/collections/community/mysql/mysql_db_module.html.
Резервное копирование базы данных MySQL/MariaDB:
1 2 3 4 5 6 7 |
- name: Dump mysql databases community.mysql.mysql_db: state: dump name: - db1 - db2 target: /tmp/dump.sql |
* в данном примере мы создадим 2 дампа из баз db1 и db2 и сохраним результат в файл /tmp/dump.sql.
PostgreSQL
Рассмотрим разные примеры работы с СУБД PostgreSQL. Мы будем использовать следующие модули:
Обратите внимание, что в некоторых случаях понадобиться установка модуля командой:
1 |
ansible-galaxy collection install <имя модуля> |
Подробнее можно найти инструкцию по ссылке выше.
1. Резервное копирование:
1 2 3 4 5 6 7 8 9 |
- name: Create dump postgresql_db: name: DATABASE login_host: SERVER port: PORT login_user: LOGIN login_password: PASSWORD state: dump target: '/tmp/dump.sql' |
2. Восстановление из дампа:
Сценарий напоминает резервное копирование, за исключением опции state:
1 2 3 4 5 6 7 8 9 |
- name: Create dump postgresql_db: name: DATABASE login_host: SERVER port: PORT login_user: LOGIN login_password: PASSWORD state: restore target: '/tmp/dump.sql' |
3. Выполнение запроса:
Запрос в базу можно сделать с помощью модуля postgresql_query:
1 2 3 4 5 6 7 8 |
- name: Select query postgresql_query: query: "SELECT * FROM users" db: DATABASE login_host: SERVER port: PORT login_user: LOGIN login_password: PASSWORD |
Рассмотрим конкретный пример с сохранением результата в переменную:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
- name: Select query postgresql_query: query: "SELECT * FROM users WHERE name = '%s' or family = '%s'" db: clients login_host: localhost port: 5432 login_user: dbuser login_password: dbpassword positional_args: - Andrey - Ivanov register: myclients - name: Set variable set_fact: my_clients: myclients.query_result |
* что мы сделали:
- подключились к серверу баз данных на локальном сервере под пользователем dbuser с паролем dbpassword.
- сделали запрос к базе clients. Запрос должен получить список всех записей из таблицы users, где в поле name есть значение Andrey или в поле family — Ivanov. Обратите внимание, что мы использовали паттерны с применением positional_args.
- результат работы задачи мы зафиксировали в переменной myclients.
- создали переменную my_clients, куда занесли результыты выборки.
Запуск плейбука
Рассмотрим отдельно некоторые возможности при запуске плейбука.
1. Начинать выполнение с определенной задачи.
При выполнении отладки, полезно запустить плейбук, но начать выполнение с определенной задачи. Остальные пропустить.
Это можно сделать с помощью опции --start-at-task:
1 |
ansible-playbook ... --start-at-task="Start Job" |
* в данном примере плейбук начнет выполнять задания с задачи Start Job.
2. Передача переменных.
Мы можем определить переменные при запуске плейбука с помощью опции --extra-vars. Возможна передача одной или нескольких переменных:
1 |
ansible-playbook ... --extra-vars "{ 'domain_name':'test.local', 'ver':'5', 'vm_name':'test' }" |
3. Использование тегов.
Теги позволяют пропускать или выполнять определенные задания. Предположим, у нас есть задание:
1 2 3 4 5 6 |
- name: Install postfix package: name: postfix state: present tags: - init postfix |
* в данном примере мы навесили тег init postfix на задание Install postfix.
Теперь мы можем запустить плейбук:
1 |
ansible-playbook ... --tags "init postfix" |
И задание будет выполнено.
Или так:
1 |
ansible-playbook ... --skip-tags "init postfix" |
И задание будет проигнорировано.
О тегах: https://docs.ansible.com/ansible/latest/user_guide/playbooks_tags.html.
Обработка ошибок
В процессе работы мы сталкиваемся с проблемами в отработки сценариев. Можно настроить поведение при выполнении заданий, если что-то пошло не так.
1. Игнорировать ошибки.
Если ansible столкнется с ошибкой при выполнении задачи, работа плейбука будет завершена. Иногда, нужно пропустить ошибку при выполнении определенной задачи, чтобы выполнение было продолжено. Для этого существует опция ignore.
а) чтобы пропустить ошибки выполнения, в настройка задачи используем:
1 2 3 |
- name: Bad Task ... ignore_errors: yes |
б) чтобы игнорировать ошибки при подключении к хосту:
1 2 3 |
- name: Bad Task ... ignore_unreachable: yes |
2. Выбрасывать ошибку по своему условию.
Мы можем задать свое условие, когда Ansible должен считать, что задача отработана неправильно и нужно остановить работу. Это делается с помощью модуля fail.
а) выкинуть ошибку, если переменная не равна определенному значению:
1 2 3 4 |
- name: Fail if my data not loaded fail: msg: My data has not been loaded completely. when: my_dataload_status != "complete" |
* в данном примере если переменная my_dataload_status не равна complete, системв вернет ошибку.
б) ошибка, если переменная содержит запрещенный символ:
1 2 3 4 |
- name: Fail if * in var fail: msg: "Ошибка — в переменной login есть символ *" when: '"*" in login' |
О fail: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/fail_module.html.
3. Действия в случае ошибки.
Рассмотрим 2 варианта:
- Мы, в любом случае, хотим выполнить чать задачь, даже если сценарий прекратит работу из-за ошибки.
- Мы хотим выполнить определенное действие, если сценарий прекратит работу из-за ошибки.
Это возможно реализовать с помощью block + rescue/always.
О blocks: https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_blocks.html.
Рассмотрим пример.
а) Выполнять задачу после rescue, если основной сценарий выполнился с ошибкой:
1 2 3 4 5 6 7 |
- name: Основной сценарий block: ... rescue: - name: Выполнить, если произошла ошибка debug: msg: 'Show error' |
б) Выполнять задачу после always независимо от результата выполнения основного сценария:
1 2 3 4 5 6 |
- name: Основной сценарий block: ... always: - name: Выполнить в любом случае command: rm -f /tmp/test |
в) И то и другое:
1 2 3 4 5 6 7 8 9 10 |
- name: Основной сценарий block: ... rescue: - name: Выполнить, если произошла ошибка debug: msg: 'Show error' always: - name: Выполнить в любом случае command: rm -f /tmp/test |
* в данном примере мы отобразим текст Show error только при наличии ошибки, а также выполним команду rm -f /tmp/test независимо от исхода работы сценария.
Работа с выводом и строками
В данном разделе будут рассмотрены варианты манипуляции данными — парсинг, фильтры, обработки и подобные действия. Так как ansible является декларативным языком, описанные операции лучше свести к минимуму — автору показалась такая работа не совсем удобной.
Конвертация типов данных
От типа данных зависят операции и способы работы с ними. Приведу пример типов данных, с которыми мы можем столкнуться:
- Строка — набор символов. Заключаются в кавычки.
- Число — набор цифр. Записываются без кавычек.
- Список (list) — массив данных. В качестве ключа используется пронумерованный индекс. Записываются в квадратных скобках.
- Словарь (dict) — массив данных. В качестве ключа используется символный индекс. Записываются в фигурных скобках.
- JSON — строка, имеющая формат записи в виде словаря. Считается удобным для передачи данных между системами.
- YAML — строка, в которой данные разделяются переносами, а вложенность определяется отступами. Формат строго зависит от последних — лишний пробел нарушает его обработку. Удобно использовать для наглядного вывода информации.
Рассмотрим примеры преобразований данных из одного типа в другой.
1. Строку в число. Преобразуем с помощью int:
"100" | int
2. Список в json. Можно выполнить с помощью to_json:
list_var | to_json
или в json, который будет удобно читать:
list_var | to_nice_json
Преобразование строк
Полученные строки могут требовать дальнейшего разбора, например, парсинга. Рассмотрим несколько примеров.
1. Получение массива с помощью split.
Метод split позволяет разбить строку на части по определенному символу. Полученные части станут значениями массива:
1 2 3 |
- name: Set variable set_fact: newvar: "{{ results.split(':') }}" |
* в данном примере мы разобьем строку из переменной results на части по двоеточию и сохраним все это в переменную newvar.
2. Замены с помощью replace.
С помощью данной функции можно в нашем выводе заменить одни символы другими:
1 2 3 |
- name: Set variable set_fact: newvar: "{{ results | replace(' ', '') }}" |
* тут в переменную newvar будет записана строка results, из которой мы уберем все пробелы.
Работа с массивами
В данном подразделе будем работать со списками и словарями.
1. Объединение.
Предположим, что у нас есть два списка из словарей:
1 2 3 4 5 6 7 8 9 10 11 |
vars: list1: - name: name1 value1: value11 - name: name2 value1: value12 list2: - name: name1 value2: value21 - name: name3 value2: value22 |
Мы можем их объединить по одному из полей, например, name:
1 2 |
- debug: msg: "{{ list1 | community.general.lists_mergeby(list2, 'name') }}" |
* для lists_mergeby обязательно полное написание — community.general.lists_mergeby.
В результате, мы получим такой массив:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
{ name: name1 value1: value11 value2: value21 }, { name: name2 value1: value12 }, { name: name3 value2: value22 } |
* как видим, два массива слились в один. Для ключа name1 также добавлено поле value2.
О lists_mergeby: https://docs.ansible.com/ansible/devel/collections/community/general/lists_mergeby_filter.html.
2. Преобразование элемента списка в словарь.
Предположим, у нас есть:
1 2 3 4 5 6 7 8 |
vars: list: - key: key1 value: value1 - key: key2 value: value2 - key: key3 value: value3 |
Мы можем его преобразовать в словарь по имеющимся ключам (key).
а) Если имя ключа для ключа key, а имя ключа для значения value:
1 2 |
- debug: msg: "{{ list | items2dict }}" |
Мы должны увидеть что-то на подобие:
1 2 3 4 5 |
{ "key1": "value1", "key2": "value2", "key3": "value3" } |
б) Если имя ключа для ключа НЕ key, а имя ключа для значения НЕ value:
1 2 |
- debug: msg: "{{ list | items2dict(key_name='name', value_name='model') }}" |
* в данном примере предполагается, что у нас есть массив с ключами name и model. Мы хотим, чтобы в качестве ключа для создаваемого словаря использовался первый, а для значения второй.
О items2dict: https://docs.ansible.com/ansible/devel/collections/ansible/builtin/items2dict_filter.html.
Формат JSON
Посмотрим, что мы можем сделать с json.
1. Фильтр данных. С помощью json_query мы можем выбирать только те данные, которые соответствуют критерию поиска.
а) один критерий:
1 2 3 4 5 6 7 |
- name: Display json filtered data debug: var: item loop: "{{ my_json | from_json | json_query(json_query_string) }}" vars: json_query_string: >- [?state=='running'] |
* в данном примере мы сделаем выборку всех данных, где значение поля state равно running.
б) два критерия:
1 2 3 4 5 6 7 |
- name: Display json filtered data debug: var: item loop: "{{ my_json | from_json | json_query(json_query_string) }}" vars: json_query_string: >- [?state=='running' && code==`200`] |
* в данном примере мы добавили критерий code==`200`. Обратите внимание, что в нем мы используем другой тип кавычек. Это сделано не просто так — для строк используются кавычки " или ', для цифр — `.
в) фильтр с выбором конкретных полей:
1 2 3 4 5 6 7 |
- name: Display json filtered data debug: var: item loop: "{{ my_json | from_json | json_query(json_query_string) }}" vars: json_query_string: >- [?state=='running' && code==`200`].{key1: key1, key2: key2, key3: key3} |
* в этои примере мы взяли ранее использовавшейся фильр и перечислили конкретные поля, которые мы хотим показать при выводе информации.
Шаблоны Jinja2
Помимо сценария с заданиями, при автоматизации сильно помогает шаблонизатор Jinja2. С его помощью можно на лету формировать файл с любым содержимым, которое должно быть сформировано динамически. Также шаблоны jinja можно применять в самих заданиях. Рассмотрим это на нескольких примерах.
1. Перебор массива.
Предположим, нам нужно перебрать все элементы массива в шаблоне. Это можно сделать конструкцией:
1 2 3 |
{% for host in my_hosts %} server "{{ host }}" {% endfor %} |
* в данном примере мы сделаем перебор по переменной my_hosts. Для каждого элемента массива будет создана строка со значением server <значение переменной>.
2. Задаем значение переменной с IF.
Допустим, наша переменная должна иметь значение, в зависимости от значения другой переменной. Для этого можно применить один из следующих синтаксисов:
1 2 3 4 5 |
vars: var1: "test{% if hostname %}-value{% endif %}" var2: "{% if hostname %}value1{% else %}value2{% endif %}" var3: "{% if hostname == 'myhost' %}value1{% else %}value2{% endif %}" var4: "{{'value1' if (hostname) else 'value2'}}" |
* мы рассмотрели следующие варианты:
- var1 — если есть переменная hostname, то добавляем к test еще и -value. В итоге, получится test-value.
- var2 — если есть переменная hostname, значение будет value1, в противном случае — value2.
- var3 — если переменная hostname равна myhost, задаем для var3 значение value1, в противном случае — value2.
- var4 — другой формат записи. Если есть переменная hostname, значение будет value1, в противном случае — value2.
Разное
В данном разделе будет рассказано о дополнительных опциях, которые позволяют менять поведение выполнения задач, добавляет функциональности или все то, для чего не найдена отдельная подходящая категория.
1. Шифрование строки.
С помощью ansible-vault мы можем шифровать файлы и папки. Это позволит нам хранить секреты не в открытом виде. Данные расшифровываются в момент выполнения задач.
Данной командой мы получаем шифрованную строку:
1 |
ansible-vault encrypt_string |
Система запросит ввести дважды пароль и предложит ввести строку, которую нужно зашифровать. После мы должны нажать 2 раза Ctrl + D — мы получим строку, которая начинается с !Vault и различные символы.
Для того, чтобы в момент выполнения задачи ansible расшифровал данные, при запуске плейбука мы должны указать ключ --ask-vault-pass:
1 |
ansible-playbook ... --ask-vault-pass |
Об ansible-vault: https://docs.ansible.com/ansible/latest/user_guide/vault.html.
2. Завершить выполнение плейбука после определенной задачи.
С помощью данной конструкции:
1 2 |
- name: Stop Play meta: end_play |
Мы можем полностью остановить выполнение задач для хоста:
1 2 |
- name: Stop Play for Host meta: end_host |
О meta: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/meta_module.html.
3. Зависимые роли.
С помощью файла meta/main.yml в роли мы можем определить пред-роль, от которой зависит выполнение текущей роли. Для этого настраивается опция dependencies:
1 2 |
dependencies: - role: pred |
4. Вставка роли и ее задач.
Позволяет в процессе выполнения задачи подключить роль. Делается при помощи include_role: