Ansible для автоматизации серверов: первые playbook
Как устроен Ansible, как описать серверы в inventory и написать первый плейбук, который настраивает новый VPS одной командой.

Коротко: Ansible — это способ описать нужное состояние сервера в YAML-файле (плейбуке) и применить его к одной машине или к сотне по SSH, без агентов на серверах. Для старта нужны три вещи: Ansible на вашем компьютере, файл inventory со списком серверов и первый плейбук. Повторный запуск ничего не ломает: модули меняют только то, что отличается от описанного.
Пока сервер один, его удобно настраивать руками: зашёл по SSH, поставил пакеты, поправил конфиг. Проблемы начинаются через полгода, когда нужно поднять второй такой же сервер или восстановить первый после сбоя, а вспомнить, что именно и в каком порядке делалось, уже никто не может. Bash-скрипт помогает частично: его трудно запускать повторно и он плохо переносит частичные сбои (об этом — в статье про bash-скрипты для автоматизации). Ansible решает ту же задачу декларативно: вы пишете не «что сделать», а «каким должен стать сервер».
Это полезно не только админам. Если сайт или сервис вам делает подрядчик, попросите команду, которая делает вам сайт, сдать вместе с кодом и настройку сервера в виде плейбука или роли. Тогда сервер можно пересобрать за полчаса без автора проекта, перенести к другому хостеру и понять, что на нём вообще установлено. Ниже — как устроен Ansible, первый рабочий плейбук для нового VPS и ошибки, на которых спотыкается почти каждый новичок.
Что такое Ansible и как он работает?
Ansible — инструмент управления конфигурацией с открытым кодом, который развивает Red Hat. Его ключевая особенность — безагентность: на управляемых серверах ничего ставить не нужно, достаточно SSH-доступа и Python (он есть почти в любом дистрибутиве). Ansible стоит только на управляющей машине — вашем ноутбуке, отдельном bastion-сервере или раннере CI.
Порядок работы при запуске плейбука:
- Ansible читает inventory — список серверов и групп.
- Подключается к каждому серверу по SSH и собирает факты: дистрибутив, версию, IP-адреса, объём памяти.
- Для каждой задачи плейбука копирует на сервер небольшой модуль на Python, выполняет его и удаляет.
- Модуль сравнивает текущее состояние с желаемым и меняет систему только при расхождении, возвращая статус
ok(ничего не менялось) илиchanged.
По умолчанию задачи идут параллельно на пяти серверах (параметр forks), поэтому десяток машин настраивается почти так же быстро, как одна.
Чем Ansible отличается от bash-скриптов и других инструментов?
Главное отличие — идемпотентность. Строка echo "PermitRootLogin no" >> /etc/ssh/sshd_config в скрипте при каждом запуске дописывает строку заново. Модуль lineinfile проверит, есть ли нужная строка, и тронет файл только если её нет. Поэтому плейбук можно запускать хоть каждый час — это одновременно и настройка, и проверка, что никто не поменял сервер руками.
| Критерий | Bash-скрипт | Ansible | Puppet / Chef / Salt |
|---|---|---|---|
| Агент на сервере | Не нужен | Не нужен (SSH) | Обычно нужен |
| Повторный запуск | Надо писать проверки вручную | Безопасен «из коробки» | Безопасен |
| Язык описания | Bash | YAML + шаблоны Jinja2 | Свой DSL или Ruby |
| Порог входа | Низкий | Низкий | Средний и выше |
| Много серверов сразу | Через циклы и ssh | Встроено (inventory, группы) | Встроено |
| Режим «что изменится» | Нет | --check --diff | Есть |
Terraform и OpenTofu часто путают с Ansible, но у них другая зона: они создают инфраструктуру (виртуальные машины, сети, DNS-записи у облачного провайдера), а Ansible настраивает то, что уже создано. На практике их используют вместе.
Как установить Ansible?
Ставить нужно только на управляющую машину. На Windows Ansible как управляющий узел не работает — используйте WSL2.
# Debian / Ubuntu — версия из репозитория дистрибутива
sudo apt update && sudo apt install -y ansible
# Fedora / RHEL-совместимые
sudo dnf install -y ansible-core
# Свежая версия в изолированном окружении (любой Linux или macOS)
pipx install --include-deps ansible
# Проверка
ansible --version
Пакет ansible-core содержит движок и встроенные модули ansible.builtin.*. Полный пакет ansible добавляет популярные коллекции, например community.general и ansible.posix, — для примеров ниже удобнее он. Недостающую коллекцию всегда можно доставить командой ansible-galaxy collection install community.general.
До начала проверьте, что на сервер можно зайти по SSH-ключу без пароля: Ansible ходит на машины так же, как вы. Если ключей ещё нет — сначала настройте вход по SSH-ключам.
Inventory: как описать свои серверы?
Inventory — это файл со списком хостов, разбитых на группы. Самый простой формат — INI:
# inventory.ini
[web]
web1 ansible_host=203.0.113.10
web2 ansible_host=203.0.113.11
[db]
db1 ansible_host=203.0.113.20
[all:vars]
ansible_user=deploy
ansible_python_interpreter=/usr/bin/python3
Группы позволяют применять разные плейбуки к разным ролям серверов, а встроенная группа all включает всё сразу. Переменные можно задавать прямо в inventory, но удобнее вынести их в каталоги group_vars/ и host_vars/ рядом с файлом: group_vars/web.yml применится ко всем веб-серверам, host_vars/db1.yml — только к базе.
Проверить связь со всеми машинами можно ad-hoc командой, без плейбука:
ansible all -i inventory.ini -m ping
# web1 | SUCCESS => {"changed": false, "ping": "pong"}
ansible web -i inventory.ini -a "uptime"
ansible db -i inventory.ini -m ansible.builtin.apt -a "name=htop state=present" --become
Ad-hoc команды хороши для разовых действий: посмотреть аптайм, перезапустить службу на всех серверах. Всё, что нужно повторять, оформляйте в плейбук.
Из чего состоит плейбук?
Плейбук — YAML-файл с одним или несколькими play. Каждый play говорит: «на этих хостах, с такими правами, выполни такие задачи». Основные элементы:
- hosts — группа или хост из inventory, к которым применяется play.
- become — выполнять задачи через
sudo(нужно почти всегда, когда меняете систему). - vars — переменные, доступные в задачах и шаблонах через
{{ имя }}. - tasks — список задач; каждая вызывает один модуль с параметрами.
- handlers — задачи, которые выполняются только по уведомлению (
notify) и один раз в конце play, например перезапуск службы после смены конфига.
Модули, которые понадобятся в первую очередь:
| Модуль | Что делает |
|---|---|
ansible.builtin.apt / dnf / package | Ставит, обновляет и удаляет пакеты |
ansible.builtin.user | Создаёт пользователей и группы |
ansible.posix.authorized_key | Добавляет SSH-ключи пользователю |
ansible.builtin.copy / template | Кладёт файлы на сервер; template подставляет переменные через Jinja2 |
ansible.builtin.lineinfile | Гарантирует наличие или отсутствие строки в файле |
ansible.builtin.service / systemd | Запускает, останавливает и включает службы |
community.general.ufw | Управляет файрволом UFW |
Полный список с параметрами — в индексе модулей официальной документации Ansible.
Как написать первый playbook: настройка нового VPS по шагам
Задача: свежий Ubuntu-сервер, на котором есть только root. Нужно создать пользователя для работы, пустить его по ключу, запретить вход root и по паролю, включить файрвол и поставить Nginx. Это ровно то, что обычно делают руками в первые полчаса жизни сервера (подробнее о смысле каждого шага — в статье про базовую защиту Linux-сервера).
Шаг 1. Создайте каталог проекта и положите в него inventory:
mkdir -p ~/infra/templates && cd ~/infra
cat > inventory.ini <<'EOF'
[web]
web1 ansible_host=203.0.113.10 ansible_user=root
EOF
Шаг 2. Напишите плейбук setup.yml:
---
- name: Базовая настройка веб-сервера
hosts: web
become: true
vars:
admin_user: deploy
admin_key: "{{ lookup('file', '~/.ssh/id_ed25519.pub') }}"
tasks:
- name: Обновить кэш пакетов и систему
ansible.builtin.apt:
update_cache: true
upgrade: safe
cache_valid_time: 3600
- name: Поставить нужные пакеты
ansible.builtin.apt:
name: [nginx, ufw, fail2ban, unattended-upgrades]
state: present
- name: Создать рабочего пользователя
ansible.builtin.user:
name: "{{ admin_user }}"
groups: sudo
append: true
shell: /bin/bash
- name: Разрешить sudo без пароля
ansible.builtin.copy:
dest: "/etc/sudoers.d/{{ admin_user }}"
content: "{{ admin_user }} ALL=(ALL) NOPASSWD:ALL\n"
mode: "0440"
validate: "visudo -cf %s"
- name: Добавить SSH-ключ
ansible.posix.authorized_key:
user: "{{ admin_user }}"
key: "{{ admin_key }}"
- name: Запретить вход root и по паролю
ansible.builtin.lineinfile:
path: /etc/ssh/sshd_config
regexp: "{{ item.re }}"
line: "{{ item.line }}"
validate: "sshd -t -f %s"
loop:
- { re: '^#?PermitRootLogin', line: 'PermitRootLogin no' }
- { re: '^#?PasswordAuthentication', line: 'PasswordAuthentication no' }
notify: Перезапустить sshd
- name: Открыть SSH и HTTP(S) в файрволе
community.general.ufw:
rule: allow
name: "{{ item }}"
loop: [OpenSSH, "Nginx Full"]
- name: Включить файрвол
community.general.ufw:
state: enabled
policy: deny
- name: Положить конфиг сайта
ansible.builtin.template:
src: templates/site.conf.j2
dest: /etc/nginx/sites-available/default
notify: Перечитать nginx
handlers:
- name: Перезапустить sshd
ansible.builtin.service:
name: ssh
state: restarted
- name: Перечитать nginx
ansible.builtin.service:
name: nginx
state: reloaded
Шаг 3. Создайте шаблон templates/site.conf.j2. Переменные Ansible подставит сам, в том числе факты о сервере:
server {
listen 80 default_server;
server_name {{ inventory_hostname }};
root /var/www/html;
# Сгенерировано Ansible для {{ ansible_facts['distribution'] }} {{ ansible_facts['distribution_version'] }}
}
Шаг 4. Сначала посмотрите, что изменится, ничего не меняя, и только потом применяйте:
ansible-playbook -i inventory.ini setup.yml --syntax-check
ansible-playbook -i inventory.ini setup.yml --check --diff
ansible-playbook -i inventory.ini setup.yml
Шаг 5. После первого прогона root по SSH больше не пустит. Поменяйте в inventory ansible_user=root на ansible_user=deploy и запустите плейбук ещё раз. В итоговой сводке PLAY RECAP должно быть changed=0 — это и есть идемпотентность: сервер уже в нужном состоянии.
Обратите внимание на validate в задачах с sudoers и sshd_config: Ansible проверит новый файл командой visudo -c или sshd -t до того, как положит его на место. Ошибка в конфиге SSH без такой проверки — классический способ потерять доступ к серверу.
Переменные, шаблоны и секреты: что нужно знать сразу?
Переменные в Ansible можно задать в десятке мест, и у них есть приоритет: значение, переданное через -e в командной строке, побеждает всё остальное, host_vars сильнее group_vars, а значения из defaults роли — самые слабые. Новичку достаточно правила: общие значения — в group_vars, исключения для конкретной машины — в host_vars, разовое переопределение — через -e.
Пароли, токены и ключи API в открытом виде в репозиторий не кладут. Для этого есть встроенный Ansible Vault:
# зашифровать файл с переменными целиком
ansible-vault encrypt group_vars/db/vault.yml
# или одно значение, чтобы вставить его в обычный YAML
ansible-vault encrypt_string 'S3cr3t' --name 'db_password'
# запуск с запросом пароля от vault
ansible-playbook -i inventory.ini site.yml --ask-vault-pass
Зашифрованные файлы можно спокойно хранить в git, а пароль от vault — в менеджере паролей или в защищённой переменной CI.
Когда пора переходить на роли?
Когда плейбук перевалил за сотню строк или одни и те же задачи нужны в нескольких проектах, их выносят в роли. Роль — это каталог со стандартной структурой, который Ansible понимает без дополнительных настроек:
ansible-galaxy role init roles/nginx
roles/nginx/
├── tasks/main.yml # задачи
├── handlers/main.yml # обработчики
├── templates/ # шаблоны Jinja2
├── files/ # статические файлы
├── defaults/main.yml # значения по умолчанию
├── vars/main.yml # переменные роли
└── meta/main.yml # зависимости
Главный плейбук после этого становится коротким и читается как опись сервера:
- hosts: web
become: true
roles:
- common
- nginx
- app
Готовые роли для популярного ПО (PostgreSQL, Docker, Node.js) есть в Ansible Galaxy, но перед использованием прочитайте их код: чужая роль выполняется на вашем сервере с правами root.
Частые ошибки новичков
- Модули
shellиcommandвместо профильных. Они не идемпотентны: Ansible не знает, изменилось ли что-то, и при каждом прогоне пишетchanged. Если без них не обойтись, добавляйтеcreates,removesилиchanged_when. - Отступы в YAML. Табы запрещены, а сдвиг на один пробел меняет смысл. Проверяйте файлы через
--syntax-checkиansible-lint. - Забытый
become: true. Задача падает с «Permission denied» — а дело в том, что без повышения прав модуль работает от обычного пользователя. - Перезапуск служб задачей, а не через handler. Служба перезапускается при каждом прогоне, даже если конфиг не менялся.
- Правки на сервере руками «в обход». Следующий прогон плейбука их перезапишет. Всё, что важно, — только через репозиторий.
- Запуск сразу на проде. Начинайте с
--check --diffи с флага--limit web1, чтобы применить изменения сначала к одной машине.
Как встроить Ansible в повседневную работу
Плейбуки — это код, и обращаться с ними стоит как с кодом: держать в git (см. git для начинающих), проверять ревью и прогонять линтер. Следующий логичный шаг — запускать их не с ноутбука, а из конвейера: при мердже в основную ветку CI делает ansible-lint, затем --check на стейджинге и применяет изменения. Как собрать такой конвейер, разобрано в статье GitLab CI с нуля за час.
Несколько привычек, которые экономят время:
- Положите рядом с inventory файл
ansible.cfgс путём к inventory иforks = 20— не придётся писать-iв каждой команде. - Включите
pipelining = Trueв секции[ssh_connection]: прогоны ускоряются в разы за счёт меньшего числа SSH-операций. - Размечайте задачи тегами (
tags: [nginx]) и запускайте только нужную часть:--tags nginx. - Раз в неделю прогоняйте плейбук в режиме
--checkпо всем серверам: любойchangedпокажет, где кто-то правил руками.
Частые вопросы
Нужно ли устанавливать Ansible на сами серверы?
Нет. Ansible ставится только на управляющую машину. На серверах нужны SSH-доступ и Python 3, который есть в стандартной установке почти всех современных дистрибутивов.
Есть ли смысл в Ansible, если сервер всего один?
Да. Плейбук становится документацией сервера: видно, что установлено и как настроено. При сбое или переезде к другому хостеру сервер поднимается заново одной командой, а не по памяти.
Чем плейбук отличается от роли?
Плейбук говорит, какие задачи и роли применить к каким хостам. Роль — переиспользуемый набор задач, шаблонов и переменных со стандартной структурой каталогов, который подключается в разные плейбуки и проекты.
Можно ли управлять Windows-серверами?
Да, через WinRM или SSH и модули из коллекции ansible.windows. Но управляющая машина всё равно должна быть на Linux или macOS — на Windows её запускают внутри WSL2.
Как проверить плейбук, не ломая сервер?
Запустите его с флагами --check --diff: Ansible покажет, что изменил бы, ничего не меняя. Для полноценной проверки используйте тестовую виртуальную машину или контейнер, а для ролей — фреймворк Molecule.
Где хранить пароли и токены для плейбуков?
В файлах, зашифрованных Ansible Vault, или во внешнем хранилище секретов. Пароль от vault держат в менеджере паролей или в защищённой переменной CI, но не в репозитории.
Ansible или Terraform — что учить первым?
Если у вас уже есть серверы, которые нужно настраивать, — Ansible. Terraform создаёт облачную инфраструктуру с нуля: машины, сети, DNS. В зрелых проектах они работают вместе: Terraform создаёт сервер, Ansible его настраивает.