Argo CD
Argo CD is a declarative GitOps continuous-delivery controller for Kubernetes: it watches Git repositories and keeps cluster state in sync with the manifests (here: Helm charts + values) they declare.
Why it's in KubeAid
Argo CD is the engine the whole platform runs on. Every directory under argocd-helm-charts/ is a wrapper
chart that a per-cluster Argo CD Application (defined in your kubeaid-config repo and created by the root app)
deploys — the Helm umbrella pattern. This chart deploys Argo CD
itself the same way, so after bootstrap Argo CD manages its own upgrades from Git too.
Upstream chart: argo-cd from argo-helm.
Key values / KubeAid-specific configuration
Upstream values live under the argo-cd: key. KubeAid defaults (values.yaml):
configs.cm.application.resourceTrackingMethod: annotation— required for the Crossplane integration: cluster- scoped XRs generated from an XR Claim inherit theargocd.argoproj.io/instancelabel, which would leave the tracking app permanently out-of-sync (and prune-dangerous). Annotation tracking avoids that.- A long
configs.cm.resource.exclusionslist — Endpoints/EndpointSlices, Leases, authn/authz reviews, CSRs, Cilium identities/endpoints, Kyverno reports, Velero and CNPGBackups — to cut watched events and UI clutter. dex.enabled: false(SSO is done via Keycloak OIDC instead, configured per cluster),installCRDs: false.- Prometheus
metrics+ServiceMonitorenabled on controller, server, repo-server, redis, and notifications, with modest resource requests/limits on all components.blackbox.probe: trueadds a blackbox-exporter probe.
Two KubeAid-specific manifests ship in this wrapper's templates/:
kubeaidAppProject.enabled(defaulttrue) — anAppProjectnamedkubeaidthat all KubeAid Applications belong to (allows all source repos, destinations, and resource kinds).- A ConfigMap declaring the
kubeaid-agentlocal account withapiKeycapability, used to generate API tokens for automation.
Repository credentials are not in values: Argo CD needs one Secret per Git repo, created as a SealedSecret
(labelled argocd.argoproj.io/secret-type: repository) in the sealed-secrets/argocd/ directory of your
kubeaid-config repo. The admin password lives in the argocd-secret Secret, also managed as a SealedSecret.
Operational notes
-
Bootstrap is the one manual step: after cluster creation, render and apply the root app from your kubeaid-config repo:
helm template k8s/<clustername>/argocd-apps --show-only templates/root.yaml | kubectl apply -f -Argo CD takes over from there.
-
Never
helm install/upgradecharts directly on a KubeAid cluster — Argo CD renders and applies everything, so the only lever is the values files in Git. -
Avoid force/replace syncs of the argo-cd app itself; that can take Argo CD down mid-sync. In the UI, "out-of-sync" doesn't necessarily mean "missing" — check the compact app diff before assuming so.
Argo CD CLI
-
Install:
curl -sSL -o argocd-linux-amd64 https://github.com/argoproj/argo-cd/releases/latest/download/argocd-linux-amd64 && sudo install -m 555 argocd-linux-amd64 /usr/local/bin/argocd -
Log in via the Kubernetes API server (after pointing kubeconfig at the target cluster):
kubectl config set-context --current --namespace=argocdargocd login <argocd-server-url> --core -
From a shell inside the argocd-server pod you can instead log in against the pod itself:
argocd login <podname>:8080(default port8080).
Create an Argo CD API token
Assumes a user with apiKey capability already exists (the kubeaid-agent local account this chart's ConfigMap
declares is one such user):
# login using admin creds
argocd login <argocd ingress> --username admin --password <admin password>
# List all argocd accounts to make sure the user with apikey capabilities are there
argocd account list
# Create a password for that user
argocd account update-password --account apiuser --current-password <existing admin user password> --new-password <newpassword>
# Generate the token using user
argocd account generate-token -a apiuser --server-name <argocd ingress>
Create / reset the admin password
Create (as a fresh SealedSecret):
gopass pwgen 30
bcrypt-tool hash <generated-password> 10 # sudo snap install bcrypt-tool
kubectl create secret generic argocd-secret --namespace argocd --dry-run=client \
--from-literal=admin.password='<bcrypt-hash>' \
--from-literal=admin.passwordMtime="$(date +%FT%T%Z)" \
--from-literal=server.secretkey='<random-string>' \
--output=yaml | kubeseal --controller-name sealed-secrets-controller --controller-namespace sealed-secrets -o yaml > argocd-secret.yaml
kubectl apply -f argocd-secret.yaml # apply directly - argocd is down, you can't sync
Then delete the argo-cd-argocd-server-* pod(s) so the Deployment recreates them, and once Argo CD is back sync
the sealed-secret app so it matches Git.
Reset in place on a running cluster (leading space before bcrypt-tool is intentional — keeps the plaintext
password out of shell history):
bcrypt-tool hash "newpassword" 10
kubectl -n argocd patch secret argocd-secret -p '{"stringData": { "admin.password": "<bcrypt-hash>", "admin.passwordMtime": "'$(date +%FT%T%Z)'" }}'
then kill the argo-cd-argocd-server-* pod(s).
Adding a Git repo
Argo CD needs one Secret per repo (see the SealedSecret note above). With yq and kubeseal installed:
kubectl create secret generic repo-token-name --namespace argocd --dry-run=client \
--from-literal=type='git' --from-literal=name='repo-token-name' \
--from-literal=url='https://gitea.obmondo.com/EnableIT/repo-name.git' \
--from-literal=username='enableit_bot' --from-literal=password='SECRETPASSWORD' \
--output yaml \
| yq eval '.metadata.labels.["argocd.argoproj.io/secret-type"]="repository"' - \
| yq eval '.metadata.annotations.["sealedsecrets.bitnami.com/managed"]="true"' - \
| yq eval '.metadata.annotations.["managed-by"]="argocd.argoproj.io"' - \
| kubeseal --controller-namespace sealed-secrets --controller-name sealed-secrets-controller --format yaml - > repo-token-name.yaml
kubectl apply -f repo-token-name.yaml -n argocd
Confirm in the UI under Settings -> Repositories — CONNECTION STATUS should read Successful. To switch an
existing app to a different source repo, edit its manifest (or, for single-source apps, the Summary tab) from
the app's page in the UI, then re-sync.
Keycloak OIDC
Upstream doc: https://argo-cd.readthedocs.io/en/stable/operator-manual/user-management/keycloak/#keycloak-and-argocd-with-pkce
(follow it for the Keycloak client/groups setup — pick the realm your users are in, not the master realm). Add
to this chart's configs values:
rbac:
policy.csv: |
g, ArgoCDAdmins, role:admin
g, ArgoCDDevs, role:readonly
scopes: '[groups, email]'
cm:
oidc.config: |
name: Keycloak
issuer: https://keycloak.obmondo.com/auth/realms/Obmondo
clientID: argocd
clientSecret: $oidc.keycloak.clientSecret
requestedScopes: ["openid", "profile", "email", "groups"]
and set oidc.keycloak.clientSecret on the argocd-secret SealedSecret (merge into an existing one with
kubeseal --merge-into). Grant admin access by adding a Keycloak user to the matching group under Users ->
select user -> Groups.
Troubleshooting: confirm argocd-secret in the argocd namespace has an oidc.keycloak.clientSecret key,
check the user's group memberships in the Keycloak UI, and match policy.csv against those group names.
Troubleshooting
- Application stuck in
Progressing: a known interaction between Argo CD and Traefik (and a few other ingress controllers) — see https://github.com/traefik/traefik/issues/3377.status.loadBalanceron the argocd-server Ingress stays empty. Check withkubectl -n argocd get ing argo-cd-argocd-server -o jsonpath={.status}. spec.source.repoURL and spec.source.path either spec.source.chart are requiredafter an upgrade: the Application CRD didn't pick up its new schema. Re-apply it (kubectl apply -f application-crd.yaml, or add--server-sideif that fails). Do not delete the CRD hoping Argo CD regenerates it — that takes down every Application using it.ComparisonError: groupVersion shouldn't be empty: an Application manifest (usually the root one) has the wrongapiVersion.