Руководство для L2 DevOps — ShivaVPN Ansible¶
Для кого: Ярослав и другие L2-инженеры
Репо: git@git.karmann.tech:devops/shiva-ansible.git (project 54)
Wiki: https://wiki.shiva-app.io/operations/ansible/
1. Быстрый старт¶
# Клонировать репо
git clone git@git.karmann.tech:devops/shiva-ansible.git
cd shiva-ansible
# Создать файл с vault-паролем (пароль — в Vaultwarden, item "Ansible Vault Password")
echo "ПАРОЛЬ" > .vault_pass
chmod 600 .vault_pass
# Проверить SSH-доступ к одному серверу
ansible -i inventory/servers.ini vpn -m ping --limit vpn-monovm-austria-01
SSH-ключи (должны лежать в ~/.ssh/):
| Ключ | Для чего |
|---|---|
~/.ssh/id_ed25519_shivavpn |
VPN серверы (внешние) + внутренняя инфра |
~/.ssh/id_ed25519 |
Общая инфра Karmann |
2. Структура инвентаря¶
inventory/
├── servers.ini ← ЕДИНСТВЕННЫЙ source of truth (~208 хостов)
├── group_vars/
│ ├── all.yml ← SSH-ключи, общие переменные
│ └── vault.yml ← Зашифрованные секреты (ansible-vault)
└── host_vars/ ← Переменные для WL-proxy серверов (nftables)
Группы серверов в servers.ini:
| Группа | Кол-во | Назначение |
|---|---|---|
[vpn] |
55 | VPN серверы (3x-ui/XUI панель) |
[vpn_wl_proxy] |
14 | Whitelist proxy (VK Cloud, nftables DNAT) |
[proxy] |
6 | API/subscription proxy |
[telegram-proxies] |
3 | Telegram MTProto proxy |
[internal_servers] |
~69 | Внутренняя инфра Karmann (через jump host) |
[api-db] |
3 | MySQL серверы |
[grafana] |
1 | Grafana + Prometheus |
[vpn_inactive] |
0 активных, 16 закомментированных | Удалять серверы из inventory, не перемещать сюда (git history = аудит) |
3. SSH-доступ¶
# VPN серверы — прямой SSH
ssh -o IdentitiesOnly=yes -i ~/.ssh/id_ed25519_shivavpn root@<IP>
# Внутренние серверы (10.99.87.x) — только через jump host
ssh -J root@212.70.189.60:2255 -i ~/.ssh/id_ed25519_shivavpn root@10.99.87.249
# Через вспомогательный скрипт (в основном репо shivavpn)
./scripts/ssh-internal.sh 10.99.87.249
# Проверить доступ ко всем VPN одновременно
ansible vpn -i inventory/servers.ini -m ping --forks=30
4. Частые операции¶
Развернуть новый VPN сервер¶
# 1. Добавить хост в [vpn] секцию servers.ini, указать ansible_host
# 2. Полный деплой (SSH hardening → UFW → sysctl → nginx → node_exporter → 3x-ui)
ansible-playbook -i inventory/servers.ini playbooks/vpn.yml \
--limit vpn-monovm-austria-09 --vault-password-file .vault_pass
# 3. Если SSH-ключей ещё нет на сервере (новый VPS — только пароль root)
ansible-playbook -i inventory/servers.ini playbooks/vpn.yml \
--limit vpn-monovm-austria-09 --vault-password-file .vault_pass --ask-pass
vpn.ymlбезопасен на уже живых серверах —x-ui.dbне перезаписывается. Для обновления XUI на живых серверах —update_3xui_xray.yml.
Обновить 3x-ui на серверах¶
# Один сервер
ansible-playbook -i inventory/servers.ini playbooks/update_3xui_xray.yml \
--limit vpn-monovm-austria-01 --vault-password-file .vault_pass
# Группа серверов (паттерн по имени)
ansible-playbook -i inventory/servers.ini playbooks/update_3xui_xray.yml \
--limit "vpn-monovm-austria-*" --vault-password-file .vault_pass
# Все VPN серверы
ansible-playbook -i inventory/servers.ini playbooks/update_3xui_xray.yml \
--vault-password-file .vault_pass
Откатить 3x-ui¶
ansible-playbook -i inventory/servers.ini playbooks/rollback_3xui_xray.yml \
--limit vpn-monovm-austria-01 --vault-password-file .vault_pass
Добавить SSH-ключ на серверы¶
# Вариант 1: добавить ключ временно через --extra-vars
ansible-playbook -i inventory/servers.ini playbooks/add_ssh_key.yml \
-e "ssh_key='ssh-ed25519 AAAA... user@host'"
# Вариант 2 (постоянно): добавить ключ в inventory/group_vars/all.yml → root_public_keys
# затем применить роль SSH на все серверы:
ansible-playbook -i inventory/servers.ini playbooks/ssh.yml
Отозвать SSH-ключ¶
# Перенести ключ из root_public_keys в revoked_public_keys в all.yml
# Применить:
ansible-playbook -i inventory/servers.ini playbooks/ssh.yml
Проверить состояние серверов¶
# Ping всех VPN
ansible vpn -i inventory/servers.ini -m ping --forks=30
# Версия xray на конкретном сервере
ansible -i inventory/servers.ini vpn -m shell \
-a "docker exec 3x-ui xray version 2>&1 | head -1" \
--limit vpn-monovm-austria-01
# Статус docker-контейнера 3x-ui
ansible -i inventory/servers.ini vpn -m shell \
-a "docker ps --filter name=3x-ui --format '{{.Status}}'"
Настроить WL-proxy (whitelist)¶
WL серверы — это прокси в VK Cloud с nftables DNAT. Конфиги хранятся в inventory/host_vars/.
# Применить конфиг для одного WL сервера
ansible-playbook -i inventory/servers.ini playbooks/proxy.yml \
--limit whitelist-vk-at-01 --vault-password-file .vault_pass
# Установить node_exporter на WL серверах (мониторинг)
ansible-playbook -i inventory/servers.ini playbooks/setup_node_exporter_whitelist.yml \
--vault-password-file .vault_pass
Конфиги nftables для конкретного WL хоста — в inventory/host_vars/whitelist-vk-XX.yml.
Развернуть Remnawave ноду¶
ansible-playbook -i inventory/servers.ini playbooks/remnawave.yml \
--limit remna-monovm-austria-01 \
-e remnawave_panel_url=https://market-plus.vip \
-e remnawave_node_token=TOKEN_FROM_PANEL \
--vault-password-file .vault_pass
Токен ноды берётся в Remnawave панели: https://market-plus.vip/ → Nodes → Add Node.
Оптимизация производительности¶
# sysctl, ulimits, Docker daemon.json, nginx workers — после деплоя нового сервера
ansible-playbook -i inventory/servers.ini vpn_xray_optimization.yml \
--limit vpn-monovm-austria-09
# Только kernel tuning (BBR, conntrack, TCP buffers)
ansible-playbook -i inventory/servers.ini playbooks/sysctl.yml \
--limit vpn-monovm-austria-09
5. Работа с vault¶
Vault-пароль хранится в Vaultwarden: https://vault.shivavpn.io → item "Ansible Vault Password".
# Посмотреть секреты
ansible-vault view inventory/group_vars/vault.yml --vault-password-file .vault_pass
# Редактировать (открывает $EDITOR)
ansible-vault edit inventory/group_vars/vault.yml --vault-password-file .vault_pass
# Просмотреть CA-ключ
ansible-vault view CA/ca.key --vault-password-file .vault_pass
# Зашифровать новый файл
ansible-vault encrypt myfile.yml --vault-password-file .vault_pass
Переменные в vault.yml:
| Переменная | Что это |
|---|---|
vault_xray_admin_password |
Пароль 3x-ui панели |
vault_xray_basic_auth_password |
htpasswd nginx → 3x-ui |
vault_node_exporter_basic_auth_password |
htpasswd node_exporter |
vault_loki_password |
Loki basic auth |
vault_infra_become_password |
sudo на внутренних серверах |
vault_xray_inbound_private_key |
x25519 Reality private key |
Когда нужен --ask-pass vs --vault-password-file:
| Флаг | Когда нужен | Что вводить |
|---|---|---|
--vault-password-file .vault_pass |
Всегда когда плейбук использует vault.yml | Файл с ansible vault паролем |
--ask-pass |
Только на новых VPS без наших ключей | root-пароль от хостинга |
6. Устранение проблем¶
Сервер не отвечает на ping:
# 1. Проверить IP и порт в servers.ini
# 2. Попробовать вручную
ssh -i ~/.ssh/id_ed25519_shivavpn -p 22 root@<IP>
# 3. Проверить файрвол (если зашли)
ufw status
Permission denied (publickey):
# Проверить какие ключи указаны
cat inventory/group_vars/all.yml | grep -A5 root_public_keys
# Проверить что ключ добавлен на сервер
ssh-copy-id -i ~/.ssh/id_ed25519_shivavpn root@<IP>
3x-ui не отвечает:
ssh -i ~/.ssh/id_ed25519_shivavpn root@<IP>
docker ps --filter name=3x-ui
docker restart 3x-ui
journalctl -u docker -n 30
xray не запускается:
ssh -i ~/.ssh/id_ed25519_shivavpn root@<IP>
docker exec 3x-ui xray version
docker logs 3x-ui --tail 50
systemctl restart xray # если xray как systemd-сервис
WL proxy не работает (трафик не идёт через EU):
ssh -i ~/.ssh/id_ed25519_shivavpn root@<WL_IP>
nft list ruleset # проверить nftables правила DNAT
ss -tlnp | grep 443 # проверить что порт слушается
ping <upstream_vpn_ip> # проверить связь с VPN сервером
Плейбук падает на группе, которой нет в inventory:
Некоторые плейбуки используют группы, не определённые в servers.ini — это ошибки в плейбуках.
Обходное решение: использовать --limit с конкретным хостом.
# Вместо:
ansible-playbook -i inventory/servers.ini playbooks/loki.yml
# Делать:
ansible-playbook -i inventory/servers.ini playbooks/loki.yml --limit km-loki01
7. Главное правило: Ansible inventory first¶
inventory/servers.ini = единственный источник истины по серверам.
Любое изменение серверного парка → сначала Ansible, потом всё остальное:
| Действие | Порядок |
|---|---|
| Новый сервер | 1. servers.ini → 2. ansible-playbook → 3. MySQL → 4. мониторинг |
| Удаление сервера | 1. MySQL is_active=0 → 2. удалить из servers.ini → 3. мониторинг |
| Смена IP | 1. servers.ini + host_vars → 2. Prometheus → 3. MySQL |
| Изменение в панели | Если затрагивает inventory (порт, IP) → сначала servers.ini |
Если информация в MySQL/Grafana/wiki расходится с servers.ini — servers.ini прав.
8. Миграция XUI → Remnawave¶
55 серверов XUI мигрируют на Remnawave (gRPC+mTLS, центральная PostgreSQL).
- План:
planning/PLAN-XUI-TO-REMNAWAVE-MIGRATION.mdв monorepo - Новые серверы добавляются уже как Remnawave:
playbooks/remnawave.yml - XUI-плейбуки (
vpn.yml,update_3xui_xray.yml) остаются до завершения миграции - Wiki: https://wiki.shiva-app.io → Инфраструктура → Remnawave
9. Правила работы¶
- НЕ коммитить credentials в открытом виде — только через ansible-vault
- НЕ запускать
docker logsбез--tailна продакшн серверах (был OOM-краш на 10.99.87.249) - НЕ менять servers.ini без pull request и ревью у DevOps Lead
- Перед деструктивными операциями всегда проверять через
--check: - Лимитировать охват
--limitдо минимума — не гонять плейбук на все серверы без нужды - Стиль коммитов: кратко, по делу, без AI-слов ("robust", "enhance", etc.)
10. Ресурсы¶
| Ресурс | Адрес |
|---|---|
| GitLab репо | git.karmann.tech/devops/shiva-ansible (project 54) |
| Wiki (ansible раздел) | https://wiki.shiva-app.io/operations/ansible/ |
| Vaultwarden (пароли) | https://vault.shivavpn.io |
| Grafana | 10.99.87.5:3001 (через jump host) |
| Remnawave Panel | https://market-plus.vip/ |
| DevOps Lead | @zardes в Telegram |
Документация в основном репо shivavpn:
- SESSION-START.md — быстрый старт, SSH, сервисы
- docs/devops/RUNBOOKS.md — процедуры при инцидентах
- docs/guides/KNOWLEDGE-BASE-INDEX.md — полный индекс знаний