Restructure repo: per-server folders, inventory, domains

- servers/inventory.yml — реестр узлов (имена, IP, роли, сервисы).
- domains.yml — реестр DNS-записей для mxti.ru (reg.ru).
- docker/ → servers/ru-docker-infra/ — конфиги основного сервера.
- CLAUDE.md переписан: структура репо, чек-листы по изменениям,
  конвенции (роли вместо IP, секреты тоже в репо).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-06-13 12:50:37 +03:00
parent c254bd03fe
commit 6dd6b586b5
7 changed files with 136 additions and 41 deletions
+48 -41
View File
@@ -1,61 +1,68 @@
# Personal Infrastructure
Репозиторий моей личной инфраструктуры. Всё разворачивается через Docker (предпочтительный метод), конфиги хранятся в этом репо.
Репозиторий моей личной инфраструктуры: VPN, git-хостинг, автоматизация, реверс-прокси и всё, что будет добавляться дальше. Цель — держать в одном месте всё, что нужно, чтобы развернуть любой узел с нуля.
## Серверы
Принципы:
| IP | Локация | Роль |
|---|---|---|
| `111.88.253.112` | Yandex Cloud | Основной сервер — все основные сервисы (n8n, gitea, traefik, portainer, postgres, redis, xray, mtg) |
| `62.84.114.137` | Yandex Cloud | Входная нода VPN (3x-ui) |
| `94.247.214.20` | Нидерланды | Реверс-прокси для n8n (принимает SSH-туннель с основного сервера) |
| `94.247.214.112` | Нидерланды | Выходная нода VPN (3x-ui) |
- **Docker — предпочтительный способ развёртывания.** Если можно завернуть в compose — заворачиваем.
- **Репозиторий — единственный источник правды.** На самих серверах ничего уникального жить не должно: все конфиги, compose-файлы, скрипты, артефакты, `.env` — в git. Сервер тянет нужную папку и поднимает её.
- **Секреты тоже в репо.** `.env` с паролями коммитим вместе с остальным (репо приватный). Приватные ключи (`ssh/tunnel_key` и т.п.) — туда же, как файлы рядом с compose.
## Структура репозитория
Всё, что нужно для развёртывания, хранится в этом репо. Под каждый сервер — отдельная папка верхнего уровня со всеми его конфигами, compose-файлами, артефактами и т.п.
```
servers/
inventory.yml # источник правды о парке серверов
<server-name>/ # одна папка на сервер, имя совпадает с inventory.servers[].name
docker-compose.yml # compose этого узла
.env # переменные окружения узла (включая пароли)
<service>/ # конфиги конкретных сервисов (xray, mtg, nginx, ...)
ssh/ # ключи и прочие чувствительные файлы
domains.yml # источник правды о DNS-записях для всех доменов
CLAUDE.md # этот файл — обзор и правила
README.md
```
| Папка | Сервер | Назначение |
|---|---|---|
| `docker/` | `111.88.253.112` | Основной сервер (текущее содержимое) |
| _tbd_ | `62.84.114.137` | Входная нода VPN |
| _tbd_ | `94.247.214.20` | NL реверс-прокси для n8n |
| _tbd_ | `94.247.214.112` | Выходная нода VPN |
`servers/inventory.yml` — реестр узлов: имена, адреса, роли, локации, ssh-настройки, список сервисов. Любые ответы про «какой сервер», «где что крутится», «какой у него IP» берём оттуда.
Папки для остальных серверов добавим по мере того, как будем переносить туда конфиги. Имена папок — по роли сервера (например, `vpn-entry/`, `nl-proxy/`, `vpn-exit/`), не по IP.
`domains.yml` — реестр DNS-записей. Для каждого домена перечислены все нужные записи (A, CNAME, TXT, MX и т.п.) с указанием либо узла из inventory (`server: <name>` — IP подтягивается), либо буквального значения (`value: ...`). Состояние DNS у регистратора должно совпадать с этим файлом.
Правило: если сервис где-то крутится — его конфиг должен лежать в папке соответствующего сервера в этом репо. Никаких «живёт только на сервере» артефактов.
Папка `servers/<name>/` содержит всё, что нужно для развёртывания этого узла. Имя папки = `name` в inventory.
## Сервисы основного сервера (`docker/docker-compose.yml`)
## Где что прописывать при изменениях
Развёрнуты на `111.88.253.112`:
**Добавили новый сервер:**
1. Запись в `servers/inventory.yml` (name, host, role, env, location, tags, services).
2. Папка `servers/<name>/` с `docker-compose.yml` и `.env`.
3. Если узлу нужен домен — добавить записи в `domains.yml` и прописать их у регистратора.
4. Если узел упоминается смысловой ролью (например, тут есть бэкенд для другого узла) — обновить соответствующие compose/конфиги, ссылаясь на роль, а не IP.
- **traefik** — реверс-прокси + Let's Encrypt, слушает 80/443, маршрутизирует по Host-меткам.
- **postgres** (16-alpine) — общая БД для n8n и gitea.
- **redis** (7-alpine) — кэш и очередь Bull для n8n.
- **n8n** — автоматизация. HTTP(S)_PROXY через `xray` для обхода блокировок. Публикуется по `${N8N_HOST}` через traefik, а также пробрасывается SSH-туннелем (`tunnel` сервис) на `94.247.214.20:5678` для внешнего доступа через NL-реверс-прокси.
- **tunnel** (autossh) — обратный SSH-туннель `-R` на `94.247.214.20`, пользователь `tunnel`, ключ в `./ssh/tunnel_key`.
- **xray** — исходящий прокси для n8n (порт 8080 во внутренней сети).
- **gitea** — git-хостинг, БД в общем postgres (`gitea`/`gitea`), SSH на хосте проброшен на `222`.
- **portainer** — UI для управления docker.
- **mtg** — Telegram MTProto-прокси с fake TLS, проксируется через traefik TCP по SNI `${MTG_FAKE_TLS_HOST}`.
**Добавили сервис на существующий сервер:**
1. Сервис в `servers/<name>/docker-compose.yml`. Внешние — через traefik-метки (`traefik.http.routers.<name>.*`) с хостом из `.env`. Внутренние — только в сети `internal`.
2. Конфиги сервиса — в `servers/<name>/<service>/`, монтировать в контейнер read-only.
3. Новые переменные — в `.env` узла. В compose использовать `${VAR}`, не хардкодить.
4. Запись `services: [...]` у узла в `inventory.yml`.
5. Если сервис публикуется наружу — DNS-запись в `domains.yml` и у регистратора.
Сети: `proxy` (внешний трафик через traefik) и `internal` (между сервисами и БД).
**Изменили IP / переехали:**
1. Только `inventory.yml` (поле `host`). Compose-файлы IP не содержат — там ссылки на сервисы по имени или на роли.
2. Проверить `domains.yml`: записи со ссылкой `server: <name>` подтянут новый IP автоматически — но у регистратора надо обновить вручную.
## VPN
**Добавили / изменили домен:**
1. Запись в `domains.yml`: блок `domains` для домена, нужные `records`. Для записей, указывающих на наш узел — `server: <name>`, иначе `value: <строка>`.
2. Синхронизировать у регистратора. Состояние DNS должно совпадать с файлом.
Входная (`62.84.114.137`) и выходная (`94.247.214.112`) ноды — оба на **3x-ui**. На основном сервере отдельно крутится **xray** как исходящий прокси для n8n и **mtg** как Telegram-прокси.
**Добавили секрет:**
1. В `.env` узла либо как файл в `servers/<name>/ssh/` (или аналогичной служебной папке).
2. Никогда не хардкодить в compose или конфиг-файлах — только через `${VAR}` или монтируемый файл.
## Конфиги в репо
- `docker/docker-compose.yml` — основной compose.
- `docker/.env` — переменные (`*_HOST`, пароли, `N8N_ENCRYPTION_KEY`, `ACME_EMAIL`, `TIMEZONE`). Не коммитить секреты.
- `docker/xray/config.json` — конфиг xray-клиента.
- `docker/mtg/config.toml` — конфиг mtg.
- `docker/ssh/tunnel_key` — приватный ключ для autossh-туннеля (не в git).
**Удалили сервис / сервер:**
1. Убрать запись из `inventory.yml` (или из `services` узла).
2. Удалить соответствующий блок из compose и папку с конфигами.
## Конвенции
- Новые сервисы добавляем в `docker/docker-compose.yml`, публикуем через traefik с метками `traefik.http.routers.<name>.*` и хостом из `.env`.
- Внутренние сервисы — только в сети `internal`, публичные — в `proxy` (или в обеих, если нужен доступ к БД).
- Секреты — через `.env`, не хардкодить в compose.
- Адреса серверов — только в `inventory.yml`. В compose и доках ссылаемся на роли (`core`, `vpn-entry`, `reverse-proxy`, `vpn-exit` и т.п.).
- Сети compose: `proxy` для внешнего трафика через traefik, `internal` для межсервисной коммуникации и БД. Сервис в обеих сетях — только если ему правда нужен доступ и снаружи, и к внутренним зависимостям.
- Имя папки сервера = `name` в inventory, по роли, не по IP.
- Любой артефакт, без которого сервер не поднимется (compose, `.env`, конфиг, ключ) — лежит в `servers/<name>/` и коммитится в репо.
+32
View File
@@ -0,0 +1,32 @@
# domains.yml — источник правды о DNS-записях.
# Любые домены, которыми мы пользуемся, должны быть здесь.
# Изменения в инфраструктуре (новый сервис, переезд узла, смена IP) обязательно
# отражать тут — у регистратора должно быть ровно то, что описано в этом файле.
# Допустимые типы записей: A, AAAA, CNAME, MX, TXT, SRV, CAA.
# server: <name> — ссылка на запись в servers/inventory.yml (host подставляется оттуда).
# value: <строка> — буквальное значение записи (для CNAME/TXT/MX/...).
domains:
- name: mxti.ru
registrar: reg.ru
records:
- host: www
type: A
server: ru-docker-infra
purpose: "основной сервер (traefik)"
- host: git
type: A
server: ru-docker-infra
purpose: "gitea через traefik"
- host: portainer
type: A
server: ru-docker-infra
purpose: "portainer UI"
- host: n8n
type: A
server: nl-reverse-proxy
purpose: "n8n через NL реверс-прокси (autossh-туннель с ru-docker-infra)"
+56
View File
@@ -0,0 +1,56 @@
# inventory.yml — источник правды о парке серверов.
# Добавить сервер = добавить запись сюда + папку servers/<name>/ с docker-compose.yml.
defaults: # общие настройки, чтобы не повторять в каждом сервере
ssh_user: agent # отдельный non-root пользователь для бота
branch: main # ветка, которую тянут серверы
compose_path: servers/{name}/docker-compose.yml # {name} подставляется из поля name
servers:
- name: ru-docker-infra # основной сервер в Яндекс.Облаке
host: 111.88.253.112
role: core # основной узел: все ключевые сервисы
env: prod
location: yandex-cloud
tags: [traefik, n8n, gitea, postgres, redis, portainer, xray, mtg]
services:
- traefik
- postgres
- redis
- n8n
- tunnel # autossh-туннель на nl-reverse-proxy
- xray # исходящий прокси для n8n
- gitea
- portainer
- mtg # Telegram MTProto-прокси
managed: true
- name: ru-vpn # входная нода VPN
host: 62.84.114.137
role: vpn-entry
env: prod
location: yandex-cloud
tags: [3x-ui, vpn]
services:
- 3x-ui
managed: true
- name: nl-reverse-proxy # реверс-прокси для n8n (принимает SSH-туннель)
host: 94.247.214.20
role: reverse-proxy
env: prod
location: netherlands
tags: [n8n, ssh-tunnel]
services:
- nginx # tbd: уточнить, чем именно проксируется
managed: true
- name: nl-vpn # выходная нода VPN
host: 94.247.214.112
role: vpn-exit
env: prod
location: netherlands
tags: [3x-ui, vpn]
services:
- 3x-ui
managed: true