Full GitLab CI → Argo migration: Argo Events webhook trigger, per-service compile WorkflowTemplates, selective deploy, test workspace, training compile, and IaC templates. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
223 lines
11 KiB
Markdown
223 lines
11 KiB
Markdown
# Full GitLab CI → Argo Workflows Migration
|
|
|
|
**Date**: 2026-03-04
|
|
**Status**: Approved
|
|
|
|
## Problem
|
|
|
|
The entire CI/CD pipeline (test, compile, deploy, training, IaC) runs in GitLab CI via a 1600+ line `.gitlab-ci.yml`. GitLab CE lacks advanced pipeline features (child pipelines, pipeline subscriptions). The training stage already runs in Argo Workflows. Moving everything to Argo gives a single orchestration layer with DAG workflows, parameterized templates, a proper UI (ci.fxhnt.ai), and event-driven triggers.
|
|
|
|
## Design
|
|
|
|
### 1. Trigger Layer — Argo Events
|
|
|
|
Install Argo Events (Helm chart) in the `foxhunt` namespace. Configure a webhook EventSource that receives GitLab push events.
|
|
|
|
**EventSource** (`gitlab-push`):
|
|
- Type: `webhook`
|
|
- Port: 12000, endpoint: `/push`, method: POST
|
|
- Exposed as ClusterIP service `gitlab-push-eventsource-svc` in `foxhunt` namespace
|
|
|
|
**Sensor** (`ci-pipeline-trigger`):
|
|
- Dependency: `gitlab-push` EventSource
|
|
- Filter: `body.ref == "refs/heads/main"` (only main branch pushes)
|
|
- Trigger: Submit `ci-pipeline` Workflow with parameters:
|
|
- `commits`: JSON array of commit objects (contains `added`, `modified`, `removed` arrays)
|
|
- `checkout-sha`: the HEAD commit SHA
|
|
- `pipeline-iid`: extracted from webhook or generated
|
|
|
|
**GitLab webhook config**: Settings → Webhooks → URL: `http://gitlab-push-eventsource-svc.foxhunt:12000/push`, trigger: Push events, branch filter: `main`, secret token for HMAC validation.
|
|
|
|
### 2. CI Pipeline WorkflowTemplate (Orchestrator)
|
|
|
|
A single `ci-pipeline` WorkflowTemplate orchestrates the full pipeline as a DAG:
|
|
|
|
```
|
|
detect-changes
|
|
├── test-workspace (if code changed)
|
|
├── compile-api-gateway (if api_gateway deps changed)
|
|
├── compile-trading-service (if trading_service deps changed)
|
|
├── compile-ml-training-service (if ml deps changed)
|
|
├── compile-backtesting-service (if backtesting deps changed)
|
|
├── compile-trading-agent-service (if trading_agent deps changed)
|
|
├── compile-broker-gateway (if broker deps changed)
|
|
├── compile-data-acquisition-service (if data_acquisition deps changed)
|
|
├── compile-web-gateway (if web-gateway deps changed)
|
|
├── compile-training (if ML/training crates changed)
|
|
├── build-web-dashboard (if web-dashboard/ changed)
|
|
└── deploy (after all compile steps complete)
|
|
```
|
|
|
|
The `detect-changes` step:
|
|
- Receives the commits JSON from the webhook payload
|
|
- Extracts all changed file paths from `added + modified + removed`
|
|
- Checks each service's dependency paths (same map as GitLab `changes:` filters)
|
|
- Outputs a JSON object: `{"services": ["api-gateway", "trading-service"], "training": true, "web_dashboard": false, "test": true, "infra": false}`
|
|
- Downstream DAG tasks use `when` expressions to conditionally execute
|
|
|
|
#### Service dependency map (hardcoded in detect-changes script)
|
|
|
|
| Service | Trigger paths |
|
|
|---|---|
|
|
| api-gateway | `services/api_gateway/`, `crates/common/`, `crates/config/`, `crates/trading_engine/`, `bin/fxt/`, `Cargo.toml`, `Cargo.lock` |
|
|
| trading-service | `services/trading_service/`, `services/api_gateway/`, `crates/common/`, `crates/config/`, `crates/data/`, `crates/database/`, `crates/ml/`, `crates/risk/`, `crates/storage/`, `crates/trading_engine/`, `Cargo.toml`, `Cargo.lock` |
|
|
| ml-training-service | `services/ml_training_service/`, `crates/common/`, `crates/config/`, `crates/data/`, `crates/ml/`, `crates/risk/`, `crates/storage/`, `crates/trading_engine/`, `Cargo.toml`, `Cargo.lock` |
|
|
| backtesting-service | `services/backtesting_service/`, `crates/common/`, `crates/config/`, `crates/data/`, `crates/ml/`, `crates/model_loader/`, `crates/risk/`, `crates/storage/`, `crates/trading_engine/`, `bin/fxt/`, `Cargo.toml`, `Cargo.lock` |
|
|
| trading-agent-service | `services/trading_agent_service/`, `crates/common/`, `crates/config/`, `crates/ml/`, `crates/risk/`, `Cargo.toml`, `Cargo.lock` |
|
|
| broker-gateway | `services/broker_gateway_service/`, `crates/common/`, `crates/config/`, `crates/trading_engine/`, `vendor/ctrader-openapi/`, `Cargo.toml`, `Cargo.lock` |
|
|
| data-acquisition-service | `services/data_acquisition_service/`, `crates/common/`, `crates/config/`, `crates/storage/`, `Cargo.toml`, `Cargo.lock` |
|
|
| web-gateway | `crates/web-gateway/`, `crates/common/`, `Cargo.toml`, `Cargo.lock` |
|
|
| training | `crates/ml/`, `crates/trading_engine/`, `crates/common/`, `crates/risk/`, `crates/data/`, `crates/config/`, `crates/storage/`, `services/training_uploader/`, `Cargo.toml`, `Cargo.lock` |
|
|
|
|
### 3. Compile Service WorkflowTemplate
|
|
|
|
Single parameterized template, invoked once per service that needs recompilation:
|
|
|
|
```yaml
|
|
name: compile-service
|
|
arguments:
|
|
parameters:
|
|
- name: service-package # e.g. "api-gateway"
|
|
- name: commit-sha
|
|
```
|
|
|
|
- **Image**: `ci-builder-cpu:latest` (Rust 1.89 + mold + sccache, no CUDA)
|
|
- **Node**: `ci-compile-cpu` pool (POP2-32C-128G)
|
|
- **Resources**: 4 CPU request / 6 CPU limit, 8Gi / 16Gi memory (same as current per-service jobs)
|
|
- **Volumes**: sccache PVC (`/mnt/sccache`), workspace volumeClaimTemplate for build output
|
|
- **Script**: `cargo build --profile dev-release -p <service-package>`, strip, copy to workspace
|
|
- **Artifact output**: Binary at `/workspace/services/<service-package>` → Argo artifact (S3/MinIO)
|
|
|
|
Git checkout: The container needs the source code. Options:
|
|
- **Git init container**: Clone repo at `checkout-sha` into workspace volume
|
|
- Use Argo's built-in `git` artifact source
|
|
|
|
A shared `git-checkout` step at the start of the DAG clones once into a workspace PVC. All compile steps mount the same PVC (ReadWriteOnce — all on same node, which is the case with 1 POP2-32C node).
|
|
|
|
### 4. Compile Training WorkflowTemplate
|
|
|
|
Same as current `compile-training` job but as an Argo template:
|
|
|
|
- **Image**: `ci-builder:latest` (CUDA 12.4 + Rust 1.89 — needs CUDA stubs)
|
|
- **Node**: `ci-compile-cpu` pool (CPU compile with CUDA stubs, no GPU needed)
|
|
- **Variables**: `CUDA_COMPUTE_CAP=89`
|
|
- **Script**: `cargo build --profile dev-release -p ml --features ml/cuda --example train_baseline_rl ...`
|
|
- **Output**: 7 training binaries as Argo artifacts
|
|
|
|
### 5. Build Web Dashboard WorkflowTemplate
|
|
|
|
- **Image**: `node:22-slim`
|
|
- **Node**: `ci-compile-cpu` pool
|
|
- **Resources**: 2 CPU / 2Gi memory
|
|
- **Script**: `cd web-dashboard && npm ci && npm run build`
|
|
- **Output**: `web-dashboard/dist/` as Argo artifact
|
|
|
|
### 6. Test Workspace WorkflowTemplate
|
|
|
|
- **Image**: `ci-builder:latest` (needs CUDA stubs for full workspace test)
|
|
- **Node**: `ci-compile-cpu` pool
|
|
- **Resources**: 14 CPU / 24Gi memory (same as current test job)
|
|
- **Services**: Redis sidecar container
|
|
- **Script**: `cargo test --workspace --lib`
|
|
|
|
### 7. Deploy WorkflowTemplate
|
|
|
|
Collects artifacts from all compile steps, uploads to MinIO, applies manifests, does selective rollout restart.
|
|
|
|
Steps:
|
|
1. **Upload binaries**: For each compiled service binary artifact, `aws s3 cp` to `s3://foxhunt-binaries/services/`
|
|
2. **Upload training binaries**: `aws s3 cp` to `s3://foxhunt-binaries/training/`
|
|
3. **Upload web assets**: `aws s3 cp` to `s3://foxhunt-binaries/static/`
|
|
4. **Apply migrations**: Same kubectl exec logic as current deploy
|
|
5. **Apply monitoring**: `kubectl apply -f infra/k8s/monitoring/`
|
|
6. **Apply dashboards**: Same ConfigMap generation script
|
|
7. **Apply service manifests**: `kubectl apply -f infra/k8s/services/`
|
|
8. **Selective rollout restart**: Only restart deployments whose binary was uploaded
|
|
9. **Wait for rollout**: `kubectl rollout status` with 300s timeout
|
|
|
|
**RBAC**: Dedicated ServiceAccount `argo-deploy` with:
|
|
- `deployments`, `statefulsets`, `configmaps`, `services` — get, list, create, update, patch
|
|
- `pods` — get, list, exec (for migrations)
|
|
- Namespace: `foxhunt`
|
|
|
|
### 8. Image Build WorkflowTemplate
|
|
|
|
Handles CI builder image rebuilds when Dockerfiles change:
|
|
|
|
- **Trigger**: `detect-changes` identifies `infra/docker/Dockerfile.*` changes
|
|
- **Image**: `gcr.io/kaniko-project/executor:debug`
|
|
- **Script**: Kaniko build + push to Scaleway Container Registry
|
|
- **Trivy scan**: Follow-up step scans pushed image for CRITICAL/HIGH vulns
|
|
|
|
### 9. IaC WorkflowTemplate
|
|
|
|
Terragrunt plan/apply:
|
|
|
|
- **Trigger**: `detect-changes` identifies `infra/` changes
|
|
- **Image**: `infra-runner:latest`
|
|
- **Plan step**: `terragrunt run-all plan` (always runs)
|
|
- **Apply step**: `terragrunt run-all apply -auto-approve` (only on main)
|
|
|
|
### 10. Volumes & Storage
|
|
|
|
| Volume | Type | Mount | Used by |
|
|
|---|---|---|---|
|
|
| sccache | PVC (existing) | `/mnt/sccache` | compile-service, compile-training, test |
|
|
| training-data | PVC (existing) | `/mnt/training-data` | training workflows |
|
|
| workspace | volumeClaimTemplate (5Gi) | `/workspace` | per-workflow scratch space |
|
|
| source | PVC or emptyDir | `/src` | git checkout, shared by all compile steps |
|
|
|
|
### 11. Migration Strategy
|
|
|
|
Deploy new first, test, then disable old:
|
|
|
|
1. **Install Argo Events**: `helm install argo-events argo/argo-events -n foxhunt -f infra/k8s/argo/events-values.yaml`
|
|
2. **Deploy WorkflowTemplates**: `kubectl apply -f infra/k8s/argo/`
|
|
3. **Deploy EventSource + Sensor**: `kubectl apply -f infra/k8s/argo/events/`
|
|
4. **Configure GitLab webhook**: Point to EventSource endpoint
|
|
5. **Test**: Push a small change to main, verify Argo workflow runs and deploys correctly
|
|
6. **Disable GitLab CI**: Set all jobs to `when: never` in `.gitlab-ci.yml`
|
|
7. **Validation period**: Run for 1-2 weeks with GitLab CI disabled but `.gitlab-ci.yml` still present
|
|
8. **Delete**: Remove `.gitlab-ci.yml`, uninstall GitLab Runner
|
|
|
|
**Rollback**: Revert `.gitlab-ci.yml` to re-enable GitLab CI. The webhook can coexist — Argo Events ignores pushes if sensor is deleted.
|
|
|
|
## File Layout
|
|
|
|
```
|
|
infra/k8s/argo/
|
|
├── values.yaml # Argo Workflows Helm values (existing)
|
|
├── events-values.yaml # Argo Events Helm values (new)
|
|
├── kustomization.yaml # Updated to include new templates
|
|
├── argo-workflow-netpol.yaml # Network policy (existing)
|
|
├── training-workflow-template.yaml # Training DAG (existing, unchanged)
|
|
├── ci-pipeline-template.yaml # Main CI orchestrator (new)
|
|
├── compile-service-template.yaml # Per-service compile (new)
|
|
├── compile-training-template.yaml # Training binary compile (new)
|
|
├── build-web-dashboard-template.yaml # Vite build (new)
|
|
├── test-workspace-template.yaml # cargo test (new)
|
|
├── deploy-template.yaml # Deploy to k8s (new)
|
|
├── build-ci-image-template.yaml # Kaniko image builds (new)
|
|
├── infra-template.yaml # Terragrunt plan/apply (new)
|
|
├── deploy-rbac.yaml # ServiceAccount + RBAC for deploy (new)
|
|
└── events/
|
|
├── gitlab-push-eventsource.yaml # Webhook EventSource (new)
|
|
└── ci-pipeline-sensor.yaml # Sensor + trigger (new)
|
|
```
|
|
|
|
## What Gets Deleted
|
|
|
|
After validation:
|
|
- `.gitlab-ci.yml` (1600+ lines)
|
|
- GitLab Runner pods and configuration
|
|
- GitLab Runner Helm release
|
|
- CI-related GitLab project settings (variables stay as k8s secrets)
|
|
|
|
## What Stays
|
|
|
|
- GitLab CE as git server (push, pull, webhooks)
|
|
- All existing Argo Workflows infrastructure
|
|
- MinIO for artifacts and binary storage
|
|
- Scaleway Container Registry for images
|
|
- All k8s service manifests, monitoring, IaC
|