Когда серверов становится больше десятка, ручная настройка превращается в утомительный ритуал. Залогиниться по SSH, прогнать одни и те же команды, проверить версии пакетов, поправить конфиги, перезагрузить службу. Затем повторить всё на втором сервере. На третьем. На двадцатом. Каждый администратор, который через это прошёл, рано или поздно задаётся вопросом, нельзя ли поручить рутину машине. Ответ нашёлся ещё в 2012 году, когда Майкл ДеХаан выпустил первую версию Ansible - инструмента, который превратил управление парком серверов в дело нескольких минут.
Прелесть Ansible в том, что он не требует установки агентов на управляемые узлы. Никаких демонов, никаких отдельных портов, никакой возни с обновлением клиентской части. Достаточно SSH-доступа и интерпретатора Python на целевой машине - и всё, серверы готовы подчиняться. Этот подход, известный как agentless, выгодно отличает Ansible от его соперников вроде Puppet или Chef. Меньше движущихся частей - меньше точек отказа.
В материале подробно разбирается развёртывание Ansible на управляющем сервере с Ubuntu 22.04, настройка инвентарного файла с группировкой узлов, обмен SSH-ключами для беспарольного доступа, базовые команды и первый рабочий плейбук на YAML. Каждый шаг сопровождается пояснениями, чтобы материал работал не как сборник заклинаний, а как осознанная инструкция.
Подготовка инфраструктуры и проверка предварительных требований перед установкой Ansible
Прежде чем браться за установку, стоит трезво оценить, что должно быть готово к моменту запуска первой команды. Понадобится минимум два сервера на Ubuntu 22.04 - один станет управляющим узлом, остальные превратятся в управляемые. На всех машинах должен работать OpenSSH-сервер, а сами серверы должны быть доступны по публичным IP-адресам или через приватную сеть с настроенной маршрутизацией.
На управляющем сервере нужен обычный пользователь с правами sudo, а вот на клиентских узлах для первоначальной настройки удобнее иметь активный root-аккаунт с парольной аутентификацией. Позднее этот доступ можно и нужно ограничить, заменив парольный вход на ключевой и создав отдельного пользователя для работы Ansible. Такая последовательность действий не прихоть, а здравый смысл - сначала надёжно соединяемся, потом закручиваем гайки безопасности.
Бывает, что у новичков возникает соблазн пропустить настройку отдельного пользователя и вечно ходить под root-ом. Соблазн понятный, но коварный. Стоит однажды засветить ключ или пароль, и всё хозяйство окажется во власти недобрых людей. Минимальная сегрегация прав окупается многократно.
Установка пакета Ansible из официального репозитория и проверка работоспособности утилиты
Ubuntu тащит Ansible в стандартных репозиториях, но версии там часто отстают от актуальных. Поэтому правильнее подключить официальный PPA от разработчиков - так на сервер попадёт свежий релиз с последними модулями и патчами безопасности. Делается это одной командой.
$ sudo add-apt-repository ppa:ansible/ansible
Команда добавляет источник пакетов и автоматически обновляет индекс репозиториев. Аббревиатура PPA расшифровывается как Personal Package Archive - механизм Ubuntu, позволяющий разработчикам публиковать свои сборки без необходимости проходить долгий путь включения в основные репозитории дистрибутива. PPA от команды Ansible считается каноническим источником, ему доверяют миллионы пользователей по всему миру.
После подключения репозитория устанавливается сам пакет.
$ sudo apt install ansible -y
Флаг -y пропускает интерактивные подтверждения - удобно, когда команда запускается в скрипте или когда заранее известно, что зависимости устроят. Менеджер пакетов подтянет интерпретатор Python нужной версии, библиотеку Jinja2 для шаблонов, OpenSSH-клиент и пару десятков других зависимостей. Установка занимает обычно меньше минуты на любом современном сервере.
Сразу после завершения установки имеет смысл проверить, что Ansible действительно встал и работает. Для этого служит флаг версии.
$ ansible --version
ansible [core 2.13.3rc1]
config file = /etc/ansible/ansible.cfg
configured module search path = ['/home/navjot/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /usr/lib/python3/dist-packages/ansible
ansible collection location = /home/navjot/.ansible/collections:/usr/share/ansible/collections
executable location = /usr/bin/ansible
python version = 3.10.4 (main, Jun 29 2022, 12:14:53) [GCC 11.2.0]
jinja version = 3.0.3
libyaml = True
Вывод многословен, но информативен. Кроме номера версии видны пути к конфигурационному файлу, расположение модулей, версия Python и Jinja2. Особое внимание стоит уделить строке libyaml = True - это значит, что подключена быстрая C-библиотека для разбора YAML, а не медленный pure-Python парсер. Для крупных плейбуков разница в скорости заметна на глаз.
В качестве приятного дополнения стоит включить автодополнение команд по клавише Tab. Это сильно ускоряет работу и спасает от опечаток в названиях модулей.
$ sudo apt install python3-argcomplete
$ sudo activate-global-python-argcomplete3
Первая команда ставит пакет с механизмом автодополнения, вторая активирует его глобально для всех пользователей системы. После этого набираешь ansible- и жмёшь Tab - bash сам подскажет доступные подкоманды.
Создание инвентарного файла с описанием управляемых узлов и логичной группировкой серверов
Сердце любой установки Ansible - инвентарный файл. Это обычный текстовый документ, в котором перечислены все узлы под управлением и заданы их атрибуты. По умолчанию Ansible ищет инвентарь в /etc/ansible/hosts, но никто не запрещает держать его в произвольном месте и подключать через флаг -i.
Открывается стандартный инвентарь привычным редактором.
$ sudo nano /etc/ansible/hosts
В конец файла добавляются строки с описанием серверов. Формат напоминает классические INI-файлы из мира Windows - заголовки в квадратных скобках, под ними список хостов с параметрами.
[servers]
server1 ansible_host=203.0.113.111
server2 ansible_host=203.0.113.112
server3 ansible_host=203.0.113.113
[all:vars]
ansible_python_interpreter=/usr/bin/python3
Раздел [servers] объединяет три машины в одну группу. Каждая строка содержит человекочитаемое имя (server1, server2, server3) и параметр ansible_host с реальным IP-адресом или DNS-именем. Удобство такого подхода в том, что в командах и плейбуках можно обращаться к серверу по короткому имени, не запоминая длинные адреса.
Специальная группа [all:vars] задаёт переменные для всех узлов сразу. Параметр ansible_python_interpreter явно указывает путь к Python 3, что снимает любые двусмысленности на современных Ubuntu, где Python 2 уже выпилен из системы. Без этой строки на свежих дистрибутивах изредка случаются загадочные ошибки, когда Ansible не может найти подходящий интерпретатор.
После сохранения файла полезно убедиться, что синтаксис разобран корректно. Для этого служит отдельная утилита.
$ ansible-inventory --list -y
all:
children:
servers:
hosts:
server1:
ansible_host: 203.0.113.111
ansible_python_interpreter: /usr/bin/python3
server2:
ansible_host: 203.0.113.112
ansible_python_interpreter: /usr/bin/python3
server3:
ansible_host: 203.0.113.113
ansible_python_interpreter: /usr/bin/python3
ungrouped: {}
Вывод в формате YAML показывает разобранную структуру инвентаря. Видна корневая группа all, дочерняя servers, три хоста с присвоенными параметрами. Если бы в файле была опечатка или неправильный отступ, утилита честно сообщила бы об ошибке.
Когда серверов много и они выполняют разные роли, плоский список превращается в кашу. Тут на помощь приходит группировка по функциональному признаку. Один узел можно одновременно включить в несколько групп, и это нормальная практика. Вот пример более продуманной структуры.
[webservers]
203.0.113.111
203.0.113.112
[dbservers]
203.0.113.113
server_hostname
[development]
203.0.113.111
203.0.113.113
[production]
203.0.113.112
server_hostname
Здесь два сервера отнесены к веб-уровню, два других - к базам данных, причём один из веб-серверов и один сервер БД находятся в окружении разработки, а оставшиеся два работают на проде. Такая матричная схема позволяет одной командой обновить, скажем, все веб-серверы независимо от окружения или, наоборот, все продуктивные машины разом.
Генерация SSH-ключей и настройка беспарольного доступа управляющего узла к клиентским серверам
Ansible общается с управляемыми узлами через SSH, поэтому без работающей аутентификации никуда. Парольный вход теоретически тоже подходит, но представь, что нужно прогнать команду на пятидесяти серверах. Каждый раз вводить пароль? Никто на это не подписывался. Ключевая аутентификация решает проблему элегантно.
На управляющем сервере генерируется пара ключей.
$ ssh-keygen -t rsa -b 4096 -C "Ansible key"
Параметр -t rsa задаёт алгоритм RSA, -b 4096 указывает длину ключа в битах. Четыре тысячи девяносто шесть бит - это компромисс между параноидальной стойкостью и приемлемой скоростью работы. Современные процессоры справляются с такими ключами без заметных задержек, а взломать их перебором не получится даже у больших и нехороших организаций. Флаг -C добавляет к ключу комментарий-метку - полезно, когда на машине лежит несколько разных ключей и нужно их различать.
После генерации публичная половина ключа разлетается по управляемым узлам.
$ ssh-copy-id -i $HOME/.ssh/id_rsa.pub Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript.
$ ssh-copy-id -i $HOME/.ssh/id_rsa.pub Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript.
$ ssh-copy-id -i $HOME/.ssh/id_rsa.pub Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript.
Утилита ssh-copy-id подключается к удалённому серверу под указанным пользователем, запрашивает пароль один раз, а затем дописывает содержимое публичного ключа в файл ~/.ssh/authorized_keys на удалённой стороне. После этой процедуры пароль больше не понадобится - SSH будет узнавать управляющий сервер по криптографической подписи.
Иногда узлы уже сконфигурированы с включённой ключевой аутентификацией и закрытым root-входом. В таком сценарии создаётся отдельный сервисный пользователь специально под нужды Ansible. Он получит право выполнять sudo без пароля, но только с управляющего сервера и только при входе по ключу.
$ sudo adduser ansible
При создании задаётся надёжный пароль, остальные поля можно оставить пустыми - имя, телефон, описание тут не нужны. Затем настраивается беспарольный sudo для нового пользователя.
$ echo "ansible ALL=(ALL) NOPASSWD:ALL" | sudo tee /etc/sudoers.d/ansible
Команда создаёт отдельный файл в каталоге /etc/sudoers.d/, где правила хранятся отдельно от основного sudoers. Такой подход безопаснее прямого редактирования главного файла, потому что синтаксическая ошибка в одном файле не сломает работу всей системы повышения привилегий. Запись NOPASSWD:ALL разрешает пользователю ansible выполнять любые команды через sudo без ввода пароля.
Дальше копируется ключ под нового пользователя.
$ ssh-copy-id Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript.
И финальный штрих - блокировка парольного входа для пользователя ansible. После этого войти под ним можно будет только по ключу.
$ sudo usermod -L ansible
Опция -L (lock) блокирует пароль учётной записи, не удаляя её саму. Пользователь продолжает существовать, его файлы и права остаются на месте, но логин по паролю невозможен. Получается уютная конструкция - беспарольный sudo доступен, но дотянуться до этого sudo способен только тот, у кого есть приватный ключ с управляющего сервера. Эту последовательность действий придётся повторить на каждом узле.
Проверка связности между управляющим узлом и серверами через модуль ping
Когда инвентарь собран, а ключи разложены, наступает момент истины. Работает или нет? Для быстрой проверки в Ansible есть модуль ping. Не путать с одноимённой ICMP-утилитой - тут совсем другая механика. Модуль подключается по SSH, проверяет наличие Python на удалённой стороне и возвращает строку pong в случае успеха.
$ ansible all -m ping -u root
Параметр all указывает применить команду ко всем узлам инвентаря. Флаг -m ping выбирает модуль ping, а -u root задаёт пользователя для подключения. Если на узлах настроен кастомный пользователь ansible, имя меняется соответствующе.
Удачный вывод выглядит так.
server1 | SUCCESS => {
"changed": false,
"ping": "pong"
}
server2 | SUCCESS => {
"changed": false,
"ping": "pong"
}
server3 | SUCCESS => {
"changed": false,
"ping": "pong"
}
Поле changed: false означает, что состояние сервера не было изменено - модуль ping ничего и не должен ничего менять, его задача только проверить отзывчивость. Если же на одном из серверов вместо SUCCESS вылезет UNREACHABLE с описанием ошибки, копать нужно в сторону SSH-доступа, ключей или сетевой связности.
При первом подключении к каждому серверу SSH честно спросит, доверяем ли мы отпечатку хоста. Отвечая yes, отпечаток сохраняется в файле ~/.ssh/known_hosts, и в дальнейшем подключения происходят без вопросов.
Работа с ad-hoc командами для разовых задач администрирования и таргетинг отдельных узлов или групп
Ad-hoc команды - это разовые директивы, которые гоняются прямо из терминала без всяких плейбуков. Удобно, когда нужно быстро проверить состояние парка или выполнить простое действие на множестве машин. Базовый синтаксис прост.
$ ansible all -a "command" -u <username>
Ключ -a передаёт модулю аргументы. Если модуль явно не указан флагом -m, Ansible использует модуль command по умолчанию - он просто выполняет переданную строку как обычную shell-команду.
Например, проверка занятости дисковой подсистемы на всех серверах сразу.
$ ansible all -a "df -h" -u root
server1 | CHANGED | rc=0 >>
Filesystem Size Used Avail Use% Mounted on
tmpfs 198M 972K 197M 1% /run
/dev/sda2 50G 3.9G 44G 9% /
tmpfs 989M 0 989M 0% /dev/shm
tmpfs 5.0M 0 5.0M 0% /run/lock
tmpfs 198M 4.0K 198M 1% /run/user/1000
server2 | CHANGED | rc=0 >>
Filesystem Size Used Avail Use% Mounted on
tmpfs 198M 922K 197M 1% /run
/dev/sda2 50G 4.9G 43G 10% /
tmpfs 989M 0 989M 0% /dev/shm
tmpfs 5.0M 0 5.0M 0% /run/lock
tmpfs 198M 4.0K 198M 1% /run/user/1000
Одной строкой получается срез по дисковому пространству на всём парке. Согласись, удобнее, чем ходить по каждому серверу руками.
Не всегда нужно бить по всем целям сразу. Ansible умеет адресоваться к конкретному узлу.
$ ansible server1 -a "uptime" -u root
server1 | CHANGED | rc=0 >>
21:38:26 up 11 min, 2 users, load average: 0.00, 0.20, 0.19
Здесь команда uptime отрабатывает только на сервере с именем server1. Можно перечислить несколько узлов через двоеточие.
$ ansible server1:server2 -m ping -u root
Или адресоваться к группе по имени, заданному в инвентаре.
$ ansible groupname -m ping -u <username>
Такой подход открывает простор для манёвра. Хочется обновить только веб-серверы, оставив базы в покое? Достаточно указать webservers вместо all. Нужно прогнать диагностику только по продуктивному окружению? Указывается production. Вся гибкость инвентарной модели расцветает именно в ad-hoc режиме.
Один из самых частых сценариев - массовое обновление пакетов. Для этого пригодится модуль apt, заточенный под работу с пакетным менеджером Debian-семейства.
$ ansible all -m apt -a "update_cache=yes upgrade=yes" -u root
Аргумент update_cache=yes соответствует команде apt update - обновляет локальный индекс пакетов. Параметр upgrade=yes запускает процедуру обновления установленного софта, аналог apt upgrade. Обе операции выполняются последовательно и независимо на каждом узле.
Если на серверах настроен пользователь ansible с sudo-привилегиями, синтаксис чуть усложняется.
$ ansible server2 -m apt -a "update_cache=yes upgrade=yes" -K -b -u ansible
Флаг -b (become) переключает контекст исполнения на суперпользователя через sudo, а -K (ask-become-pass) запрашивает пароль для повышения привилегий. В связке эти флаги дают тот самый рабочий режим, который уже описывался при настройке сервисного пользователя.
После масштабных обновлений ядра или системных библиотек серверы иногда требуют перезагрузки. Ansible справляется и с этим.
$ ansible all -a "reboot" -u root
После такой команды все управляемые узлы уходят в перезагрузку одновременно. Связь с ними на короткое время прервётся, но это ожидаемое поведение.
Создание первого плейбука на YAML для автоматизации развёртывания пользователя и веб-сервера Nginx
Ad-hoc команды хороши для разовых задач, но когда последовательность действий повторяется регулярно, нужен инструмент посерьёзнее. Тут в дело вступают плейбуки - YAML-файлы, описывающие пьесу из шагов. Каждая пьеса (play) состоит из задач (tasks), каждая задача вызывает определённый модуль с параметрами.
Создаётся отдельный каталог для будущих плейбуков.
$ mkdir ~/ansible
Внутри открывается новый файл для редактирования.
$ cd ~/ansible
$ nano testplaybook.yml
Перед тем как заполнять плейбук, понадобится одна вспомогательная утилита для генерации хешированных паролей. Хранить пароли открытым текстом в YAML-файлах - дурной тон и прямой риск, поэтому учётные данные кладутся в зашифрованном виде.
$ sudo apt install whois
Пакет whois в комплекте принесёт утилиту mkpasswd, которая умеет считать SHA-512 хеши в формате, понятном системам Linux.
$ mkpasswd --method=sha-512
Password:
$6$dGbprm2oVqClDDDh$Epk6r5eXYkYBaQpQpP.H7VCdz0g9Aj0aO8hjy/WXq4WmfQ7GvQP2/cl/cNhd7.LRFuCKix9uCF2t8X5/Pv0Lk1
Полученная строка - это и есть хеш пароля, который потом подставится в плейбук. Префикс $6$ обозначает алгоритм SHA-512, далее идёт соль, а после второго доллара сам хеш. Перед глобальной катастрофой такой формат не спасёт, но от случайной утечки конфигурационного файла защитит надёжно.
Теперь сам плейбук с тремя задачами - создание пользователя, обновление пакетов и установка Nginx.
---
- name: Test playbook
hosts: all
become: true
vars:
state: latest
user: navjot
tasks:
- name: Add the user {{ user }}
ansible.builtin.user:
name: "{{ user }}"
password: '$6$dGbprm2oVqClDDDh$Epk6r5eXYkYBaQpQpP.H7VCdz0g9Aj0aO8hjy/WXq4WmfQ7GvQP2/cl/cNhd7.LRFuCKix9uCF2t8X5/Pv0Lk1'
group: sudo
- name: Upgrade all apt packages
apt:
update_cache: yes
upgrade: dist
- name: Install the {{ state }} of package "nginx"
apt:
name: "nginx"
state: "{{ state }}"
Разберём содержимое последовательно. Три дефиса в начале файла - стандартный маркер начала YAML-документа. Директива name задаёт человекочитаемое имя пьесы, оно появится в логах при выполнении. Параметр hosts: all определяет, что пьеса применяется ко всем узлам инвентаря, его можно заменить на имя группы или конкретного хоста.
Строка become: true сообщает Ansible о необходимости повышения привилегий через sudo при выполнении задач. Без неё установка пакетов и создание пользователей просто не пройдёт.
Раздел vars объявляет две переменные - state со значением latest и user со значением navjot. В дальнейшем они подставляются в задачи через двойные фигурные скобки в синтаксисе Jinja2. Подход с переменными удобен, потому что одно изменение в шапке плейбука распространится на все места, где переменная используется.
Первая задача вызывает модуль ansible.builtin.user для создания учётной записи. Передаётся имя пользователя, хешированный пароль и группа sudo. Хеш в одинарных кавычках - это важно, иначе YAML может попытаться разобрать содержимое как специальные символы.
Вторая задача использует модуль apt с двумя параметрами. Параметр update_cache: yes обновляет индекс пакетов, а upgrade: dist запускает полное обновление дистрибутива, включая замену устаревших зависимостей и установку нового ядра при необходимости.
Третья задача снова обращается к модулю apt, но уже для установки Nginx. Имя пакета задано явно, состояние определяется переменной state. Если в шапке плейбука изменить state с latest на present, Ansible установит Nginx, но не будет пытаться обновлять его до самой свежей версии.
Запускается плейбук отдельной утилитой ansible-playbook.
$ ansible-playbook testplaybook.yml --ask-become-pass
Флаг --ask-become-pass запрашивает пароль для sudo. Для серверов с пользователем ansible и беспарольным sudo этот флаг не понадобится.
Успешное выполнение даёт примерно такой отчёт.
BECOME password:
PLAY [Test playbook] ***************************************************************************************************
TASK [Gathering Facts] *************************************************************************************************
ok: [server1]
ok: [server2]
ok: [server3]
TASK [Add the user casablanca] *****************************************************************************************
changed: [server3]
changed: [server2]
changed: [server1]
TASK [Upgrade all apt packages] ****************************************************************************************
changed: [server1]
changed: [server2]
changed: [server3]
TASK [Install the latest of package "nginx"] ***************************************************************************
changed: [server3]
changed: [server2]
changed: [server1]
PLAY RECAP *************************************************************************************************************
server1 : ok=4 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
server2 : ok=4 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
server3 : ok=4 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Шаг Gathering Facts - это автоматическая разведка, Ansible собирает с каждого узла информацию о системе перед выполнением задач. Затем последовательно отрабатывают три задачи плейбука. В сводном отчёте PLAY RECAP по каждому серверу выводится статистика - сколько шагов прошло успешно, сколько изменили состояние системы, сколько упало с ошибкой.
Если инвентарь хранится не в стандартном /etc/ansible/hosts, а в произвольном месте, путь к нему передаётся через флаг -i.
$ ansible-playbook -i /etc/ansible/custominventory testplaybook.yml --ask-become-pass
Практические сценарии применения и типичные ошибки начинающих администраторов при работе с Ansible
Освоив базовый сценарий, легко увлечься и натащить в один плейбук десятки задач. Опыт показывает, что монолитные плейбуки на тысячу строк превращаются в кошмар при отладке. Лучше дробить логику на роли (roles) - переиспользуемые модули, которые подключаются как кубики LEGO. Роль для Nginx, роль для PostgreSQL, роль для базовой настройки безопасности. Когда понадобится новый веб-сервер, достаточно собрать плейбук из готовых ролей.
Ещё одна частая ошибка - забывать про идемпотентность. Хороший плейбук должен давать одинаковый результат при многократном запуске. Если вместо модуля apt использовать command: apt install nginx, то Ansible попытается переустанавливать пакет каждый раз, даже если он уже стоит. Модули вроде apt, user, file спроектированы так, чтобы сначала проверять текущее состояние и менять что-то только при реальной необходимости. Команда command идемпотентность не гарантирует.
Стоит присмотреться к Ansible Vault - встроенному механизму шифрования секретов. Пароли, токены API, приватные ключи - всё это можно положить прямо в репозиторий рядом с плейбуками, но в зашифрованном виде. Расшифровка происходит автоматически при запуске, нужен только мастер-пароль или ключевой файл. Решение особенно ценное для команд, где плейбуки хранятся в Git и видны нескольким разработчикам.
При работе с большим парком серверов выручает параллелизм. По умолчанию Ansible выполняет задачи на пяти узлах одновременно, остальные ждут очереди. Параметр forks в конфигурации позволяет поднять планку до сотни и больше, но тут важно не переборщить - управляющий узел может задохнуться от количества одновременных SSH-сессий. Эмпирически удобное значение для среднего сервера - 20 - 30 параллельных подключений.
Не стоит забывать про режим проверки. Запуск плейбука с флагом --check имитирует выполнение без реального изменения системы - удобно проверить, что планируется сделать, прежде чем нажимать боевую кнопку. В связке с флагом --diff Ansible покажет точные различия между текущим и целевым состоянием конфигурационных файлов.
Освоение Ansible открывает путь к настоящей инфраструктуре как коду. Серверы перестают быть капризными питомцами с уникальной судьбой - они превращаются в одинаковые, легко заменяемые единицы, описанные текстом в репозитории. Сломался узел? Поднимается новый, прогоняется плейбук, и через десять минут он функционально неотличим от утраченного. Этот сдвиг мышления меняет саму философию администрирования. Вместо ремонта живых систем приходит идея описывать желаемое состояние и доверять автоматике приводить инфраструктуру к этому состоянию. Тот, кто однажды попробовал работать так, обратно к ручной настройке уже не возвращается.