Примеры работы с Ansible

Используемые термины: Ansible .

Инструкция представляет из себя шпаргалку по работе с Ansible. У автора не стоит задачи подробного пояснения всех операций — только описание задачи и пример того, как ее можно решить с помощью ansible. Для более подробного описания я постараюсь указать ссылки на официальную документацию. Также в данную шпаргалку не войдут все возможные действия, которые можно выполнить с помощью данной системы — только популярные и те, с которыми приходилось сталкиваться самому автору. По мере возможности, их список будет пополняться.

Получение информации

Сюда войдут примеры, которые позволят собирать информацию, выводить ее на экран, помогать в отладке и всякое такое.

1. Сбор общей информации о системе.

Чтобы собрать информацию о системе, на которой выполняется сценарий ansible, нужно использовать модуль setup , например:

— name: Get service facts
setup:

Или можно применить фильтр, чтобы сбор выполнялся быстрее:

— name: Get service facts
setup:
filter: ‘ansible_os_family’

Данная информация будет записана в переменную ansible_facts. При желании, ее значение можно отобразить с помощью модуля debug :

— name: Print all available facts
debug:
var: ansible_facts

Также мы можем обратиться к конкретному элементу массива ansible_facts, получив информацию о конкретной настройке или опции:


ansible_facts.hostname

О setup: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/setup_module.html .

2. Получить определенную информацию о компьютере.

Выполняется с помощью различных модулей с окончанием _facts. Рассмотрим примеры.

а) список сервисов. Для этого существует service_facts :

— name: Populate service facts
ansible.builtin.service_facts:

— name: Print all available services
debug:
var: ansible_facts.services

* цель достигается двумя задачами. В первой мы собираем информацию о сервисах с помощью service_facts , второй — выводим на экран содержимое.

О service_facts: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/service_facts_module.html .

б) установленные пакеты. Используем package_facts :

— name: Gather the package facts
package_facts:
manager: auto

— name: Print all available packages
debug:
var: ansible_facts.packages

О package_facts: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/package_facts_module.html .

3. Отображение на экран переменной.

Выше мы уже использовали debug для отображения переменной — принцип тот же:

— 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. Сохранение результата при выполнении задания.

Создать и использовать переменную можно в процессе выполнения работы с помощью директивы register :

— name: Run a shell command and register its output as a variable
shell: command
register: command_result

Также нам может понадобиться сохранить в переменную результат обработки переменной:

— name: Set variable
set_fact:
my_hosts: «{{ command_result.stdout }}»

* в данном случае мы взяли переменную, которую получили в примере выше и разбили строку по знаку «:». Таким образом, мы получим массив данных, который будет сохранен в переменную my_hosts .

Работа с переменными

В сценариях нам постоянно нужно будет использовать переменные. Их можно передавать при запуске, получать в процессе выполнения задач или описать в самом сценарии заранее. Рассмотрим эти действия на некоторых примерах.

1. Переменные в ролях.

Для описания переменных в ролях существуют два каталога — vars и defaults. Последние имеют самый низкий приоритет, это значит, что их можно переопределить.

Независимо от каталога, в котором идет описание переменной, синтаксис один:

hostname: mail.remontka.local

* в данном примере определена переменная hostname со значением mail.remontka.local .

2. Переменные в файле inventory.

Инвентарный файл используется для описания хостов, которые могут быть задействованы при работе сценариев ansible. Переменная может быть определена на уровне всех хостов или на уровне конкретного хоста:

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. Переменные в плейбуке.

Задаются в файле плейбука:

— hosts: all

vars:
vm_name: test-vm

* в данном примере это переменная vm_name .

4. При запуске плейбука.

Когда мы запускаем на выполнение наш плейбук, мы можем передать переменные с помощью опции extra-vars :

ansible-playbook … —extra-vars «{ ‘address’:’remontka.local’, ‘version’:’5.1.2′ }»

* передаем переменные address и version .

Также extra-vars принимает в качестве значений файл, в котором будут переменные:

ansible-playbook … —extra-vars «@secrets_file.yml»

5. Системные переменные.

Мы можем использовать не только определенные нами переменные, но и вытаскивать системные переменные. Для этого используется плагин lookup вместе с модулем env:

— name: Show Value of System Variable
debug:
var: lookup(‘env’, ‘HOME’)

* в данном примере мы получим значение системной переменной HOME (домашней директории пользователя, под которым запущен сценарий ansible).

Или можно записать ее в переменную, чтобы использовать в других задачах:

— name: Set sys_home From System Variable
set_fact:
sys_home: «{{ lookup(‘env’, ‘HOME’) }}»

— name: Show Value of sys_home Variable
debug:
var: sys_home

О lookup + env: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/env_lookup.html .

Проверки, условия и действия на основе этих проверок

В данную группу войдут действия, которые помогут нам ограничить выполнение задач или изменить ход выполнения.

1. Проверка на пустую папку.

Задачи сводится к двум операциям:

  • получении списка файлов в целевом каталоге (например, с помощью команды ls ) и регистрации полученного значения в переменную с помощью register .
  • проверка содержимого переменной, которую мы получили на шаге 1 с помощью when .

Пример будет таким:

— 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 (не определена):

when: pgpro is defined

when: pgpro is not defined

* в данном примере мы проверим наличие переменной pgpro . На практике такая проверка имеет значение, так как если мы попробуем выполнить действия с несуществующей переменной, Ansible нам вернет ошибку.

