diff --git a/docs/superpowers/specs/2026-07-30-processing-engine-k3s-scheduling-design.md b/docs/superpowers/specs/2026-07-30-processing-engine-k3s-scheduling-design.md new file mode 100644 index 0000000..7d85d9d --- /dev/null +++ b/docs/superpowers/specs/2026-07-30-processing-engine-k3s-scheduling-design.md @@ -0,0 +1,155 @@ +# Дизайн: workflows-engine в контуре через k3s (планирование Job'ов) + +Дата: 2026-07-30 +Ветка: `aero` +Статус: согласован, готов к плану реализации + +## Цель и рамки + +Добавить в контур **workflows-engine** — фоновый оркестратор processing-подсистемы, +который вычитывает workflow из `workflow_db` и запускает задачи как **Kubernetes Job'ы**. + +**Успех = планирование Job'ов:** engine стартует, подключается к `workflow_db`, +получает доступ к kube-API k3s и создаёт Job-объекты в namespace `processing`; +Job-поды планируются на ноду и достукиваются до `postgres`/`minio` из compose-сети. + +**Вне рамок (принятые ограничения):** +- Полное end-to-end выполнение конвертаций НЕ гарантируется: engine тянет множество + downstream-сервисов (documentations/PDM, resources, bim-api-v2, workspace-api, + issue-api, comparisons, SMTP/Mailgun), которых в контуре нет. Все их `ENABLE_*` + выключаем; включаем только `ENABLE_S3_STORAGE`. +- Валидный S3 service-account для рантайма джоб не требуется для *планирования* + (S3 нужен на этапе выполнения задачи, не создания Job-объекта) — допускается плейсхолдер. +- Только одна нода k3s (`k3s-server`), без `k3s-worker`. + +## Ключевые находки (обоснование дизайна) + +Источники: `apps/processing/base/{api,engine,engine-low}.yaml`, +`apps/processing/workflows-engine.CONFIGURATION.md`. + +1. **Джобы НЕ ходят в `workflow_db`.** В списке env, прокидываемых engine в Job-поды + (`pkg/kube_services/services.go`, CONFIGURATION.md §«…пробрасываемые в под'ы задач»), + `POSTGRES_*` отсутствует. В `workflow_db` ходит только сам engine. Джобам достаётся + S3 (через yc-s3 SA JSON при `ENABLE_S3_STORAGE`) и опционально JSON-конфиги прикладных + БД (bim/workspace/…) — только при соответствующих `ENABLE_*`. +2. **Доступ compose→k3s штатный.** У engine есть `KUBE_CONFIG` (путь; пусто = in-cluster, + задан = out-of-cluster), `KUBE_CONTEXT`, `KUBE_ADDR` (адрес apiserver + InsecureSkipTLSVerify), + `JOBS_NAMESPACE`. RBAC/ServiceAccount не нужны — аутентификация кредами из kubeconfig. +3. **RabbitMQ не нужен.** `pkg/rabbitmq` используется только при `ENABLE_AMQP_EXECUTOR=1`. + При K8s-исполнителе брокер не дёргается. +4. **`postgres:12-alpine`** генерит `pg_hba` как `host all all all md5` → пускает с любого + адреса по паролю. SNAT-источник (IP ноды k3s) не блокируется — нужен лишь верный + `processing`/пароль. +5. **`k3s-server` в compose уже готов:** `privileged`, `cgroup: host`, `--disable=traefik`, + kubeconfig пишется в `./k3s/kubeconfig.yaml`, в общей (default) compose-сети с `postgres`/`minio`. + +## Архитектура + +Три части, все в существующем `iac/docker-compose.yaml` + новые k8s-манифесты. + +### A. engine — сервис docker-compose + +Образ: `${SAREX_WORKFLOWS_ENGINE_IMAGE:-cr.yandex/crp3ccidau046kdj8g9q/workflows-endigne_prod:075fc0}`. + +Ключевой env: +- Исполнители: `ENABLE_KUBERNETES_EXECUTOR=1`, `ENABLE_AMQP_EXECUTOR=0`. +- Воркеры: `COUNT_RUNNING_WORKERS=1`, `COUNT_CANCELING_WORKERS=1`, `COUNT_HANDLE_JOB_WORKERS=1` + (иначе задачи не разбираются). +- Доступ в k3s: `KUBE_CONFIG=/kube/config`, `KUBE_CONTEXT=default`, + `KUBE_ADDR=https://k3s-server:6443`, `JOBS_NAMESPACE=processing`. + (`--tls-san=k3s-server` уже задан у k3s-server; при использовании `KUBE_ADDR` TLS + проверка отключается, креды берутся из смонтированного kubeconfig.) +- Свой доступ к БД: `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}`, `POSTGRES_SSL_USE=0`. +- Хранилища: `ENABLE_S3_STORAGE=1`, `S3_SERVICE_ACCOUNT=/etc/sarex/yc-s3/yc-s3-service-account.json` + (JSON указывает на `http://minio:9000` — прямой доступ к S3, мимо s3-proxy). + Остальные `ENABLE_*` = 0. +- Прочее по умолчанию: `WORKFLOW_PRIORITY=low`, `MAX_WORKFLOWS_LIMIT=5`, `MAX_RUNNING_JOBS` + дефолт, ресурсные `DEFAULT_*_REQUESTS`. + +Монтирования: +- `./k3s/kubeconfig.yaml:/kube/config:ro,z` — kubeconfig из k3s-server. +- yc-s3 SA JSON (плейсхолдер/рабочий) → `/etc/sarex/yc-s3/yc-s3-service-account.json`. + +`depends_on`: +- `postgres` (service_healthy), `postgres-init` (service_completed_successfully), +- `k3s-server` (service_healthy), +- `processing-k8s-init` (service_completed_successfully) — DNS-мост применён до старта engine. + +### B. k3s — только сервер + +Поднимаем существующий `k3s-server` (несёт kubelet → поды планируются на него). +`k3s-worker` НЕ поднимаем. Добавляем `k3s-server` (и `processing-k8s-init`, `engine`) +в список поднимаемых сервисов ансибла. + +### C. DNS-мост `postgres` + `minio` в k3s + +Чтобы Job-поды резолвили те же имена, что и в compose: +- Namespace `processing`. +- `Service` (ClusterIP, без селектора) `postgres` и `minio` + ручные `Endpoints`, + указывающие на статические IP compose-контейнеров. +- Бареме-имена `postgres`/`minio` резолвятся в подах namespace `processing` через + search-domain `…processing.svc.cluster.local`. + +Применение — одноразовый helper-контейнер **`processing-k8s-init`** (по образцу `postgres-init`): +образ с `kubectl`, монтирует `./k3s/kubeconfig.yaml` и каталог манифестов +(`./k3s/manifests/processing/`), выполняет `kubectl --kubeconfig … apply -f`. +Идемпотентно, `restart: "no"`. Ждёт `k3s-server` (service_healthy). + +Манифесты (новый каталог `iac/k3s/manifests/processing/`): +- `namespace.yaml` — namespace `processing`. +- `bridge-postgres.yaml` — Service+Endpoints `postgres` → `172.28.0.10:5432`. +- `bridge-minio.yaml` — Service+Endpoints `minio` → `172.28.0.11:9000`. + +## Сеть и стабильность адресов + +Решение: **статические IP в compose** (прочтение «адреса как в docker-compose»). + +- Верхнеуровневой `networks.default` задаём IPAM с фиксированным subnet, напр. `172.28.0.0/16`. +- `postgres` → `172.28.0.10`, `minio` → `172.28.0.11` (пиним только их; остальным IP динамический из того же subnet — их не трогаем). +- k8s `Endpoints` ссылаются на эти статические IP → не «плывут» при рестарте, декларативно. + +Маршрут Job-под → `postgres`/`minio`: +под выходит через ноду `k3s-server` (её интерфейс в той же `172.28.0.0/16`), +трафик доходит до контейнера напрямую/по SNAT ноды. Аутентификация postgres — +по паролю с любого хоста (см. находку 4). S3 — по app-кредам MinIO. + +## Интеграция с Ansible (`aero/roles/sarex_stack`) + +- `sarex_services`: добавить `k3s-server`, `processing-k8s-init`, `engine` + (в правильном порядке; `depends_on` обеспечит последовательность). +- Копирование `iac/k3s/manifests/` на хост рядом с compose (в `deploy_dir`). +- Секреты: переиспользуем `SAREX_PROCESSING_DB_PASSWORD`, `SAREX_MINIO_APP_USER/PASSWORD`. + Новый образ — переменная `SAREX_WORKFLOWS_ENGINE_IMAGE` в `.env.example`. +- kubeconfig генерится k3s-server'ом в `./k3s/kubeconfig.yaml` при первом старте — + helper и engine его переиспользуют (server-адрес перекрывается `KUBE_ADDR`). + +## Порядок провижининга + +1. `k3s-server` up → healthy (apiserver `/readyz`). +2. `processing-k8s-init` → `kubectl apply` namespace + DNS-мост (Service/Endpoints). +3. `postgres` healthy + `postgres-init` completed (роль/БД `workflow_db`/`processing`). +4. `engine` up → коннектится к `workflow_db` + kube-API, пуллит workflow, создаёт Job'ы. + +## Риски и точки проверки при реализации + +1. **`KUBE_ADDR` vs правка server-URL.** Проверить, что engine с `KUBE_ADDR=https://k3s-server:6443` + + кредами из kubeconfig реально ходит в apiserver (InsecureSkipTLSVerify). Fallback — + sed-правка `server:` в kubeconfig на `https://k3s-server:6443` (SAN уже покрывает имя). +2. **Стартовые проверки engine.** Убедиться, что engine поднимается с выключенными downstream + `ENABLE_*` и не падает на отсутствии их конфигов/URL при старте. +3. **Формат yc-s3 SA JSON.** Точная схема, которую ждёт S3-либа engine/джоб. Для *планирования* + не критично (S3 нужен в рантайме джобы) — плейсхолдер допустим, помечаем как follow-up. +4. **Образ Job-задачи.** Поды задач тянут приватные образы конверторов; при их отсутствии + под уйдёт в `ImagePullBackOff`. Для критерия «планирование» это допустимо — важно, что + Job-объект создан и под запланирован. +5. **cgroup/SELinux на RedOS.** `k3s-server` уже сконфигурен (`privileged`, `cgroup: host`), + но первый реальный старт k3s на хосте надо проверить (dmesg/логи kubelet). + +## Проверка результата + +- `kubectl --kubeconfig ./k3s/kubeconfig.yaml -n processing get svc,endpoints` → `postgres`, `minio` присутствуют. +- Логи engine: успешный коннект к `workflow_db` и kube-API, нет паник. +- Создать/инициировать workflow → `kubectl -n processing get jobs,pods` показывает созданный Job. +- (follow-up) end-to-end выполнение — отдельная задача с поднятием downstream-сервисов.