Один стек для cdn.shnt.top и mm.shnt.top за общим Traefik. Co-authored-by: Cursor <cursoragent@cursor.com>
6.4 KiB
Развёртывание 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 |
| Env-шаблон | deploy/env.traefik.example |
| SSO | integrate-auth-portal.md |
| Релизы | releasing.md |
CDN + MikrotikManager на одном Traefik: deploy/docker-compose.cdn-mm.yml + deploy/env.cdn-mm.example — cdn.shnt.top и mm.shnt.top, каталог /opt/cdn-mm.
Документация Traefik: Expose Docker, ACME DNS challenge.
Internet → :80/:443 (Traefik) → cdnmanager:8080
↑
Cloudflare DNS TXT (ACME)
Предпосылки
- Зона домена в Cloudflare (например
shnt.top). - Docker Engine + Compose plugin на VPS.
- Свободные порты 80 и 443 на хосте (этот стек сам поднимает Traefik).
docker login git.shx.one.- Для 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 — шаблон 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 (серое облако) |
dig +short cdn.shnt.top A
Traefik для сертификата создаёт TXT _acme-challenge.cdn… через Cloudflare API.
2. Файлы на сервере
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. Запуск
cd /opt/cdnmanager
docker login git.shx.one
docker compose pull
docker compose up -d
docker compose ps
docker compose logs -f --tail=100
Проверка:
curl -fsS https://cdn.shnt.top/health
# {"status":"ok",…}
Откройте https://cdn.shnt.top → редирект на auth-portal → handoff на /auth/callback.
4. Обновление
cd /opt/cdnmanager
docker compose pull
docker compose up -d
Миграции SQLite применяются при старте API. Volume ./data сохраняется.
5. Остановка
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.