Модуль 01 — Управление конфигурацией и Ansible

Почему «я помню, как настраивал тот сервер» не масштабируется на второй сервер

Прогресс курса Модуль 1 из 9

Что вы освоите в этом модуле

01

Серверы, настроенные руками

Первый сервер администратор настраивает по шагам: поставить пакеты, создать пользователей, поправить конфигурацию nginx, открыть порты. Через полгода нужен второй такой же — и выясняется, что половина шагов нигде не записана. Через год серверов десять, и все немного разные: на одном обновили OpenSSL, на другом вручную поправили лимит соединений во время аварии и забыли вернуть. Это называют дрейфом конфигурации.

ПроблемаСледствие
Шаги настройки в голове или в устаревшей викиНовый сервер настраивается днями и всё равно не так, как старые
Серверы постепенно расходятсяОшибка воспроизводится только на одном из десяти; обновление ломает «особенный» сервер
Нет истории измененийНепонятно, кто и зачем поменял настройку; откатить нечего
Одно и то же руками на многих машинахЧасы рутины и опечатки

Управление конфигурацией (configuration management) — описать нужное состояние серверов в файлах, хранить их в Git и применять программой. Описание проходит ревью, как код; новый сервер получает ту же конфигурацию за минуты; повторный запуск возвращает «уплывший» сервер к описанному состоянию.

02

Идемпотентность

Скрипт на bash описывает действия: «добавь строку в файл», «создай пользователя». Запусти его дважды — строка добавится дважды, а создание пользователя упадёт с ошибкой. Ansible описывает состояние: «пакет установлен», «в файле есть такая строка», «служба запущена». Модуль сначала проверяет, каково текущее состояние, и меняет что-то, только если оно отличается от нужного.

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

ЗадачаСкрипт: действиеAnsible: состояние
Пакет nginxapt install nginxapt: name=nginx state=present
Пользователь deployuseradd deploy — второй раз ошибкаuser: name=deploy state=present
Строка в конфигурацииecho … >> файл — второй раз дубльlineinfile: line=… state=present
Службаsystemctl restart nginx — каждый разsystemd: name=nginx state=started — только если не запущена
03

Как устроен Ansible

  управляющий узел (ваш компьютер или сервер администрирования)
    ansible / ansible-playbook  +  инвентарь  +  плейбуки
          │  SSH (ключ), по узлам параллельно
          ├──────────────▶ web1  ┐  на узле нужны только sshd и python3:
          ├──────────────▶ web2  │  Ansible копирует модуль во временный файл,
          └──────────────▶ db1   ┘  выполняет его и удаляет — агента нет

Такая схема называется push без агента: управляющий узел сам подключается к серверам. У Puppet и Chef по-другому — на каждом сервере работает агент, который забирает конфигурацию с центрального сервера (pull). Push проще начать: не нужно ставить и обновлять агентов, достаточно SSH. Terraform решает соседнюю задачу — создаёт сами серверы, сети и балансировщики в облаке; Ansible настраивает то, что внутри серверов. Их часто используют вместе.

04

Установка в Debian 13

Ansible ставится только на управляющий узел. Проще всего — из пакетов Debian:

$ sudo apt update
$ sudo apt install ansible
$ ansible --version | grep -E "^ansible|python version|jinja version"
ansible [core 2.19.11]
  python version = 3.13.5 (main, Aug 10 2026, 12:06:59) [GCC 14.2.0] (/usr/bin/python3)
  jinja version = 3.1.6
$ dpkg -l ansible ansible-core | tail -2 | awk "{print \$2, \$3}"
ansible 12.0.0+dfsg-0+deb13u1
ansible-core 2.19.11-0+deb13u1

Пакетов два. ansible-core — сама программа: команды, движок плейбуков и базовые модули из коллекции ansible.builtin. Пакет ansible добавляет к нему десятки коллекций — наборов модулей для конкретных систем: PostgreSQL, файрволов, облаков, сетевого оборудования. Номера версий у пакетов разные, это нормально: Ansible 12 построен на ansible-core 2.19.