В официальной документации про это сказано в статье о when (ссылка выше).

3. Выполнение команды, если сервис в рабочем состоянии.

Нам необходимо получить информацию о службах с помощью service_facts, после чего можно уже делать проверку с помощью when:

— name: Populate service facts
ansible.builtin.service_facts:

— name: Stop Service If Running One
shell: systemctl stop apache2
when: ansible_facts.services[«apache2.service»].state | default(false) == «running»

* в данном примере мы проверим, есть ли служба apache2 и запущена ли она. Если это так, то мы ее останавливаем.

Подробнее о service_facts можно прочитать в документации (ссылка выше в разделе 4. Получить список сервисов ).

4. Существует ли файл.

Проверка может быть выполнена с помощью stat:

— 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 мы можем получить информацию об операционной системы, которая работает на целевом компьютере. Это можно использовать для выполнения проверок и задания условий.

а) Проверка по семейству:

— 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.

б) Проверка по версии системы:

— 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»

— name: Run task3 if OS is Red Hat less 8

when: ansible_os_family == «RedHat» and ansible_distribution_major_version | int < 8

* в данном примере мы запустим задачу task1 в системах на основе Debian версии 9, task2 — на основе RedHat версии 8 и task3 на системах ниже 8 версии.

6. Выдать ошибку и остановить выполнение.

Для этого предусмотрен модуль fail или опция задания failed_when . Рассмотрим примеры использования обоих.

а) Модуль fail. Является отдельным заданием. Его можно выполнить просто так, но без условия его применение нелогично. Пример:

— 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. Задает условие, при соблюдении которого задание выполняется с ошибкой, например:

— 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.

Мы можем создать задание, которое остановит выполнение плейбука, пока не будет выполнено определенное условие:

  • Просто ждать определенное время.
  • Удачное соединение по сети с узлом.
  • Определенное содержимое в файле.
  • Пока не появится или удалится файл или папка.

О wait_for https://docs.ansible.com/ansible/latest/collections/ansible/builtin/wait_for_module.html .

Рассмотрим несколько примеров.

а) Ожидаение файла:

— name: Wait rpm file
wait_for:
path: /tmp/build/my_package.rpm

* в данном примере выполнение плейбука остановится, пока по пути /tmp/build/my_package.rpm не появится файл.

б) Ждем, пока хост не станет доступен по SSH:

— name: Ждем, пока не сможем подключиться к хосту по SSH
wait_for:
host: 192.168.1.11
port: 22
state: «started»
timeout: 180

8. Проверка условий с выводом ошибки или успеха.

Предположим, нам нужно сделать какую-либо проверку, в результате которой ansible должен вернуть ошибку или продолжить выполнение. Для этого хорошо подходит модуль assert . Рассмотрим несколько примеров.

а) Значение переменной my_key должно быть от 0 до 100:

— name: Значение переменной my_key должно быть от 0 до 100
assert:
that:
— my_key <= 100
— my_key >= 0
fail_msg: «Неправильное значение my_key — должно быть от 0 до 100»
success_msg: «Значение my_key верное»

б) Значение my_login не должно быть только из цифр:

— name: Проверяем название датасета
assert:
that:
— my_login is not regex(«^[0-9]+$»)
fail_msg: «Имя my_login не может состоять только из цифр»
success_msg: «Имя my_login задано верно»

О assert: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/assert_module.html .

Работа с репозиториями

Рассмотрим настройку репозиториев для систем на базе DEB и RPM.

Работа с репозиториями RPM

Ведется с помощью модуля yum_repository.

О yum_repository: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/yum_repository_module.html .

1. Добавление репозитория:

— 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

2. Для удаления репозитория также используем yum_repository

— name: «Remove YUM repositories»
yum_repository:
name: pgsql95
state: absent

* в нашем примере будет удален репозиторий с названием pgsql95 .

Ansible может не распозновать некоторые репозитории, которые настроена в пользовательских файлах. В таком случае необходимо указать на эти файлы с помощью опции file:

— name: «Remove YUM repositories»
yum_repository:
name: pgsql95
state: absent
file: pgsql

3. Установка пакетов из dnf-модулей. Начиная с версии 8 дистрибутивов на основе RPM пакеты могут устанавливаться из модульных репозиториев, в которых идет четкое разграничение между версиями пакетов. В ansible, если необходимо установить пакет из определнного модуля, используем сценарий на подобие:

— name:
dnf:
name:
— @postgresql:13/client
— @postgresql:13/server
state: present

* в данном примере мы установим пакеты postgresql-client и postgresql-server из модульного репозитория postgresql:13.

Работа с репозиториями DEB

1. Добавление репозитория:

По ссылке:

— name: «Add DEB repository»
apt_repository:
repo: deb http://dl.google.com/linux/chrome/deb/ stable main
filename: new_deb_repo

С использованием персонального архива пакетов (PPA):

— name: «Add DEB PPA»
apt_repository:
repo: ppa:deadsnakes/ppa

О apt_repository: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/apt_repository_module.html .

2. Для удаления репозитория нужно воспользоваться директивой state со значением absent , например:

— name: «Remove Debian default repository»
apt_repository:
repo: deb https://ftp.debian.org/debian stretch main contrib
state: absent

* в данном примере будет удален репозиторий, который идет по умолчанию для Debian Stretch.

3. Импорт ключа.

Рассмотрим примеры с добавлением gpg-ключа по URL и с сервера ключей:

— 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 .

