8724360fb2
- Updated AGENTS.md to include new directories for execution plans and implemented feature reports. - Modified Ansible host variables for dev-02 and gpu-01 to change API endpoints and add new configurations for Scaleway. - Adjusted firewall rules in gpu-01 to allow HTTPS traffic instead of the previous Ollama port. - Added OAuth2 proxy configurations to gpu-01 for enhanced security. - Removed deprecated open-webui module from Terraform app configurations. - Updated Terraform configurations to reflect changes in local variables and removed unused secrets. - Created a new sectool.json file for CloudNS integration with Bitwarden. - Enhanced SELinux configurations for nginx to allow connections to any port and added oauth2-proxy port.
9.4 KiB
9.4 KiB
Gitea → MicroK8s Webhook Orchestrator → Scaleway Containers (CI Runner System)
A lightweight CI orchestration system where a self-hosted Gitea instance triggers a Go-based webhook service running inside MicroK8s. The service decides whether to run jobs locally or launch ephemeral CI runners in Scaleway Serverless Containers.
1. High-Level Architecture
On-Premises (MicroK8s Cluster)
┌──────────────────────────────────────────────────────────────┐
│ Gitea │
│ │
│ Repo push / PR event │
└───────────────┬──────────────────────────────────────────────┘
│ Webhook (internal cluster network)
▼
┌──────────────────────────────────────────────────────────────┐
│ ci-orchestrator (Go service) │
│ │
│ - Validates webhook signature (HMAC) │
│ - Parses event payload │
│ - Applies routing rules │
│ - Decides runner target (local vs cloud) │
└───────────────┬──────────────────────────────────────────────┘
│ HTTPS API call (authenticated)
▼
──────────────────────── Internet ───────────────────────────────
│
▼
┌──────────────────────────────────────────────────────────────┐
│ Scaleway Serverless Containers API │
│ │
│ - Starts ephemeral container │
│ - Runs Gitea Runner │
│ - Executes CI job │
│ - Shuts down after completion │
└──────────────────────────────────────────────────────────────┘
2. Current State
| Component | Location | Status |
|---|---|---|
| Gitea | prod-01.alexpires.me (K8s) |
Deployed via Terraform module, TLS, ingress, S3 storage, MariaDB |
| MicroK8s | prod-01, dev-01 |
nginx ingress, cert-manager, multiple apps running |
| Scaleway | fr-par |
Registry + S3 buckets + IAM keys exist, no Serverless Containers yet |
| Gitea Runners | ci-01.lab.alexpires.me |
Windows Podman + native runners already deployed |
| DNS | CoreDNS in dev-01 |
ci-01 → 10.19.4.207 already resolved |
3. Design Decisions
- Orchestrator cluster:
prod-01for low latency to Gitea - Routing: Cloud runners only trigger when
cloudlabel is present - Runner image:
gitea/act_runner(official) - Webhook registration: Terraform-managed via
go-gitea/giteaprovider
4. Implementation Plan
Phase 1: Scaleway IaC (terraform/iac/scaleway/)
New Terraform resources:
scaleway_container_namespace— CI runners namespacescaleway_container— thegitea/act_runnercontainer (ephemeral, privacy=private)scaleway_iam_policy—ContainersInvokepermission for the CI orchestrator appscaleway_container_domain(optional) — custom domain if desired
IAM:
- New app
ci_runner_appwith policy grantingContainersInvoke - API key for the Go service to invoke containers
Existing reuse:
data.terraform_remote_state.scalewayinprod-01for outputs- Existing
scaleway_registry_namespacefor pushing the runner image
Phase 2: Go Service (ci-orchestrator/)
ci-orchestrator/
├── go.mod
├── main.go # Entry: HTTP server
├── Dockerfile # Multi-stage, distroless
├── .dockerignore
└── internal/
├── webhook/
│ ├── gitea.go # Parse Gitea push/PR payloads
│ └── hmac.go # HMAC-SHA256 validation
├── router/
│ ├── router.go # Check if job has "cloud" label
│ └── rules.go # Config-based routing rules
└── runner/
└── scaleway.go # Invoke SCW Serverless Container
Routing logic:
- Parse webhook → extract repo, branch, commit, labels (from PR body or commit message convention)
- If
cloudlabel → invoke Scaleway container - If no label → ignore (existing runners on
ci-01handle it)
Endpoints:
POST /webhook/gitea— main webhook receiverGET /health— health check
Phase 3: Runner Container Image
FROM gitea/act_runner:latest
ENV GITEA_RUNNER_IGNORE_RUNNING_JOBS_IN_SCRIPT=true
# Entrypoint configured via Terraform env vars
Push to: rg.fr-par.scw.cloud/<ci-namespace>/act-runner:<tag>
Phase 4: Terraform K8s Deployment (terraform/modules/apps/ci-orchestrator/)
Deployed in prod-01 namespace:
| Resource | Purpose |
|---|---|
kubernetes_namespace |
ci-orchestrator |
kubernetes_deployment |
Go service (hardened security context like other modules) |
kubernetes_service |
ClusterIP, port 8080 |
kubernetes_ingress_v1 |
Ingress via nginx, path /webhook/gitea |
kubernetes_config_map |
Routing rules config |
kubernetes_secret |
HMAC key, Scaleway API credentials, Gitea URL |
Phase 5: Gitea Webhook Registration
provider "gitea" {
address = "https://code.alexpires.me"
username = "<user>"
token = "<token>"
}
resource "gitea_repository_webhook" "ci_orchestrator" {
username = "<org-or-user>"
name = "<repo-name>"
type = "gitea"
url = "https://code.alexpires.me/webhook/gitea"
secret = var.webhook_secret
content_type = "json"
branch_filter = "*"
events = ["push", "pull_request"]
active = true
}
Can be per-repo or a single wildcard. The Go service routes based on labels.
Phase 6: CI Flow
push/PR to Gitea
│
▼
Webhook → ci-orchestrator (prod-01)
│
├─ no cloud label → ignore (local runners handle it)
│
└─ cloud label → invoke Scaleway container
│
▼
gitea/act_runner starts
Registers with Gitea
Receives and executes job
Exits → container shuts down (cost: per-second)
5. Provider Compatibility
go-gitea/gitea (v0.7.0)
gitea_repository_webhookresource — creates and manages webhooks- Supports:
url,secret,content_type,events,branch_filter,active,type
scaleway/scaleway (v2.77.1)
scaleway_container— Serverless Container resourceprivacycan be set toprivate(requires IAM auth)min_scale = 0,max_scale = 1for ephemeral execution- Supports
command,args,environment_variables,secret_environment_variables
scaleway_container_namespace— namespace for containersscaleway_iam_policy— IAM policies for container invocation
7. Knowns (Updated)
Gitea
- URL:
https://code.alexpires.me - Admin user:
alexandre.pires - API token:
${GITEA_ADMIN_TOKEN}(via sectool) - Provider config:
gitea_credentialsinterraform/iac/scaleway/orprod-01/
Webhook registration
- Target: all existing repos + new ones
- Method: one-time Ansible script to register webhooks on all repos, then run once per new repo
- Webhook target URL:
https://code.alexpires.me/webhook/gitea(points to Go service ingress)
Runner resources
- CPU: 2 vCPU
- Memory: 4Gi
- Image:
gitea/act_runner:latest(or pinned version)
Repository
- Moved to Gitea:
https://code.alexpires.me/alexpires.me/a13labs.infra - Origin remote updated to
ssh://git@code.alexpires.me:22/alexpires.me/a13labs.infra.git - All branches and tags pushed
CI repo workflow
ci-01runner builds Terraform images using Gitea Actions- Images pushed to Scaleway Registry
- Terraform state managed via S3 on Scaleway
8. Open Decisions (to be addressed during implementation)
- Routing labels — How will the Go service detect "cloud" intent? Options:
- PR body labels (
[cloud]in title/body) - Commit message convention (
[cloud]prefix) - Branch naming convention
- Separate "cloud" branch filter per repo
- PR body labels (
- Go service ingress path — Current plan:
/webhook/gitea. Need to ensure it doesn't conflict with Gitea's own routes. - Scaling strategy —
max_scale=1per invocation, but multiple concurrent jobs? May need pool or queue.