Ansible удобен тем, что не требует агента на сервере: управляющая машина подключается к VDS по SSH, выполняет модули, возвращает результат и закрывает соединение. Для администратора это означает быстрый старт: нужен корректный inventory, рабочий SSH-доступ, Python на управляемом Linux-сервере и понятная политика повышения привилегий через become.
В этой инструкции соберём минимальный, но уже пригодный для работы набор: отдельный SSH-ключ для автоматизации, структуру проекта, inventory для нескольких VDS, первичную проверку доступа, безопасное использование become и первый playbook, который можно расширять под веб-сервер, базу данных, мониторинг или деплой приложения.
Главная идея: сначала добиваемся предсказуемого подключения и повторяемого результата, а уже потом усложняем роли, переменные, CI/CD и GitOps. Половина проблем с Ansible на VDS начинается не в YAML, а в SSH, sudo и inventory.
Что понадобится перед стартом
У нас будет одна управляющая машина — ноутбук администратора, рабочая станция или отдельный bastion-хост. С неё Ansible будет ходить на один или несколько серверов. Управляемые VDS могут быть на Debian, Ubuntu, AlmaLinux, Rocky Linux, CentOS Stream, Oracle Linux или Fedora: для базового сценария отличия в основном касаются пакетного менеджера, названия группы sudo и некоторых служб.
Если вы только выбираете площадку для тестового стенда или продакшена, удобнее сразу брать сервер с чистым Linux-образом и доступом по SSH: например, посмотреть тарифы на облачный VDS и затем подключить его к Ansible как обычный SSH-хост.
Проверьте, что у вас есть:
- IP-адреса VDS и доступ по SSH, хотя бы временно под
rootили под пользователем с sudo; - локальный OpenSSH-клиент;
- Ansible на управляющей машине;
- Python 3 на управляемых серверах;
- понимание, какой пользователь будет постоянным административным пользователем, например
deployилиadmin.
Не стоит сразу отключать root-доступ, менять порт SSH и включать сложные firewall-правила до того, как вы проверили новый ключ, пользователя и become. Сначала создаём рабочий путь входа, затем аккуратно ужесточаем политику.
Установка Ansible на управляющую машину
Ansible запускается на управляющей машине. На сам VDS устанавливать Ansible не нужно, если вы используете классический push-подход. На управляемой стороне нужны SSH, Python и права для выполнения задач.
Debian и Ubuntu
sudo apt update
sudo apt install -y ansible openssh-client python3
ansible --version
RHEL-based и Fedora
Для AlmaLinux, Rocky Linux, CentOS Stream, Oracle Linux и Fedora чаще используется пакет ansible-core. В некоторых RHEL-based системах может понадобиться подключённый EPEL, но на современных дистрибутивах базового ansible-core обычно хватает для первых playbook.
sudo dnf install -y ansible-core openssh-clients python3
ansible --version
Структура проекта Ansible
Даже для одного VDS лучше не хранить всё в домашней папке вперемешку. Небольшая структура дисциплинирует: inventory отдельно, playbook отдельно, конфигурация Ansible отдельно. Такой проект легко положить в Git, передать коллеге и подключить к CI.
mkdir -p ansible-vds/inventory
mkdir -p ansible-vds/playbooks
mkdir -p ansible-vds/group_vars
cd ansible-vds
Минимальная структура будет такой:
ansible-vds/
ansible.cfg
inventory/
hosts.yml
group_vars/
all.yml
playbooks/
bootstrap.yml
base.yml
ansible.cfg задаёт поведение по умолчанию, inventory/hosts.yml описывает серверы и группы, group_vars/all.yml хранит общие переменные, а playbook выполняют конкретную работу. Для маленького проекта это не избыточность, а страховка от хаоса.

