Архив рассылок сообщества снова открыт. Читать письма 2003-2009
Главная/Блог/Разработка
Разработка14 мин чтенияНиколай Ковалёв

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.

Порядок работы при запуске плейбука:

  1. Ansible читает inventory — список серверов и групп.
  2. Подключается к каждому серверу по SSH и собирает факты: дистрибутив, версию, IP-адреса, объём памяти.
  3. Для каждой задачи плейбука копирует на сервер небольшой модуль на Python, выполняет его и удаляет.
  4. Модуль сравнивает текущее состояние с желаемым и меняет систему только при расхождении, возвращая статус ok (ничего не менялось) или changed.

По умолчанию задачи идут параллельно на пяти серверах (параметр forks), поэтому десяток машин настраивается почти так же быстро, как одна.

Чем Ansible отличается от bash-скриптов и других инструментов?

Главное отличие — идемпотентность. Строка echo "PermitRootLogin no" >> /etc/ssh/sshd_config в скрипте при каждом запуске дописывает строку заново. Модуль lineinfile проверит, есть ли нужная строка, и тронет файл только если её нет. Поэтому плейбук можно запускать хоть каждый час — это одновременно и настройка, и проверка, что никто не поменял сервер руками.

КритерийBash-скриптAnsiblePuppet / Chef / Salt
Агент на сервереНе нуженНе нужен (SSH)Обычно нужен
Повторный запускНадо писать проверки вручнуюБезопасен «из коробки»Безопасен
Язык описанияBashYAML + шаблоны 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 с нуля за час.

Несколько привычек, которые экономят время:

  1. Положите рядом с inventory файл ansible.cfg с путём к inventory и forks = 20 — не придётся писать -i в каждой команде.
  2. Включите pipelining = True в секции [ssh_connection]: прогоны ускоряются в разы за счёт меньшего числа SSH-операций.
  3. Размечайте задачи тегами (tags: [nginx]) и запускайте только нужную часть: --tags nginx.
  4. Раз в неделю прогоняйте плейбук в режиме --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 его настраивает.

Вернуться в блог