
Вы арендовали новый VPS. Теперь нужно: обновить пакеты, создать пользователя с sudo, отключить вход по паролю для root, установить файрвол, Fail2ban и nginx. Руками это занимает 20-30 минут на один сервер. А если серверов пять? Или двадцать?
Ansible решает эту задачу иначе: вы описываете желаемое состояние сервера в YAML-файле (playbook), и Ansible приводит сервер к этому состоянию через SSH. Никаких агентов, никаких дополнительных сервисов на серверах. Нужен только Python, который уже есть почти в каждом Linux-дистрибутиве.
Как работает Ansible
Ansible запускается на вашей управляющей машине (ноутбук, рабочая станция или отдельный control node). Он подключается к серверам по SSH и выполняет задачи последовательно.
Три основных понятия:
- Inventory - список серверов, которыми управляет Ansible. Хосты объединяются в группы.
- Playbook - YAML-файл с описанием задач. Каждая задача использует модуль Ansible:
aptдля установки пакетов,userдля управления пользователями,copyдля копирования файлов и так далее. - Module - отдельная единица функциональности. В Ansible более 3000 встроенных модулей.
Схема выполнения: вы запускаете ansible-playbook playbook.yml → Ansible читает inventory → подключается по SSH к каждому хосту → выполняет задачи → сообщает о результатах.
Ansible идемпотентен: если задача уже выполнена (пакет установлен, пользователь создан), Ansible пропустит её без изменений. Можно запускать тот же playbook несколько раз без побочных эффектов.
Установка Ansible
На управляющей машине с Ubuntu/Debian:
apt update
apt install -y software-properties-common
add-apt-repository --yes --update ppa:ansible/ansible
apt install -y ansible
ansible --version
На macOS через Homebrew:
brew install ansible
Через pip (работает на любой платформе с Python 3):
pip3 install ansible
Проверьте установку:
ansible --version
Вывод покажет версию и путь к конфигурационному файлу.
Inventory-файл
Создайте рабочую директорию и inventory-файл:
mkdir ~/ansible-server-setup
cd ~/ansible-server-setup
nano inventory.ini
[web]
web1 ansible_host=203.0.113.10 ansible_user=root
web2 ansible_host=203.0.113.11 ansible_user=root
[db]
db1 ansible_host=203.0.113.20 ansible_user=root
[all:vars]
ansible_ssh_private_key_file=~/.ssh/id_ed25519
ansible_python_interpreter=/usr/bin/python3
Здесь определены две группы: web (два веб-сервера) и db (один сервер базы данных). В секции [all:vars] указаны общие переменные для всех хостов.
Проверьте связь со всеми хостами:
ansible all -i inventory.ini -m ping
Каждый доступный хост ответит pong. Если какой-то хост недоступен, Ansible сообщит об ошибке подключения.
Полный playbook для начальной настройки сервера
Создайте файл setup.yml:
---
- name: Initial server setup
hosts: all
become: true
vars:
new_user: deploy
ssh_public_key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA... user@laptop"
ssh_port: 22
tasks:
# Обновление системы
- name: Update apt cache
apt:
update_cache: yes
cache_valid_time: 3600
- name: Upgrade all packages
apt:
upgrade: dist
autoremove: yes
# Установка базовых пакетов
- name: Install base packages
apt:
name:
- curl
- wget
- vim
- git
- htop
- ufw
- fail2ban
- nginx
- unattended-upgrades
state: present
# Создание пользователя sudo
- name: Create deploy user
user:
name: "{{ new_user }}"
shell: /bin/bash
groups: sudo
append: yes
create_home: yes
state: present
- name: Add SSH key for deploy user
authorized_key:
user: "{{ new_user }}"
key: "{{ ssh_public_key }}"
state: present
- name: Allow sudo without password for deploy user
lineinfile:
path: /etc/sudoers
state: present
regexp: "^{{ new_user }}"
line: "{{ new_user }} ALL=(ALL) NOPASSWD: ALL"
validate: visudo -cf %s
# Настройка SSH
- name: Disable root SSH login
lineinfile:
path: /etc/ssh/sshd_config
regexp: "^PermitRootLogin"
line: "PermitRootLogin no"
notify: restart ssh
- name: Disable password authentication
lineinfile:
path: /etc/ssh/sshd_config
regexp: "^PasswordAuthentication"
line: "PasswordAuthentication no"
notify: restart ssh
# Настройка UFW
- name: Set UFW default incoming to deny
ufw:
direction: incoming
policy: deny
- name: Set UFW default outgoing to allow
ufw:
direction: outgoing
policy: allow
- name: Allow SSH
ufw:
rule: allow
port: "{{ ssh_port }}"
proto: tcp
- name: Allow HTTP
ufw:
rule: allow
port: 80
proto: tcp
- name: Allow HTTPS
ufw:
rule: allow
port: 443
proto: tcp
- name: Enable UFW
ufw:
state: enabled
# Настройка Fail2ban
- name: Copy fail2ban jail config
copy:
dest: /etc/fail2ban/jail.local
content: |
[DEFAULT]
bantime = 3600
findtime = 600
maxretry = 5
[sshd]
enabled = true
port = {{ ssh_port }}
logpath = %(sshd_log)s
backend = %(sshd_backend)s
notify: restart fail2ban
# Включение автообновлений безопасности
- name: Enable unattended upgrades
copy:
dest: /etc/apt/apt.conf.d/20auto-upgrades
content: |
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";
APT::Periodic::AutocleanInterval "7";
# Nginx
- name: Start and enable nginx
systemd:
name: nginx
state: started
enabled: yes
handlers:
- name: restart ssh
systemd:
name: sshd
state: restarted
- name: restart fail2ban
systemd:
name: fail2ban
state: restarted
Запуск playbook
Перед запуском проверьте синтаксис:
ansible-playbook -i inventory.ini setup.yml --syntax-check
Запустите в режиме --check (dry run). Ansible покажет, что будет изменено, но ничего не применит:
ansible-playbook -i inventory.ini setup.yml --check --diff
Флаг --diff показывает конкретные изменения в файлах: что было и что станет после выполнения задачи.
Применение изменений:
ansible-playbook -i inventory.ini setup.yml
Для запуска только на одном хосте:
ansible-playbook -i inventory.ini setup.yml --limit web1
Для запуска только определённых задач по тегу добавьте теги в playbook и используйте --tags:
ansible-playbook -i inventory.ini setup.yml --tags "ufw,fail2ban"


Структура проекта для нескольких серверов
Когда playbook вырастает, имеет смысл разбить его на роли:
ansible-server-setup/
├── inventory.ini
├── setup.yml
├── group_vars/
│ ├── all.yml # переменные для всех хостов
│ └── web.yml # переменные только для группы web
├── host_vars/
│ └── web1.yml # переменные только для web1
└── roles/
├── common/
│ └── tasks/
│ └── main.yml
├── nginx/
│ └── tasks/
│ └── main.yml
└── security/
└── tasks/
└── main.yml
Такая структура позволяет применять роли к разным группам хостов независимо.
Итог
Ansible позволяет настроить новый сервер за один запуск команды вместо ручных операций. Playbook из этой статьи выполняет обновление системы, создание безопасного пользователя, настройку UFW, Fail2ban и Nginx. Режим --check позволяет проверить изменения до их применения.
После запуска playbook на VPS от Zomro вы получаете сервер с закрытым root-доступом, настроенным файрволом и автоматическими обновлениями безопасности.
Один и тот же playbook можно применить к любому количеству серверов.