iac/docs/superpowers/specs/2026-07-30-processing-engine-k3s-scheduling-design.md

11 KiB
Raw Blame History

Дизайн: 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-serverprocessing-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 postgres172.28.0.10:5432.
  • bridge-minio.yaml — Service+Endpoints minio172.28.0.11:9000.

Сеть и стабильность адресов

Решение: статические IP в compose (прочтение «адреса как в docker-compose»).

  • Верхнеуровневой networks.default задаём IPAM с фиксированным subnet, напр. 172.28.0.0/16.
  • postgres172.28.0.10, minio172.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-initkubectl 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,endpointspostgres, minio присутствуют.
  • Логи engine: успешный коннект к workflow_db и kube-API, нет паник.
  • Создать/инициировать workflow → kubectl -n processing get jobs,pods показывает созданный Job.
  • (follow-up) end-to-end выполнение — отдельная задача с поднятием downstream-сервисов.