Перейти к содержанию

Руководство для 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:
    ansible-playbook -i inventory/servers.ini playbooks/vpn.yml --limit <host> --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 — полный индекс знаний