Skip to content

infra(traefik): move Traefik from k3s addon to ArgoCD (follow-up to 2026-09-26 outage) #106

Open

Description

@sre-helmcode

Context

2026-09-26, 03:04-06:21 UTC (~3h15m): api.nan.builders and api.helmcode.com answered 404 on every inference route. LiteLLM was healthy. Root cause: nan-eu001 took the k3s controller lease and re-applied its stale on-disk /var/lib/rancher/k3s/server/manifests/traefik-config.yaml (no stripstream plugin); Traefik dropped every LiteLLM router (plugin: unknown plugin type: stripstream).

Short-term fix (nan-devops PR "fix(traefik): sync traefik-config.yaml to every k3s server from git"): a DaemonSet keeps that file identical to git on the three servers, the Traefik image is pinned, and new alerts cover routers going missing and config drift.

That still leaves Traefik managed by the k3s addon/helm-controller machinery: config lives on node disks, whichever server holds the lease re-renders the HelmChart from its own k3s binary, and deleting the file garbage-collects the live config. This issue removes that machinery.

Goal

Traefik is an ordinary ArgoCD-managed Helm release (pinned chart + image, values in git), with k3s no longer deploying or reconciling it.

Plan (maintenance window, both tenants affected)

  1. Pre-work (no prod impact)
    • Align k3s: upgrade nan-eu001 v1.34.6 -> v1.35.4 (same as eu006/eu007), one server at a time, etcd health checked between steps.
    • Build the ArgoCD app kube-system/traefik (upstream traefik/traefik chart, pinned version matching the running one; values = the current HelmChartConfig values + what the k3s HelmChart injects: priorityClassName, tolerations, publishedService, image). helm template diff against the live Deployment/Service/RBAC/IngressClass must be empty or explained.
    • Decide CRD ownership (traefik-crd chart today): Argo app with Replace=true/SSA, or keep CRDs static.
    • Rehearse the whole cutover on a throwaway k3s (3 servers) incl. rollback.
  2. Cutover (window)
    • Take over the existing Helm release in place (same release name traefik, namespace kube-system) so Deployment/Service are adopted, not recreated: no ClusterIP change, cloudflared keeps working.
    • Add --disable=traefik to the k3s config on all 3 servers and restart k3s one at a time. Before that, confirm on the rehearsal cluster what k3s does to the existing release/HelmChart objects when disabled (it deletes the HelmChart; the helm-delete job must NOT uninstall the release Argo now owns).
    • Remove traefik.yaml / traefik-config.yaml from the manifests dirs and retire the traefik-config-sync DaemonSet and its alert.
  3. Verification
    • Gatus all green; traefik-router-missing-* silent; SSE stream test (no coalescing, no 60s cut) on both hosts; access logs still in VictoriaLogs; stripstream headers still stripped.
    • Force a lease change (restart k3s on the current leader) and confirm Traefik does not roll.
  4. Rollback: re-enable traefik in k3s config, restore traefik-config.yaml from git to the manifests dirs, restart k3s on one server.

Acceptance

  • k3s servers on the same version
  • Traefik deployed only by ArgoCD; no Traefik files in any server/manifests
  • Lease change drill does not touch Traefik (2026-10-02: the leader restart re-took the lease, so the lease never moved; the manifests re-apply did not touch Traefik)
  • Runbook + postmortem updated (platform/devops/docs/postmortems/, docs/INCIDENTS.md)

Target: week of 2026-09-28.

Activity

  1. sre-helmcode commented on Oct 2, 2026

    @sre-helmcode
    ContributorAuthor

    Cutover done (2026-10-02, 18:14–18:48 UTC)

    Traefik is the ArgoCD app prod-traefik:

    • upstream traefik/traefik 39.0.7, Server-Side Apply, prune: false;
    • manual sync by decision;
    • the 29 CRDs come from the chart's crds/.

    k3s no longer deploys it. The three servers run with /etc/rancher/k3s/config.yaml.d/50-traefik-cutover.yaml (disable-helm-controller: true, disable: [traefik]). No HelmChart, HelmChartConfig, Addon, Helm release secret or Traefik manifest file remains. The adopted objects are the same ones as before: Deployment UID ff21b294…, Service ClusterIP 10.245.78.152.

    • Procedure, rehearsal findings and execution log: docs/runbooks/traefik-k3s-to-argocd-cutover.md (helmcode/nan-devops#730).
    • PRs: #731 (adoption), #732 (traefik-config-sync retired), #733 (readiness fix), #734 (docs).
    • Prerequisite done in the same window: nan-eu001 v1.34.6 → v1.35.4. eu002 is still v1.34.6 (agent, separate window).

    Incident: ~23 s of 502 (128 requests on both public APIs) during the adoption sync. A fresh Traefik pod went Ready in 3 s, before the LiteLLM NetworkPolicies admitted its IP. Fixed with readinessProbe.initialDelaySeconds: 60 (#733); its own rollout had no burst. Postmortem: docs/postmortems/2026-10-02-traefik-cutover-502-burst.md.

    Open: a real lease-change drill. The leader restart re-took the lease within 15 s. Forcing a move means keeping eu001 down past the ~50 s CNPG deadline, so it was not done.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions