Files
CDNManager/docs/deploy-traefik.md
T
DenozordecandCursor f063378e6b
quality / commitlint (push) Skipped
CD / update-wiki (push) Successful in 7s
quality / changes (push) Successful in 5s
quality / web (push) Skipped
quality / api (push) Skipped
quality / docker-check (push) Skipped
CD / quality (push) Successful in 6s
CD / publish (push) Successful in 31s
chore(deploy): добавить общий compose CDN и MikrotikManager с Traefik
Один стек для cdn.shnt.top и mm.shnt.top за общим Traefik.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-04 19:07:49 +07:00

167 lines
6.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Развёртывание CDN Manager + Traefik (один Docker Compose)
Один стек: **Traefik** (HTTPS, Let's Encrypt DNS-01 через Cloudflare) + **cdnmanager** (API + SPA, SQLite).
| | |
|---|---|
| Образ | `git.shx.one/denozord/cdnmanager` (алиас `cdn-manager`) |
| Runtime | `node:22-alpine`, API + SPA + SQLite |
| Публичный URL | `https://cdn.shnt.top` |
| Compose | [`deploy/docker-compose.traefik.yml`](../deploy/docker-compose.traefik.yml) |
| Env-шаблон | [`deploy/env.traefik.example`](../deploy/env.traefik.example) |
| SSO | [`integrate-auth-portal.md`](./integrate-auth-portal.md) |
| Релизы | [`releasing.md`](./releasing.md) |
**CDN + MikrotikManager на одном Traefik:** [`deploy/docker-compose.cdn-mm.yml`](../deploy/docker-compose.cdn-mm.yml) + [`deploy/env.cdn-mm.example`](../deploy/env.cdn-mm.example) — `cdn.shnt.top` и `mm.shnt.top`, каталог `/opt/cdn-mm`.
Документация Traefik: [Expose Docker](https://doc.traefik.io/traefik/expose/docker/basic/), [ACME DNS challenge](https://doc.traefik.io/traefik/https/acme/).
```
Internet → :80/:443 (Traefik) → cdnmanager:8080
Cloudflare DNS TXT (ACME)
```
## Предпосылки
1. Зона домена в Cloudflare (например `shnt.top`).
2. Docker Engine + Compose plugin на VPS.
3. Свободные порты **80** и **443** на хосте (этот стек сам поднимает Traefik).
4. `docker login git.shx.one`.
5. Для SSO — работающий auth-portal с тем же `JWT_SECRET` / `AUTH_ISSUER`.
> Если на сервере уже крутится другой Traefik на 80/443 — либо остановите его, либо смените `TRAEFIK_HTTP_PORT` / `TRAEFIK_HTTPS_PORT`. Два Traefik на одних портах не запустятся.
---
## 1. Cloudflare: токены и DNS
### Два разных токена
| Переменная | Куда | Назначение |
|------------|------|------------|
| `CF_DNS_API_TOKEN` | контейнер **Traefik** | ACME DNS-01 (TXT `_acme-challenge`) |
| `CLOUDFLARE_API_TOKEN` | контейнер **app** | Zone DNS Edit + Zone Read для sync флота |
Можно один токен с нужными правами продублировать в обе переменные, либо разделить (рекомендуется least privilege).
[API Tokens → Create Token](https://dash.cloudflare.com/profile/api-tokens) — шаблон **Edit zone DNS** или Custom:
| Permission | Access |
|------------|--------|
| Zone → DNS | Edit |
| Zone → Zone | Read (желательно) |
**Zone Resources:** Include → Specific zone → ваша зона. Не Global API Key, не в Git.
### DNS-запись
| Type | Name | Content | Proxy |
|------|------|---------|-------|
| `A` / `AAAA` | `cdn` | IP VPS | **DNS only** (серое облако) |
```bash
dig +short cdn.shnt.top A
```
Traefik для сертификата создаёт TXT `_acme-challenge.cdn…` через Cloudflare API.
---
## 2. Файлы на сервере
```bash
mkdir -p /opt/cdnmanager/data
cd /opt/cdnmanager
# из репозитория:
# cp deploy/docker-compose.traefik.yml docker-compose.yml
# cp deploy/env.traefik.example .env
curl -fsSL -o docker-compose.yml \
https://git.shx.one/denozord/CDNManager/raw/branch/main/deploy/docker-compose.traefik.yml
curl -fsSL -o .env \
https://git.shx.one/denozord/CDNManager/raw/branch/main/deploy/env.traefik.example
nano .env # заполнить секреты
```
### Обязательные переменные в `.env`
| Переменная | Назначение |
|------------|------------|
| `CF_DNS_API_TOKEN` | Cloudflare token для ACME DNS-01 (Traefik) |
| `LETSENCRYPT_EMAIL` | Email для Let's Encrypt |
| `CDN_DOMAIN` | Хост в Traefik rule, по умолчанию `cdn.shnt.top` |
| `CLOUDFLARE_API_TOKEN` | Token приложения для DNS sync |
| `AUTH_JWT_SECRET` / `JWT_SECRET` | = `JWT_SECRET` auth-portal (HS256) |
| `AUTH_ISSUER` | `https://auth.shnt.top` |
| `AUTH_PORTAL_URL` | URL UI портала |
Опционально: `CDN_IMAGE_TAG`, `TRAEFIK_IMAGE_TAG`, `TRAEFIK_HTTP_PORT`, `TRAEFIK_HTTPS_PORT`, `AUTH_AUDIT_INGEST_SECRET`, `AUTH_REQUIRED` (default `true`).
На стороне портала добавьте origin в `RETURN_TO_ALLOWLIST` (`https://cdn.shnt.top`) и выдайте app **`cdn`** + права `cdn:*`. App Switcher URL: тот же origin.
**Важно:** `AUTH_JWT_SECRET` в CDN Manager **должен совпадать** с `JWT_SECRET` auth-portal, `AUTH_ISSUER`с `ISSUER` портала. Иначе после SSO UI зацикливается / «Страница не отвечает».
---
## 3. Запуск
```bash
cd /opt/cdnmanager
docker login git.shx.one
docker compose pull
docker compose up -d
docker compose ps
docker compose logs -f --tail=100
```
Проверка:
```bash
curl -fsS https://cdn.shnt.top/health
# {"status":"ok",…}
```
Откройте `https://cdn.shnt.top` → редирект на auth-portal → handoff на `/auth/callback`.
---
## 4. Обновление
```bash
cd /opt/cdnmanager
docker compose pull
docker compose up -d
```
Миграции SQLite применяются при старте API. Volume `./data` сохраняется.
---
## 5. Остановка
```bash
docker compose down
# с удалением ACME volume (сертификаты перевыпустятся):
# docker compose down -v
```
Данные SQLite в `./data` на хосте остаются, пока не удалите каталог.
---
## Troubleshooting
| Симптом | Причина |
|---------|---------|
| Порт 80/443 занят | другой Traefik/nginx; смените порты или хост |
| ACME timeout | `CF_DNS_API_TOKEN` без DNS Edit; Cloudflare Proxy на A-записи (нужен DNS only) |
| SSO loop / 401 | разный `AUTH_JWT_SECRET` / `AUTH_ISSUER` vs portal |
| 403 «Нет доступа» | у пользователя нет app `cdn` |
| return_to rejected | `https://cdn.shnt.top` не в `RETURN_TO_ALLOWLIST` портала |
| Sync CF падает | пустой/`CLOUDFLARE_API_TOKEN` без Zone DNS Edit |
Без Traefik (plain `docker run` / простой Compose): [`deploy-docker.md`](./deploy-docker.md).