GitOps & Continuous Delivery

ArgoCD for Platform Teams

4 min read

ArgoCD is the most popular GitOps tool for Kubernetes. For platform teams, it provides the foundation for automated, auditable deployments across multiple clusters and environments.

Installing ArgoCD

Deploy ArgoCD to your cluster:

# Create namespace
kubectl create namespace argocd

# Install ArgoCD (HA mode for production)
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/ha/install.yaml

# Or using Helm for more control
helm repo add argo https://argoproj.github.io/argo-helm
helm repo update

helm install argocd argo/argo-cd \
  --namespace argocd \
  --create-namespace \
  --set server.service.type=LoadBalancer \
  --set configs.params."server\.insecure"=true

# Wait for pods
kubectl wait --for=condition=Ready pods --all -n argocd --timeout=300s

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

# Access UI
kubectl port-forward svc/argocd-server -n argocd 8080:443
# Open https://localhost:8080

ArgoCD Architecture

┌─────────────────────────────────────────────────────────┐
│                    ARGOCD COMPONENTS                     │
├─────────────────────────────────────────────────────────┤
│                                                          │
│  ┌──────────────────┐     ┌──────────────────┐          │
│  │   API Server     │────│    Web UI        │          │
│  │  (argocd-server) │     │  (React app)     │          │
│  └────────┬─────────┘     └──────────────────┘          │
│           │                                              │
│  ┌────────┴─────────┐     ┌──────────────────┐          │
│  │   Repo Server    │────│  Application     │          │
│  │ (argocd-repo-    │     │  Controller      │          │
│  │  server)         │     │ (reconciliation) │          │
│  └──────────────────┘     └────────┬─────────┘          │
│           │                        │                     │
│           ▼                        ▼                     │
│  ┌──────────────────┐     ┌──────────────────┐          │
│  │   Git Repos      │     │    Clusters      │          │
│  │  (source of      │     │  (deployment     │          │
│  │   truth)         │     │   targets)       │          │
│  └──────────────────┘     └──────────────────┘          │
│                                                          │
└─────────────────────────────────────────────────────────┘

Creating Applications

Deploy your first application with ArgoCD:

# application.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: user-service
  namespace: argocd
  finalizers:
    - resources-finalizer.argocd.argoproj.io
spec:
  project: default

  source:
    repoURL: https://github.com/acme/gitops-repo.git
    targetRevision: main
    path: apps/overlays/production/user-service

  destination:
    server: https://kubernetes.default.svc
    namespace: production

  syncPolicy:
    automated:
      prune: true        # Delete resources removed from Git
      selfHeal: true     # Fix drift automatically
      allowEmpty: false  # Don't sync if source is empty
    syncOptions:
      - CreateNamespace=true
      - PrunePropagationPolicy=foreground
      - PruneLast=true
    retry:
      limit: 5
      backoff:
        duration: 5s
        factor: 2
        maxDuration: 3m
# Apply the application
kubectl apply -f application.yaml

# Check status
argocd app get user-service

# Sync manually if needed
argocd app sync user-service

# View in UI
argocd app list

ApplicationSets for Scale

ApplicationSets generate multiple Applications from a single template:

# applicationset-all-apps.yaml
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: all-applications
  namespace: argocd
spec:
  generators:
    # Generate from directory structure
    - git:
        repoURL: https://github.com/acme/gitops-repo.git
        revision: main
        directories:
          - path: apps/overlays/production/*

  template:
    metadata:
      name: '{{path.basename}}'
      namespace: argocd
    spec:
      project: default
      source:
        repoURL: https://github.com/acme/gitops-repo.git
        targetRevision: main
        path: '{{path}}'
      destination:
        server: https://kubernetes.default.svc
        namespace: production
      syncPolicy:
        automated:
          prune: true
          selfHeal: true

Multi-Cluster ApplicationSets

Deploy to multiple clusters:

# applicationset-multi-cluster.yaml
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: multi-cluster-apps
  namespace: argocd
spec:
  generators:
    - matrix:
        generators:
          # Clusters
          - clusters:
              selector:
                matchLabels:
                  environment: production
          # Applications
          - git:
              repoURL: https://github.com/acme/gitops-repo.git
              revision: main
              directories:
                - path: apps/base/*

  template:
    metadata:
      name: '{{name}}-{{path.basename}}'
    spec:
      project: default
      source:
        repoURL: https://github.com/acme/gitops-repo.git
        targetRevision: main
        path: 'apps/overlays/{{metadata.labels.environment}}/{{path.basename}}'
      destination:
        server: '{{server}}'
        namespace: '{{path.basename}}'
      syncPolicy:
        automated:
          prune: true
          selfHeal: true
        syncOptions:
          - CreateNamespace=true

Adding Clusters

Register external clusters with ArgoCD:

# Add cluster using kubeconfig context
argocd cluster add production-cluster --name production

# Or via declarative Secret
kubectl apply -f - <<EOF
apiVersion: v1
kind: Secret
metadata:
  name: production-cluster
  namespace: argocd
  labels:
    argocd.argoproj.io/secret-type: cluster
    environment: production
type: Opaque
stringData:
  name: production
  server: https://production.k8s.acme.com
  config: |
    {
      "bearerToken": "...",
      "tlsClientConfig": {
        "insecure": false,
        "caData": "..."
      }
    }
EOF

# List clusters
argocd cluster list

Projects for Multi-Tenancy

ArgoCD Projects provide RBAC boundaries:

# project-team-orders.yaml
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
  name: team-orders
  namespace: argocd
spec:
  description: "Order team applications"

  # Allowed source repositories
  sourceRepos:
    - https://github.com/acme/orders-*
    - https://github.com/acme/gitops-repo.git

  # Allowed destinations
  destinations:
    - namespace: 'orders-*'
      server: https://kubernetes.default.svc
    - namespace: team-orders
      server: '*'

  # Allowed cluster resources
  clusterResourceWhitelist:
    - group: ''
      kind: Namespace

  # Allowed namespace resources
  namespaceResourceWhitelist:
    - group: '*'
      kind: '*'

  # Denied resources
  namespaceResourceBlacklist:
    - group: ''
      kind: ResourceQuota
    - group: ''
      kind: LimitRange

  # Role definitions
  roles:
    - name: developer
      description: "Can sync and view applications"
      policies:
        - p, proj:team-orders:developer, applications, get, team-orders/*, allow
        - p, proj:team-orders:developer, applications, sync, team-orders/*, allow
      groups:
        - team-orders-developers

    - name: admin
      description: "Full access to team applications"
      policies:
        - p, proj:team-orders:admin, applications, *, team-orders/*, allow
      groups:
        - team-orders-admins

Notifications

Configure ArgoCD notifications for deployment events:

# argocd-notifications-cm.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: argocd-notifications-cm
  namespace: argocd
data:
  service.slack: |
    token: $slack-token

  template.app-deployed: |
    message: |
      Application {{.app.metadata.name}} is now {{.app.status.sync.status}}.
      {{if eq .app.status.sync.status "Synced"}}
      :white_check_mark: Deployment successful!
      {{end}}
    slack:
      attachments: |
        [{
          "title": "{{.app.metadata.name}}",
          "color": "{{if eq .app.status.sync.status \"Synced\"}}good{{else}}warning{{end}}",
          "fields": [
            {"title": "Sync Status", "value": "{{.app.status.sync.status}}", "short": true},
            {"title": "Repository", "value": "{{.app.spec.source.repoURL}}", "short": true}
          ]
        }]

  trigger.on-deployed: |
    - when: app.status.operationState.phase in ['Succeeded'] and app.status.health.status == 'Healthy'
      send: [app-deployed]
# Add annotation to Application
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: user-service
  annotations:
    notifications.argoproj.io/subscribe.on-deployed.slack: deployments-channel

In the next lesson, we'll explore progressive delivery patterns with Argo Rollouts for safer deployments. :::

Quiz

Module 4: GitOps & Continuous Delivery

Take Quiz