Представь: у тебя три сервера. Один для продакшена, один для стейджинга, один для тестов. Каждый раз, когда нужно поставить новый пакет или изменить конфиг nginx — идёшь на каждый по SSH и делаешь одно и то же три раза. Потом появляется четвёртый сервер. Потом пятый. И вот ты уже провёл полдня за одинаковыми командами.

Ansible решает именно эту проблему. Один раз описываешь, что должно быть на сервере — и он сам всё настраивает. На одном сервере или на ста.

Что такое Ansible и почему он популярен

Ansible — это инструмент для автоматизации настройки инфраструктуры. Написан на Python, работает по SSH, не требует агентов на целевых машинах. Последнее — большое преимущество перед Chef и Puppet, где на каждый сервер нужно ставить клиент.

Почему его выбирают:

  • Пороговый вход низкий. YAML-конфиги читаются почти как обычный текст.
  • Идемпотентность. Запускай playbook хоть десять раз — результат одинаковый, лишних действий не будет.
  • Огромное сообщество. Официальная коллекция модулей покрывает AWS, GCP, Azure, Docker, Kubernetes, базы данных и ещё несколько сотен вещей.
  • Agentless. Нужен только Python на целевом хосте и SSH-доступ.

Ansible не идеален для всего. Если у тебя 1000+ серверов и высокая частота изменений — посмотри в сторону SaltStack или Puppet. Но для большинства проектов Ansible отлично справляется.

Основные понятия

Прежде чем лезть в примеры — разберём терминологию. Без неё документация выглядит как шифр.

Inventory — список серверов, которыми управляет Ansible. Может быть статическим файлом или динамическим (например, тянет список из AWS EC2).

Playbook — файл с инструкциями. Описывает, что и на каких серверах нужно сделать.

Task — одно конкретное действие: поставить пакет, скопировать файл, перезапустить сервис.

Role — набор задач, шаблонов и переменных, упакованных в переиспользуемую структуру. Например, роль nginx настраивает nginx одинаково на всех проектах.

Module — встроенный инструмент Ansible для конкретной задачи. apt — для установки пакетов на Debian/Ubuntu, yum — на CentOS/RHEL, copy — для копирования файлов, service — для управления сервисами.

Handler — задача, которая запускается только если что-то изменилось. Классический пример: перезапустить nginx только если конфиг действительно поменялся.

Установка и первый запуск

На управляющей машине (твой ноутбук или CI-сервер):

pip install ansible

Или через пакетный менеджер:

# Ubuntu
sudo apt install ansible

# macOS
brew install ansible

Проверяем:

ansible --version

Теперь создаём простой инвентарь. Файл inventory.ini:

[web]
192.168.1.10 ansible_user=ubuntu ansible_ssh_private_key_file=~/.ssh/id_rsa
192.168.1.11 ansible_user=ubuntu ansible_ssh_private_key_file=~/.ssh/id_rsa

[db]
192.168.1.20 ansible_user=ubuntu ansible_ssh_private_key_file=~/.ssh/id_rsa

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

ansible all -i inventory.ini -m ping

Если в ответ получаешь pong — всё работает.

Первый playbook

Напишем простой playbook, который ставит nginx и запускает его. Файл setup_nginx.yml:

---
- name: Настройка nginx
  hosts: web
  become: true  # sudo

  tasks:
    - name: Обновить кэш apt
      apt:
        update_cache: yes
        cache_valid_time: 3600

    - name: Установить nginx
      apt:
        name: nginx
        state: present

    - name: Запустить и включить nginx
      service:
        name: nginx
        state: started
        enabled: yes

    - name: Скопировать конфиг
      copy:
        src: files/nginx.conf
        dest: /etc/nginx/nginx.conf
        owner: root
        group: root
        mode: '0644'
      notify: Перезапустить nginx

  handlers:
    - name: Перезапустить nginx
      service:
        name: nginx
        state: restarted

Запускаем:

ansible-playbook -i inventory.ini setup_nginx.yml

Ansible пройдётся по всем серверам в группе web, выполнит задачи по порядку и в конце покажет статистику: сколько задач выполнено, сколько изменений внесено, были ли ошибки.