$ ansible-galaxy collection list -p /usr/lib/python3/dist-packages 2>/dev/null | grep -E "^(community\.general|ansible\.posix|community\.postgresql|community\.crypto) "
ansible.posix                            2.1.0  
community.crypto                         3.0.3  
community.general                        11.2.1 
community.postgresql                     4.1.0  
$ ansible-galaxy collection list -p /usr/lib/python3/dist-packages 2>/dev/null | grep -c -E "^[a-z_]+\.[a-z_]+ "
89

Коллекции из пакета не нужно скачивать из интернета: Ansible Galaxy, каталог коллекций, из России открывается не всегда. Свежие версии Ansible выходят и в PyPI (pipx install ansible), но для курса и большинства задач версии из Debian достаточно, а обновления безопасности приходят через apt upgrade.

05

Подготовка узлов: пользователь и ключ

В курсе три учебных «сервера» с Debian 13: web1 (172.30.50.11), web2 (.12) и db1 (.21). У вас это могут быть три виртуальные машины в одной сети с любыми адресами.

Ansible подключается к узлам отдельным пользователем, а не вашим личным и не root. На каждом узле один раз, руками или скриптом при создании машины:

$ sudo useradd -m -s /bin/bash ansible
$ echo 'ansible ALL=(ALL) NOPASSWD: ALL' | sudo tee /etc/sudoers.d/ansible
$ sudo chmod 440 /etc/sudoers.d/ansible
$ sudo install -d -m 700 -o ansible -g ansible /home/ansible/.ssh
$ sudo install -m 600 -o ansible -g ansible ansible_ed25519.pub /home/ansible/.ssh/authorized_keys

На управляющем узле — отдельный ключ для автоматизации (ssh-keygen -t ed25519 -f ~/.ssh/ansible_ed25519 -C ansible@control), его открытая часть и копируется на узлы:

$ ls ~/.ssh
ansible_ed25519
ansible_ed25519.pub
known_hosts
$ ssh-keygen -lf ~/.ssh/ansible_ed25519.pub
256 SHA256:4wyxH0M3XheIwFKvsrqTc70AYF7msDoeUrtkgC3STCE ansible@control (ED25519)
06

Отпечатки узлов

При первом подключении SSH спрашивает, доверять ли ключу сервера. Ansible подключается ко многим узлам сразу и отвечать на вопросы не может. Отпечатки заранее собирают в ~/.ssh/known_hosts и сверяют с теми, что показывает сам сервер:

$ ssh-keyscan -t ed25519 172.30.50.11 172.30.50.12 172.30.50.21 2>/dev/null > ~/.ssh/known_hosts
$ ssh-keygen -lf ~/.ssh/known_hosts
256 SHA256:Td6TuDgimAJgbnO3HW9bu0y9jWDtkkjrmwMZLzgvb9c 172.30.50.12 (ED25519)
256 SHA256:gCtIr4PtE6UFGVh2JW8wOCx0jYxZdqCJ7d765RsibYk 172.30.50.11 (ED25519)
256 SHA256:R/LqIOXlXBEt+jf5e14Qv11ZdsyaszbiAHnSZaPQZzU 172.30.50.21 (ED25519)
web1$ ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub
256 SHA256:gCtIr4PtE6UFGVh2JW8wOCx0jYxZdqCJ7d765RsibYk root@web1 (ED25519)

Отпечаток web1 в known_hosts совпадает с отпечатком, который администратор видит в консоли узла. Так проверяют каждый узел — через консоль виртуальной машины, панель облака или того, кто создавал сервер.

Во многих инструкциях пишут host_key_checking = False, «чтобы не мешало». Это выключает проверку подлинности узлов: подменивший адрес сервера получит подключение с ключом, который даёт root на всех ваших машинах. Отпечатки собирают один раз при создании узла — это минута работы.

Проверим подключение обычным SSH — пользователь, его права и sudo:

$ ssh -i ~/.ssh/ansible_ed25519 ansible@172.30.50.11 "hostname; id; sudo -n whoami"
web1
uid=1000(ansible) gid=1000(ansible) groups=1000(ansible)
root
07

Первые команды

Без файлов настройки всё указывается в командной строке: -i — список узлов (запятая в конце означает «это список, а не файл»), -u — пользователь, --private-key — ключ, -m — модуль. Модуль ping не про ICMP: он проверяет, что Ansible может зайти на узел и выполнить там Python:

$ ansible all -i 172.30.50.11, -u ansible --private-key ~/.ssh/ansible_ed25519 -m ping
[WARNING]: Host '172.30.50.11' is using the discovered Python interpreter at '/usr/bin/python3.13', but future installation of another Python interpreter could cause a different interpreter to be discovered. See https://docs.ansible.com/ansible-core/2.19/reference_appendices/interpreter_discovery.html for more information.
172.30.50.11 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python3.13"
    },
    "changed": false,
    "ping": "pong"
}

Предупреждение говорит, что Ansible сам нашёл Python на узле. Как убрать его настройкой — в модуле 02; дальше в этом модуле вывод показан без него.

Теперь идемпотентность на деле. Установим пакет tree модулем apt; -b (become) — выполнить через sudo:

$ ansible all -i 172.30.50.11, -u ansible --private-key ~/.ssh/ansible_ed25519 -b -m apt -a 'name=tree state=present update_cache=true' | grep -E '\| (CHANGED|SUCCESS)|"changed"'
172.30.50.11 | CHANGED => {
    "changed": true,

CHANGED и "changed": true — пакета не было, Ansible его поставил. Та же команда ещё раз:

$ ansible all -i 172.30.50.11, -u ansible --private-key ~/.ssh/ansible_ed25519 -b -m apt -a 'name=tree state=present' | grep -E '\| (CHANGED|SUCCESS)|"changed"'
172.30.50.11 | SUCCESS => {
    "changed": false

SUCCESS и "changed": false: пакет уже есть, ничего не менялось. Модуль command просто выполняет команду — он не знает, меняет она что-то или нет, и поэтому каждый раз честно сообщает CHANGED:

$ ansible all -i 172.30.50.11, -u ansible --private-key ~/.ssh/ansible_ed25519 -m command -a 'tree -L 1 /srv'
172.30.50.11 | CHANGED | rc=0 >>
/srv

0 directories, 0 files
$ ansible all -i 172.30.50.11, -u ansible --private-key ~/.ssh/ansible_ed25519 -m command -a 'tree -L 1 /srv'
172.30.50.11 | CHANGED | rc=0 >>
/srv

0 directories, 0 files

Отсюда правило на весь курс: если для задачи есть модуль, описывающий состояние, используем его. command и shell — последнее средство, и тогда об идемпотентности заботимся сами (модули 03 и 08).

Ключевые выводы модуля

Запомните главное

  • Управление конфигурацией: нужное состояние серверов описано в файлах в Git и применяется программой
  • Идемпотентность: повторный запуск ничего не меняет; модули описывают состояние, а не действия
  • Ansible работает push без агента: SSH и Python на узлах, модули выполняются и удаляются
  • В Debian 13: apt install ansible — ansible-core и коллекции, Galaxy не нужен
  • Отдельный пользователь ansible, отдельный ключ, отпечатки узлов в known_hosts; host_key_checking = False — никогда
  • CHANGED — состояние изменилось, SUCCESS с changed: false — уже было таким; command всегда CHANGED
Практические задания — на учебном портале. Задания, критерии оценивания, сдача работ и оценки преподавателя — в курсе на portal.nevabit.ru. Учётную запись выдаёт преподаватель.
Все модули Модуль 02: Инвентарь и разовые команды