196 lines
4.7 KiB
Markdown
196 lines
4.7 KiB
Markdown
# Развёртывание Flamy Trade
|
||
|
||
Документ описывает production-развёртывание сайта `trade.flamy.studio` без ML-runtime и будущего API. Эти части подключаются отдельным этапом.
|
||
|
||
## Схема
|
||
|
||
```text
|
||
Internet
|
||
-> Router :80/:443
|
||
-> edge-proxy VM / Caddy
|
||
-> 10.20.0.20:18082
|
||
-> flamy_trade-nginx-1
|
||
-> flamy_trade-app-1:3000
|
||
```
|
||
|
||
Compose project name зафиксирован как `flamy_trade`. `container_name` не задаётся, чтобы не ломать стандартное управление Compose.
|
||
|
||
## Переменные
|
||
|
||
Скопируйте `.env.example` в `.env` на сервере и проверьте значения:
|
||
|
||
```bash
|
||
PUBLIC_SITE_URL=https://trade.flamy.studio
|
||
PUBLIC_SITE_NAME=Flamy Trade
|
||
PUBLIC_SITE_DESCRIPTION=Публичная витрина ML-прогнозов для крипторынка.
|
||
PUBLIC_DASHBOARD_DATA_MODE=research-static
|
||
|
||
HOST=0.0.0.0
|
||
PORT=3000
|
||
ORIGIN=https://trade.flamy.studio
|
||
BODY_SIZE_LIMIT=1M
|
||
SHUTDOWN_TIMEOUT=15
|
||
APP_STOP_GRACE_PERIOD=20s
|
||
|
||
APP_IMAGE_REPOSITORY=registry.example.com/flamy-trade
|
||
APP_IMAGE_TAG=<git-sha>
|
||
BUILD_DATE=<utc-iso-date>
|
||
VCS_REF=<git-sha>
|
||
|
||
APP_HOST_IP=10.20.0.20
|
||
APP_PUBLISHED_PORT=18082
|
||
NGINX_IMAGE=nginxinc/nginx-unprivileged:1.30.4-alpine-perl@sha256:36fa38fb8f34faba45e7e1857644d6561f2faf45e7ce6b15e97d1a2a1682a0cd
|
||
```
|
||
|
||
Перед фиксацией порта проверьте, что `18082` свободен:
|
||
|
||
```bash
|
||
sudo ss -lntup | grep ':18082'
|
||
docker ps --format 'table {{.Names}}\t{{.Ports}}'
|
||
```
|
||
|
||
## Проверки перед сборкой
|
||
|
||
```bash
|
||
pnpm install --frozen-lockfile
|
||
pnpm format:check
|
||
pnpm lint
|
||
pnpm check
|
||
pnpm build
|
||
pnpm audit
|
||
docker compose config
|
||
```
|
||
|
||
## Сборка
|
||
|
||
Перед сборкой image задайте provenance:
|
||
|
||
```bash
|
||
export BUILD_DATE="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
||
export VCS_REF="$(git rev-parse HEAD)"
|
||
export APP_IMAGE_TAG="$VCS_REF"
|
||
```
|
||
|
||
Сборка и запуск:
|
||
|
||
```bash
|
||
docker compose build --pull
|
||
docker compose up -d --wait
|
||
docker compose ps
|
||
docker compose logs --tail=200
|
||
```
|
||
|
||
## Caddy
|
||
|
||
Блок для edge-proxy:
|
||
|
||
```caddyfile
|
||
trade.flamy.studio {
|
||
encode zstd gzip
|
||
|
||
reverse_proxy 10.20.0.20:18082 {
|
||
header_up Host {host}
|
||
header_up X-Real-IP {remote_host}
|
||
header_up X-Forwarded-For {remote_host}
|
||
header_up X-Forwarded-Proto {scheme}
|
||
}
|
||
}
|
||
```
|
||
|
||
Перед reload:
|
||
|
||
```bash
|
||
sudo cp -a /etc/caddy/Caddyfile /etc/caddy/Caddyfile.bak.$(date +%F_%H%M%S)
|
||
sudo caddy validate --config /etc/caddy/Caddyfile
|
||
sudo systemctl reload caddy
|
||
```
|
||
|
||
## Smoke-check
|
||
|
||
С Docker-runtime:
|
||
|
||
```bash
|
||
curl -fsS http://10.20.0.20:18082/healthz
|
||
curl -fsSI http://10.20.0.20:18082/
|
||
curl -fsSI http://10.20.0.20:18082/robots.txt
|
||
curl -fsSI http://10.20.0.20:18082/sitemap.xml
|
||
```
|
||
|
||
Снаружи:
|
||
|
||
```bash
|
||
curl -fsSI https://trade.flamy.studio/
|
||
curl -fsS https://trade.flamy.studio/healthz
|
||
curl -fsS https://trade.flamy.studio/robots.txt
|
||
curl -fsS https://trade.flamy.studio/sitemap.xml
|
||
```
|
||
|
||
Маршруты:
|
||
|
||
```text
|
||
/ 200
|
||
/about 200
|
||
/blog 200
|
||
/blog/osnovy-upravleniya-riskami 200
|
||
/dashboard 200
|
||
/healthz 200
|
||
/favicon.svg 200
|
||
/robots.txt 200
|
||
/sitemap.xml 200
|
||
/nesuschestvuyuschiy-url 404
|
||
```
|
||
|
||
Проверка headers:
|
||
|
||
```bash
|
||
curl -fsSI https://trade.flamy.studio/
|
||
curl -fsSI https://trade.flamy.studio/favicon.svg
|
||
curl -fsSI https://trade.flamy.studio/_app/immutable/<real-file>.js
|
||
```
|
||
|
||
Ожидаемо:
|
||
|
||
```text
|
||
X-Content-Type-Options: nosniff
|
||
Referrer-Policy: strict-origin-when-cross-origin
|
||
X-Frame-Options: SAMEORIGIN
|
||
Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=()
|
||
```
|
||
|
||
`Content-Security-Policy` задаётся SvelteKit и ожидается на HTML-ответах приложения.
|
||
|
||
Для `/_app/immutable/*` ожидается:
|
||
|
||
```text
|
||
Cache-Control: public, max-age=31536000, immutable
|
||
```
|
||
|
||
Для HTML:
|
||
|
||
```text
|
||
Cache-Control: no-cache
|
||
```
|
||
|
||
## Rollback
|
||
|
||
Надёжный rollback должен опираться на immutable image tag:
|
||
|
||
```text
|
||
${APP_IMAGE_REPOSITORY}:<git-sha>
|
||
```
|
||
|
||
На production-сервере меняется только `APP_IMAGE_TAG`, затем выполняется:
|
||
|
||
```bash
|
||
docker compose pull app
|
||
docker compose up -d --no-build --wait
|
||
```
|
||
|
||
Если проблема в маршрутизации, восстановите backup Caddyfile и выполните:
|
||
|
||
```bash
|
||
sudo systemctl reload caddy
|
||
```
|
||
|
||
Предыдущий image не удаляется до успешного smoke-check нового релиза.
|