Обрати внимание на notify и handlers. Конфиг скопируется всегда, но nginx перезапустится только если файл реально изменился. Это и есть идемпотентность в действии.

Переменные и шаблоны

Жёсткие значения в playbook — плохая практика. Для разных окружений (dev, staging, prod) обычно нужны разные настройки.

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

# В самом playbook
vars:
  app_port: 8080
  app_user: deploy

# В отдельном файле group_vars/web.yml
app_port: 8080
max_connections: 1000

Шаблоны — это конфиги с переменными. Ansible использует Jinja2. Файл templates/nginx.conf.j2:

server {
    listen {{ app_port }};
    server_name {{ domain }};

    location / {
        proxy_pass http://127.0.0.1:{{ backend_port }};
    }
}

В playbook используем модуль template вместо copy:

- name: Скопировать конфиг nginx из шаблона
  template:
    src: templates/nginx.conf.j2
    dest: /etc/nginx/sites-available/app.conf
  notify: Перезапустить nginx

Теперь для dev указываем одни переменные, для prod — другие. Сам playbook не трогаем.

Роли: структура для больших проектов

Когда playbook разрастается до 200+ строк — пора разбивать его на роли.

Структура роли:

roles/
  nginx/
    tasks/
      main.yml       # основные задачи
    handlers/
      main.yml       # хендлеры
    templates/
      nginx.conf.j2  # шаблоны
    files/
      index.html     # статические файлы
    vars/
      main.yml       # переменные роли
    defaults/
      main.yml       # дефолтные значения

Playbook с ролями выглядит чисто:

---
- name: Настройка веб-серверов
  hosts: web
  become: true
  roles:
    - common
    - nginx
    - app

Готовые роли можно брать с Ansible Galaxy — официального репозитория. Там тысячи ролей для всего подряд: PostgreSQL, Redis, Docker, Let's Encrypt и так далее.

ansible-galaxy install geerlingguy.nginx
ansible-galaxy install geerlingguy.postgresql

Реальный сценарий: деплой приложения

Посмотрим на практический пример — деплой Python-приложения на новый сервер. Что нужно сделать:

  1. Поставить Python, pip, virtualenv
  2. Создать пользователя для приложения
  3. Склонировать репозиторий
  4. Установить зависимости
  5. Настроить systemd-юнит
  6. Настроить nginx как прокси
  7. Получить SSL-сертификат

Без Ansible это час работы руками. С Ansible — первый запуск те же 15-20 минут на написание playbook, но потом каждый новый сервер настраивается за 3-5 минут автоматически.

Фрагмент playbook:

- name: Создать пользователя приложения
  user:
    name: "{{ app_user }}"
    shell: /bin/bash
    home: "/home/{{ app_user }}"
    create_home: yes

- name: Клонировать репозиторий
  git:
    repo: "{{ git_repo }}"
    dest: "{{ app_dir }}"
    version: "{{ git_branch }}"
  become_user: "{{ app_user }}"

- name: Установить зависимости Python
  pip:
    requirements: "{{ app_dir }}/requirements.txt"
    virtualenv: "{{ app_dir }}/venv"
  become_user: "{{ app_user }}"

- name: Настроить systemd-юнит
  template:
    src: templates/app.service.j2
    dest: "/etc/systemd/system/{{ app_name }}.service"
  notify:
    - Перезагрузить systemd
    - Перезапустить приложение

Работа с секретами — Ansible Vault

Пароли, ключи API и токены нельзя хранить в открытом виде в репозитории. Для этого есть Ansible Vault.

Зашифровать файл с секретами:

ansible-vault encrypt secrets.yml

Внутри файла — обычный YAML:

db_password: super_secret_password
api_key: abc123xyz

При запуске playbook передаём пароль от vault:

ansible-playbook site.yml --ask-vault-pass
# или через файл
ansible-playbook site.yml --vault-password-file ~/.vault_pass

В CI/CD пароль от vault передаётся через переменную окружения. Секреты в репозитории есть, но в зашифрованном виде — можно коммитить без страха.

Ansible в CI/CD

Ansible отлично встраивается в пайплайны. Типичная схема:

  1. Разработчик пушит код
  2. CI собирает артефакт (Docker-образ или архив)
  3. Ansible разворачивает его на серверах

Пример шага в GitLab CI:

deploy:
  stage: deploy
  image: python:3.11
  before_script:
    - pip install ansible
    - echo "$VAULT_PASSWORD" > .vault_pass
  script:
    - ansible-playbook -i inventory/prod.ini deploy.yml --vault-password-file .vault_pass
  only:
    - main

Теперь каждый пуш в main автоматически деплоит приложение. Руки больше не нужны.

Типичные ошибки новичков

Не используют теги. Теги позволяют запускать только часть playbook:

- name: Установить nginx
  apt:
    name: nginx
  tags: nginx
ansible-playbook site.yml --tags nginx

Без тегов при каждом изменении конфига приходится гонять весь playbook.

Хардкодят пути и версии. Всё, что может меняться между окружениями — выносить в переменные. Иначе придётся менять playbook при каждом обновлении.

Не проверяют изменения перед применением. Ansible умеет работать в режиме check:

ansible-playbook site.yml --check --diff

Показывает, что изменится, но ничего не трогает. Обязательно использовать перед изменениями на продакшене.

Игнорируют идемпотентность. Если используешь модуль command или shell — они не идемпотентны сами по себе. Нужно добавлять creates или when, чтобы команда не выполнялась повторно:

- name: Инициализировать базу данных
  command: /app/manage.py migrate
  args:
    creates: /app/.db_initialized

Когда Ansible не нужен

Ansible — не серебряная пуля. Есть ситуации, когда он избыточен или плохо подходит:

  • Один сервер, который настраивается раз и навсегда. Bash-скрипт справится.
  • Оркестрация контейнеров. Для Kubernetes есть Helm и Argo CD.
  • Очень частые изменения конфигурации (несколько раз в минуту). Здесь нужен другой инструмент.
  • Сложная логика с условиями и циклами. YAML для этого не приспособлен — придётся писать некрасивый код.

Ansible хорошо работает там, где нужно настроить инфраструктуру один раз и поддерживать её в актуальном состоянии. Первичная настройка серверов, управление конфигами, деплой приложений — его стихия.

Где хранить playbooks

Отдельный git-репозиторий для инфраструктурного кода — хорошая практика. Структура проекта:

infra/
  inventory/
    production/
      hosts.ini
      group_vars/
        all.yml
        web.yml
    staging/
      hosts.ini
      group_vars/
        all.yml
  roles/
    common/
    nginx/
    postgresql/
    app/
  playbooks/
    site.yml
    deploy.yml
    backup.yml
  secrets/
    vault.yml  # зашифрован
  ansible.cfg

Файл ansible.cfg в корне проекта задаёт дефолтные настройки:

[defaults]
inventory = inventory/production
roles_path = roles
vault_password_file = ~/.vault_pass
host_key_checking = False
retry_files_enabled = False

Теперь не нужно каждый раз указывать -i inventory/production в командной строке.

Итого

Ansible — один из тех инструментов, после знакомства с которым непонятно, как без него жили раньше. Порог входа невысокий: за день можно написать первый рабочий playbook и автоматизировать рутину.

С чего начать:

  1. Установи Ansible и попробуй ping на тестовый сервер
  2. Напиши playbook для установки nginx или любого другого инструмента, который ставишь руками
  3. Вынеси конфиг в шаблон с переменными
  4. Разбей большой playbook на роли
  5. Добавь Vault для секретов
  6. Встрой в CI/CD

Каждый шаг даёт ощутимый результат. Не обязательно сразу делать всё идеально — начни с малого и наращивай сложность.

Если ты занимаешься развитием бизнеса и у тебя пока нет времени разбираться с DevOps-инструментами — можно отдать инфраструктурные задачи на аутсорс. В REEXY (r3xy.ru) занимаются не только разработкой сайтов и ботов, но и автоматизацией: скрипты, интеграции, настройка серверной части. Стоимость автоматизации — от 1 500 ₽ в зависимости от задачи.