— 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. Обновление кэша репозитория.

Выполняется с помощью модуля apt и опции update_cache:

— name: Update repositories cache
apt:
update_cache: yes

Об apt: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/apt_module.html .

Установки пакетов, модулей и расширений

В данном разделе мы коснемся всего, что приводит к установке чего бы то ни было. А именно:

  • Установки пакетов в систему.
  • Загрузки исходников.
  • Установке дополнительных модулей.
  • Распаковке архивов.

Обратите внимание на опцию state . С ее помощью мы можем управлять поведением установки:

  • present — пакет должен быть установлен.
  • latest — должна быть установлена последняя версия.
  • absent — пакет должен быть удален.

Рассмотрим это подробнее.

1. Установка/удаление пакетов в систему.

Выполняется с помощью универсального модуля package или с помощью специализированных yum и apt . Последние имеют больше полезных опций.

а) package:

— name: Install NTP-client
package:
name: chrony
state: latest

* по данной инструкции в нашей системе должен быть установлен пакет chrony последней версии.

Для установки пакета конкретной версии, ее нужно указать в названии пакета:

— name: Install NTP-client
package:
name: chrony-3.4
state: present

О package: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/package_module.html .

б) dnf/yum.

Установка:

— name: Install NTP-client
yum:
name: chrony
state: present

Для удаления:

— name: Remove NTP-client
yum:
name: chrony
state: absent

Обновление:

— name: Обновить пакеты
yum:
name: ‘*’
state: latest

А так можно сделать даунгрейд:

— name: Downgrade NTP-client
yum:
name: chrony-1.0.0
state: present
allow_downgrade: true

О yum: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/yum_module.html .

в) apt.

Установка:

— name: Install NTP-client
apt:
name: chrony
state: present

Если нам нужно установить пакет из файла deb, синтаксис будет следующий:

— 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 .

Выполнить только обновление установленных пакетов:

— name: Установка обновлений
apt:
name: «*»
state: latest
update_cache: true
cache_valid_time: 3600

Об apt: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/apt_module.html .

2. Установка модуля в nodejs.

Установка модулей в nodejs выполняется с помощью npm. Для него в ansible есть отдельная функция:

— 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 .

3. Установка расширений для python.

Используем модуль pip. Рассмотрим несколько примеров.

а) установка пакета python:

— name: Pip install psycopg2
pip:
name: psycopg2
state: present

* в данном примере будет установлен psycopg2 .

б) обновление пакетов:

— name: Upgrade pip and wheel
pip:
name: «{{ item }}»
extra_args: —upgrade
executable: pip3
loop:
— pip
— wheel

* в нашем примере будут обновлены пакеты pip и wheel .

в) использовать определенную версию pip:

— 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 .

4. Распаковка архива.

Выполняется с помощью unarchive :

— 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:

— 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. Создание учетной записи.

Для этого есть модуль user. У него много опций, рассмотрим некоторые из них.

а) Простая учетная запись:

— name: Create User1
user:
name: user1
shell: /bin/bash
create_home: yes

* в данном примере мы создадим пользователя user1 с домашней директорией. Также мы указали для использования командную оболочку /bin/bash .

б) Для создания системной учетной записи нам достаточно:

— name: Create User Consul
user:
name: consul
system: yes
comment: «Consul Agent»

* в данном примере будет создана учетная запись consul .

в) Создаем пользователя с паролем:

— name: Create User2
user:
name: user2
shell: /bin/bash
create_home: yes
password: «{{ ‘my_passw0rd’ | password_hash(‘sha512’) }}»

* будет создан пользователь user2 с паролем my_passw0rd .

г) Добавляем пользователя в группу:

— name: Добавляем пользователя clamav в группу amavis
user:
name: vmail
groups: mail
append: yes

* в данном примере пользователь vmail будет добавлен в группу mail .

О user: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/user_module.html .

д) Создание группы:

— name: Создаем группу vmail
group:
name: vmail
state: present

О group: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/group_module.html .

3. Работа с systemd.

Для данной настройки есть одноименный модуль systemd. Рассмотрим варианты его использования.

а) перечитать конфигурацию (необходимо делать каждый раз, когда мы меняем настройки юнита):

— name: systemd reload
systemd:
daemon_reload: yes

б) разрешить сервис (автозапуск):

— name: mysql enable
systemd:
name: mysql
enabled: yes

* для сервиса mysql .

в) перезапустить сервис:

— name: mysql reload
systemd:
name: mysql
state: restarted

г) остановить сервис:

— name: mysql stoped
systemd:
name: mysql
state: stopped

О systemd: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/systemd_module.html .

4. Имя компьютера.

Для указания имени компьютера можно использовать модуль hostname:

— name: Задаем имя компьютера
hostname:
name: myweb
use: systemd

* в данном примере мы задаем имя myweb . Обратите внимание на опцию use — в зависимости от операционной системы или ее версии, ее значение должно отличаться.

О hostname: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/hostname_module.html .

5. Часовой пояс.

Часовой пояс можно настроить с помощью модуля timezone , например:

— name: Задаем часовой пояс
timezone:
name: Europe/Moscow

О timezone: https://docs.ansible.com/ansible/latest/collections/community/general/timezone_module.html .

6. Локали.

Добавить локаль можно с помощью модуля locale_gen :

— name: Добавить локаль в систему
locale_gen:
name:
— en_US.UTF-8
— ru_RU.UTF-8
state: present

