Когда в очередной раз возникла задача — за два часа поднять десяток однотипных веб-серверов, а через месяц ещё пять таких же, но с парой отличий — стало окончательно ясно: постоянная ручная настройка по SSH давно должна была превратиться в автоматическое развёртывание. Никаких больше копипаст bash-скриптов, потерянных настроек и ночных правок конфигов. Только Infrastructure as Code: описал сервер один раз — и дальше он воспроизводится через Ansible, как только появляется новый хост.

Процесс начинается не с написания YAML, а с того, что мы фиксируем в чек-листе каждую мелочь, которую раньше «и так помнили». Из этого чек-листа рождается структурированная конфигурация, разделённая на роли, переменные и задачи. Ниже — пошаговый путь: от аудита текущих машин и выявления типовых настроек до готового плейбука, который разворачивает стандартный Linux-сервер (Nginx/PHP/MySQL или просто базовую ОС) с учётом безопасности, мониторинга и оптимизации под нагрузку. Получите конкретный чек-лист, рабочие примеры и понимание, как не наступить на привычные грабли при миграции от рутины к автоматизации.

Почему чек-лист — это фундамент автоматизации

Бывает, что инженер сразу садится писать плейбук, не продумав до конца, что именно должно оказаться на каждом сервере. Результат предсказуем: в коде появляются «забытые» настройки, которые потом приходится докручивать руками, а это ломает главный принцип единого источника конфигурации. Ansible не требует агентов на целевых узлах и работает по SSH — за счёт этого его и любят. Но гибкость инструмента требует жёстко заданной структуры входных данных: что ставить, какие порты открыть, куда писать логи.

Чек-лист в контексте IaC — не просто список дел, а матрица требований на все компоненты сервера:

  • Базовая ОС: версия ядра, дистрибутив (Ubuntu, CentOS, AstraLinux), архитектура.
  • Сетевые настройки: IP-адресация, DNS, прокси, правила firewall.
  • Пакеты и сервисы: какой софт должен стоять (Nginx, Docker, Zabbix-agent) и в каком состоянии (запущен, остановлен).
  • Безопасность: настройка SSH (отключение root, только ключи), права доступа, план обновлений.
  • Мониторинг и логирование: куда пишутся логи, какой агент собирает метрики.
  • Оптимизация: параметры ядра, лимиты ресурсов, временная зона.

Однажды на площадке забыли прописать таймзону — вроде мелочь. Но когда через месяц в логах начали расходиться таймстемпы, а Zabbix начал ложно срабатывать по ночам, поиск причины занял несколько часов. Чек-лист исключает такие «сюрпризы».

Чек-лист стандартного сервера (Базовая версия)

Ниже — универсальный каркас для стандартного Linux-сервера. Адаптируйте его под свои задачи: для базы данных добавите настройки swap, буферов и планировщика ввода-вывода; для веб-сервера — параметры worker_processes и keepalive. Но базовый скелет обязан быть зафиксирован.

Категория Параметр Значение / Требование Примечание
ОС Дистрибутив Ubuntu 22.04 / CentOS 8 Версия должна быть зафиксирована в коде
Ядро Стандартное (LTS) Не использовать experimental-ядра
Сеть hostname Уникальный FQDN Например, srv-web-01.corp.ru
DNS Резервные серверы Настройка через /etc/resolv.conf
Firewall UFW / iptables Закрыть все порты, кроме 80, 443, 22
SSH Пользователь root Отключен вход PermitRootLogin no
Ключи Только SSH-ключи PasswordAuthentication no
Port 22 (или нестандартный) Смена порта для защиты от сканеров
Пакеты Обновления apt update / yum update Обновление списка пакетов перед установкой
Базовые ПО git, curl, vim, wget Стандартный набор инструментов
Сервисы Timezone UTC или локальное Например, Europe/Moscow
NTP Синхронизация времени Установка chrony или ntp
Безопасность Лицензии Авто-подтверждение DEBIAN_FRONTEND=noninteractive
Fail2Ban Включен Защита от brute-force
Мониторинг Агент Zabbix / Prometheus Установка и настройка конфига
Логи journalctl + роллинг Настройка logrotate

Архитектура шаблона: от инвентаризации до ролей

После того как чек-лист утверждён, проектируем структуру проекта Ansible. Ansible оперирует плейбуком — YAML-файлом, описывающим желаемое состояние узлов. Плейбук состоит из «игр» (plays), каждая из которых выполняет набор задач на конкретной группе хостов.