SSH-ключи для Ansible
Не используйте один и тот же ключ для личного входа, деплоя, резервного копирования и автоматизации. Отдельный ключ упрощает ротацию и аудит: если ключ Ansible нужно заменить, вы не ломаете все остальные процессы.
ssh-keygen -t ed25519 -a 100 -f ~/.ssh/ansible_vds -C ansible-vds
Закройте права на приватный ключ:
chmod 700 ~/.ssh
chmod 600 ~/.ssh/ansible_vds
chmod 644 ~/.ssh/ansible_vds.pub
Если у вас пока есть root-доступ к новому VDS, можно временно скопировать ключ root-пользователю, чтобы затем создать постоянного пользователя и настроить sudo через Ansible.
ssh-copy-id -i ~/.ssh/ansible_vds.pub root@203.0.113.10
Перед первым массовым запуском полезно заранее добавить host key сервера в known_hosts. Это снижает риск интерактивных запросов посреди playbook и помогает не отключать проверку host key.
ssh-keyscan -H 203.0.113.10 >> ~/.ssh/known_hosts
Здесь важно не путать два разных типа ключей. SSH-ключ пользователя подтверждает, что вы имеете право войти на сервер. SSH host key подтверждает, что вы подключаетесь именно к тому серверу, а не к подменённому узлу. Отключать host_key_checking ради удобства можно только в одноразовых лабораторных окружениях; для постоянного VDS лучше оставить проверку включённой.
Первый inventory: один VDS и несколько групп
inventory — это карта инфраструктуры для Ansible. В нём описывают хосты, группы и переменные подключения. Даже если сейчас у вас один сервер, называйте группы осмысленно: web, db, monitoring, stage, prod. Когда серверов станет больше, структура уже будет готова.
Создайте файл inventory/hosts.yml:
all:
children:
web:
hosts:
web1:
ansible_host: 203.0.113.10
db:
hosts:
db1:
ansible_host: 203.0.113.11
vars:
ansible_user: deploy
ansible_ssh_private_key_file: ~/.ssh/ansible_vds
ansible_python_interpreter: /usr/bin/python3
ansible_become: true
ansible_become_method: sudo
Пока пользователя deploy может ещё не быть на сервере. Это нормально: ниже мы сделаем bootstrap-playbook, который подключится под root и создаст такого пользователя. После этого основной inventory будет использовать уже не root, а отдельного администратора.
Если сервер один, inventory всё равно может выглядеть так:
all:
hosts:
vds1:
ansible_host: 203.0.113.10
vars:
ansible_user: deploy
ansible_ssh_private_key_file: ~/.ssh/ansible_vds
ansible_python_interpreter: /usr/bin/python3
ansible_become: true
ansible_become_method: sudo
Имя vds1 — это alias внутри Ansible. Оно не обязано совпадать с DNS-именем. Но в продакшене удобнее использовать понятные имена: web-prod-1, db-stage-1, monitoring-1.
ansible.cfg: меньше флагов в командной строке
Чтобы каждый раз не указывать inventory и ключ вручную, создайте ansible.cfg в корне проекта:
[defaults]
inventory = inventory/hosts.yml
remote_user = deploy
private_key_file = ~/.ssh/ansible_vds
host_key_checking = True
retry_files_enabled = False
interpreter_python = auto_silent
[privilege_escalation]
become = True
become_method = sudo
become_ask_pass = False
Параметр host_key_checking лучше держать в значении True. Если после переустановки VDS вы видите предупреждение о смене host key, не игнорируйте его автоматически. Сначала убедитесь, что сервер действительно переустановлен, затем удалите старую запись.
ssh-keygen -R 203.0.113.10
ssh-keyscan -H 203.0.113.10 >> ~/.ssh/known_hosts
become_ask_pass = False означает, что Ansible не будет спрашивать пароль sudo. Для этого у пользователя должен быть настроен passwordless sudo либо нужно запускать playbook с опцией запроса пароля. Для автоматизации VDS чаще выбирают sudo без пароля для отдельного административного пользователя, но доступ к самому ключу защищают правами, passphrase и политикой хранения секретов.
Bootstrap: создаём пользователя для Ansible
Первый запуск часто выполняется под root, потому что на новом VDS ещё нет постоянного администратора. Задача bootstrap — подготовить сервер так, чтобы дальнейшие playbook работали через обычного пользователя с become.
На минимальных образах Python может отсутствовать. Если ansible all -m ping -u root падает именно из-за Python, один раз выполните raw-команду под root:
ansible all -u root -m raw -a 'test -x /usr/bin/python3 || (command -v apt-get && apt-get update && apt-get install -y python3 sudo) || (command -v dnf && dnf install -y python3 sudo)'
Теперь создайте playbooks/bootstrap.yml:
- name: Bootstrap VDS access
hosts: all
gather_facts: true
remote_user: root
vars:
admin_user: deploy
tasks:
- name: Install sudo and Python on Debian and Ubuntu
ansible.builtin.apt:
name:
- sudo
- python3
state: present
update_cache: true
when: ansible_os_family == 'Debian'
- name: Install sudo and Python on RHEL-based systems and Fedora
ansible.builtin.dnf:
name:
- sudo
- python3
state: present
when: ansible_os_family == 'RedHat'
- name: Create admin user
ansible.builtin.user:
name: "{{ admin_user }}"
shell: /bin/bash
create_home: true
state: present
- name: Add admin user to sudo group on Debian and Ubuntu
ansible.builtin.user:
name: "{{ admin_user }}"
groups: sudo
append: true
when: ansible_os_family == 'Debian'
- name: Add admin user to wheel group on RHEL-based systems and Fedora
ansible.builtin.user:
name: "{{ admin_user }}"
groups: wheel
append: true
when: ansible_os_family == 'RedHat'
- name: Install SSH public key for admin user
ansible.builtin.authorized_key:
user: "{{ admin_user }}"
state: present
key: "{{ lookup('file', '~/.ssh/ansible_vds.pub') }}"
- name: Allow passwordless sudo for admin user
ansible.builtin.copy:
dest: "/etc/sudoers.d/{{ admin_user }}"
owner: root
group: root
mode: '0440'
content: |
{{ admin_user }} ALL=(ALL) NOPASSWD: ALL
validate: /usr/sbin/visudo -cf %s
В этом playbook есть важный момент: группа администраторов отличается по семействам ОС. В Debian и Ubuntu это обычно sudo, в AlmaLinux, Rocky Linux, CentOS Stream, Oracle Linux и Fedora — wheel. Поэтому задачи разделены через when.
Запустите bootstrap, явно указав временного пользователя root, если в ansible.cfg уже прописан deploy:
ansible-playbook playbooks/bootstrap.yml -u root
После успешного выполнения проверьте вход под новым пользователем:
ssh -i ~/.ssh/ansible_vds deploy@203.0.113.10
sudo id
Если команда sudo id выполняется без запроса пароля и показывает uid=0(root), значит become у Ansible тоже должен работать.
Passwordless sudo — сильное право. Его стоит выдавать не личным пользователям всех сотрудников, а отдельному техническому пользователю с контролируемым SSH-ключом, журналированием и понятной процедурой ротации.
Проверяем inventory и SSH-доступ
Теперь можно проверить, что Ansible видит хосты из inventory:
ansible-inventory --graph
Проверка подключения:
ansible all -m ping
Модуль ping в Ansible — это не ICMP ping. Он подключается по SSH, запускает Python на удалённой машине и возвращает результат. Если здесь ошибка, проблема обычно в SSH-ключе, пользователе, Python или host key.
Проверьте выполнение простой команды без повышения привилегий:
ansible all -m command -a 'whoami'
Проверьте выполнение команды с become:
ansible all -b -m command -a 'id'
Если первая команда возвращает deploy, а вторая — root, базовая цепочка работает: inventory найден, SSH-ключ принят, пользователь существует, sudo настроен, become поднимает права.
Первый рабочий playbook для базовой настройки VDS
Теперь напишем playbook, который устанавливает базовые пакеты, включает службу синхронизации времени и создаёт служебный каталог. Он не делает ничего разрушительного, но показывает ключевые принципы: условия по семейству ОС, идемпотентность и выполнение через become.
Создайте playbooks/base.yml:
- name: Base setup for VDS
hosts: all
become: true
gather_facts: true
tasks:
- name: Install base packages on Debian and Ubuntu
ansible.builtin.apt:
name:
- curl
- git
- htop
- tmux
- chrony
state: present
update_cache: true
cache_valid_time: 3600
when: ansible_os_family == 'Debian'
- name: Install base packages on RHEL-based systems and Fedora
ansible.builtin.dnf:
name:
- curl
- git
- htop
- tmux
- chrony
state: present
when: ansible_os_family == 'RedHat'
- name: Enable chrony on Debian and Ubuntu
ansible.builtin.service:
name: chrony
state: started
enabled: true
when: ansible_os_family == 'Debian'
- name: Enable chronyd on RHEL-based systems and Fedora
ansible.builtin.service:
name: chronyd
state: started
enabled: true
when: ansible_os_family == 'RedHat'
- name: Create application directory
ansible.builtin.file:
path: /opt/apps
state: directory
owner: root
group: root
mode: '0755'
- name: Set managed server notice
ansible.builtin.copy:
dest: /etc/motd
owner: root
group: root
mode: '0644'
content: |
This VDS is managed by Ansible.
Manual changes may be overwritten by playbooks.
Запустите сначала в режиме проверки:
ansible-playbook playbooks/base.yml --check --diff
--check показывает, что Ansible собирается изменить, а --diff полезен для файлов: видно, какие строки будут добавлены или удалены. Не все модули идеально поддерживают check mode, но для apt, dnf, file и copy это хороший способ снизить риск.
Если вывод ожидаемый, примените изменения:
ansible-playbook playbooks/base.yml
Повторный запуск должен показать минимум изменений или полный ok. Это и есть идемпотентность: playbook можно запускать много раз, а сервер будет приходить к заданному состоянию, а не выполнять одно и то же действие снова и снова.
Как группировать VDS в inventory
Когда серверов становится больше, inventory превращается в основу всей devops automation. Важно не сваливать все хосты в одну группу servers. Группы должны отвечать на практические вопросы: где продакшен, где staging, где web, где база, где мониторинг.
all:
children:
production:
children:
prod_web:
hosts:
web-prod-1:
ansible_host: 203.0.113.10
web-prod-2:
ansible_host: 203.0.113.12
prod_db:
hosts:
db-prod-1:
ansible_host: 203.0.113.11
staging:
children:
stage_web:
hosts:
web-stage-1:
ansible_host: 198.51.100.10
vars:
ansible_user: deploy
ansible_ssh_private_key_file: ~/.ssh/ansible_vds
ansible_python_interpreter: /usr/bin/python3
ansible_become: true
ansible_become_method: sudo
Теперь можно запускать playbook только на staging:
ansible-playbook playbooks/base.yml --limit staging
Или только на web-серверах продакшена:
ansible-playbook playbooks/base.yml --limit prod_web
--limit — полезная страховка, но не замена аккуратной структуре. Если вы случайно назвали группу слишком широко, Ansible честно выполнит задачу на всех подходящих хостах.

