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