Ключевые компоненты:

  1. Inventory (инвентаризация): файл со списком серверов, сгруппированных по ролям ([web], [db], [monitoring]).
  2. Playbook (site.yml): основной сценарий, который запускает задачи.
  3. Roles (роли): логические блоки — base, security, nginx. Позволяют не раздувать один плейбук, а наращивать модули.
  4. Variables (переменные): файлы vars и host_vars с конкретными значениями (порт, пользователь, версия ПО).

Структура проекта Ansible

ansible/
├── ansible.cfg
├── inventory/
│   ├── production.ini
│   └── staging.ini
├── playbooks/
│   └── site.yml
├── roles/
│   ├── base/
│   │   ├── tasks/
│   │   │   └── main.yml
│   │   ├── handlers/
│   │   │   └── main.yml
│   │   └── templates/
│   ├── security/
│   │   ├── tasks/
│   │   │   └── main.yml
│   │   └── templates/
│   │       └── sshd_config.j2
│   └── nginx/
│       ├── tasks/
│       │   └── main.yml
│       └── templates/
│           └── nginx.conf.j2
├── host_vars/
│   └── srv-web-01/
│       └── roles.yml
└── group_vars/
    └── all.yml

Такая структура легко масштабируется. Новый сервер — просто строка в inventory и, при необходимости, пара уникальных переменных в host_vars. Никакой магии.

Выбор формата инвентаризации

Инвентаризация может быть в простом INI или более структурированном YAML. В корпоративных средах часто хватает INI — его проще читать и быстро поправить.

Пример inventory/production.ini:

[web]
srv-web-01 ansible_host=10.0.1.10 ansible_user=deploy
srv-web-02 ansible_host=10.0.1.11 ansible_user=deploy

[db]
srv-db-01 ansible_host=10.0.2.10 ansible_user=deploy

[all:vars]
ansible_python_interpreter=/usr/bin/python3
ansible_ssh_private_key_file=/home/deploy/.ssh/id_rsa

Для облачных платформ (AWS, Yandex.Cloud) изобретать велосипед не нужно — берите динамический инвентори, который сам подтянет хосты из API облака. Это избавляет от постоянного обновления списка IP-адресов вручную, особенно когда инстансы пересоздаются автомасштабированием.

Пошаговая реализация: создание плейбука для базовой настройки

Теперь от проектирования переходим к практике. Напишем плейбук, который закрывает пункты нашего чек-листа: базовые пакеты, hardening SSH, обновления, временная зона.

Шаг 1: Установка Ansible на управляющую машину

Ansible ставится только на control node, на целевые серверы — ничего лишнего.

Ubuntu:

sudo apt update
sudo apt install ansible -y

CentOS:

sudo yum install epel-release -y
sudo yum install ansible -y

Проверим версию:

ansible --version

Шаг 2: Настройка SSH-доступа

Ansible работает по SSH, поэтому на управляющей машине должны быть ключи. Никаких паролей в открытом виде — это сразу исключает риски утечки.

Создаём ключ, если его нет:

ssh-keygen -t ed25519 -C "ansible-control"

Копируем публичный ключ на целевой хост:

ssh-copy-id -i ~/.ssh/id_ed25519.pub [email protected]

Проверяем:

ansible all -m ping -i inventory/production.ini

Если видим SUCCESS — контакт установлен.

Шаг 3: Создание основного плейбука (site.yml)

Создаём playbooks/site.yml:

---
- name: Configure standard server
  hosts: all
  become: true
  vars:
    admin_user: "deploy"
    admin_public_key: "{{ lookup('file', '~/.ssh/id_ed25519.pub') }}"
    timezone: "Europe/Moscow"
    packages:
      - git
      - curl
      - vim
      - wget
      - htop
      - unzip
  tasks:
    - name: Update apt cache (Debian)
      apt:
        update_cache: yes
      when: ansible_os_family == "Debian"

    - name: Install base packages
      package:
        name: "{{ packages }}"
        state: present

    - name: Set timezone
      timezone:
        name: "{{ timezone }}"

    - name: Ensure admin user exists
      user:
        name: "{{ admin_user }}"
        groups: sudo
        shell: /bin/bash
        password: "{{ admin_password | password_hash('sha512') }}"
        createhome: yes

    - name: Add authorized key for admin
      authorized_key:
        user: "{{ admin_user }}"
        state: present
        key: "{{ admin_public_key }}"

Здесь admin_password — переменная, которую мы зашифруем через Ansible Vault, чтобы пароль никогда не лежал в репозитории в чистом виде.

Шаг 4: Использование ролей для модульности

Раздувать один YAML-файл до бесконечности — путь к хаосу. Вынесем безопасность в отдельную роль.

Структура роли roles/security/tasks/main.yml:

---
- name: Disable root login and password auth
  lineinfile:
    path: /etc/ssh/sshd_config
    regexp: "{{ item.regexp }}"
    line: "{{ item.line }}"
    state: present
  with_items:
    - { regexp: '^PermitRootLogin', line: 'PermitRootLogin no' }
    - { regexp: '^PasswordAuthentication', line: 'PasswordAuthentication no' }
  notify: restart sshd

А в site.yml подключаем роль:

---
- name: Configure standard server
  hosts: all
  become: true
  roles:
    - base
    - security

Такой подход позволяет включать/отключать компоненты по необходимости: не нужен Nginx — убираем роль nginx из списка.

Шаг 5: Переменные и спецификация серверов

Разные серверы требуют своих параметров: порт Nginx, лимиты worker процессов. Для этого создаём host_vars/srv-web-01/roles.yml:

---
nginx_port: 8080
worker_connections: 2048

В шаблоне роли используем переменные Jinja2:

server {
    listen {{ nginx_port }};
    ...
}

Меняете конфигурацию только через переменные, не трогая код плейбука.

Чек-лист безопасности: от SSH до firewall

Безопасность — обязательная часть любого стандартного сервера. В базовом чек-листе мы перечислили ключевые моменты, теперь смотрим на реализацию в коде.

1. Настройка SSH

Минимальный набор:

  • Запрет входа root (PermitRootLogin no).
  • Отключение парольной аутентификации (PasswordAuthentication no).
  • Ограничение списка пользователей (AllowUsers admin webadmin).
  • Смена порта (если ушли с 22, не забудьте отразить ansible_port в инвентаризации).

Задача в roles/security/tasks/main.yml через шаблон:

- name: Deploy sshd config
  template:
    src: sshd_config.j2
    dest: /etc/ssh/sshd_config
    owner: root
    group: root
    mode: 0644
    validate: '/usr/sbin/sshd -t -f %s'
  notify: restart sshd

Шаблон sshd_config.j2:

Port {{ ansible_port | default(22) }}
PermitRootLogin no
PasswordAuthentication no
AllowUsers {{ allowed_ssh_users | join(' ') }}

2. Firewall (UFW)

Для Debian/Ubuntu:

- name: Enable UFW and allow SSH
  ufw:
    rule: allow
    port: "{{ ansible_port | default('22') }}"
    proto: tcp

3. Fail2Ban

- name: Install and start fail2ban
  apt:
    name: fail2ban
    state: present
- name: Ensure fail2ban is running
  service:
    name: fail2ban
    state: started
    enabled: yes

4. Обновления системы

Не надо надеяться на ручной apt update раз в месяц. В плейбуке мы обновляем кеш перед установкой пакетов, а для автоматического применения патчей безопасности можно добавить unattended-upgrades:

- name: Install unattended-upgrades
  apt:
    name: unattended-upgrades
    state: present
- name: Enable automatic security updates
  debconf:
    name: "unattended-upgrades"
    question: "unattended-upgrades/enable_auto_updates"
    value: "true"
    vtype: "boolean"

Типичные ошибки и как их избежать

При миграции от ручного управления к шаблонам попадаются грабли, о которые больно споткнуться. Разберём, что чаще всего идёт не так.

1. Отсутствие проверки зависимостей

Если вы ставите пакет, который требует Python, а его нет — playbook упадёт с неочевидной ошибкой. Всегда проверяйте необходимые компоненты:

- name: Ensure python3 is present
  raw: test -e /usr/bin/python3 || (apt update && apt install -y python3)
  register: python_check
  changed_when: python_check.rc != 0

2. Использование паролей в коде

Никаких admin_password: "secret" в незашифрованных файлах. Ansible Vault решает проблему:

ansible-vault encrypt_string 'secret' --name 'admin_password'

Затем вставляете зашифрованную строку в плейбук или отдельный vars-файл.

3. Неправильная обработка ошибок

По умолчанию Ansible не останавливается при ошибке одной задачи. Иногда это нужно, но часто приводит к каскадным сбоям. Контролируйте поведение:

- name: Check nginx config
  command: nginx -t
  register: nginx_check
  failed_when: "'test failed' in nginx_check.stderr"

4. Забывание о временной зоне и NTP

Как только логи начинают писаться в разнобой, аудит превращается в детектив. NTP и таймзона — первые задачи в базовой роли, иначе любой инцидент замедлится.

5. Отсутствие тестирования

Не применяйте плейбук сразу на всей production-группе. Сначала --check, потом на одном тестовом хосте.

Валидация и тестирование шаблона

