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