Files
foxhunt/docs/plans/2026-02-27-training-deploy-design.md
jgrusewski 93115c1fc5 docs: add training pipeline & model deployment design
Two parallel tracks:
- Track 1: fxt CLI -> gRPC -> JobSpawner (PostgreSQL) -> K8sDispatcher -> GPU Jobs
- Track 2: S3 checkpoints -> model loader -> promotion manager -> serving

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 22:08:26 +01:00

128 lines
5.4 KiB
Markdown

# Training Pipeline & Model Deployment Design
**Date**: 2026-02-27
**Status**: Approved
**Tracks**: Two independent parallel tracks
## Overview
Wire the complete training-to-serving pipeline on Kapsule. Track 1 handles training job dispatch (fxt CLI → gRPC → PostgreSQL → K8s GPU Jobs). Track 2 handles model serving and promotion (S3 checkpoints → model loader → inference → operator-gated promotion).
Both tracks share S3 (`foxhunt-models/`) as the handoff point.
## Track 1: Training Pipeline
### Goal
`fxt train start tft ES.FUT --epochs 50` creates a real GPU training job on Kapsule.
### Flow
```
fxt train start tft ES.FUT --epochs 50
→ gRPC StartTraining (ml_training_service:50053)
→ service.rs handler → JobSpawner::spawn_batch() → PostgreSQL
→ In-process queue consumer (tokio::spawn, 5s poll)
→ JobSpawner::get_next_pending_job()
→ K8sDispatcher::dispatch() → batch/v1 Job
├── training container (GPU, train_baseline_rl/supervised)
└── uploader sidecar (S3 → foxhunt-models/)
→ Job completion callback → update status in PostgreSQL
```
### Existing Code
| Component | File | Status |
|-----------|------|--------|
| fxt train start | `bin/fxt/src/commands/train/start.rs` | Done (sends gRPC) |
| fxt train status | `bin/fxt/src/commands/train/status.rs` | Done |
| fxt train list | `bin/fxt/src/commands/train/list.rs` | Done |
| K8sDispatcher | `services/ml_training_service/src/k8s_dispatcher.rs` | Done (builds Jobs) |
| JobSpawner | `services/ml_training_service/src/job_spawner.rs` | Done (PostgreSQL CRUD) |
| Job template | `infra/k8s/training/job-template.yaml` | Done (reference only) |
### Changes Needed
1. **Wire gRPC handler to JobSpawner**: `service.rs` StartTraining handler must call `JobSpawner::spawn_batch()` instead of returning a stub response.
2. **Add queue consumer loop**: In `main.rs`, `tokio::spawn` a loop that polls `get_next_pending_job()` every 5s and calls `K8sDispatcher::dispatch()`.
3. **Job completion callback**: The uploader sidecar already has `CALLBACK_ENDPOINT`. Add a gRPC or HTTP endpoint in ml_training_service that receives completion notifications and calls `JobSpawner::update_job_status()`.
4. **Deploy training-output-pvc**: Currently only `training-data-pvc` is deployed. Need `training-output-pvc` for job artifact staging.
5. **PostgreSQL schema**: Deploy `batch_jobs` and `child_jobs` tables. Add sqlx migration.
6. **Docker image**: Rebuild ml-training-service image with kube-rs K8sDispatcher compiled in.
### K8s Resources
- `ml-training-service` Deployment (exists, needs redeploy with new image)
- `training-data-pvc` (exists, 10Gi scw-bssd)
- `training-output-pvc` (new, needs creation)
- `s3-credentials` Secret (exists)
- `scw-registry` Secret (exists)
- `gpu-training` node pool (Scaleway, autoscales 0→N)
## Track 2: Model Serving & Validation
### Goal
Trained model checkpoints deploy to a serving endpoint with operator-gated promotion and automatic rollback.
### Flow
```
Training job completes → uploader sidecar → S3 (foxhunt-models/)
→ Callback to ml_training_service
→ PromotionManager::register_completion()
├── No active model → auto-register
└── Compare metrics → PendingPromotion / NoImprovement
→ fxt model promote <model-id> (operator approval)
→ DeploymentPipeline::deploy()
├── Rolling update
├── Health check (inference smoke test)
└── Auto-rollback on failure
```
### Existing Code
| Component | File | Status |
|-----------|------|--------|
| S3ModelLoader | `crates/model_loader/src/lib.rs` | Done (S3 + LRU cache + versioning) |
| PromotionManager | `services/ml_training_service/src/promotion_manager.rs` | Done (metric comparison, pending/active tracking) |
| DeploymentPipeline | `services/ml_training_service/src/deployment_pipeline.rs` | Done (rolling update, health check, rollback) |
| ValidationPipeline | `services/ml_training_service/src/validation_pipeline.rs` | Exists |
### Changes Needed
1. **fxt model commands**: Add `fxt model promote`, `fxt model list`, `fxt model status` CLI subcommands.
2. **Wire promotion callback**: Uploader sidecar completion → PromotionManager::register_completion(). Extract metrics from training output.
3. **Model serving deployment**: K8s Deployment that loads active model from S3 via S3ModelLoader and serves predictions via gRPC.
4. **Validation test**: Send sample market data to inference endpoint, verify prediction response shape and latency.
5. **Proto additions**: Add `PromoteModel`, `ListModels`, `GetModelStatus` RPCs to ml_training proto.
## Shared Infrastructure
| Resource | Status | Notes |
|----------|--------|-------|
| PostgreSQL | Running in foxhunt ns | Needs schema migration |
| S3 foxhunt-models | Exists | Bucket + credentials |
| training-data-pvc | Deployed | 10Gi, read-only for jobs |
| training-output-pvc | Needs creation | Staging for job artifacts |
| scw-registry secret | Deployed | Image pull auth |
| gpu-training pool | Configured | Autoscales 0→N |
## Decision Log
| Decision | Choice | Rationale |
|----------|--------|-----------|
| Job dispatch path | DB-backed (JobSpawner) | Full history, batch orchestration, rollback |
| Queue consumer | In-process tokio task | Zero additional infra, simple |
| Track split | Training vs Serving | Independent concerns, parallel development |
| Promotion gating | Operator approval | Production safety for HFT system |