Lesson 14 GitOps with ArgoCD
Theme:

Homework 14 Solutions — GitOps with ArgoCD

Lesson 14 · Cloud Infrastructure & DevOps

Solution to Task 1 — ArgoCD Notifications child Application

Create cloud-devops/sample-app/argocd/children/argocd-notifications.yaml:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: argocd-notifications
  namespace: argocd
  annotations:
    argocd.argoproj.io/sync-wave: "-1"
spec:
  project: default
  source:
    repoURL: https://argoproj.github.io/argo-helm
    chart: argocd-notifications
    targetRevision: 1.8.1
    helm:
      values: |
        notifiers:
          service.webhook.test-hook: |
            url: https://webhook.site/<your-tempid>
            headers:
              - name: Content-Type
                value: application/json
        subscriptions:
        - recipients: [test-hook]
          triggers: [on-sync-succeeded, on-sync-failed, on-health-degraded]
  destination:
    server: https://kubernetes.default.svc
    namespace: argocd
  syncPolicy:
    automated: {prune: true, selfHeal: true}

Then add the subscription annotation to app-url-shortener.yaml (and to children/url-shortener.yaml):

metadata:
  annotations:
    argocd.argoproj.io/sync-wave: "0"
    notifications.argoproj.io/subscribe.on-sync-succeeded.test-hook: ""
    notifications.argoproj.io/subscribe.on-sync-failed.test-hook: ""
Common mistake: subscribing to on-sync-running instead of on-sync-succeeded. Sync-running fires on every reconciliation — you will receive a webhook every 3 minutes even when nothing changed. Subscribe to succeeded and failed only. The on-health-degraded trigger is useful for alerting when a Deployment’s pods are crash-looping.

Solution to Task 2 — Sealed Secrets migration plan

Sample plan using the Sealed Secrets path:

  1. Install Sealed Secrets via an ArgoCD child Application (Helm chart sealed-secrets from https://bitnami-labs.github.io/sealed-secrets), wave −1 so it precedes the app.
  2. CRD: SealedSecret (group bitnami.com/v1alpha1) — a custom resource that wraps an encrypted blob. The bitnami Sealed Secrets controller decrypts it inside the cluster using the cluster’s private key and creates a regular Secret.
  3. Where unencrypted values live: only on the developer’s laptop, never in git. The developer runs kubeseal CLI, which reads a plaintext Secret YAML on stdin, encrypts it with the controller’s public key, and prints a SealedSecret YAML to stdout. The SealedSecret is what goes in git.
  4. Migrate values-prod.yaml by extracting each secret value into a separate Kubernetes Secret YAML, sealing it (kubeseal < secret.yaml > sealed-secret.yaml), committing the SealedSecret, and referencing the resulting in-cluster Secret name from the Deployment via envFrom.secretRef. The values-prod.yaml then only references secret names, not values.
  5. Rotation: generate a new plaintext Secret, seal it, commit the new SealedSecret. ArgoCD syncs. The Sealed Secrets controller sees the updated resource and updates the in-cluster Secret. No plaintext ever reaches git.
  6. The cluster controller’s public key is the only material the developer needs locally (fetched via kubeseal --fetch-cert --controller-namespace kube-system). The cluster’s private key never leaves the cluster.
  7. SealedSecrets are namespace-scoped by default — a SealedSecret created for the url-shortener namespace cannot be decrypted into kube-system. This is deliberate; respect the boundary.
Common mistake: copying a SealedSecret YAML between two clusters. Each cluster has a unique private key — the SealedSecret can only be decrypted by the cluster whose public key was used for encryption. Run kubeseal --fetch-cert per cluster.

Solution to Task 3 — Argo Rollouts vs blue/green comparison

Rollout CR vs Deployment. Rollout (argoproj.io/v1alpha1) is a CRD that replaces Deployment for the canary use-case. The pod spec (spec.template.spec) is identical to a Deployment’s. The difference is spec.strategy.canary with steps:

spec:
  strategy:
    canary:
      steps:
      - setWeight: 10
      - pause: {duration: 5m}
      - analysis:
          templates:
          - templateName: success-rate
      - setWeight: 50
      - pause: {duration: 5m}
      - setWeight: 100

Each setWeight updates a traffic split (via the ingress controller’s annotations or a service mesh). Each analysis step runs an AnalysisTemplate — a CRD containing a Prometheus query that must return a passing result; failure aborts the rollout and triggers a rollback.

AnalysisTemplate example. A template that asserts the canary’s error rate is below 1%:

apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata: {name: success-rate}
spec:
  metrics:
  - name: success-rate
    interval: 30s
    successCondition: result[0] >= 0.99
    provider:
      prometheus:
        address: http://kps-kube-prometheus-stack-prometheus.monitoring.svc:9090
        query: |
          sum(rate(http_requests_total{code!~"5..",service="{{args.service-name}}"}[2m]))
            / sum(rate(http_requests_total{service="{{args.service-name}}"}[2m]))

The Rollout controller polls this query every 30 seconds and gates the next setWeight step on the result. If the canary’s error rate exceeds 1%, the analysis fails, the rollout halts, and traffic is routed back to the stable version automatically.

When to stick with blue/green from L13. When you do not yet have a meaningful metric-comparison story. Argo Rollouts is “blue/green plus automated metric analysis”. If you cannot write a PromQL query that meaningfully separates a healthy canary from an unhealthy one (because you have too little traffic, or your metrics are not instrumented at the right granularity), canary buys you nothing — it just slows down the deploy. For a 2-service URL shortener with ≤10 rps in development, blue/green with a manual 30-second observation window is correct. For a multi-service product with ≥1000 rps and SRE-owned SLIs, Argo Rollouts pays for itself in the first incident it prevents.

Common mistake: treating Rollout as “Deployment + traffic split” and omitting the analysis block entirely. A canary without metric analysis is just a slower blue/green — you get the complexity of Argo Rollouts without the automated safety gate that justifies it.

Course complete. You built the platform around an app, then watched ArgoCD make the platform self-managing. From here: pick a real app you own; run it through the same stack; operate it for 30 days; come back and re-read your own dashboards and runbook. That’s the loop. Everything else is taste.