Skip to content

CI/CD

Current Architecture

ArgoCD is deployed in the cluster for GitOps continuous delivery. GitLab CI handles builds using cloud runners. GitLab Runner is deployed in the K8s cluster using Helm.

mermaid
graph LR
    DEV[Developer Push] -->|git push| GL[GitLab]
    GL -->|triggers| RUNNER[GitLab Runner in K8s]
    RUNNER -->|builds image| REG[Container Registry]
    REG -->|image available| ARGO[ArgoCD]
    ARGO -->|syncs| K8S[Kubernetes - quinza]

ArgoCD

ArgoCD watches GitLab repositories and automatically syncs K8s manifests to the cluster.

PropertyValue
Namespaceargocd
URLhttps://argocd.quinza.dev (proxied by Cloudflare)
Internal accesshttp://10.10.1.1 with Host header argocd.quinza.dev
Helm chartargo/argo-cd
Mode--insecure (TLS terminated at Caddy/Cloudflare)
CredentialsStored in 1Password

Applications

Each project has its own ArgoCD Application resource pointing to a GitLab repo:

ApplicationRepositoryPathNamespaceSync
carzyinggitlab.com/aitorquinza/carzyingk8s/carzyingAuto (prune + selfHeal)

To add a new project:

yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: my-project
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://gitlab.com/aitorquinza/my-project.git
    targetRevision: main
    path: k8s
  destination:
    server: https://kubernetes.default.svc
    namespace: my-project
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
      - CreateNamespace=true

Private Repositories

ArgoCD uses credential templates to authenticate with private GitLab repos. A SOPS-encrypted Secret with a deploy token is stored at talos/manifests/argocd/repo-creds-gitlab.enc.yaml.

The credential template uses the label argocd.argoproj.io/secret-type: repo-creds and matches any repository URL starting with https://gitlab.com/carzying. This means new repos under that prefix are automatically authenticated -- no per-repo Secret needed.

To apply the credentials:

bash
sops decrypt talos/manifests/argocd/repo-creds-gitlab.enc.yaml | kubectl apply -f -

KSOPS planned

Currently credentials are applied manually via sops decrypt | kubectl apply. KSOPS integration with ArgoCD is planned so that encrypted Secrets can be synced via GitOps automatically.

GitLab CI Pipeline

The pipeline builds Docker images and pushes to GitLab Container Registry. ArgoCD then detects manifest changes and deploys.

StageActionRunner
qualityLint, typecheck, testsGitLab Runner (K8s)
buildDocker build + push to registryGitLab Runner (K8s)
deploy-previewApply preview manifests to K8sGitLab Runner (K8s)
deploy-prodManaged by ArgoCD (not pipeline)ArgoCD

GitLab Runner

GitLab Runner is deployed in the cluster using the official Helm chart (gitlab/gitlab-runner) with the Kubernetes executor.

PropertyValue
Namespacegitlab-runner
Helm chartgitlab/gitlab-runner
ExecutorKubernetes
Concurrency2
Default imagealpine:3.21
Tagsk8s, homelab
Registered projectaitorquinza/carzying
Runner tokenStored as a K8s Secret

Manifests: talos/manifests/gitlab-runner/values.yaml and install.yaml.

CI Deploy Service Account

A dedicated ServiceAccount gitlab-ci-deploy in kube-system provides least-privilege access for GitLab CI deploy jobs.

ResourceFile
ServiceAccounttalos/manifests/ci-deploy/service-account.yaml
RBACtalos/manifests/ci-deploy/rbac.yaml

The ClusterRole grants access to deployments, services, ingresses, secrets, HPA, and PDB resources in the carzying and carzying-preview namespaces only.

To generate a kubeconfig for GitLab CI:

bash
./scripts/generate-ci-kubeconfig.sh

Preview Environments

Feature branches get ephemeral preview deployments:

PropertyValue
TriggerMerge request opened
DNS*.preview.quinza.dev (wildcard, points to bastion)
URL patterncarzying-{branch}.preview.quinza.dev
CleanupAuto-destroyed on MR close or after 3 days

Not yet configured

Preview environments require Caddy wildcard config for *.preview.quinza.dev. GitLab Runner is deployed in K8s but the preview cleanup automation is not yet implemented.

Quinza Infrastructure