Ansible
Ansible за 15 минут: инвентарь, плейбуки, модули, переменные, факты, циклы, хендлеры, шаблоны Jinja2, роли, теги и Vault — всё в комментариях.
Ansible — система управления конфигурацией и автоматизации развёртывания. Без агентов, по SSH, описательно (YAML), идемпотентно. Весь экспресс-тур ниже — в комментариях к коду. Читай сверху вниз.
Что такое Ansible
Управляющая машина подключается к узлам по SSH и приводит их к нужному состоянию.
# Ansible — agentless: на управляемых хостах НЕ нужен демон/агент.
# Нужны только: SSH-доступ и Python на удалённой машине.
#
# Идемпотентность: повторный запуск НЕ меняет уже корректное состояние.
# 1-й запуск: changed (пакет установлен)
# 2-й запуск: ok (уже установлен — ничего не делаем)
#
# Декларативность: описываем РЕЗУЛЬТАТ ("пакет nginx установлен"),
# а не последовательность команд ("скачай, распакуй, запусти...").
#
# Комментарии в YAML начинаются с #
name: пример # inline-комментарий после значения тоже работает
# --- три дефиса — необязательный маркер начала YAML-документа ---
Инвентарь (inventory)
Список управляемых хостов, сгруппированных по ролям. Формат INI или YAML.
# === INI-формат: inventory.ini ===
# [имя_группы]
# хост параметры_подключения
#
# [web]
# web1.example.com
# web2.example.com ansible_host=10.0.0.5 # реальный IP
#
# [db]
# db1.example.com ansible_user=admin ansible_port=2222
#
# [prod:children] # группа групп: prod включает web и db
# web
# db
# === YAML-формат: inventory.yml ===
all: # корневая группа "все хосты"
children:
web: # группа web
hosts:
web1.example.com:
web2.example.com:
ansible_host: 10.0.0.5
db:
hosts:
db1.example.com:
ansible_user: admin
ansible_port: 2222
# Проверка: ansible-inventory --list -i inventory.yml
# Пинг группы: ansible web -m ping -i inventory.yml
Плейбук (playbook)
YAML-файл со сценарием: какие задачи и на каких хостах выполнить.
# site.yml — плейбук состоит из списка "плеев" (plays)
- name: Настройка веб-серверов # человекочитаемое имя плея
hosts: web # к какой группе/хосту применять
become: true # выполнять с sudo (повышение прав)
gather_facts: true # собрать факты о хостах (по умолч. true)
tasks: # список задач, выполняются по порядку
- name: Установить nginx # имя задачи (видно в выводе)
ansible.builtin.apt: # модуль (что делаем)
name: nginx # параметры модуля
state: present
- name: Запустить nginx
ansible.builtin.service:
name: nginx
state: started
# Запуск: ansible-playbook -i inventory.yml site.yml
Модули
Модуль — единица работы. Каждая задача вызывает ровно один модуль.
tasks:
# Пакеты: apt (Debian/Ubuntu), yum/dnf (RHEL/CentOS)
- name: Установить пакет
ansible.builtin.apt:
name: nginx
state: present # present | absent | latest
update_cache: true # apt update перед установкой
# Копирование файла на хост
- name: Скопировать конфиг
ansible.builtin.copy:
src: files/app.conf # локальный файл (на управляющей машине)
dest: /etc/app/app.conf # путь на удалённом хосте
owner: root
mode: '0644'
# Управление файлами/каталогами/правами
- name: Создать каталог
ansible.builtin.file:
path: /var/www/app
state: directory # directory | file | absent | touch | link
mode: '0755'
# Сервисы (systemd)
- name: Включить и запустить сервис
ansible.builtin.service:
name: nginx
state: started # started | stopped | restarted | reloaded
enabled: true # автозапуск при загрузке
# Произвольные команды
- name: Команда без шелла (быстрее, безопаснее)
ansible.builtin.command: /usr/bin/whoami
- name: shell — нужен, когда есть | && > пайпы/перенаправления
ansible.builtin.shell: cat /var/log/app.log | grep ERROR > errors.txt
Переменные
Подстановка через {{ имя }}. Источники: плейбук, файлы, group_vars.
# 1) Прямо в плее
- hosts: web
vars:
app_port: 8080
app_user: deploy
vars_files: # 2) подключить файл переменных
- vars/secrets.yml
tasks:
- name: Использовать переменную
ansible.builtin.debug:
# {{ }} — подстановка. В начале значения берём в кавычки:
msg: "Порт = {{ app_port }}, юзер = {{ app_user }}"
- name: Значение по умолчанию через фильтр
ansible.builtin.debug:
msg: "{{ app_env | default('production') }}"
# 3) group_vars/web.yml — авто-подхват для всей группы web
# host_vars/web1.example.com.yml — для конкретного хоста
# Приоритет (упрощённо): command-line -e > host_vars > group_vars > defaults
Факты (facts)
Автоматически собранные данные о хосте: ОС, IP, память, диски.
- hosts: all
gather_facts: true # сбор фактов перед задачами (по умолч. включён)
tasks:
- name: Показать факты
ansible.builtin.debug:
msg: >
ОС: {{ ansible_facts['distribution'] }}
{{ ansible_facts['distribution_version'] }},
ядер CPU: {{ ansible_facts['processor_vcpus'] }},
IP: {{ ansible_facts['default_ipv4']['address'] }}
# Частые факты:
# ansible_facts['os_family'] -> Debian / RedHat
# ansible_facts['hostname'] -> имя хоста
# ansible_facts['memtotal_mb'] -> объём ОЗУ
- name: Свой факт на лету (доступен в дальнейших задачах)
ansible.builtin.set_fact:
deploy_dir: "/srv/{{ app_user }}"
# Посмотреть все факты: ansible web -m setup -i inventory.yml
Условия и циклы
when — выполнять задачу по условию, loop — повторять по списку.
tasks:
# when: задача выполнится, только если условие истинно
- name: Установить пакет только на Debian
ansible.builtin.apt:
name: nginx
state: present
when: ansible_facts['os_family'] == "Debian"
# Сравнения: == != > < >= <= ; логика: and / or / not
- name: Несколько условий
ansible.builtin.debug:
msg: "ОЗУ много и это prod"
when: ansible_facts['memtotal_mb'] > 2048 and app_env == "prod"
# loop: повтор задачи по списку. Текущий элемент — в item
- name: Установить несколько пакетов
ansible.builtin.apt:
name: "{{ item }}"
state: present
loop:
- git
- curl
- htop
# with_items — старый синоним loop (ещё встречается в коде)
- name: Создать пользователей
ansible.builtin.user:
name: "{{ item }}"
with_items:
- alice
- bob
Хендлеры (handlers)
Задачи, запускаемые по сигналу notify — только если что-то изменилось.
- hosts: web
become: true
tasks:
- name: Положить конфиг nginx
ansible.builtin.copy:
src: files/nginx.conf
dest: /etc/nginx/nginx.conf
notify: Перезапустить nginx # дёрнуть хендлер с таким именем
# notify сработает, ТОЛЬКО если задача дала статус changed.
# Если конфиг не менялся — хендлер не вызовется.
handlers: # хендлеры в конце плея
- name: Перезапустить nginx # имя должно совпасть с notify
ansible.builtin.service:
name: nginx
state: restarted
# Хендлеры выполняются ОДИН раз в конце плея,
# даже если их вызвали несколько задач.
Шаблоны (Jinja2)
Модуль template рендерит файл .j2 с подстановкой переменных.
# Задача в плейбуке:
- name: Сгенерировать конфиг из шаблона
ansible.builtin.template:
src: templates/nginx.conf.j2 # шаблон Jinja2
dest: /etc/nginx/nginx.conf
notify: Перезапустить nginx
# Содержимое templates/nginx.conf.j2:
# ----------------------------------
# server {
# listen {{ app_port }}; # подстановка переменной
# server_name {{ ansible_facts['hostname'] }};
#
# {% if app_env == "prod" %} # условие в шаблоне
# access_log /var/log/nginx/access.log;
# {% endif %}
#
# {% for srv in backends %} # цикл в шаблоне
# upstream {{ srv }};
# {% endfor %}
# }
# ----------------------------------
# {{ }} — вывод значения, {% %} — управляющая конструкция (if/for).
Роли (roles)
Способ переиспользовать и структурировать плейбуки по стандартным каталогам.
# Структура каталогов роли (создаётся: ansible-galaxy init nginx):
# roles/
# nginx/
# tasks/main.yml # основные задачи роли
# handlers/main.yml # хендлеры роли
# templates/ # шаблоны .j2
# files/ # статические файлы для copy
# vars/main.yml # переменные (высокий приоритет)
# defaults/main.yml # переменные по умолчанию (низкий приоритет)
# meta/main.yml # зависимости роли
# Подключение ролей в плейбуке:
- hosts: web
become: true
roles:
- nginx # выполнит roles/nginx/tasks/main.yml
- role: app
vars:
app_port: 9090 # передать переменную в роль
Теги, проверка, become
Запуск части задач по тегам; «сухой прогон» без изменений; повышение прав.
tasks:
- name: Установить nginx
ansible.builtin.apt:
name: nginx
state: present
become: true # выполнить эту задачу через sudo
tags:
- install # пометить задачу тегом
- nginx
- name: Залить конфиг
ansible.builtin.template:
src: templates/nginx.conf.j2
dest: /etc/nginx/nginx.conf
tags: [config]
# Запустить только задачи с тегом config:
# ansible-playbook site.yml --tags config
# Пропустить задачи с тегом install:
# ansible-playbook site.yml --skip-tags install
#
# --check — dry-run: показать, ЧТО изменилось бы, ничего не меняя
# --diff — показать построчные различия в файлах
# ansible-playbook site.yml --check --diff
#
# become: true — повышение прав (sudo)
# become_user: deploy — стать конкретным пользователем
Ansible Vault
Шифрование секретов (пароли, ключи, токены) прямо в репозитории.
# Создать зашифрованный файл с секретами:
# ansible-vault create vars/secrets.yml
# Редактировать существующий:
# ansible-vault edit vars/secrets.yml
# Зашифровать готовый файл / расшифровать:
# ansible-vault encrypt vars/secrets.yml
# ansible-vault decrypt vars/secrets.yml
# Внутри (после расшифровки) — обычный YAML с переменными:
# db_password: "super-secret"
# api_token: "abc123"
# Использование в плейбуке как обычные vars_files:
- hosts: db
vars_files:
- vars/secrets.yml # Ansible сам расшифрует при запуске
tasks:
- name: Применить пароль БД
ansible.builtin.debug:
msg: "Пароль задан" # значение {{ db_password }} НЕ светим в лог
# Запуск с запросом пароля Vault:
# ansible-playbook site.yml --ask-vault-pass
# Или указать файл с паролем:
# ansible-playbook site.yml --vault-password-file ~/.vault_pass
Типичный плейбук: веб-сервер
Полный сценарий — установка и настройка nginx с шаблоном и хендлером.
---
- name: Развернуть веб-сервер nginx
hosts: web
become: true
gather_facts: true
vars:
app_port: 80
site_root: /var/www/app
tasks:
- name: Установить nginx
ansible.builtin.apt:
name: nginx
state: present
update_cache: true
when: ansible_facts['os_family'] == "Debian"
- name: Создать корень сайта
ansible.builtin.file:
path: "{{ site_root }}"
state: directory
mode: '0755'
- name: Сгенерировать конфиг из шаблона
ansible.builtin.template:
src: templates/nginx.conf.j2
dest: /etc/nginx/sites-available/app.conf
mode: '0644'
notify: Перезагрузить nginx # хендлер сработает при изменении
- name: Включить сайт (симлинк)
ansible.builtin.file:
src: /etc/nginx/sites-available/app.conf
dest: /etc/nginx/sites-enabled/app.conf
state: link
notify: Перезагрузить nginx
- name: Сервис nginx запущен и в автозагрузке
ansible.builtin.service:
name: nginx
state: started
enabled: true
handlers:
- name: Перезагрузить nginx
ansible.builtin.service:
name: nginx
state: reloaded
# Запуск: ansible-playbook -i inventory.yml webserver.yml
# Проверка без изменений: ansible-playbook -i inventory.yml webserver.yml --check