CI/CD Pipeline Integration

Manually building and deploying containers works for development, but production demands automation. I’ve built CI/CD pipelines that deploy hundreds of times per day while maintaining security and reliability. The key is treating your pipeline as code and building in quality gates at every step.

Pipeline Architecture Strategy

A robust container CI/CD pipeline includes several stages that fail fast and provide clear feedback:

  1. Source Stage: Code commit triggers the pipeline
  2. Test Stage: Unit tests, integration tests, and code quality checks
  3. Security Stage: Vulnerability scanning and compliance checks
  4. Build Stage: Container image creation and optimization
  5. Deploy Stage: Progressive rollout with monitoring and rollback capabilities

GitHub Actions Container Pipeline

GitHub Actions provides excellent container support with built-in Docker registry integration:

# .github/workflows/container-pipeline.yml
name: Container CI/CD Pipeline

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '18'
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Run tests
        run: npm test -- --coverage

      - name: Upload coverage
        uses: codecov/codecov-action@v3

  security-scan:
    runs-on: ubuntu-latest
    needs: test
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Run Trivy scanner
        uses: aquasecurity/trivy-action@master
        with:
          scan-type: 'fs'
          format: 'sarif'
          output: 'trivy-results.sarif'

      - name: Upload scan results
        uses: github/codeql-action/upload-sarif@v2
        with:
          sarif_file: 'trivy-results.sarif'

  build-and-push:
    runs-on: ubuntu-latest
    needs: [test, security-scan]
    if: github.event_name != 'pull_request'
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Log in to 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: .
          platforms: linux/amd64,linux/arm64
          push: true
          tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

  deploy-staging:
    runs-on: ubuntu-latest
    needs: build-and-push
    if: github.ref == 'refs/heads/develop'
    environment: staging
    steps:
      - name: Deploy to staging
        run: |
          kubectl set image deployment/web-app web=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }} -n staging
          kubectl rollout status deployment/web-app -n staging

Advanced Testing Strategies

Comprehensive testing prevents issues from reaching production:

# docker-compose.test.yml
version: '3.8'

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile.test
    environment:
      - NODE_ENV=test
      - DATABASE_URL=postgresql://test:test@db:5432/testdb
    depends_on:
      db:
        condition: service_healthy

  db:
    image: postgres:15-alpine
    environment:
      - POSTGRES_DB=testdb
      - POSTGRES_USER=test
      - POSTGRES_PASSWORD=test
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U test -d testdb"]
      interval: 5s
      retries: 5

  integration-tests:
    build:
      context: .
      dockerfile: Dockerfile.test
    environment:
      - API_URL=http://app:8080
    depends_on:
      - app
    command: npm run test:integration

Security Integration

Security scanning should be integrated throughout the pipeline:

#!/bin/bash
# security-scan.sh

IMAGE_NAME=${1:-myapp:latest}
SEVERITY_THRESHOLD=${2:-HIGH}

echo "Running security scan for $IMAGE_NAME"

# Scan the built image
trivy image \
  --severity $SEVERITY_THRESHOLD,CRITICAL \
  --format json \
  --output image-scan.json \
  $IMAGE_NAME

# Scan for secrets
trivy fs \
  --scanners secret \
  --format json \
  --output secret-scan.json \
  .

# Check results
CRITICAL_VULNS=$(jq '.Results[]?.Vulnerabilities[]? | select(.Severity == "CRITICAL") | length' image-scan.json | wc -l)

if [ "$CRITICAL_VULNS" -gt 0 ]; then
    echo "Critical vulnerabilities found. Failing build."
    exit 1
fi

echo "Security scan passed"

Progressive Deployment

Implement safe deployment patterns that minimize risk:

# canary-deployment.yaml
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: web-app-rollout
spec:
  replicas: 10
  strategy:
    canary:
      steps:
      - setWeight: 10
      - pause: {duration: 2m}
      - setWeight: 50
      - pause: {duration: 5m}
      analysis:
        templates:
        - templateName: success-rate
      canaryService: web-app-canary
      stableService: web-app-stable
  selector:
    matchLabels:
      app: web-app
  template:
    spec:
      containers:
      - name: web
        image: myregistry.io/web-app:latest

Multi-Environment Pipeline

Manage deployments across environments with proper promotion gates:

# Multi-environment workflow
jobs:
  deploy-dev:
    needs: build
    environment: development
    steps:
      - name: Deploy to dev
        run: kubectl set image deployment/web-app web=${{ needs.build.outputs.image }} -n dev

  test-dev:
    needs: deploy-dev
    steps:
      - name: Run smoke tests
        run: curl -f https://dev.myapp.com/health

  deploy-staging:
    needs: test-dev
    environment: staging
    steps:
      - name: Deploy to staging
        run: kubectl set image deployment/web-app web=${{ needs.build.outputs.image }} -n staging

  deploy-production:
    needs: deploy-staging
    environment: production
    steps:
      - name: Deploy to production
        run: |
          kubectl set image deployment/web-app web=${{ needs.build.outputs.image }} -n production
          kubectl rollout status deployment/web-app -n production

Pipeline Troubleshooting

Common issues I’ve encountered and their solutions:

Flaky tests causing failures:

- name: Run tests with retry
  uses: nick-invision/retry@v2
  with:
    timeout_minutes: 10
    max_attempts: 3
    command: npm test

Docker build cache misses:

- name: Build with cache
  uses: docker/build-push-action@v5
  with:
    cache-from: type=gha
    cache-to: type=gha,mode=max

A well-designed CI/CD pipeline becomes the backbone of your containerization strategy. It should be fast, reliable, and provide clear feedback when things go wrong. In the next part, I’ll cover monitoring and observability for production container environments.