Где хранить переменные и секреты
Inventory должен оставаться читаемой картой серверов. Общие настройки лучше переносить в group_vars/all.yml, а специфичные — в файлы групп. Например, создайте group_vars/all.yml:
admin_user: deploy
base_packages:
- curl
- git
- htop
- tmux
Для продакшена можно добавить group_vars/production.yml:
environment_name: production
backup_enabled: true
monitoring_enabled: true
Для staging — group_vars/staging.yml:
environment_name: staging
backup_enabled: false
monitoring_enabled: true
Так вы уменьшаете копипасту и снижаете риск, что staging внезапно получит настройки production. Особенно это важно для задач с базами данных, firewall, SSL, доменами и расписаниями резервного копирования.
Пароли баз данных, приватные ключи, токены API и резервные коды нельзя хранить в открытом виде. Для начала подойдёт Ansible Vault, а в более зрелых процессах — внешний менеджер секретов. Если вы уже строите GitOps-подход, пригодится отдельный разбор про SOPS, age и безопасное хранение секретов в Git.
Become: когда включать глобально, а когда точечно
become говорит Ansible выполнить задачу с повышенными правами. В примерах выше мы включили его глобально: become: true на уровне play. Это удобно для базовой настройки сервера, где почти все задачи требуют root: установка пакетов, изменение файлов в /etc, управление службами.
Но в playbook деплоя приложения лучше не делать всё от root. Например, получение кода из Git, сборка фронтенда или запуск composer/npm часто должны идти от пользователя приложения. Тогда become включают точечно или используют become_user.
- name: Create release directory as app user
hosts: web
become: true
tasks:
- name: Ensure release directory exists
ansible.builtin.file:
path: /var/www/example/releases
state: directory
owner: www-data
group: www-data
mode: '0755'
- name: Show app user identity
ansible.builtin.command: id
become_user: www-data
changed_when: false
Для RHEL-based систем пользователь веб-сервера может называться apache, а не www-data. Поэтому для переносимых ролей такие значения лучше выносить в переменные по семействам ОС. Если дальше вы автоматизируете Nginx и PHP-FPM, посмотрите практику с несколькими PHP-FPM пулами под Nginx: там особенно важно не смешивать системные и прикладные права.
Типовые ошибки и быстрая диагностика
Permission denied по SSH
Проверьте, что Ansible использует именно нужный ключ и пользователя:
ansible all -m ping -vvvv
Флаг -vvvv покажет фактическую SSH-команду. Частые причины: приватный ключ имеет слишком широкие права, публичный ключ не добавлен в authorized_keys, пользователь указан не тот, на сервере отключён вход по ключам или SSH слушает нестандартный порт.
Если порт нестандартный, добавьте его в inventory:
all:
hosts:
vds1:
ansible_host: 203.0.113.10
ansible_port: 2222
sudo: a password is required
Это означает, что become сработал, но sudo запросил пароль. Для ручного запуска можно использовать:
ansible-playbook playbooks/base.yml --ask-become-pass
Для регулярной автоматизации лучше настроить sudoers для технического пользователя и защитить доступ к SSH-ключу. Проверяйте sudoers только через visudo или через Ansible-модуль copy с параметром validate, как в bootstrap-примере.
Python не найден
На минимальных образах VDS Python может отсутствовать. Тогда Ansible не сможет выполнить большинство модулей. Первичную установку Python можно сделать вручную по SSH или через raw-команду под root.
Для Debian и Ubuntu:
apt update
apt install -y python3 sudo
Для RHEL-based систем и Fedora:
dnf install -y python3 sudo
Host key verification failed
Ошибка означает, что SSH не доверяет host key сервера или видит его изменение. Если VDS новый, добавьте ключ через ssh-keyscan. Если сервер переустанавливался, сначала убедитесь, что IP действительно ваш, затем удалите старую запись через ssh-keygen -R.
Практические правила для Ansible на VDS
- Держите inventory, playbook и переменные в Git, но не храните там приватные ключи и секреты в открытом виде.
- Запускайте опасные изменения сначала с
--check --diff, затем с--limitна одном staging-хосте. - Не отключайте
host_key_checkingбез крайней необходимости. - Разделяйте bootstrap, базовую настройку, безопасность, web-стек и деплой приложения на разные playbook или роли.
- Используйте понятные имена групп:
production,staging,web,db,monitoring. - Не превращайте Ansible в набор shell-скриптов. Если есть модуль
apt,dnf,file,copy,service, используйте модуль. - После ручных правок на сервере переносите изменения в playbook, иначе инфраструктура быстро перестанет быть воспроизводимой.
Куда развивать проект дальше
После первого playbook логичный следующий шаг — роли. Например, отдельные роли common, users, firewall, nginx, php, mysql, backup. Роли делают проект более модульным: один и тот же базовый набор можно применить к новым VDS без копирования YAML между файлами.
Для продакшена полезны теги. Например, базовые пакеты, firewall, пользователи и сервисы можно пометить разными tags, чтобы запускать только нужную часть playbook. Но не злоупотребляйте тегами вместо нормального разделения на роли: чрезмерно сложная матрица тегов быстро становится непонятной.
И наконец, добавьте привычку проверять изменения маленькими шагами. Ansible позволяет быстро применить конфигурацию к десяткам VDS, но эта же сила превращает ошибку в массовую проблему. Хороший inventory, отдельные окружения, --check, --diff, --limit и аккуратный become — базовый набор безопасности для повседневной Linux administration и devops automation.
Итог
Для старта Ansible на VDS не нужен большой фреймворк. Достаточно отдельного SSH-ключа, проверенного host key, понятного inventory, пользователя с sudo, аккуратной настройки become и одного идемпотентного playbook. Такой фундамент уже решает реальные задачи: базовая настройка серверов, установка пакетов, подготовка каталогов, включение служб и контроль различий между staging и production.
Когда эта основа работает стабильно, её можно расширять ролями, шаблонами, секретами, CI/CD и мониторингом. Главное — не пропускать базовый слой. Надёжная автоматизация начинается не с красивого YAML, а с воспроизводимого SSH-доступа, понятных прав и inventory, которому можно доверять.


