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
yamlapiVersion: 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
| Tool | Type | Where | Strengths |
|---|---|---|---|
| GitHub Actions | CI + CD | Cloud | Native GitHub integration |
| GitLab CI | CI + CD | Cloud/self-hosted | All-in-one DevOps |
| Jenkins | CI + CD | Self-hosted | Highly customizable |
| ArgoCD | CD (GitOps) | In-cluster | K8s-native, Git-driven |
| Flux | CD (GitOps) | In-cluster | Lightweight, CNCF |
| Tekton | CI + CD | In-cluster | K8s-native CI |
| Argo Rollouts | Progressive CD | In-cluster | Canary/blue-green |
| Spinnaker | CD | Self-hosted | Multi-cloud, Netflix-scale |
FAANG Interview Angle
Common questions:
- "Describe your ideal CI/CD pipeline for a K8s application"
- "What is GitOps and why is it beneficial?"
- "How do you handle rollbacks in a CI/CD pipeline?"
- "How do you manage secrets in CI/CD?"
- "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 reverttriggers ArgoCD sync; orhelm 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