О locale_gen: https://docs.ansible.com/ansible/latest/collections/community/general/locale_gen_module.html .

Локаль по умолчанию назначается с помощью shell:

— name: Задаем локаль по умолчанию
command: localectl set-locale LANG=en_US.UTF-8

Безопасность

Вынесем в отдельный раздел задачи, связанные с системами безопасности.

1. Настройка брандмауэра.

Выполняется разными модулями в зависимости от используемой системы управления netfilter:

  • firewalld
  • iptables
  • ufw

Рассмотрим небольшие примеры.

а) firewalld:

— 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.

Запрещаем:

— name: Заблокировать запросы от определенного IP адреса
iptables:
chain: INPUT
source: 8.8.8.8
jump: DROP

Разрешаем:

— name: Добавляем разрешающее правило для портов
iptables:
action: insert
rule_num: 1
chain: INPUT
protocol: tcp
destination_port: «{{ item }}»
ctstate: NEW
jump: ACCEPT
loop:
— «80»
— «443»

Подробнее: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/iptables_module.html .

в) UFW.

Добавить 80 порт:

— name: Allow all access to tcp port 80
ufw:
rule: allow
port: ’80’
proto: tcp

Добавить порты с циклом:

— 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 .

2. SELinux.

Смена контекста безопасности для файлов:

— name: Разрешить контекст для PostgreSQL
sefcontext:
target: ‘/data/pgsql(/.*)?’
setype: postgresql_db_t
state: present

Для применения политики придется воспользоваться шелом:

— name: Применяем контекст для каталога
command: restorecon -irv /data/pgsql

Работа с папками и файлами

Рассмотрим задачи, которые помогут нам создавать, копировать и работать с файлами.

1. Создание каталогов и файлов.

Создание файлов и каталогов выполняется с помощью модуля file .

а) для каталога в качестве state указываем directory :

— 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 ):

— 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 :

— 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. Задать права.

Это можно выполнить с помощью модуля создания файла или каталога:

— name: Set File Rights
file:
path: «/var/www/site1/index.php»
owner: www-data
group: www-data
mode: 0664

* обратите внимание, это пример из предыдущено раздела. Для созданного файла мы просто немного изменили права.

3. Копирование файлов из каталога.

Для копирования данных мы используем модуль copy:

