LEARN X · ЗА 15 МИН

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
Поддержать проект