# Дизайн: 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-сервисов.
