Представь: у тебя три сервера. Один для продакшена, один для стейджинга, один для тестов. Каждый раз, когда нужно поставить новый пакет или изменить конфиг 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-приложения на новый сервер. Что нужно сделать:
- Поставить Python, pip, virtualenv
- Создать пользователя для приложения
- Склонировать репозиторий
- Установить зависимости
- Настроить systemd-юнит
- Настроить nginx как прокси
- Получить 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 отлично встраивается в пайплайны. Типичная схема:
- Разработчик пушит код
- CI собирает артефакт (Docker-образ или архив)
- 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 и автоматизировать рутину.
С чего начать:
- Установи Ansible и попробуй
ping на тестовый сервер
- Напиши playbook для установки nginx или любого другого инструмента, который ставишь руками
- Вынеси конфиг в шаблон с переменными
- Разбей большой playbook на роли
- Добавь Vault для секретов
- Встрой в CI/CD
Каждый шаг даёт ощутимый результат. Не обязательно сразу делать всё идеально — начни с малого и наращивай сложность.
Если ты занимаешься развитием бизнеса и у тебя пока нет времени разбираться с DevOps-инструментами — можно отдать инфраструктурные задачи на аутсорс. В REEXY (r3xy.ru) занимаются не только разработкой сайтов и ботов, но и автоматизацией: скрипты, интеграции, настройка серверной части. Стоимость автоматизации — от 1 500 ₽ в зависимости от задачи.