Прежде чем запускать плейбук на боевых серверах, убедитесь, что он делает именно то, что вы задумали.

1. Сухой запуск (Dry Run)

ansible-playbook playbooks/site.yml -i inventory/production.ini --check --diff

Покажет, что изменится, без реального воздействия на систему.

2. Запуск на ограниченной группе

ansible-playbook playbooks/site.yml -i inventory/production.ini --limit srv-web-01

3. Проверка подключения

ansible all -m ping -i inventory/production.ini

4. Проверка состояния сервисов

ansible web -m service -a "name=nginx state=started" -i inventory/production.ini

5. Автоматическое тестирование

Для серьёзных проектов подключайте Molecule — он разворачивает роли в изолированных контейнерах и проверяет корректность конфигураций до того, как они попадут на реальное железо.

Интеграция с облачными средами и гибридными архитектурами

Сегодня серверы живут и в облаках, и на своих стойках. Ansible гибко объединяет и то, и другое. Динамический инвентори для Yandex.Cloud, например, автоматически подтягивает список виртуалок:

ansible-inventory -i inventory/yandex.oci.yml --list

Для гибридных сред удобно комбинировать несколько инвентаризаций:

ansible-playbook playbooks/site.yml -i inventory/production.ini -i inventory/cloud.ini

Такой подход избавляет от ручного добавления хостов при автомасштабировании и позволяет применять единые политики конфигурации как в on-prem, так и в облаке.

Чек-лист для финальной проверки шаблона

Перед тем, как закрыть задачу, пробегитесь по этому списку:

  • Инвентаризация: Все серверы перечислены, группы корректны.
  • SSH: Доступ только по ключам, root отключён, пароли запрещены.
  • Пакеты: Базовый софт установлен, обновления применены.
  • Сервисы: NTP, Timezone, Firewall работают.
  • Переменные: Пароли зашифрованы (Vault), чувствительные данные не в открытом виде.
  • Тестирование: Плейбук прогнан в --check, ошибки обработаны.
  • Мониторинг: Агент установлен, логи ротируются.
  • Документация: Есть README с описанием переменных и инструкцией по запуску.

FAQ: Часто задаваемые вопросы

Можно ли использовать Ansible для Windows-серверов?
Да, Ansible поддерживает Windows через WinRM. Потребуется настроить соответствующие подключения и убедиться, что PowerShell и .NET Framework нужных версий присутствуют. По умолчанию весь инструментарий заточен под Linux, но Windows тоже автоматизируется.
Как обновить шаблон, если изменилась версия ПО?
Обновите переменную с версией в vars/main.yml или host_vars/. Не редактируйте сам плейбук — код должен быть независим от данных. При следующем запуске Ansible сам подтянет нужный пакет.
Что делать, если плейбук не работает на конкретном сервере?
Используйте --limit для изоляции проблемного хоста, добавьте --diff и -vvv для детального вывода. Часто проблема кроется в устаревшем ключе SSH или несовпадении версий Python.
Как защитить переменные с паролями?
Обязательно Ansible Vault. Шифруйте чувствительные строки или целые файлы. При запуске указываете пароль от vault или файл-ключ.
Можно ли применить один шаблон для разных дистрибутивов?
Да, через условия when: ansible_os_family == "Debian" и т.д. Роль базовой настройки сама определит, какие менеджеры пакетов использовать.
Как откатить изменения, если плейбук применился некорректно?
Ansible сам по себе не хранит историю и не умеет откатывать. Для инфраструктурных объектов используйте Terraform, а в самом Ansible — делайте резервные копии конфигураций до применения любых изменений (модуль copy с backup: yes).

Вывод

Описать стандартный сервер в виде шаблона — это первый кирпичик в фундаменте инженерии надёжности. Начинается всё с чек-листа, в котором зафиксировано каждое требование, а заканчивается плейбуком, разворачивающим сервер без единого ручного входа.

Главные принципы, которые стоит запомнить:

  1. Чек-лист — без него шаблон будет дырявым.
  2. Модульность: роли делают код читаемым и повторно используемым.
  3. Переменные: не зашивайте данные в код, выносите их в vars и host_vars.
  4. Тестирование: --check и Molecule — ваши лучшие друзья.
  5. Безопасность: SSH, firewall и Fail2Ban — не опциональные дополнения, а обязательная часть.

Когда серверов переваливает за сотню, рутина начинает душить. Шаблонизация — это способ не просто сэкономить время, а перейти от тушения ночных пожаров к осознанному проектированию надёжных систем, где ручные правки конфигов больше не нужны. И с этого начинается путь от классического админа к SRE-инженеру, который автоматизирует всё, включая собственные задачи.