GitOps — Practical
GitOps — Practical patterns
Section titled “GitOps — Practical patterns”Argo CD bootstrap (helm)
Section titled “Argo CD bootstrap (helm)”helm repo add argo https://argoproj.github.io/argo-helmhelm install argocd argo/argo-cd -n argocd --create-namespace \ --set server.service.type=LoadBalancer \ --set configs.params."server\.insecure"=true # devLogin:
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath='{.data.password}' | base64 -dargocd login <host>App-of-apps root
Section titled “App-of-apps root”apiVersion: argoproj.io/v1alpha1kind: Applicationmetadata: { name: bootstrap, namespace: argocd }spec: project: default source: repoURL: https://github.com/org/manifests path: argocd-apps targetRevision: main destination: { server: https://kubernetes.default.svc, namespace: argocd } syncPolicy: { automated: { prune: true, selfHeal: true } }Then argocd-apps/*.yaml contains one Application per service.
Kustomize overlay structure
Section titled “Kustomize overlay structure”manifests/ base/ api/ kustomization.yaml deployment.yaml service.yaml overlays/ prod/ api/ kustomization.yaml replicas-patch.yaml staging/ api/ kustomization.yaml image-patch.yamlapiVersion: kustomize.config.k8s.io/v1beta1kind: Kustomizationnamespace: appresources: - ../../../base/apipatches: - path: replicas-patch.yamlimages: - name: ghcr.io/org/api newTag: 1.2.3Helm + Argo CD
Section titled “Helm + Argo CD”apiVersion: argoproj.io/v1alpha1kind: Applicationmetadata: { name: nginx-ingress, namespace: argocd }spec: source: repoURL: https://kubernetes.github.io/ingress-nginx chart: ingress-nginx targetRevision: 4.10.0 helm: values: | controller: service: type: LoadBalancer metrics: enabled: true destination: { server: https://kubernetes.default.svc, namespace: ingress } syncPolicy: { automated: { prune: true, selfHeal: true } }Image automation (Argo Image Updater)
Section titled “Image automation (Argo Image Updater)”apiVersion: argoproj.io/v1alpha1kind: Applicationmetadata: name: api namespace: argocd annotations: argocd-image-updater.argoproj.io/image-list: api=ghcr.io/org/api argocd-image-updater.argoproj.io/api.update-strategy: digest argocd-image-updater.argoproj.io/write-back-method: git argocd-image-updater.argoproj.io/git-branch: mainspec: ...Sealed Secrets
Section titled “Sealed Secrets”kubeseal --controller-namespace kube-system --controller-name sealed-secrets \ --format yaml < secret.yaml > sealed-secret.yamlsealed-secret.yaml is safe to commit. Cluster’s controller decrypts in place.
External Secrets Operator (from AWS Secrets Manager)
Section titled “External Secrets Operator (from AWS Secrets Manager)”apiVersion: external-secrets.io/v1beta1kind: ClusterSecretStoremetadata: { name: aws-sm }spec: provider: aws: service: SecretsManager region: eu-west-1 auth: jwt: serviceAccountRef: { name: eso, namespace: external-secrets }---apiVersion: external-secrets.io/v1beta1kind: ExternalSecretmetadata: { name: db, namespace: app }spec: refreshInterval: 1h secretStoreRef: { kind: ClusterSecretStore, name: aws-sm } target: { name: db-secret } data: - secretKey: password remoteRef: { key: prod/db, property: password }Argo Rollouts (canary)
Section titled “Argo Rollouts (canary)”apiVersion: argoproj.io/v1alpha1kind: Rolloutmetadata: { name: api }spec: replicas: 10 strategy: canary: maxSurge: "25%" maxUnavailable: 0 steps: - setWeight: 10 - pause: { duration: 5m } - setWeight: 25 - pause: { duration: 10m } - setWeight: 50 - pause: { duration: 10m } - setWeight: 100 analysis: templates: [{ templateName: success-rate }] startingStep: 1 selector: { matchLabels: { app: api } } template: ...---apiVersion: argoproj.io/v1alpha1kind: AnalysisTemplatemetadata: { name: success-rate }spec: args: [{ name: service-name }] metrics: - name: success-rate interval: 1m successCondition: result[0] >= 0.99 failureLimit: 3 provider: prometheus: address: http://prometheus.monitoring:9090 query: | sum(rate(http_requests_total{service="{{args.service-name}}",status=~"2.."}[1m])) / sum(rate(http_requests_total{service="{{args.service-name}}"}[1m]))ignoreDifferences for HPA fights
Section titled “ignoreDifferences for HPA fights”spec: ignoreDifferences: - group: apps kind: Deployment jsonPointers: [/spec/replicas]Promotion via PR (GitHub Actions)
Section titled “Promotion via PR (GitHub Actions)”- name: Bump prod manifest run: | sed -i "s|newTag:.*|newTag: ${{ github.sha }}|" \ manifests/overlays/prod/api/kustomization.yaml git config user.name "release-bot" git config user.email "bot@example.com" git checkout -b prod-bump-${{ github.sha }} git commit -am "prod: api -> ${{ github.sha }}" git push -u origin HEAD gh pr create --base main --head prod-bump-${{ github.sha }} \ --title "prod: api -> ${{ github.sha }}" \ --reviewer @org/platformRepo layout (multi-cluster)
Section titled “Repo layout (multi-cluster)”manifests/ clusters/ prod-eu/ argocd-apps/{api,web,workers}.yaml staging-eu/ argocd-apps/... apps/ api/ base/ overlays/{prod,staging,dev}/ infra/ cert-manager/ ingress-nginx/ external-secrets/- Argo CD / Flux — controllers.
- Argo Rollouts / Flagger — progressive delivery.
- Argo Workflows / Tekton — CI on K8s.
- Argo Events — event-driven workflows.
- Image Updater / Renovate — version bumping.
- Sealed Secrets / SOPS / External Secrets — secret mgmt.
- kargo — promotion automation.
- Lens / Headlamp — UI debugging.