From 3a2ee9d6b651e9328bdee04e32c0d4a829ff9408 Mon Sep 17 00:00:00 2001 From: emelinda Date: Wed, 22 Jul 2026 18:37:24 +0300 Subject: [PATCH] Add init script for Postgres roles/databases, Processing and Measurements services, and update `.env.example`, `docker-compose.yaml`, and Nginx configuration --- .env.example | 26 +++++- docker-compose.yaml | 112 +++++++++++++++++++++++--- nginx/templates/default.conf.template | 23 ++++++ postgres/initdb/00-init.sh | 36 +++++++++ 4 files changed, 184 insertions(+), 13 deletions(-) create mode 100644 postgres/initdb/00-init.sh diff --git a/.env.example b/.env.example index 71ae326..8b290a1 100644 --- a/.env.example +++ b/.env.example @@ -9,6 +9,12 @@ K3S_API_HOST=10.0.0.10 SAREX_POSTGRES_DB=sarex_db SAREX_POSTGRES_USER=sarex SAREX_POSTGRES_PASSWORD=sarex +# Отдельные роли приложений с паролями (создаются init-скриптом postgres). +# Приложения ходят в БД под ними, а не под суперпользователем. Без одинарных кавычек. +SAREX_DJANGO_DB_USER=django +SAREX_DJANGO_DB_PASSWORD=django-secret +SAREX_PROCESSING_DB_USER=processing +SAREX_PROCESSING_DB_PASSWORD=processing-secret SAREX_RABBITMQ_VHOST=api SAREX_RABBITMQ_USER=rabbit SAREX_RABBITMQ_PASSWORD=rabbit @@ -20,6 +26,11 @@ SAREX_MINIO_BUCKET=sarex-media-storage SAREX_MINIO_APP_USER=sarex-app SAREX_MINIO_APP_PASSWORD=sarex-app-secret SAREX_MINIO_APP_POLICY=sarex-app-rw +# Пользователь/бакет MinIO для processing (workflows-api) +SAREX_PROCESSING_MINIO_BUCKET=workflows-storage +SAREX_PROCESSING_MINIO_USER=sarex-processing +SAREX_PROCESSING_MINIO_PASSWORD=sarex-processing-secret +SAREX_PROCESSING_MINIO_POLICY=sarex-processing-rw SAREX_MINIO_REGION=us-east-1 # Внешний URL консоли MinIO (корень minio-домена на верхнем nginx, https) SAREX_MINIO_CONSOLE_URL=https://minio.sarex.local @@ -31,4 +42,17 @@ MINIO_DOMAIN=minio.sarex.local # MinIO: S3 API (/) + консоль (/con # Образы приложения (приватный реестр cr.yandex — нужен docker login) SAREX_BACKEND_IMAGE=cr.yandex/crp3ccidau046kdj8g9q/sarex-backend-dev:latest -SAREX_FRONTEND_IMAGE=cr.yandex/crp3ccidau046kdj8g9q/sarex-frontend-dev:latest \ No newline at end of file +SAREX_FRONTEND_IMAGE=cr.yandex/crp3ccidau046kdj8g9q/sarex-frontend-dev:latest + +# Приложение processing (workflows-api + workflows-frontend) +SAREX_PROCESSING_DB=workflow_db +SAREX_PROCESSING_API_IMAGE=cr.yandex/crp3ccidau046kdj8g9q/workflows-api_prod:075fc0 +SAREX_PROCESSING_FRONTEND_IMAGE=cr.yandex/crp3ccidau046kdj8g9q/workflows-frontend:ugok2_85f6ce2c + +# Сервис measurements (FastAPI). Django-backend ходит в него по MEASUREMENTS_HOST. +SAREX_MEASUREMENTS_IMAGE=cr.yandex/crp3ccidau046kdj8g9q/measurements:production_a5bb3e84 +SAREX_MEASUREMENTS_HOST=http://measurements:8000/ +SAREX_MEASUREMENTS_MINIO_BUCKET=measurements +SAREX_MEASUREMENTS_MINIO_USER=sarex-measurements +SAREX_MEASUREMENTS_MINIO_PASSWORD=sarex-measurements-secret +SAREX_MEASUREMENTS_MINIO_POLICY=sarex-measurements-rw \ No newline at end of file diff --git a/docker-compose.yaml b/docker-compose.yaml index cce1d13..5613082 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -93,13 +93,22 @@ services: container_name: postgres restart: unless-stopped environment: + # суперпользователь (администрирование) POSTGRES_DB: ${SAREX_POSTGRES_DB:-sarex_db} POSTGRES_USER: ${SAREX_POSTGRES_USER:-sarex} POSTGRES_PASSWORD: ${SAREX_POSTGRES_PASSWORD:-sarex} + # роли приложений с паролями — читаются init-скриптом 00-init.sh + DJANGO_DB: ${SAREX_POSTGRES_DB:-sarex_db} + DJANGO_DB_USER: ${SAREX_DJANGO_DB_USER:-django} + DJANGO_DB_PASSWORD: ${SAREX_DJANGO_DB_PASSWORD:-django-secret} + PROC_DB: ${SAREX_PROCESSING_DB:-workflow_db} + PROC_DB_USER: ${SAREX_PROCESSING_DB_USER:-processing} + PROC_DB_PASSWORD: ${SAREX_PROCESSING_DB_PASSWORD:-processing-secret} ports: - "5432:5432" volumes: - sarex-postgres-data:/var/lib/postgresql/data + - ./postgres/initdb:/docker-entrypoint-initdb.d:ro,z # создаёт workflow_db (только на пустом томе) healthcheck: test: ["CMD-SHELL", "pg_isready -U ${SAREX_POSTGRES_USER:-sarex} -d ${SAREX_POSTGRES_DB:-sarex_db}"] interval: 10s @@ -154,8 +163,8 @@ services: volumes: - sarex-minio-data:/data - # Одноразовый init: ждёт MinIO, создаёт бакет и отдельного пользователя - # приложения со scoped-политикой (доступ только к своему бакету). + # Одноразовый init: ждёт MinIO и на каждое приложение заводит бакет + + # отдельного пользователя со scoped-политикой (доступ только к своему бакету). minio-init: image: cr.yandex/crp3ccidau046kdj8g9q/mc:250724 depends_on: @@ -167,22 +176,38 @@ services: until mc alias set local http://minio:9000 "$$MINIO_ROOT_USER" "$$MINIO_ROOT_PASSWORD"; do echo 'waiting for minio...'; sleep 2; done - mc mb -p "local/$$MINIO_BUCKET" - # Пользователь приложения (не root) с доступом только к своему бакету - mc admin user add local "$$MINIO_APP_USER" "$$MINIO_APP_PASSWORD" - printf '{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":["s3:*"],"Resource":["arn:aws:s3:::%s","arn:aws:s3:::%s/*"]}]}' \ - "$$MINIO_BUCKET" "$$MINIO_BUCKET" > /tmp/app-policy.json - mc admin policy create local "$$MINIO_APP_POLICY" /tmp/app-policy.json || true - mc admin policy attach local "$$MINIO_APP_POLICY" --user "$$MINIO_APP_USER" || true - echo "bucket $$MINIO_BUCKET + user $$MINIO_APP_USER ready" + # provision + provision() { + mc mb -p "local/$$1" + mc admin user add local "$$2" "$$3" + printf '{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":["s3:*"],"Resource":["arn:aws:s3:::%s","arn:aws:s3:::%s/*"]}]}' "$$1" "$$1" > /tmp/policy.json + mc admin policy create local "$$4" /tmp/policy.json || true + mc admin policy attach local "$$4" --user "$$2" || true + echo "bucket $$1 + user $$2 ready" + } + + provision "$$MINIO_BUCKET" "$$MINIO_APP_USER" "$$MINIO_APP_PASSWORD" "$$MINIO_APP_POLICY" + provision "$$PROC_MINIO_BUCKET" "$$PROC_MINIO_USER" "$$PROC_MINIO_PASSWORD" "$$PROC_MINIO_POLICY" + provision "$$MEAS_MINIO_BUCKET" "$$MEAS_MINIO_USER" "$$MEAS_MINIO_PASSWORD" "$$MEAS_MINIO_POLICY" environment: MINIO_ROOT_USER: ${SAREX_MINIO_ROOT_USER:-minioadmin} MINIO_ROOT_PASSWORD: ${SAREX_MINIO_ROOT_PASSWORD:-minioadmin} + # django MINIO_BUCKET: ${SAREX_MINIO_BUCKET:-sarex-media-storage} MINIO_APP_USER: ${SAREX_MINIO_APP_USER:-sarex-app} MINIO_APP_PASSWORD: ${SAREX_MINIO_APP_PASSWORD:-sarex-app-secret} MINIO_APP_POLICY: ${SAREX_MINIO_APP_POLICY:-sarex-app-rw} + # processing + PROC_MINIO_BUCKET: ${SAREX_PROCESSING_MINIO_BUCKET:-workflows-storage} + PROC_MINIO_USER: ${SAREX_PROCESSING_MINIO_USER:-sarex-processing} + PROC_MINIO_PASSWORD: ${SAREX_PROCESSING_MINIO_PASSWORD:-sarex-processing-secret} + PROC_MINIO_POLICY: ${SAREX_PROCESSING_MINIO_POLICY:-sarex-processing-rw} + # measurements + MEAS_MINIO_BUCKET: ${SAREX_MEASUREMENTS_MINIO_BUCKET:-measurements} + MEAS_MINIO_USER: ${SAREX_MEASUREMENTS_MINIO_USER:-sarex-measurements} + MEAS_MINIO_PASSWORD: ${SAREX_MEASUREMENTS_MINIO_PASSWORD:-sarex-measurements-secret} + MEAS_MINIO_POLICY: ${SAREX_MEASUREMENTS_MINIO_POLICY:-sarex-measurements-rw} restart: "no" # --------------------------------------------------------------------------- @@ -204,8 +229,8 @@ services: JWT_PUBLIC_KEY: "JPK" DJANGO_POSTGRES_HOST: postgres DJANGO_POSTGRES_DATABASE: ${SAREX_POSTGRES_DB:-sarex_db} - DJANGO_POSTGRES_USER: ${SAREX_POSTGRES_USER:-sarex} - DJANGO_POSTGRES_PASSWORD: ${SAREX_POSTGRES_PASSWORD:-sarex} + DJANGO_POSTGRES_USER: ${SAREX_DJANGO_DB_USER:-django} + DJANGO_POSTGRES_PASSWORD: ${SAREX_DJANGO_DB_PASSWORD:-django-secret} DJANGO_REDIS_HOST: redis DJANGO_RABBIT_HOSTNAME: rabbitmq DJANGO_RABBIT_USER: ${SAREX_RABBITMQ_USER:-rabbit} @@ -218,6 +243,8 @@ services: S3_PASSWORD: ${SAREX_MINIO_APP_PASSWORD:-sarex-app-secret} S3_BUCKET: ${SAREX_MINIO_BUCKET:-sarex-media-storage} S3_REGION: ${SAREX_MINIO_REGION:-us-east-1} + # Сервис measurements (MeasurementSettings, env_prefix MEASUREMENTS_) + MEASUREMENTS_HOST: ${SAREX_MEASUREMENTS_HOST:-http://measurements:8000/} tty: true ports: - "8000:8000" # HTTP API (uWSGI), migrate выполняется в entrypoint @@ -257,6 +284,65 @@ services: depends_on: - backend + # --------------------------------------------------------------------------- + # Приложение processing (workflows-api + workflows-frontend). + # Образы приватные (cr.yandex). БД — отдельная workflow_db в общем postgres. + # Версии/env взяты из apps/processing/base/*.yaml. + # --------------------------------------------------------------------------- + processing-api: + image: ${SAREX_PROCESSING_API_IMAGE:-cr.yandex/crp3ccidau046kdj8g9q/workflows-api_prod:075fc0} + container_name: processing-api + restart: unless-stopped + entrypoint: ["/httpserver"] + command: ["migrate"] # в манифесте: /httpserver migrate (миграции + сервер на :8080) + environment: + HTTP_HOST: 0.0.0.0:8080 + POSTGRES_ADDRESS: postgres + POSTGRES_PORT: "5432" + POSTGRES_DB: ${SAREX_PROCESSING_DB:-workflow_db} + POSTGRES_USER: ${SAREX_PROCESSING_DB_USER:-processing} + POSTGRES_PASSWORD: ${SAREX_PROCESSING_DB_PASSWORD:-processing-secret} + POSTGRES_POOL_SIZE: "3" + POSTGRES_SSL_USE: "0" + ENABLE_SQL_QUERY: "0" + DJANGO_HOST: http://backend:8000 # django-backend в этом же compose + PUBLIC_KEY: "JPK" # JWT public key (заглушка, как у backend) + ports: + - "8081:8080" + depends_on: + postgres: + condition: service_healthy + + processing-frontend: + image: ${SAREX_PROCESSING_FRONTEND_IMAGE:-cr.yandex/crp3ccidau046kdj8g9q/workflows-frontend:ugok2_85f6ce2c} + container_name: processing-frontend + restart: unless-stopped + ports: + - "8082:8080" # web UI (контейнер слушает 8080) + depends_on: + - processing-api + + # --------------------------------------------------------------------------- + # measurements — FastAPI, считает измерения по растрам из S3/MinIO (GDAL vsis3). + # БД/воркера нет. Обязателен S3_JSON_SETTINGS. Источник: apps/measurements/base. + # --------------------------------------------------------------------------- + measurements: + image: ${SAREX_MEASUREMENTS_IMAGE:-cr.yandex/crp3ccidau046kdj8g9q/measurements:production_a5bb3e84} + container_name: measurements + restart: unless-stopped + entrypoint: ["/opt/entrypoint.sh"] + environment: + # host http:// → GDAL переключается на AWS_HTTPS=NO (см. CONFIGURATION.md) + S3_JSON_SETTINGS: '{"host":"http://minio:9000","login":"${SAREX_MEASUREMENTS_MINIO_USER:-sarex-measurements}","password":"${SAREX_MEASUREMENTS_MINIO_PASSWORD:-sarex-measurements-secret}","verify":false,"buckets":["${SAREX_MEASUREMENTS_MINIO_BUCKET:-measurements}"]}' + LOG_LEVEL: INFO + TRACING_USE: "false" + AUTH: "0" + DJANGO_HOST: http://backend:8000 + # порт наружу не публикуем — доступ только из backend по http://measurements:8000/ + depends_on: + minio-init: + condition: service_completed_successfully + # Верхний reverse-proxy: 3 домена → платформа / rabbitmq (admin) / minio nginx: image: nginx:1.27-alpine @@ -278,6 +364,8 @@ services: # - frontend - minio - rabbitmq + - processing-api + - processing-frontend healthcheck: test: ["CMD", "wget", "-qO-", "http://localhost/healthz"] interval: 10s diff --git a/nginx/templates/default.conf.template b/nginx/templates/default.conf.template index fc0cb04..364cac0 100644 --- a/nginx/templates/default.conf.template +++ b/nginx/templates/default.conf.template @@ -31,6 +31,8 @@ map $http_upgrade $connection_upgrade { upstream minio_s3 { server minio:9000; } upstream minio_console { server minio:9001; } upstream rabbitmq_mgmt { server rabbitmq:15672; } +upstream processing_api { server processing-api:8080; } +upstream processing_frontend { server processing-frontend:8080; } # Общие проксирующие заголовки. ВАЖНО: если в location задан свой proxy_set_header, # эти НЕ наследуются — там заголовки продублированы явно. @@ -55,6 +57,27 @@ server { # location /api/ { proxy_pass http://backend; } # location /admin/ { proxy_pass http://backend; } + # --- processing (workflows) под /workflows --- + # Префикс /workflows срезаем: api ждёт пути /api/v1/..., фронт — от корня. + location = /workflows { return 301 /workflows/; } + + location /workflows/api/ { + rewrite ^/workflows/(.*)$ /$1 break; + proxy_pass http://processing_api; + } + + location /workflows/ { + rewrite ^/workflows/(.*)$ /$1 break; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + proxy_pass http://processing_frontend; + } + # S3-объекты (media) по относительному пути, с поддержкой Range (TODO: в работе) # location /sarex-media-storage/ { # proxy_set_header Host $host; diff --git a/postgres/initdb/00-init.sh b/postgres/initdb/00-init.sh new file mode 100644 index 0000000..e4b4a9a --- /dev/null +++ b/postgres/initdb/00-init.sh @@ -0,0 +1,36 @@ +#!/bin/bash +# Инициализация БД sarex-стека: базы + отдельные роли приложений с паролями. +# Запускается автоматически ТОЛЬКО при первой инициализации пустого тома postgres +# (каталог /docker-entrypoint-initdb.d). Значения берутся из окружения сервиса +# postgres (см. docker-compose.yaml). Для уже существующего тома примени вручную +# те же команды (см. README) — иначе приложения не залогинятся под новыми ролями. +# +# ВНИМАНИЕ: пароли подставляются в SQL как есть — не используй одинарные кавычки в них. +set -e + +psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <