Skip to main content

10 - GitOps with ArgoCD: Declarative Deploy and Progressive Rollout

GitOps with ArgoCD: Declarative Deployment and Progressive Rollouts. GitOps is the paradigm that transforms Git from code repository to single source of truth for infrastructure, enabling automated, version-controlled deployments with audit trails and rollback capabilities.

What do you think about this article?

At a Glance

GitOps with ArgoCD: Declarative Deployment and Progressive Rollouts. GitOps is the paradigm that transforms Git from code repository to single source of truth for infrastructure, enabling automated, version-controlled deployments with audit trails and rollback capabilities.

GitOps with ArgoCD: Declarative Deploy and Progressive Rollouts

GitOps is the paradigm that transforms Git from a code repository to single source of truth for the status of your Kubernetes cluster. The idea is simple but powerful: everything is deployed in the cluster must be described in YAML manifests versioned in Git. An agent in the cluster (ArgoCD) observes the repository and ensures that the real state of the cluster always converges with the desired state in Git. If someone manually applies a hotfix directly with kubectl, ArgoCD detects it and cancels it automatically.

In this article we will see how to configure ArgoCD from scratch, implement the pattern App of Apps to manage dozens of applications declaratively, use ApplicationSet for multi-cluster and multi-environment, and integrate Argo Rollouts for canary deployment and blue-green without downtime.

What You Will Learn

  • Installation and configuration of ArgoCD with Helm
  • Create Application ArgoCD for deployment synchronized with Git
  • App of Apps pattern to manage many applications
  • ApplicationSet: automated multi-cluster and multi-environment deployment
  • Sync policies: automated sync, self-heal, pruning
  • Argo Rollouts: canary with statistical analysis, blue-green, traffic splitting
  • ArgoCD RBAC for multi-team environments
  • Slack/Teams notifications on deployment events

ArgoCD installation

# Installa ArgoCD con Helm (raccomandato per produzione)
helm repo add argo https://argoproj.github.io/argo-helm
helm repo update

helm install argocd argo/argo-cd \
  --namespace argocd \
  --create-namespace \
  --version 7.6.0 \
  --set global.domain=argocd.company.com \
  --set server.ingress.enabled=true \
  --set server.ingress.ingressClassName=nginx \
  --set configs.params."server.insecure"=true \
  --set dex.enabled=false  # disabilita se non usi SSO

# Ottieni la password admin iniziale
kubectl -n argocd get secret argocd-initial-admin-secret \
  -o jsonpath="{.data.password}" | base64 -d

# Accedi con ArgoCD CLI
argocd login argocd.company.com
argocd account update-password

# Verifica stato
argocd version
kubectl get pods -n argocd

First Application ArgoCD

Una Application ArgoCD is the main resource: defines where from get the manifests (source) and where to deploy them (destination):

# application-api-service.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: api-service-production
  namespace: argocd
  labels:
    team: team-alpha
    environment: production
  finalizers:
    - resources-finalizer.argocd.argoproj.io  # elimina risorse K8s quando Application viene eliminata
spec:
  project: team-alpha    # ArgoCD Project per RBAC

  source:
    repoURL: https://github.com/company/k8s-manifests
    targetRevision: main  # branch/tag/commit SHA
    path: apps/api-service/production  # path nel repo

  destination:
    server: https://kubernetes.default.svc  # cluster locale
    namespace: team-alpha-production

  syncPolicy:
    automated:
      prune: true      # elimina risorse non piu presenti in Git
      selfHeal: true   # ripristina modifiche manuali non autorizzate
    syncOptions:
      - CreateNamespace=true       # crea namespace se non esiste
      - PrunePropagationPolicy=foreground
      - ApplyOutOfSyncOnly=true    # applica solo le risorse cambiate
    retry:
      limit: 5
      backoff:
        duration: 5s
        factor: 2
        maxDuration: 3m

  # Ignorare alcune differenze (es. annotation aggiunte dal cluster)
  ignoreDifferences:
    - group: apps
      kind: Deployment
      jsonPointers:
        - /spec/replicas  # ignora modifiche manuali al numero di repliche

Structure of the Git Repository for GitOps

The structure of the GitOps repo directly impacts maintainability. The most pattern common and separate the application code repo from the K8s manifest repo:

# Struttura raccomandata del repo k8s-manifests:
k8s-manifests/
├── apps/                        # manifest applicazioni
│   ├── api-service/
│   │   ├── base/               # Kustomize base
│   │   │   ├── deployment.yaml
│   │   │   ├── service.yaml
│   │   │   └── kustomization.yaml
│   │   ├── dev/
│   │   │   ├── kustomization.yaml  # overlays per dev
│   │   │   └── resources-patch.yaml
│   │   ├── staging/
│   │   │   └── kustomization.yaml
│   │   └── production/
│   │       ├── kustomization.yaml
│   │       └── hpa.yaml
│   └── worker-service/
│       └── ...
├── platform/                    # risorse platform (ingress, cert-manager, etc.)
│   ├── ingress-nginx/
│   ├── cert-manager/
│   └── monitoring/
└── argocd/                      # App of Apps: Application resources
    ├── root-app.yaml            # App of Apps root
    ├── team-alpha.yaml
    └── platform.yaml

