321 lines
16 KiB
Markdown
321 lines
16 KiB
Markdown
# NODE.DC Synology deploy
|
||
|
||
Эта папка фиксирует текущий воспроизводимый NAS-deploy для `nodedc-platform` на Synology RS1221RP+.
|
||
|
||
## Правила
|
||
|
||
- Не выполнять `docker stop`, `docker restart`, `docker compose down`, `docker system prune` для старых проектов.
|
||
- Новый compose project: `nodedc-platform`.
|
||
- Новая папка на NAS: `/volume1/docker/nodedc-platform`.
|
||
- Внутренний HTTP edge использует `18080`, AI Workspace Hub — `18081`, Tasker upstream — `18090`, Ops Agents Gateway upstream — `18190`.
|
||
- Старые порты `9000` и `5678` заняты старым `nodedc-demo` и не используются.
|
||
|
||
## Текущие внешние домены
|
||
|
||
```text
|
||
https://id.nodedc.ru -> Authentik
|
||
https://hub.nodedc.ru -> Launcher / Hub
|
||
https://ops.nodedc.ru -> Tasker / Operational Core
|
||
https://ops-agents.nodedc.ru -> Ops Agents Gateway / MCP
|
||
https://ai-hub.nodedc.ru -> AI Workspace Hub / WebSocket relay
|
||
```
|
||
|
||
`id.nodedc.ru` is the user-facing OIDC/login host. Authentik Admin is intentionally not exposed through this public host; `/if/admin/*` returns `404` there.
|
||
|
||
`ai-hub.nodedc.ru` is intentionally routed by Synology DSM Reverse Proxy directly to `172.22.0.222:18081`, because this endpoint must carry WebSocket Upgrade traffic for remote Codex agents.
|
||
|
||
В `Caddyfile.http` эти домены проксируются через локальный HTTP edge, но upstream получает `X-Forwarded-Proto: https` и `X-Forwarded-Port: 443`.
|
||
|
||
## AI Workspace Hub / Assistant contract
|
||
|
||
`ai-workspace-hub` remains a thin public WebSocket relay. It is the only AI Workspace service exposed through DSM/Nginx as `https://ai-hub.nodedc.ru`.
|
||
|
||
`ai-workspace-assistant` is an internal platform service for per-user executors, selected device, shared threads and installer generation. It must not be exposed as a public route. Launcher, Engine and Tasker use it through Docker networking:
|
||
|
||
```env
|
||
NODEDC_AI_WORKSPACE_ASSISTANT_URL=http://ai-workspace-assistant:18082
|
||
PLANE_NODEDC_AI_WORKSPACE_ASSISTANT_URL=http://ai-workspace-assistant:18082
|
||
```
|
||
|
||
Installer generation must always point remote Codex workers to the deployed Hub:
|
||
|
||
```env
|
||
AI_WORKSPACE_HUB_PUBLIC_URL=wss://ai-hub.nodedc.ru/api/ai-workspace/hub
|
||
AI_WORKSPACE_HUB_INTERNAL_URL=https://ai-hub.nodedc.ru
|
||
AI_WORKSPACE_HUB_FALLBACK_URLS=
|
||
```
|
||
|
||
Server-side Hub API calls, including executor status checks, use `AI_WORKSPACE_HUB_INTERNAL_URL` and require a token accepted by Hub: `AI_WORKSPACE_HUB_TOKEN`, `NDC_AI_WORKSPACE_HUB_TOKEN`, or the shared `NODEDC_INTERNAL_ACCESS_TOKEN` where that token is intentionally common across platform services.
|
||
|
||
AI Workspace Assistant also calls product entitlement adapters before every Codex run. Ops uses the Agent Gateway internal endpoint; the token must match `NODEDC_AGENT_GATEWAY_INTERNAL_TOKEN` from the Ops Agent Gateway deployment:
|
||
|
||
```env
|
||
AI_WORKSPACE_OPS_ENTITLEMENT_URL=http://172.22.0.222:18190/api/internal/v1/ai-workspace/entitlements
|
||
AI_WORKSPACE_OPS_ENTITLEMENT_TOKEN=<same value as Ops NODEDC_AGENT_GATEWAY_INTERNAL_TOKEN>
|
||
AI_WORKSPACE_OPS_ENTITLEMENT_REQUIRED=false
|
||
```
|
||
|
||
## Локальные домены для первичной проверки
|
||
|
||
На Mac для первичной проверки добавить в `/etc/hosts`:
|
||
|
||
```text
|
||
172.22.0.222 auth.nas.nodedc
|
||
172.22.0.222 auth-admin.nas.nodedc
|
||
172.22.0.222 launcher.nas.nodedc
|
||
172.22.0.222 task.nas.nodedc
|
||
```
|
||
|
||
Первичные URL:
|
||
|
||
```text
|
||
http://auth.nas.nodedc:18080
|
||
http://auth-admin.nas.nodedc:18080/if/admin/
|
||
http://172.22.0.222:18080/if/admin/
|
||
http://launcher.nas.nodedc:18080
|
||
http://task.nas.nodedc:18080
|
||
http://task.nas.nodedc:18090
|
||
```
|
||
|
||
`auth-admin.nas.nodedc` is the technical Authentik Admin entrypoint. `172.22.0.222:18080` is a local IP fallback for workstations without `auth-admin.nas.nodedc` DNS. Both keep Authentik access separate from the public `id.nodedc.ru` login entrypoint and do not load NODE.DC auth-flow CSS.
|
||
|
||
## Что входит
|
||
|
||
- `docker-compose.platform-http.yml` поднимает новый Authentik, Launcher, Notification Core, AI Workspace Hub и Caddy edge.
|
||
- `Caddyfile.http` маршрутизирует локальные `auth/auth-admin/launcher/task.nas.nodedc`, IP fallback `172.22.0.222` для Authentik Admin и внешние `id/hub/ops.nodedc.ru`.
|
||
- `deploy-current.sh` синхронизирует compose, Caddyfile, Notification Core source, AI Workspace Hub source и опционально Launcher source в NAS mount. Authentik templates синхронизируются только при явном `SYNC_AUTHENTIK_TEMPLATES=1`.
|
||
- `backup-current.sh` делает snapshot Launcher runtime/uploads/Auth templates/config и готовит команду `pg_dump` для Authentik Postgres.
|
||
- Tasker поднимается отдельным compose из `NODEDC_TASKMANAGER/plane-app/docker-compose.yaml` на порту `18090`.
|
||
- Ops Agents Gateway поднимается отдельным compose из `NODEDC_TASKMANAGER_CODEXAPI/docker-compose.synology.yml` на `172.22.0.222:18190`; Synology reverse proxy должен вести `ops-agents.nodedc.ru` на этот порт, а не на `18090`.
|
||
|
||
## Внутренний Authentik API для Launcher
|
||
|
||
Launcher подключен и к platform identity-сети, и к engine-сети. В engine-сети тоже может быть сервис с DNS-именем `authentik-server`, поэтому это имя нельзя использовать для `NODEDC_AUTHENTIK_BASE_URL`: Docker DNS может отдать не тот Authentik, и platform API-token будет получать `403 Token invalid/expired`.
|
||
|
||
Для platform Authentik зафиксирован отдельный alias:
|
||
|
||
```text
|
||
http://nodedc-platform-authentik-server:9000
|
||
```
|
||
|
||
Live `/volume1/docker/nodedc-platform/platform/.env.synology` должен содержать:
|
||
|
||
```env
|
||
NODEDC_AUTHENTIK_BASE_URL=http://nodedc-platform-authentik-server:9000
|
||
AUTHENTIK_BASE_URL=http://nodedc-platform-authentik-server:9000
|
||
NODEDC_AUTHENTIK_SERVICE_TOKEN=<server Authentik API token>
|
||
AUTHENTIK_SERVICE_TOKEN=<same token>
|
||
```
|
||
|
||
Быстрая проверка из launcher-контейнера:
|
||
|
||
```bash
|
||
sudo /usr/local/bin/docker exec nodedc-platform-launcher-1 sh -lc '
|
||
echo "$NODEDC_AUTHENTIK_BASE_URL"
|
||
getent hosts nodedc-platform-authentik-server
|
||
for attempt in 1 2 3 4 5 6 7 8 9 10; do
|
||
wget -qSO- \
|
||
--header "Authorization: Bearer $NODEDC_AUTHENTIK_SERVICE_TOKEN" \
|
||
"$NODEDC_AUTHENTIK_BASE_URL/api/v3/core/groups/?search=nodedc_admin" \
|
||
2>&1 | head -n 25 && exit 0
|
||
echo "authentik-api-not-ready attempt=$attempt"
|
||
sleep 10
|
||
done
|
||
exit 1
|
||
'
|
||
```
|
||
|
||
Ожидаемый результат: `HTTP/1.1 200 OK`.
|
||
|
||
После изменения platform Authentik alias пересоздавать нужно `authentik-server`, `authentik-worker` и `launcher`. Один `authentik-server` может временно отдавать `503 Service Unavailable`, пока worker и bootstrap не готовы.
|
||
|
||
## Синхронизация текущего состояния
|
||
|
||
С Mac, при смонтированном `/Volumes/docker`:
|
||
|
||
```bash
|
||
cd /Users/dcconstructions/Downloads/mnt/NODEDC/platform
|
||
NAS_ROOT=/Volumes/docker/nodedc-platform \
|
||
LAUNCHER_REPO=/Users/dcconstructions/Downloads/mnt/data/nodedc_launcher \
|
||
TASKER_REPO=/Users/dcconstructions/Downloads/mnt/data/dc_taskmanager/NODEDC_TASKMANAGER \
|
||
TASKER_CHANGED_BASE=533f8c6 \
|
||
GATEWAY_REPO=/Users/dcconstructions/Downloads/mnt/data/NODEDC_TASKMANAGER_CODEXAPI \
|
||
./infra/synology/deploy-current.sh
|
||
```
|
||
|
||
Скрипт не запускает Docker сам: на NAS `sudo` интерактивный, поэтому команды применения печатаются в конце.
|
||
|
||
По умолчанию sync source-копий на SMB/NAS не сохраняет owner/group/perms/times. Для Docker build важен контент, а macOS/Synology metadata может падать на `utimensat Operation timed out`. Если metadata действительно нужно сохранить для отдельного ручного случая, запускать с `RSYNC_PRESERVE_METADATA=1`.
|
||
|
||
Что синхронизируется:
|
||
|
||
- Platform compose/Caddy.
|
||
- Notification Core source в `/volume1/docker/nodedc-platform/platform/notification-core`.
|
||
- AI Workspace Hub source в `/volume1/docker/nodedc-platform/platform/ai-workspace-hub`.
|
||
- Authentik templates только при `SYNC_AUTHENTIK_TEMPLATES=1`; по умолчанию они не трогаются, чтобы лёгкий Hub deploy не уносил экспериментальную тему/брендинг в prod.
|
||
- Launcher source в `/volume1/docker/nodedc-platform/launcher/source`.
|
||
- Tasker `plane-app/docker-compose.yaml` и, если задан `TASKER_CHANGED_BASE`, только изменённые source-файлы из диапазона `TASKER_CHANGED_BASE..HEAD`.
|
||
- Ops Agents Gateway source в `/volume1/docker/nodedc-platform/ops-agents`.
|
||
|
||
Полный sync Tasker source по SMB тяжёлый для Plane fork. Использовать его только осознанно:
|
||
|
||
```bash
|
||
TASKER_SYNC_SOURCE=1 ./infra/synology/deploy-current.sh
|
||
```
|
||
|
||
Секретные runtime env-файлы не перетираются:
|
||
|
||
- `/volume1/docker/nodedc-platform/platform/.env.synology`
|
||
- `/volume1/docker/nodedc-platform/tasker/plane-app/.env.synology`
|
||
- `/volume1/docker/nodedc-platform/ops-agents/.env`
|
||
|
||
Если emergency-fix был сделан прямо на Synology в этих env-файлах, перенести sanitized-значение в `.env.synology.example`/docs, а секрет оставить только в live env.
|
||
|
||
## Лёгкое обновление Hub / Launcher
|
||
|
||
Для правок только в Launcher/BFF не пересоздавать Authentik, reverse-proxy, Tasker и Ops Agents:
|
||
|
||
```bash
|
||
cd /volume1/docker/nodedc-platform/launcher/source
|
||
sudo /usr/local/bin/docker build --no-cache -t nodedc/launcher:local .
|
||
|
||
cd /volume1/docker/nodedc-platform/platform
|
||
sudo /usr/local/bin/docker compose \
|
||
--env-file /volume1/docker/nodedc-platform/platform/.env.synology \
|
||
-f /volume1/docker/nodedc-platform/platform/docker-compose.platform-http.yml \
|
||
up -d --force-recreate --no-deps ai-workspace-hub launcher
|
||
```
|
||
|
||
После такого deploy проверить `healthz`, запись в launcher storage/uploads и сценарий пользователя без аппрува: сохранение аватара не должно показывать экран `Заявка ожидает подтверждения`. Дополнительно проверить, что live bundle больше не содержит старый pending gate:
|
||
|
||
```bash
|
||
launcher_asset="$(
|
||
curl -k -sS --compressed -H 'Accept: text/html' https://hub.nodedc.ru/ \
|
||
| grep -aoE 'index-[A-Za-z0-9_-]+\.js' \
|
||
| head -n 1
|
||
)"
|
||
test -n "$launcher_asset"
|
||
if curl -k -sS --compressed "https://hub.nodedc.ru/assets/${launcher_asset}" \
|
||
| grep -aq 'Заявка ожидает подтверждения'; then
|
||
echo 'old pending gate still present'
|
||
exit 1
|
||
fi
|
||
echo 'launcher-pending-gate-ok'
|
||
```
|
||
|
||
## Authentik Admin и Brand CSS
|
||
|
||
NODE.DC auth-flow CSS must stay template-scoped. Do not store it in Authentik `Brand.branding_custom_css`: Authentik passes that CSS into Admin/User web component runtime and breaks native controls.
|
||
|
||
After syncing Authentik templates to NAS, recreate `reverse-proxy authentik-server authentik-worker launcher`, then clear existing global Brand CSS in the live DB:
|
||
|
||
```bash
|
||
cd /volume1/docker/nodedc-platform/platform
|
||
sudo bash clear-authentik-brand-css.sh
|
||
```
|
||
|
||
Verify:
|
||
|
||
```bash
|
||
id_admin_status="$(
|
||
curl -k -sS -o /dev/null -w '%{http_code}' https://id.nodedc.ru/if/admin/
|
||
)"
|
||
if [[ "$id_admin_status" != "404" ]]; then
|
||
echo "public id admin is not closed: status=${id_admin_status}"
|
||
exit 1
|
||
fi
|
||
echo 'public-id-admin-closed-ok'
|
||
|
||
auth_admin_page="$(
|
||
curl -k -fsS --compressed http://auth-admin.nas.nodedc:18080/if/admin/
|
||
)"
|
||
printf '%s' "$auth_admin_page" \
|
||
| grep -aE '<style data-id="brand-css"></style>|authentikBrand.branding_custom_css = ""'
|
||
|
||
auth_admin_flow="$(
|
||
curl -k -fsS --compressed http://auth-admin.nas.nodedc:18080/if/flow/default-authentication-flow/
|
||
)"
|
||
if printf '%s' "$auth_admin_flow" | grep -aq '<style data-id="nodedc-auth-login-css">'; then
|
||
echo 'admin host still has NODE.DC auth CSS'
|
||
exit 1
|
||
fi
|
||
echo 'auth-admin-css-ok'
|
||
```
|
||
|
||
## Backup текущего состояния
|
||
|
||
С Mac, при смонтированном `/Volumes/docker`:
|
||
|
||
```bash
|
||
cd /Users/dcconstructions/Downloads/mnt/NODEDC/platform
|
||
NAS_ROOT=/Volumes/docker/nodedc-platform ./infra/synology/backup-current.sh
|
||
```
|
||
|
||
Файловый backup создаётся в `/Volumes/docker/nodedc-platform/backups/platform-current-*`.
|
||
|
||
Для Authentik Postgres dump нужно выполнить напечатанную команду на Synology, потому что Docker доступен через интерактивный `sudo`:
|
||
|
||
```bash
|
||
bash /volume1/docker/nodedc-platform/backups/platform-current-YYYYMMDD-HHMMSS/run-authentik-db-dump-on-synology.sh
|
||
```
|
||
|
||
Для Notification Core Postgres dump:
|
||
|
||
```bash
|
||
bash /volume1/docker/nodedc-platform/backups/platform-current-YYYYMMDD-HHMMSS/run-notification-db-dump-on-synology.sh
|
||
```
|
||
|
||
Если планируются изменения Tasker backend/schema, дополнительно выполнить:
|
||
|
||
```bash
|
||
bash /volume1/docker/nodedc-platform/backups/platform-current-YYYYMMDD-HHMMSS/run-tasker-db-dump-on-synology.sh
|
||
```
|
||
|
||
Если Ops Agents Gateway уже был запущен и там есть production tokens/grants/audit, дополнительно выполнить:
|
||
|
||
```bash
|
||
bash /volume1/docker/nodedc-platform/backups/platform-current-YYYYMMDD-HHMMSS/run-ops-agents-db-dump-on-synology.sh
|
||
```
|
||
|
||
## Что нужно перед запуском
|
||
|
||
- Собрать или загрузить `linux/amd64` images:
|
||
- `nodedc/launcher:local`
|
||
- `nodedc/notification-core:local`
|
||
- `nodedc/plane-frontend:ru`
|
||
- `nodedc/plane-admin:ru`
|
||
- `nodedc/plane-space:ru`
|
||
- `nodedc/plane-live:local`
|
||
- `nodedc/plane-backend:local`
|
||
- `nodedc/plane-proxy:ru`
|
||
- Для Ops Agents Gateway отдельный registry image пока не обязателен: deploy из source repo выполняется через `docker compose --env-file .env -f docker-compose.synology.yml up -d --build`.
|
||
- Создать `.env.synology` из `.env.synology.example` и заменить все `replace-with-*`.
|
||
- Создать `plane.env.synology` для Tasker из `plane.env.staging.example`, но с HTTP URL на `*.nas.nodedc:18080` и портами `18090/18490`.
|
||
|
||
## Обязательные runtime-права
|
||
|
||
Launcher пишет runtime snapshot и uploads под пользователем `node` (`uid=1000`). После создания NAS-папок:
|
||
|
||
```bash
|
||
cd /volume1/docker/nodedc-platform/platform
|
||
sudo mkdir -p ../launcher/server-storage ../launcher/uploads
|
||
sudo chown -R 1000:1000 ../launcher/server-storage ../launcher/uploads
|
||
sudo chmod -R u+rwX,g+rwX ../launcher/server-storage ../launcher/uploads
|
||
```
|
||
|
||
Проверка внутри контейнера:
|
||
|
||
```bash
|
||
sudo /usr/local/bin/docker exec nodedc-platform-launcher-1 sh -lc \
|
||
'touch /app/server/storage/.write-test /app/server/storage/uploads/.write-test && rm /app/server/storage/.write-test /app/server/storage/uploads/.write-test && echo storage-ok'
|
||
```
|
||
|
||
## Проверки после деплоя
|
||
|
||
```bash
|
||
curl -k -sS --compressed https://id.nodedc.ru/if/flow/default-authentication-flow/ \
|
||
| grep -aE 'hub.nodedc.ru|launcher.local|getLauncherBaseUrl|Запросить доступ'
|
||
```
|
||
|
||
В выводе должны быть `id.nodedc.ru -> hub.nodedc.ru` и не должно быть `launcher.local`.
|