— name: Copy Cert File If Different
copy:
src: «{{ item }}»
dest: /etc/ssl/remontka
remote_src: no
mode: 0644
owner: root
group: root
with_fileglob:
— files/*

* в данном примере мы прочитаем все содержимое каталога files на компьютере с ansible, и скопируем его в каталог /etc/ssl/remontka на целевом компьютере.

О copy: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/copy_module.html .

4. Загрузка файла на хост ansible.

Реализуется с помощью fetch и является обратной операцией copy. То есть, мы загрузим файл с хоста, где выполняется задача на хост, с которого запускается ansible.

— name: Загрузка файла на ansible хост
fetch:
src: /tmp/newcert
dest: /tmp/
flat: yes

Подробнее о fetch: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/fetch_module.html .

5. Используем шаблон.

Копирование из шаблона отличается от копирования из файла тем, что в шаблоне могут использоваться переменные, которые будет заменяться их значениями в момент копирования. Для самого процесса копирования из шаблона используется модуль template :

— 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 на целевом компьютере.

Мы можем вывести в консоль результат обработки шаблона следующим образом:

— name: Show Templating Results
debug:
msg: «{{ lookup(‘template’, ‘./config.json.j2’) }}»

О template: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/template_module.html .

6. Архивирование.

Создать архив из файла или каталога можно с помощью модуля archive:

— 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, о котором мы говорили выше .

7. Поиск файлов и папок.

Выполняется с помощью модуля find. Особый интерес представляет в контексте поиска файлов и выполнение над ними определенных действий. Разберем несколько примеров.

а) Удалить последние 30 файлов. Задача решается в два этапа:

  • ищем содержимое целевого каталога.
  • сотритуем список найденных по времени изменения файлов и удаляем все, что идут после определенного числа объектов.

Поиск выполняем с помощью модуля find , удаление — file :

— 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:

— 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 .

8. Скачать файл с помощью curl.

Для этого используется модуль uri. Простой пример:

— name: CURL simple download file
uri:
url: https://remontka.com/files/winsetupfromusb.zip
dest: /tmp

* в данном примере мы загрузим файл https://remontka.com/files/winsetupfromusb.zip в каталог /tmp .

Пример посложнее:

— name: CURL download file with token auth
uri:
url: https://gitlab.remontka.com/api/v4/projects/555/repository/files/folder%2Fpath%2Fdata.sql/raw?ref=master
dest: /tmp/data.sql
owner: remontka
group: remontka
mode: 640
headers:
PRIVATE-TOKEN: access-token

* в данном примере мы скачаем файл с ресурса, где требуется аутентификация по токену, который передается в заголовке. Заголовки мы передаем с помощью параметра headers . Также мы задаем права на загруженный файл и делаем в качестве владельца пользователя и группу remontka .

Об uri: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/uri_module.html .

9. Создание временных файлов и папок.

Иногда, для работы нужно временное хранилище файлов, которое после работы можно будет удалить. Для работы с данным хранилишем в ansible можно использовать модуль tempfile .

Пример создания каталога:

— name: Create temporary ansible directory
tempfile:
state: directory
suffix: ansible
register: tmp

Путь до созданного каталога будет записан в переменную tmp.path .

После не забываем удалить созданную папку:

— 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 .

10. Работа с GIT.

О Git: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/git_module.html .

а) Клонирование проекта из гита.

Выполняется с помощью модуля git.

— name: Clone docker-compose
git:
repo: «https://github.com/docker/compose.git»
dest: /tmp/docker-compose

* в данном примере мы сделаем клон репозитория в каталог /tmp/docker-compose .

б) Изменение содержимого для файла.

Модуль ansible не поддерживает отпавку изменений в git, но можно воспользоваться API. Например, для gitlab обновить контент файла можно с помощью модуля uri:

— name:
uri:
url: «https://gitlab.remontka.com/api/v4/projects/666/repository/files/folder%2Ffilename»
method: PUT
return_content: false
body_format: json
body:
branch: «main»
author_email: «master@remontka.com»
author_name: «Dmitriy Mosk»
content: «Text for file»
commit_message: «update filename»
headers:
PRIVATE-TOKEN: 00000_1111111_33333
Content-Type: application/json
validate_certs: no

* где:

  • url — полный путь до файла. Обратите внимание на:
    • gitlab.remontka.com — адрес нашего сервера 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:

— name: Show File Content
debug:
msg: «{{ lookup(‘file’, ‘/etc/ntp.conf’) }}»

* в данном примере мы просто покажем содержимое файла /etc/ntp.conf .

О lookup: https://docs.ansible.com/ansible/latest/plugins/lookup.html .

б) На удаленном сервере.

Выполняется в два этапа — просмотр содержимого с сохранением результата в переменную, и вывод содержимого переменной:

— name: Read file content
shell: cat /path/to/file
register: file_content

— name: Show file content
debug:
var: file_content.stdout

2. Замены строки в файлах.

Замены выполняются с помощью модуля replace . Рассмотрим несколько примеров.

а) Простой поиск и замена строки:

— 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 .

б) Замена с подстановкой найденного содержимого:

— 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. Рассмотрим некоторые примеры работы с ним.

а) Удалить строку:

— name: Remove server strings
lineinfile:
path: «/etc/chrony.conf»
regexp: ‘^server .*’
state: absent

* в данном примере мы удалим все строки из файла /etc/chrony.conf , которые начинаются на server .

б) Добавить строку:

— name: Add server strings
lineinfile:
path: «/etc/chrony.conf»
line: ‘server ntp.server.local’

* в данном примере мы добавим строку server ntp.server.local в файл /etc/chrony.conf . Если данная запись уже есть в файле, ansible ничего не станет менять.

в) Добавить строку с использованием регулярного выражения:

— 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. Рассмотрим пример, когда мы регистрируем результат при выполнении команды, после чего мы разобьем его по предложениям:

— 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(«.»)

* в данном примере мы получим массив данных из предложений.

Еще один пример — разбить FQDN имя на короткое и домен. Это можно сделать с применением данных конструкций:

— name: Получаем две переменные с коротким именем и именем домена
set_fact:
short_name: «{{ fqdn_name.split(‘.’)[0] }}»
domain_name: «{{ fqdn_name.split(‘.’)[1:] | join(‘.’) }}»

5. Добавить блок текста.

Можно сделать с помощью модуля blockinfile:

— 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 для копирования файлов. Мы также можем его использовать для помещения содержимого переменной в файл:

— name: Save my_variable to data.txt file
copy:
content: «{{ my_variable }}»
dest: ‘/tmp/data.txt’

* в данном примере содержимое переменной my_variable будет сохранено в файле /tmp/data.txt .

Работа с SSL

В данном разделе рассмотрим задачи управления сертификатами и шифрованием при передачи данных.

1. Добавить публичный ключ хоста в known_hosts.

Делается с помощью known_hosts. Пример из официальной документации:

— 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 .

2. Создание новых SSH-ключей для сервера.

Создание ключей реализуется с помощью модуля openssh_keypair :

— 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 .

3. Работа с SSH authorized_key.

Данный файл содержит публичный ключ для подключения по SSH. Работа с ним через Ansible выполняется с помощью модуля authorized_key.

Пример добавления ключа:

— 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 .

4. Создание самоподписанного сертификата.

Чтобы получить, более или менее, корректный самоподписанный сертификат, нам нужно будет воспользоваться тремя модулями ansible:

  • openssl_privatekey — создаст приватный ключ.
  • openssl_csr — создаст запрос для выпуска открытого сертификат по заданным параметрам на основе закрытого ключа.
  • openssl_certificate — сгенерирует открытый сертификат на основе ключа запроса.

Рассмотрим использование данных модулей на конкретном примере:

— name: Создаем закрытый ключ (key)
openssl_privatekey:
path: /etc/ssl/mycert.key
size: 2048

— name: Создаем файл запроса (csr)
openssl_csr:
path: /etc/ssl/mycert.csr
privatekey_path: /etc/ssl/mycert.key
country_name: RU
locality_name: Russian Federation
organization_name: remontka
email_address: master@remontka.com
common_name: ansible.remontka.com
subject_alt_name:
— «DNS:ansible.remontka.com»
— «DNS:test.remontka.com»

— name: Создаем самоподписанный сертификат (crt)
openssl_certificate:
path: /etc/ssl/mycert.crt
csr_path: /etc/ssl/mycert.csr
privatekey_path: /etc/ssl/mycert.key
provider: selfsigned

* в нашем запросе мы в итоге получим ключи mycert.key и mycert.crt в каталоге /etc/ssl. Результат будет похожим, если бы мы ввели команду openssl req -nodes -new -x509 -keyout /etc/ssl/mycert.key -out /etc/ssl/mycert.crt -subj ‘/C=RU/L=Russian Federation/O=remontka/CN=ansible.remontka.com’ .

Виртуализация VMware

Работа с виртуальными машинами на платформе VMware выполняется с помощью большого количества различных модулей vmware. С полным их списком можно ознакомиться на странице https://docs.ansible.com/ansible/latest/collections/community/vmware/index.html .

Мы рассмотрим несколько примеров. В них мы будем использовать следующие переменные:

  1. vcenter_hostname — имя сервера vcenter или его IP-адрес.
  2. vcenter_username — логин для подключения к vcenter.
  3. vcenter_password — пароль для подключения. Лучше хранить с использованием vailt .
  4. vcenter_datacenter — имя датацентра, к которому нужно подключаться.

Данные переменные необходимо определить заранее. Это можно сделать при вызове плейбука, описать в самом сценарии или роли. Подробнее о работе с переменными смотрите раздел выше.

1. Модуль vmware_guest

С его помощью мы можем взаимодействовать с гостевой операционной системой виртуальной машины. Необходимо позаботиться, чтобы на последней были установлены VMware Tools .

Подробнее о vmware_guest: https://docs.ansible.com/ansible/latest/collections/community/vmware/vmware_guest_module.html .

а) Базовое подключение.

Для выполнения действий над виртуальными машинами мы должны подключиться к хосту VMware. Для этого используем данные строки:

— name: Connect to ESX Host
vmware_guest:
hostname: «{{ vcenter_hostname }}»
username: «{{ vcenter_username }}»
password: «{{ vcenter_password }}»
validate_certs: no

* параметр validate_certs , выставленный в no , позволит избежать ошибки, если у нас на хосте используется самоподписанный сертификат (как правило, так и есть).

б) Переименовать виртуальную машину.

Для выполнения действия нам нужно знать идентификатор виртуальной машины:

— 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 :

— 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 .

а) Получение информации по всем виртуальным машинам на хосте:

— 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:

— 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 .

а) Получение информации о конкретной виртуальной машине:

— 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 .

а) Включение виртуальной машины:

— 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

б) Выключение виртуальной машины:

— 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

Или грубо:

— 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

в) Перезагрузка:

— 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

Или грубо:

— 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 специализированный модуль proxmox_kvm . Его недостаток — относительно, небольшой перечень возможностей. Поэтому часть задачь будет решаться с помощью API.

Мы будем использовать следующие переменные:

  1. pve_host — имя сервера pve или его IP-адрес.
  2. pve_user — логин для подключения к vcenter.
  3. pve_password — пароль для подключения. Лучше хранить с использованием vailt .
  4. pve_node — имя ноды в датацентре.
  5. pve_port — номер порта, на котором слушает гипервизор (по умолчанию 8006).

Перейдем к примерам.

1. Сбор информации

В данном разделе мы рассмотрим примеры для получения информации с Proxmox.

а) Список виртуальных машин на хосте. Для получения информации о виртуальных машинах, которые находятся на хосте виртуализации нужно использовать API Proxmox. Для этого в ansible мы будем применять модуль URI:

— name: Get vms from pve
uri:
url: «https://pve.remontka.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.remontka.local — адрес веб-интерфейса сервера виртуализации.
  • 8006 — порт для подключения в веб-интерфейсу.
  • ansible@pve — имя учетной записи, для которой создан токен доступа. Он создается в консоле управления Proxmox ( Датацентр Разрешения API Tokens ).
  • Ansible=e94d5627-1f8d-36a7-37e2-7ad6fad65ab7 — имя токена и сам токен.

В результате мы сохраним список виртуальных машин в переменной vm_list .

б) Подробная информация о виртуальной машине или хосте виртуализации. Если мы хотим собрать побольше информации, нам также понадобится API:

— name: Get vm info
uri:
url: «https://pve.remontka.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 .

Например, для получения конфигурации виртуальной машины, используем запрос:

nodes/<имя ноды>/qemu/<VMID>/config

Чтобы узнать hostname:

nodes/<имя ноды>/qemu/<VMID>/agent/get-host-name

И так далее.

в) Информация о машине с помощью модуля proxmox_kvm :

— name: «Get VMID»
proxmox_kvm:
node: «{{ pve_node }}»
api_user: «{{ pve_user }}»
api_password: «{{ pve_password }}»
api_host: «{{ pve_host }}»
name: «myvm»
state: current
register: vm_info

2. Операции с ВМ

Рассмотрим наиболее популярные действия над виртуальными машинами.

*) Остановить:

— name: «Останавливаем виртуальную машину Proxmox»
proxmox_kvm:
api_host : «{{ pve_host }}»
api_user : «{{ pve_user }}»
api_password: «{{ pve_password }}»
node : «{{ pve_node }}»
name : «myvm»
state : stopped
force : yes
timeout : 300

*) Удалить:

— name: «Удаляем виртуальную машину Proxmox»
proxmox_kvm:
api_host : «{{ pve_host }}:{{ pve_port }}»
api_user : «{{ pve_user }}»
api_password : «{{ pve_password }}»
node : «{{ pve_node }}»
name : «myvm»
state : absent

Работа с Docker

В данном разделе мы рассмотрим работу с контейнерами Docker.

Для корректной работы модуля, на целевом хосте должен быть установлен компонент python docker:

pip3 install docker

Обратите внимание, что должен использоваться python версии 3. Для версии 2 уже возникают ошибки при установке и работе модуля.

На стороне, где запускается ansible необходимо установить коллекцию docker:

ansible-galaxy collection install community.docker

1. Работа с контейнером.

С помощью модуля docker_container мы можем работать с контейнерами. Создавать их, удалять, перезапускать и так далее. Рассмотрим несколько примеров.

а) Создать новый контейнер можно с помощью сценария:

— name: Create a new container
docker_container:
name: new_container
image: nginx

* в данном примере будет создан контейнер с именем new_container из образа nginx .

Создание контейнера с большим количиством опций:

— 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 — на каком порту должна слушать хостовая система и на какой порт пробрасывать запросы внутрь контейнера.

б) Для удаления используем:

— 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.

Пример использования:

— 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.

Данный способ подойдет в случае, когда нам нужно создать контейнер, запустить его и выполнить команду. Например:

— name: Create a new container
docker_container:
name: new_container
image: nginx
command: whoami

Облачная платформа OpenStack

В данном разделе рассмотрим модуль работы с платформой виртуализации OpenStack. Данный модуль не входит в стандартный комплект и необходимо выполнить предварительные настройки для системы.

Устанавливаем коллекцию openstack.cloud на компьютер, где запускается ansible:

ansible-galaxy collection install openstack.cloud

Ознакомиться со всеми модулями данной коллекции можно на странице с документацией Ansible.

Также необходим python версии 3 с модулем openstacksdk. Установим данный модуль на компьютере, где будет запускаться сценарий:

python3 -m pip install —upgrade pip

python3 -m pip install openstacksdk

Для подключения и работы с OpenStack необходимы данные, характерные для хостинг-провайдера облачной услуги. Для этого изучите документацию по работе с OpenStack CLI на официальном сайте последнего. Также немного о работе с OpenStack CLI на моем сайте.

Для работы с модулями группы openstack нам нужно будет проходить авторизацию. Для этого мы будем использовать переменные, например:

cloud:
auth_url: https://infra.mail.ru:35357/v3/
username: cloud-user-name
password: cloud-user-password
project_id: cloud-project-id
user_domain_name: cloud-user-domain

* где:

  • auth_url — ссылка для подключения к облаку по openstack api.
  • username — учетная запись для прохождения аутентификации.
  • password — пароль от учетной записи.
  • project_id — идентификатор проекта.
  • user_domain_name — домен пользователей.

И так, рассмотрим простые примеры. Для удобства, попробуем разбить информацию по разделам.

Работа с виртуальными машинами

Используем модуль openstack.cloud.server.

Об openstack server: https://docs.ansible.com/ansible/latest/collections/openstack/cloud/server_module.html .

1. Создание инстанса:

— name: Создать виртуальную машину в облаке VK Cloud
openstack.cloud.server:
state: present
auth:
auth_url: «{{ cloud.auth_url }}»
username: «{{ cloud.username }}»
password: «{{ cloud.password }}»
project_id: «{{ cloud.project_id }}»
user_domain_name: «{{ cloud.user_domain_name }}»
name: vm-name
region_name: cloud-region-name
image: cloud-image-id
key_name: key-ssh-name
timeout: 200
flavor: cloud-vm-template
availability_zone: MS1
nics:
— net-id: cloud-network-id
security_groups:
— default
— ssh
meta:
hostname: my-cloud-vm-01

* обратите внимание, что некоторые опции не будут работать для вашего поставщика облачной услуги. Их нужно заменить на другие, а какие именно, придется изучить соответсвующую документацию, представленную на сайте последнего.

2. Удаление виртуальной машины:

— name: Удалить виртуальную машину в облаке openstack
openstack.cloud.server:
state: absent
auth:
auth_url: «{{ cloud.auth_url }}»
username: «{{ cloud.username }}»
password: «{{ cloud.password }}»
project_id: «{{ cloud.project_id }}»
user_domain_name: «{{ cloud.user_domain_name }}»
name: vm-name

3. Используем cloud-config. Большинство облачных провайдеров используют образы операционных систем с установленным cloud-init. Данный компонент позволяет автоматизировать некоторые операции по администрированию системы. Например, мы можем при создании виртуальной машины выполнить дополнительные настройки:

— name: Создать виртуальную машину в облаке VK Cloud + выполнить предварительные настройки
openstack.cloud.server:
state: present

config_drive: true
userdata: |
#cloud-config
users:
— name: remontka
groups: [ sudo ]
shell: /bin/bash
lock_passwd: false
passwd: «$6$ssBWwe7wxa.lPToS$P5gm0y13m3X8VsK.czScN0ilTxYrB86KNhv7YvD4.4f0CdTDvfAV3W95pYQCPUdF2iRtIXBSVIw/ZYnxtBvOA.»
ssh_pwauth: true
hostname: «my-cloud-vm-01»

* в данном примере нужно обратить внимание на следующие моменты:

  • config_drive — по умолчанию, cloud-init берет информацию для настройки с сервера. Данный параметр указывает, что должен использоваться пользовательский конфиг.
  • users — создаем пользователей в системе.
    • Некоторые провайдеры не позволяют создавать учетные записи с логином admin.
    • Хэш для passwd можно получить командой mkpasswd -m sha-512 .
  • ssh_pwauth — по умолчанию на сервер нельзя зайти по ssh с использованием парольной аутентификации. Данная опция контролирует это поведение.

Получение информации

В данном разделе мы будем использовать следующие модули:

  • server_info — позволит получить информацию о виртуальной машине.
  • resources — сведения о различных ресурсах в облаке.
  • image_info — информация об образах для виртуальных дисков.

Рассмотрим следующие примеры.

1. Получение информации о виртуальной машине. Выполняем с помощью модуля server_info:

— name: Получаем информацию о виртуальной машине vm1
openstack.cloud.server_info:
auth:
auth_url: «{{ cloud.auth_url }}»
username: «{{ cloud.username }}»
password: «{{ cloud.password }}»
project_id: «{{ cloud.project_id }}»
user_domain_name: «{{ cloud.user_domain_name }}»
name: vm1
register: openstack

— debug:
var: openstack

2. Список виртуальных машин. Используем модуль resources:

— name: Получаем список виртуальных машин
openstack.cloud.resources:
auth:
auth_url: «{{ cloud.auth_url }}»
username: «{{ cloud.username }}»
password: «{{ cloud.password }}»
project_id: «{{ cloud.project_id }}»
user_domain_name: «{{ cloud.user_domain_name }}»
service: compute
type: server
register: vm_list

3. Список образов для виртуальных дисков. Используем image_info.

а) все образы:

— name: Получаем список образов
openstack.cloud.image_info:
auth:
auth_url: «{{ cloud.auth_url }}»
username: «{{ cloud.username }}»
password: «{{ cloud.password }}»
project_id: «{{ cloud.project_id }}»
user_domain_name: «{{ cloud.user_domain_name }}»
register: images_list

б) конкретный образ:

— name: Получаем информацию об образе «Ubuntu-22.04-202208»
openstack.cloud.image_info:
auth:
auth_url: «{{ cloud.auth_url }}»
username: «{{ cloud.username }}»
password: «{{ cloud.password }}»
project_id: «{{ cloud.project_id }}»
user_domain_name: «{{ cloud.user_domain_name }}»
image: Ubuntu-22.04-202208
register: image_info

Работа с базами данных

В данном блоке рассмотрим примеры работы с различными базами данных.

MySQL /MariaDB

Работа с базой данных возможна с помощью различных коллекций:

  • mysql_db — позволяет редактировать базу данных или создавать дамп.
  • mysql_query — создание запросов.
  • mysql_user — работа с ролями.

Полный перечень модулей для работы с MySQL /MariaDB можно посмотреть на странице docs.ansible.com/ansible/latest/collections/community/mysql .

Они не идут в комплекте к ansible и нам необходимо установить их командой:

ansible-galaxy collection install community.mysql

Теперь рассмотрим несколько примеров для работы с MySQL / MariaDB через ansible.

1. Резервное копирование.

Для создания дампа используем сценарий:

— name: Dump mysql databases
community.mysql.mysql_db:
state: dump
name:
— db1
— db2
target: /tmp/dump.sql

* в данном примере мы создадим 2 дампа из баз db1 и db2 и сохраним результат в файл /tmp/dump.sql .

Для восстановления из дампа:

— name: Restore mysql databases
community.mysql.mysql_db:
name: db1
state: import
target: /tmp/dump.sql

2. Создать базу:

— name: Создаем базу данных для хранения данных почтового сервера
community.mysql.mysql_db:
name: postfix
state: present
encoding: utf8
collation: utf8_general_ci

* создадим базу данныз с названием postfix и кодировкой UTF-8 .

3. Создаем пользователя:

— name: Создаем пользователя и даем ему права на базу postfix
community.mysql.mysql_user:
state: present
name: postfix
password: postfix123
priv: ‘postfix.*:ALL’

PostgreSQL

Рассмотрим разные примеры работы с СУБД PostgreSQL. Мы будем использовать следующие модули:

Обратите внимание, что нам понадобится установка модуля командой:

ansible-galaxy collection install community.postgresql

Подробнее можно найти инструкцию по ссылке выше.

1. Резервное копирование:

— 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 :

— 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:

— name: Select query
postgresql_query:
query: «SELECT * FROM users»
db: DATABASE
login_host: SERVER
port: PORT
login_user: LOGIN
login_password: PASSWORD

Рассмотрим конкретный пример с сохранением результата в переменную:

— 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 , куда занесли результыты выборки.

4. Конфигурирование PostgreSQL. С помощью модуля postgresql_set можно вносить изменения в конфигурацию СУБД на подобие команды ALTER SYSTEM.

Например:

— name: Включаем SSL в PostgreSQL
postgresql_set:
name: ssl
value: ‘on’
login_unix_socket: »
become: yes
become_user: postgres

* в данном примере мы разрешим SSL на PostgeSQL. Обратите внимание на директиву login_unix_socket — она указывает на папку, где находится сокет подключения к СУБД. Его использует встроенная учетная запись postgres, чтобы подключаться к базе. По умолчанию, ansible ищет сокетный файл .s.PGSQL.5432 в каталоге /var/run/postgresql , но в некоторых случаях, например, при работе с Postgres Pro, данный путь может отличаться. У меня он был /tmp — в таком случае путь нужно указать явно, в противном случае мы получим ошибку unable to connect to database: connection to server on socket «/var/run/postgresql/.s.PGSQL.5432″» failed: No such file or directoryntIs the server running locally and accepting connections on that socket?n .

Запуск плейбука

Чтобы запустить плейбук

Похожая запись

EnglishRussianUkrainian