# Kustomize base per api-service
# base/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
  - deployment.yaml
  - service.yaml
commonLabels:
  app: api-service
  managed-by: argocd

# production/kustomization.yaml - overlay con 3 repliche
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
bases:
  - ../base
replicas:
  - name: api-service
    count: 3
images:
  - name: company.registry.io/api-service
    newTag: "v2.5.1"   # aggiornato dalla CI/CD pipeline
patchesStrategicMerge:
  - resources-patch.yaml

App of Apps Pattern

With App of Apps, a single ArgoCD Application (the "root app") manages all other ArgoCD Applications. It is the most scalable pattern for clusters with many applications:

# argocd/root-app.yaml - la App of Apps root
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: root-app
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/company/k8s-manifests
    targetRevision: main
    path: argocd/           # questa cartella contiene le Application resources
  destination:
    server: https://kubernetes.default.svc
    namespace: argocd
  syncPolicy:
    automated:
      prune: true
      selfHeal: true

---
# argocd/team-alpha.yaml - Application che punta agli overlay di team-alpha
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: team-alpha-production
  namespace: argocd
spec:
  project: team-alpha
  source:
    repoURL: https://github.com/company/k8s-manifests
    targetRevision: main
    path: apps/api-service/production
    kustomize:
      images:
        - company.registry.io/api-service:v2.5.1  # aggiornato dalla CI
  destination:
    server: https://kubernetes.default.svc
    namespace: team-alpha-production
  syncPolicy:
    automated:
      prune: true
      selfHeal: true

ApplicationSet: Multi-Cluster and Multi-Environment

ApplicationSet automatically generates multiple ArgoCD Applications from one template. Ideal for deploying the same application on many clusters or environments:

# applicationset-multi-env.yaml
# Deploy api-service su dev, staging e production dallo stesso template
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: api-service-all-envs
  namespace: argocd
spec:
  generators:
    - list:
        elements:
          - environment: dev
            namespace: team-alpha-dev
            replicaCount: "1"
            imageTag: "latest"
            cluster: in-cluster
          - environment: staging
            namespace: team-alpha-staging
            replicaCount: "2"
            imageTag: "v2.5.0"
            cluster: in-cluster
          - environment: production
            namespace: team-alpha-production
            replicaCount: "5"
            imageTag: "v2.4.9"  # production usa tag stabile
            cluster: production-cluster
  template:
    metadata:
      name: 'api-service-{{environment}}'
      labels:
        environment: '{{environment}}'
    spec:
      project: team-alpha
      source:
        repoURL: https://github.com/company/k8s-manifests
        targetRevision: main
        path: 'apps/api-service/{{environment}}'
        kustomize:
          images:
            - 'company.registry.io/api-service:{{imageTag}}'
      destination:
        server: '{{cluster}}'
        namespace: '{{namespace}}'
      syncPolicy:
        automated:
          prune: true
          selfHeal: '{{eq environment "production" | not}}'  # no selfHeal in prod

Argo Rollouts: Canary Deploy and Blue-Green

Argo Rollouts extends Kubernetes with advanced deployment strategies: canary with analytics statistics, blue-green with manual or automatic switching, traffic splitting with Istio or NGINX input:

# Installa Argo Rollouts
kubectl create namespace argo-rollouts
kubectl apply -n argo-rollouts \
  -f https://github.com/argoproj/argo-rollouts/releases/latest/download/install.yaml

# Installa plugin kubectl
curl -LO https://github.com/argoproj/argo-rollouts/releases/latest/download/kubectl-argo-rollouts-linux-amd64
chmod +x kubectl-argo-rollouts-linux-amd64
sudo mv kubectl-argo-rollouts-linux-amd64 /usr/local/bin/kubectl-argo-rollouts

Canary Deploy with Automatic Analysis

# rollout-canary.yaml
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: api-service
  namespace: team-alpha-production
spec:
  replicas: 10
  selector:
    matchLabels:
      app: api-service
  template:
    metadata:
      labels:
        app: api-service
    spec:
      containers:
        - name: api
          image: company.registry.io/api-service:v2.5.1
          resources:
            requests:
              cpu: 250m
              memory: 512Mi
            limits:
              cpu: 500m
              memory: 1Gi

  strategy:
    canary:
      # Step 1: 10% traffico alla nuova versione
      # Step 2: analisi automatica per 5 minuti
      # Step 3: se ok, vai al 30% poi 100%
      steps:
        - setWeight: 10   # 10% traffico al canary
        - analysis:       # analisi automatica
            templates:
              - templateName: success-rate
            args:
              - name: service-name
                value: api-service
        - pause: {duration: 5m}
        - setWeight: 30
        - pause: {duration: 5m}
        - setWeight: 100

      # Canary Service: il nuovo Service per il traffico canary
      canaryService: api-service-canary
      stableService: api-service-stable

      # Traffic routing con NGINX Ingress
      trafficRouting:
        nginx:
          stableIngress: api-service-ingress
---
# AnalysisTemplate: definisce i criteri di successo
apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
  name: success-rate
  namespace: team-alpha-production
spec:
  args:
    - name: service-name
  metrics:
    - name: success-rate
      interval: 1m
      successCondition: result[0] >= 0.95   # 95% di successo richiesto
      failureLimit: 3   # fallisce dopo 3 misurazioni consecutive < 95%
      provider:
        prometheus:
          address: http://prometheus.monitoring.svc:9090
          query: |
            sum(rate(http_requests_total{service="{{args.service-name}}",status=~"2.."}[5m]))
            /
            sum(rate(http_requests_total{service="{{args.service-name}}"}[5m]))
    - name: latency-p99
      interval: 1m
      successCondition: result[0] < 0.5    # P99 < 500ms
      provider:
        prometheus:
          address: http://prometheus.monitoring.svc:9090
          query: |
            histogram_quantile(0.99,
              sum(rate(http_request_duration_seconds_bucket{service="{{args.service-name}}"}[5m])) by (le)
            )

Rollout management via CLI

# Visualizza stato del rollout in tempo reale
kubectl argo rollouts get rollout api-service -n team-alpha-production --watch

# Promuovi manualmente al prossimo step (se pause)
kubectl argo rollouts promote api-service -n team-alpha-production

# Rollback immediato se qualcosa va storto
kubectl argo rollouts abort api-service -n team-alpha-production
kubectl argo rollouts undo api-service -n team-alpha-production

# Dashboard UI di Argo Rollouts
kubectl argo rollouts dashboard &
# Apri http://localhost:3100

ArgoCD notifications for Slack and Teams

# argocd-notifications-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: argocd-notifications-cm
  namespace: argocd
data:
  service.slack: |
    token: $slack-token
  template.app-deployed: |
    message: |
      :rocket: Application *{{.app.metadata.name}}* deployed to *{{.app.spec.destination.namespace}}*
      Image: `{{index .app.status.summary.images 0}}`
      Sync: {{.app.status.sync.status}}
  template.app-health-degraded: |
    message: |
      :red_circle: Application *{{.app.metadata.name}}* health degraded!
      {{range .app.status.conditions}}{{.message}}{{end}}
  trigger.on-deployed: |
    - description: Application is synced and healthy
      send:
        - app-deployed
      when: app.status.operationState.phase in ['Succeeded'] and
            app.health.status == 'Healthy'
  trigger.on-health-degraded: |
    - description: Application has degraded
      send:
        - app-health-degraded
      when: app.health.status == 'Degraded'
---
# Applica annotazione sull'Application per ricevere notifiche
kubectl annotate app api-service-production \
  notifications.argoproj.io/subscribe.on-deployed.slack="deployments-channel" \
  -n argocd

GitOps Best Practices with ArgoCD

Production GitOps Checklist

  • Separate repos for K8s app code and manifest: The GitOps repo must not contain application code. Separate config from code
  • Image tag in the GitOps repo (not "latest"): The image tag must be a SHA or version specific, not "latest" - for reproducibility
  • Mandatory review for production: PRs to branch production require at least one approval. ArgoCD should not apply automatically without review in prod
  • Secret with External Secrets Operator: Never make Secret clear in the GitOps repo. Use ESO with AWS Secrets Manager or Vault
  • Self-heal in dev, manual in prod: Automatic self-heal is useful in dev/staging but risky in prod (may cancel urgent hotfixes)
  • Use Kustomize or Helm, not raw YAML: Templating allows reuse of overlays per environment avoiding duplication

Conclusions and Next Steps

ArgoCD transforms Kubernetes deployment from a manual and risky operation to a process automated, auditable and reversible. The combination of App of Apps for management cluster-wide declarative and Argo Rollouts for progressive canary deployments creates a delivery pipeline that balances speed and security.

The next natural step after GitOps is observability: without metrics and logging, not you can know if your canary is succeeding or introducing regressions. The article 11 of this series - Prometheus, Grafana and OpenTelemetry - completes the picture.

Related Series

Try These Tools

Free developer tools related to this topic.

Commenti

Caricamento commenti...

Sign in to comment

Discuss this article in the community

Join the Telegram group to discuss with other developers, ask questions and share your experiences.

Did you like this article?

Explore other content on the blog or discover my projects