iac/docker-compose.yaml

499 lines
22 KiB
YAML
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.

services:
k3s-server:
image: ${K3S_IMAGE:-rancher/k3s:v1.36.2-k3s1}
container_name: k3s-server
command:
- server
- --tls-san=127.0.0.1
- --tls-san=k3s-server
- --tls-san=${K3S_API_HOST:-127.0.0.1} # внешний IP/домен для доступа к apiserver извне
- --disable=traefik
privileged: true
cgroup: host # kubelet в cgroup v2 (Ubuntu 22.04+/RedOS)
restart: unless-stopped
environment:
K3S_TOKEN: ${K3S_TOKEN:-supersecrettoken}
K3S_KUBECONFIG_OUTPUT: /output/kubeconfig.yaml
K3S_KUBECONFIG_MODE: "644"
#ports:
# - "6443:6443" # kube-apiserver — доступен извне; ОБЯЗАТЕЛЬНО ограничь фаерволом до доверенных IP
# - "80:80" # ingress http — задействуются после установки ingress-контроллера
# - "443:443" # ingress https
volumes:
- k3s-server-data:/var/lib/rancher/k3s # etcd/state, образы, манифесты — только named volume
- ./k3s:/output:z # отсюда забираем контекст k8s (:z — SELinux label для RedOS)
tmpfs:
- /run
- /var/run
ulimits:
nproc: 65535
nofile:
soft: 65535
hard: 65535
healthcheck:
test: ["CMD", "kubectl", "get", "--raw=/readyz"]
interval: 10s
timeout: 5s
retries: 30
start_period: 30s
k3s-worker:
image: ${K3S_IMAGE:-rancher/k3s:v1.36.2-k3s1}
container_name: k3s-worker
command:
- agent
privileged: true
cgroup: host # kubelet в cgroup v2 (Ubuntu 22.04+/RedOS)
restart: unless-stopped
depends_on:
k3s-server:
condition: service_healthy
environment:
K3S_URL: https://k3s-server:6443
K3S_TOKEN: ${K3S_TOKEN:-supersecrettoken}
volumes:
- k3s-worker-data:/var/lib/rancher/k3s
tmpfs:
- /run
- /var/run
ulimits:
nproc: 65535
nofile:
soft: 65535
hard: 65535
gitea:
image: ${GITEA_IMAGE:-gitea/gitea:1.22}
container_name: gitea
restart: unless-stopped
environment:
USER_UID: 1000
USER_GID: 1000
GITEA__server__ROOT_URL: ${GITEA_ROOT_URL:-http://localhost:3000/}
GITEA__server__SSH_PORT: ${GITEA_SSH_PORT:-2222}
ports:
- "3000:3000" # web UI (80/443 заняты k3s)
- "${GITEA_SSH_PORT:-2222}:22" # git over ssh
volumes:
- ./gitea-data:/data:z # :z — SELinux label для RedOS
healthcheck:
test: ["CMD", "curl", "-fsS", "http://localhost:3000/api/healthz"]
interval: 10s
timeout: 5s
retries: 12
start_period: 20s
# ---------------------------------------------------------------------------
# Инфраструктурные зависимости apps/django (sarex-backend).
# Django деплоится в k3s и ходит сюда — про связность k3s→compose см. README.
# Креды/версии взяты из sarex-backend/docker-compose.yaml и apps/django/.env.example.
# ---------------------------------------------------------------------------
postgres:
image: ${POSTGRES_IMAGE:-postgres:12-alpine}
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}
volumes:
- sarex-postgres-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${SAREX_POSTGRES_USER:-sarex} -d ${SAREX_POSTGRES_DB:-sarex_db}"]
interval: 10s
timeout: 5s
retries: 12
start_period: 10s
# Одноразовый init: на каждое приложение заводит роль (LOGIN+пароль) и базу
# данных с этим владельцем. Идемпотентно (по образцу minio-init).
postgres-init:
image: ${POSTGRES_IMAGE:-postgres:12-alpine}
depends_on:
postgres:
condition: service_healthy
entrypoint:
- /bin/sh
- -c
- |
set -e
until psql -h postgres -U "$$SUPER_USER" -d "$$SUPER_DB" -c 'SELECT 1' >/dev/null 2>&1; do
echo 'waiting for postgres...'; sleep 2;
done
q() { psql -v ON_ERROR_STOP=1 -h postgres -U "$$SUPER_USER" -d "$$SUPER_DB" "$$@"; }
# provision <db> <user> <password>
provision() {
q -tc "SELECT 1 FROM pg_roles WHERE rolname='$$2'" | grep -q 1 \
|| q -c "CREATE ROLE \"$$2\" LOGIN PASSWORD '$$3'"
q -c "ALTER ROLE \"$$2\" WITH LOGIN PASSWORD '$$3'"
q -tc "SELECT 1 FROM pg_database WHERE datname='$$1'" | grep -q 1 \
|| q -c "CREATE DATABASE \"$$1\" OWNER \"$$2\""
q -c "ALTER DATABASE \"$$1\" OWNER TO \"$$2\""
q -c "GRANT ALL PRIVILEGES ON DATABASE \"$$1\" TO \"$$2\""
echo "db $$1 + user $$2 ready"
}
provision "$$DJANGO_DB" "$$DJANGO_USER" "$$DJANGO_PASSWORD"
provision "$$PROC_DB" "$$PROC_USER" "$$PROC_PASSWORD"
provision "$$BIM_DB" "$$BIM_USER" "$$BIM_PASSWORD"
provision "$$WORKSPACE_DB" "$$WORKSPACE_USER" "$$WORKSPACE_PASSWORD"
# Расширения PostgreSQL в базе django: CREATE EXTENSION может только
# суперпользователь. Django-миграции (CreateExtension) идут с IF NOT
# EXISTS → после этого станут no-op и не упадут на правах.
for ext in $$DJANGO_EXTENSIONS; do
q -c "CREATE EXTENSION IF NOT EXISTS \"$$ext\"" && echo "extension $$ext ready in $$DJANGO_DB"
done
environment:
PGPASSWORD: ${SAREX_POSTGRES_PASSWORD:-sarex}
SUPER_USER: ${SAREX_POSTGRES_USER:-sarex}
SUPER_DB: ${SAREX_POSTGRES_DB:-sarex_db}
# django (использует основную базу SAREX_POSTGRES_DB)
DJANGO_DB: ${SAREX_POSTGRES_DB:-sarex_db}
DJANGO_USER: ${SAREX_DJANGO_DB_USER:-django}
DJANGO_PASSWORD: ${SAREX_DJANGO_DB_PASSWORD:-django-secret}
DJANGO_EXTENSIONS: ${SAREX_DJANGO_DB_EXTENSIONS:-ltree}
# processing
PROC_DB: ${SAREX_PROCESSING_DB:-workflow_db}
PROC_USER: ${SAREX_PROCESSING_DB_USER:-processing}
PROC_PASSWORD: ${SAREX_PROCESSING_DB_PASSWORD:-processing-secret}
# bim
BIM_DB: ${SAREX_BIM_DB:-bim_db}
BIM_USER: ${SAREX_BIM_DB_USER:-bim}
BIM_PASSWORD: ${SAREX_BIM_DB_PASSWORD:-bim-secret}
# workspace
WORKSPACE_DB: ${SAREX_WORKSPACE_DB:-workspace_db}
WORKSPACE_USER: ${SAREX_WORKSPACE_DB_USER:-workspace}
WORKSPACE_PASSWORD: ${SAREX_WORKSPACE_DB_PASSWORD:-workspace-secret}
restart: "no"
redis:
image: ${SAREX_REDIS_IMAGE:-cr.yandex/crp3ccidau046kdj8g9q/redis:7.0.10}
container_name: redis
restart: unless-stopped
# protected-mode no: redis только во внутренней docker-сети (порт наружу не
# публикуется), иначе бэкенд/celery из других контейнеров отбиваются "DENIED"
command: ["redis-server", "--save", "60", "1", "--loglevel", "warning", "--protected-mode", "no", "--stop-writes-on-bgsave-error", "no"]
volumes:
- sarex-redis-data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 12
start_period: 5s
rabbitmq:
image: ${SAREX_RABBITMQ_IMAGE:-cr.yandex/crp3ccidau046kdj8g9q/rabbimtq:3.11.23}
container_name: rabbitmq
restart: unless-stopped
environment:
RABBITMQ_VHOST: ${SAREX_RABBITMQ_VHOST:-api}
RABBITMQ_USERNAME: ${SAREX_RABBITMQ_USER:-rabbit}
RABBITMQ_PASSWORD: ${SAREX_RABBITMQ_PASSWORD:-rabbit}
RABBITMQ_DISK_FREE_ABSOLUTE_LIMIT: 200MB
volumes:
- sarex-rabbitmq-data:/bitnami/rabbitmq/mnesia
healthcheck:
test: ["CMD", "rabbitmq-diagnostics", "-q", "ping"]
interval: 15s
timeout: 10s
retries: 12
start_period: 30s
minio:
image: ${SAREX_MINIO_IMAGE:-cr.yandex/crp3ccidau046kdj8g9q/minio:250724}
container_name: minio
restart: unless-stopped
command: ["server", "/data", "--console-address", ":9001"]
environment:
MINIO_ROOT_USER: ${SAREX_MINIO_ROOT_USER:-minioadmin}
MINIO_ROOT_PASSWORD: ${SAREX_MINIO_ROOT_PASSWORD:-minioadmin}
# Консоль отдаётся через верхний nginx с корня minio-домена
MINIO_BROWSER_REDIRECT_URL: ${SAREX_MINIO_CONSOLE_URL:-https://minio.sarex.local}
volumes:
- sarex-minio-data:/data
# Одноразовый init: ждёт MinIO и на каждое приложение заводит бакет +
# отдельного пользователя со scoped-политикой (доступ только к своему бакету).
minio-init:
image: ${SAREX_MC_IMAGE:-cr.yandex/crp3ccidau046kdj8g9q/mc:250724}
depends_on:
- minio
entrypoint:
- /bin/sh
- -c
- |
until mc alias set local http://minio:9000 "$$MINIO_ROOT_USER" "$$MINIO_ROOT_PASSWORD"; do
echo 'waiting for minio...'; sleep 2;
done
# provision <bucket> <user> <password> <policy>
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"
}
# Единый бакет на все приложения (processing/measurements тоже сюда)
provision "$$MINIO_BUCKET" "$$MINIO_APP_USER" "$$MINIO_APP_PASSWORD" "$$MINIO_APP_POLICY"
environment:
MINIO_ROOT_USER: ${SAREX_MINIO_ROOT_USER:-minioadmin}
MINIO_ROOT_PASSWORD: ${SAREX_MINIO_ROOT_PASSWORD:-minioadmin}
# единый бакет/пользователь приложения
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}
restart: "no"
# ---------------------------------------------------------------------------
# Приложение sarex (backend + celery + frontend).
# Образы приватные (cr.yandex) — нужен `docker login cr.yandex`.
# Для локальной сборки backend раскомментируй build (репозиторий — соседний).
# ---------------------------------------------------------------------------
backend:
image: ${SAREX_BACKEND_IMAGE:-cr.yandex/crp3ccidau046kdj8g9q/backend:production_80d1d847}
# build:
# context: ../../generic/sarex-backend
# dockerfile: Dockerfile
container_name: sarex-backend
restart: unless-stopped
environment: &sarex-backend-env
ALLOWED_HOSTS: "*"
DJANGO_SETTINGS_MODULE: config.settings.production
JWT_PRIVATE_KEY: ${SAREX_JWT_PRIVATE_KEY:-JWT}
JWT_PUBLIC_KEY: ${SAREX_JWT_PUBLIC_KEY:-JPK}
DJANGO_POSTGRES_HOST: postgres
DJANGO_POSTGRES_DATABASE: ${SAREX_POSTGRES_DB:-sarex_db}
DJANGO_POSTGRES_USER: ${SAREX_DJANGO_DB_USER:-django}
DJANGO_POSTGRES_PASSWORD: ${SAREX_DJANGO_DB_PASSWORD:-django-secret}
# celery: брокер rabbitmq + result backend redis (pydantic env_prefix CELERY_*)
CELERY_RABBITMQ_HOST: rabbitmq
CELERY_RABBITMQ_USER: ${SAREX_RABBITMQ_USER:-rabbit}
CELERY_RABBITMQ_PASSWORD: ${SAREX_RABBITMQ_PASSWORD:-rabbit}
CELERY_RABBITMQ_VHOST: ${SAREX_RABBITMQ_VHOST:-api}
CELERY_REDIS_HOST: redis
# S3 / объектное хранилище → MinIO (сервис minio)
S3_HOST: http://minio:9000
AWS_S3_ENDPOINT_URL: http://minio:9000
S3_LOGIN: ${SAREX_MINIO_APP_USER:-sarex-app}
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/}
# Домен платформы. HOST/SERVER_HOST → абсолютные URL, включая media
# (CustomS3Boto3Storage.url = HOST + /media/ + key). Без этого base.py
# берёт дефолт lk.sarex.io и media-ссылки уходят на чужой хост.
SERVER_HOST: https://${PLATFORM_DOMAIN:-sarex.local}
SERVER_API_HOST: https://${PLATFORM_DOMAIN:-sarex.local}
# Media: django-storage (S3). Backend отдаёт /media/<key>, верхний nginx
# проксирует их в s3-proxy (CORS+Range). Из apps/django/base/backend-deployment.yaml.
SERVER_USE_DJANGO_STORAGE: "1"
SERVER_DJANGO_URLS: "1"
tty: true
ports:
- "8000:8000" # HTTP API (uWSGI http-socket), migrate выполняется в entrypoint
volumes:
# свой uwsgi.ini поверх образного: http-socket + 4 воркера (:z — SELinux/RedOS)
- ./backend/uwsgi.ini:/opt/sarex/uwsgi.ini:ro,z
# production.py с включённой классической JWT-аутентификацией
- ./backend/production.py:/opt/sarex/config/settings/production.py:ro,z
depends_on:
postgres:
condition: service_healthy
postgres-init:
condition: service_completed_successfully
redis:
condition: service_healthy
rabbitmq:
condition: service_healthy
minio-init:
condition: service_completed_successfully
celery:
image: ${SAREX_BACKEND_IMAGE:-cr.yandex/crp3ccidau046kdj8g9q/backend:production_80d1d847}
container_name: sarex-celery
restart: unless-stopped
entrypoint: celery -A config worker -l info -E -Q default -n default_worker.%h --concurrency=1 -B
environment:
<<: *sarex-backend-env
volumes:
- ./backend/production.py:/opt/sarex/config/settings/production.py:ro,z
depends_on:
postgres:
condition: service_healthy
postgres-init:
condition: service_completed_successfully
redis:
condition: service_healthy
rabbitmq:
condition: service_healthy
minio-init:
condition: service_completed_successfully
frontend:
image: ${SAREX_FRONTEND_IMAGE:-cr.yandex/crp3ccidau046kdj8g9q/frontend:contour_42138486}
container_name: sarex-frontend
restart: unless-stopped
ports:
- "8080:80" # web UI
depends_on:
- backend
# ---------------------------------------------------------------------------
# s3-proxy — отдаёт объекты MinIO/S3 браузеру с CORS и поддержкой Range (media).
# В k8s креды приходят из Vault в /vault/secrets/s3; здесь Vault нет, поэтому
# entrypoint из манифеста gracefully пропускает отсутствующий файл, а
# AWS_*-переменные подставляем напрямую (MinIO app-пользователь + бакет).
# Источник: apps/django/base/s3-proxy.yaml.
# ---------------------------------------------------------------------------
s3-proxy:
image: ${SAREX_S3_PROXY_IMAGE:-cr.yandex/crp3ccidau046kdj8g9q/s3-proxy:vault_6e586217}
container_name: s3-proxy
restart: unless-stopped
entrypoint: ["/bin/sh", "-ec"]
command:
- |
set -a
[ -f /vault/secrets/s3 ] && . /vault/secrets/s3
set +a
exec /s3-proxy
environment:
ACCESS_LOG: "true"
CORS_ALLOW_HEADERS: "Content-Type, Accept-Ranges, Content-Range, Content-Encoding"
# Замена Vault-секрета /vault/secrets/s3 прямыми значениями:
AWS_API_ENDPOINT: http://minio:9000
AWS_S3_BUCKET: ${SAREX_MINIO_BUCKET:-sarex-media-storage}
AWS_ACCESS_KEY_ID: ${SAREX_MINIO_APP_USER:-sarex-app}
AWS_SECRET_ACCESS_KEY: ${SAREX_MINIO_APP_PASSWORD:-sarex-app-secret}
# Порт наружу не публикуем — доступ только через верхний nginx (/media/),
# как ClusterIP в k8s. Внутренняя docker-сеть резолвит s3-proxy:80.
depends_on:
minio-init:
condition: service_completed_successfully
# ---------------------------------------------------------------------------
# Приложение 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}
# Локальная сборка (Go, cmd/httpserver). Исходник — на dev-машине:
# build:
# context: /mnt/c/Users/user/PycharmProjects/platform/workflows-api
# dockerfile: Dockerfile
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: ${SAREX_JWT_PUBLIC_KEY:-JPK} # JWT public key (общий с backend)
# Порт наружу не публикуем — доступ через верхний nginx (/workflows/api/).
depends_on:
postgres:
condition: service_healthy
postgres-init:
condition: service_completed_successfully
processing-frontend:
image: ${SAREX_PROCESSING_FRONTEND_IMAGE:-cr.yandex/crp3ccidau046kdj8g9q/workflows-frontend:ugok2_85f6ce2c}
# Локальная сборка (node, npm run build-module). Исходник — на dev-машине.
# Нужны build-args BUILD_ENV=contour и NPM_NEXUS_TOKEN (приватный npm).
# build:
# context: /mnt/c/Users/user/WebstormProjects/platform/workflows-frontend
# dockerfile: Dockerfile
# args:
# BUILD_ENV: contour
# NPM_NEXUS_TOKEN: ${NPM_NEXUS_TOKEN:-}
container_name: processing-frontend
restart: unless-stopped
# Порт наружу не публикуем — доступ через верхний nginx (/workflows/).
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_MINIO_APP_USER:-sarex-app}","password":"${SAREX_MINIO_APP_PASSWORD:-sarex-app-secret}","verify":false,"buckets":["${SAREX_MINIO_BUCKET:-sarex-media-storage}"]}'
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_IMAGE:-nginx:1.27-alpine}
container_name: sarex-nginx
restart: unless-stopped
ports:
- "80:80" # http
- "443:443" # https (самоподписанный сертификат)
environment:
# подставляются envsubst'ом в шаблон при старте контейнера
PLATFORM_DOMAIN: ${PLATFORM_DOMAIN:-sarex.local}
ADMIN_DOMAIN: ${ADMIN_DOMAIN:-admin.sarex.local}
MINIO_DOMAIN: ${MINIO_DOMAIN:-minio.sarex.local}
volumes:
- ./nginx/templates:/etc/nginx/templates:ro,z # :z — SELinux label для RedOS
- ./nginx/certs:/etc/nginx/certs:ro,z # TLS-сертификаты
depends_on:
# - backend # TODO: закомментированы в шаблоне nginx, пока в работе
# - frontend
- minio
- rabbitmq
- s3-proxy
- processing-api
- processing-frontend
healthcheck:
test: ["CMD", "wget", "-qO-", "http://127.0.0.1/healthz"]
interval: 10s
timeout: 5s
retries: 6
start_period: 10s
volumes:
k3s-server-data:
k3s-worker-data:
gitea-data:
sarex-postgres-data:
sarex-redis-data:
sarex-rabbitmq-data:
sarex-minio-data: