22 - CI/CD with Docker & K8s

CI/CD Pipeline Overview

Developer pushes code
        │
        ▼
┌─── CI (Continuous Integration) ────────────────┐
│  1. Checkout code                              │
│  2. Run tests (unit, integration)              │
│  3. Build Docker image                         │
│  4. Scan image for vulnerabilities             │
│  5. Push image to registry                     │
│  6. Tag with git SHA + version                 │
└───────────────────────┬────────────────────────┘
                        │
                        ▼
┌─── CD (Continuous Delivery/Deployment) ────────┐
│  7. Update K8s manifests with new image tag    │
│  8. Deploy to staging                          │
│  9. Run E2E tests                              │
│ 10. Deploy to production (manual gate or auto) │
│ 11. Monitor rollout health                     │
└────────────────────────────────────────────────┘

GitHub Actions CI Pipeline

yaml
# .github/workflows/ci.yml name: CI on: push: branches: [main] pull_request: branches: [main] env: REGISTRY: ghcr.io IMAGE_NAME: ${{ github.repository }} jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Run tests run: | npm ci npm test npm run lint build-and-push: needs: test runs-on: ubuntu-latest permissions: contents: read packages: write steps: - uses: actions/checkout@v4 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Login to Container Registry uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Build and push uses: docker/build-push-action@v5 with: context: . push: ${{ github.event_name != 'pull_request' }} tags: | ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }} ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest cache-from: type=gha cache-to: type=gha,mode=max - name: Scan image uses: aquasecurity/trivy-action@master with: image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }} severity: CRITICAL,HIGH exit-code: 1

GitOps with ArgoCD

GitOps principle: Git is the single source of truth for infrastructure and application state.

Developer pushes code → CI builds image → CI updates manifest repo
                                                    │
                              ArgoCD watches ◄──────┘
                                    │
                                    ▼
                        ArgoCD syncs to K8s cluster
                                    │
                                    ▼
                            Application deployed

ArgoCD Setup

bash
# Install ArgoCD kubectl create namespace argocd kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml # Access UI kubectl port-forward svc/argocd-server -n argocd 8080:443 # Get initial admin password kubectl -n argocd get secret argocd-initial-admin-secret \ -o jsonpath="{.data.password}" | base64 -d

ArgoCD Application

yaml
apiVersion: argoproj.io/v1alpha1 kind: Application metadata: name: my-app namespace: argocd spec: project: default source: repoURL: https://github.com/myorg/k8s-manifests.git targetRevision: main path: apps/my-app/overlays/production destination: server: https://kubernetes.default.svc namespace: production syncPolicy: automated: prune: true # Delete resources removed from Git selfHeal: true # Revert manual changes syncOptions: - CreateNamespace=true

GitOps Repository Structure

k8s-manifests/
├── apps/
│   ├── my-app/
│   │   ├── base/
│   │   │   ├── kustomization.yaml
│   │   │   ├── deployment.yaml
│   │   │   ├── service.yaml
│   │   │   └── ingress.yaml
│   │   └── overlays/
│   │       ├── staging/
│   │       │   └── kustomization.yaml
│   │       └── production/
│   │           └── kustomization.yaml
│   └── another-app/
│       └── ...
└── infrastructure/
    ├── monitoring/
    ├── ingress-nginx/
    └── cert-manager/

Deployment Strategies in CI/CD

Progressive Delivery with Argo Rollouts

bash
# Install Argo Rollouts kubectl create namespace argo-rollouts kubectl apply -n argo-rollouts -f https://github.com/argoproj/argo-rollouts/releases/latest/download/install.yaml
yaml
# Canary deployment with automated analysis apiVersion: argoproj.io/v1alpha1 kind: Rollout metadata: name: api spec: replicas: 10 selector: matchLabels: app: api template: metadata: labels: app: api spec: containers: - name: api image: myapp:v2 strategy: canary: steps: - setWeight: 10 # 10% traffic to canary - pause: { duration: 5m } - analysis: templates: - templateName: success-rate - setWeight: 30 # 30% if analysis passes - pause: { duration: 5m } - setWeight: 60 - pause: { duration: 5m } - setWeight: 100 # Full rollout --- apiVersion: argoproj.io/v1alpha1 kind: AnalysisTemplate metadata: name: success-rate spec: metrics: - name: success-rate interval: 60s successCondition: result[0] > 0.95 provider: prometheus: address: http://prometheus.monitoring:9090 query: | sum(rate(http_requests_total{status=~"2.."}[5m])) / sum(rate(http_requests_total[5m]))

Image Tagging Strategy

bash
# Immutable tags based on git SHA (recommended) myapp:abc123f # git commit SHA myapp:main-abc123f # branch + SHA myapp:v1.2.3 # semantic version (for releases) # Mutable tags (for convenience only) myapp:latest # Latest build myapp:main # Latest on main # In CI, always use immutable tags in manifests image: ghcr.io/myorg/myapp:abc123f # Reproducible!

Multi-Environment Promotion

┌─── Development ───┐    ┌─── Staging ──────┐    ┌─── Production ───┐
│ Auto-deploy on    │    │ Auto-deploy      │    │ Manual approval  │
│ PR merge to dev   │───►│ when dev passes  │───►│ or auto after    │
│                   │    │ E2E tests        │    │ staging stable   │
│ image: app:abc123 │    │ image: app:abc123│    │ image: app:abc123│
└───────────────────┘    └──────────────────┘    └──────────────────┘

Same image, different configs (via Kustomize overlays or Helm values).

CI/CD Tools Comparison

ToolTypeWhereStrengths
GitHub ActionsCI + CDCloudNative GitHub integration
GitLab CICI + CDCloud/self-hostedAll-in-one DevOps
JenkinsCI + CDSelf-hostedHighly customizable
ArgoCDCD (GitOps)In-clusterK8s-native, Git-driven
FluxCD (GitOps)In-clusterLightweight, CNCF
TektonCI + CDIn-clusterK8s-native CI
Argo RolloutsProgressive CDIn-clusterCanary/blue-green
SpinnakerCDSelf-hostedMulti-cloud, Netflix-scale

FAANG Interview Angle

Common questions:

  1. "Describe your ideal CI/CD pipeline for a K8s application"
  2. "What is GitOps and why is it beneficial?"
  3. "How do you handle rollbacks in a CI/CD pipeline?"
  4. "How do you manage secrets in CI/CD?"
  5. "How would you implement canary deployments?"

Key answers:

  • CI: test → build → scan → push to registry. CD: update manifests → GitOps sync → progressive rollout
  • GitOps: Git as source of truth; declarative, auditable, reversible (git revert = rollback)
  • Rollback: git revert triggers ArgoCD sync; or helm rollback; or ArgoCD manual sync to previous commit
  • CI secrets: GitHub/GitLab encrypted secrets. K8s secrets: External Secrets Operator from Vault/AWS SM
  • Argo Rollouts: traffic shifting (10% → 30% → 100%) with automated Prometheus analysis between steps

Official Links