6093c52160
ci / lint (push) Failing after 1s
ci / bump (push) Has been skipped
ci / types (push) Has been skipped
ci / unit (push) Has been skipped
ci / integration (push) Has been skipped
ci / security (push) Has been skipped
ci / dockerfile (push) Has been skipped
ci / chart (push) Has been skipped
ci / image (api) (push) Has been skipped
ci / image (reconciler) (push) Has been skipped
ci / image (worker) (push) Has been skipped
A Kyverno ClusterPolicy, force-best-effort-cpu, rewrites every container's CPU request to "0" at admission. It is deliberate and predates this app by well over a year: the nodes are oversubscribed and BestEffort-on-CPU is how everything gets scheduled. The chart asks for 50m and the cluster writes 0, so all three Deployments sat permanently OutOfSync while perfectly Healthy — the failure mode where a dashboard is always yellow, everyone learns to ignore it, and it stops meaning anything the day it goes yellow for a real reason. Ignored on the Application rather than capitulating in the chart. What the chart asks for is the honest intent; what the cluster does with it is the cluster's business, and a reader of the repo should see the former. jqPathExpressions rather than jsonPointers, which would have to name a container index and this has to hold for every container in all three Deployments. Takes effect only after a hard refresh.
90 lines
4.3 KiB
YAML
90 lines
4.3 KiB
YAML
# The other half of "CI does not deploy".
|
|
#
|
|
# CI's last act is a commit that changes image.*.digest in deploy/chart/values.yaml.
|
|
# ArgoCD notices that commit and syncs. There is no kubeconfig in CI, no `helm upgrade` in
|
|
# a pipeline step, and no human running kubectl. If you want to know what is running in the
|
|
# cluster, you read git — that is the whole property, and a single `kubectl apply` from a
|
|
# laptop is what destroys it (selfHeal below exists to undo exactly that).
|
|
apiVersion: argoproj.io/v1alpha1
|
|
kind: Application
|
|
metadata:
|
|
name: svcforge
|
|
namespace: argocd
|
|
finalizers:
|
|
# Without this, deleting the Application orphans every resource it created.
|
|
- resources-finalizer.argocd.argoproj.io
|
|
spec:
|
|
project: default
|
|
source:
|
|
repoURL: https://gitea.oci-oci.duckdns.org/gitea_admin/svcforge.git
|
|
targetRevision: master
|
|
path: deploy/chart
|
|
helm:
|
|
releaseName: svcforge
|
|
# No valueFiles override and no `parameters:` block. values.yaml in the repo is the
|
|
# single source of truth — a parameter here would be a second place the deployed
|
|
# digest could come from, invisible in the chart's own diff.
|
|
destination:
|
|
server: https://kubernetes.default.svc
|
|
namespace: svcforge
|
|
syncPolicy:
|
|
automated:
|
|
# Delete resources removed from the chart. Safe because the chart owns only svcforge
|
|
# itself; tenant releases are created by the worker's helm calls and are not part of
|
|
# this Application, so prune cannot reach them.
|
|
prune: true
|
|
# Revert manual edits. A hotfix applied by hand is a lie the next sync tells on.
|
|
selfHeal: true
|
|
syncOptions:
|
|
- CreateNamespace=true
|
|
# The migrate Job is a helm pre-install/pre-upgrade hook. ArgoCD maps helm hooks onto
|
|
# its own PreSync phase, so migrations still run before any new pod starts, and a
|
|
# non-zero exit fails the sync instead of rolling out pods onto an unmigrated schema.
|
|
- ApplyOutOfSyncOnly=true
|
|
retry:
|
|
# 0, not 3, and for the same reason migrate-job.yaml sets backoffLimit: 0.
|
|
#
|
|
# An ArgoCD retry re-runs the WHOLE sync including the PreSync phase, and the migrate
|
|
# Job is a helm pre-install/pre-upgrade hook that ArgoCD maps onto PreSync. So
|
|
# `limit: 3` quietly reinstated the retry-a-failed-DDL behaviour that backoffLimit: 0
|
|
# exists to forbid — three attempts at the same failed migration, one readable error
|
|
# turned into three, against a schema that may now be half-applied.
|
|
#
|
|
# There is no recovery path here that a retry helps with. Recovery from a failed
|
|
# migration is a REVERT COMMIT: fix the SQL forward, push, and let ArgoCD sync the
|
|
# new revision. Rolling back the app image does not roll back DDL that already
|
|
# committed.
|
|
limit: 0
|
|
backoff:
|
|
duration: 20s
|
|
factor: 2
|
|
maxDuration: 3m
|
|
# The migrate Job is a hook, so ArgoCD deletes and recreates it every sync; its
|
|
# generated fields would otherwise show as permanent drift and the app would never
|
|
# report Synced.
|
|
ignoreDifferences:
|
|
- group: batch
|
|
kind: Job
|
|
jsonPointers:
|
|
- /spec/template/metadata/labels
|
|
# This cluster runs a Kyverno ClusterPolicy, `force-best-effort-cpu`, whose rule
|
|
# set-cpu-request-to-zero rewrites every container's CPU request to "0" at admission.
|
|
# It is deliberate and predates this app by well over a year: the nodes are
|
|
# oversubscribed, and making pods BestEffort on CPU is how everything gets scheduled.
|
|
#
|
|
# The chart asks for 50m and the cluster writes 0, so without this the Deployments sit
|
|
# permanently OutOfSync while being perfectly Healthy — the failure mode where a
|
|
# dashboard is always yellow, everyone learns to ignore it, and it stops meaning
|
|
# anything the day it goes yellow for a real reason.
|
|
#
|
|
# The chart deliberately keeps its real request rather than capitulating to 0. What the
|
|
# chart asks for is the honest intent; what the cluster does with it is the cluster's
|
|
# business, and a reader of the repo should see the former.
|
|
#
|
|
# jqPathExpressions, not jsonPointers: a pointer would have to name a container index,
|
|
# and this has to hold for every container in every one of the three Deployments.
|
|
- group: apps
|
|
kind: Deployment
|
|
jqPathExpressions:
|
|
- .spec.template.spec.containers[].resources.requests.cpu
|