Why This Matters - The Performance Bottleneck
When running a global e-commerce platform during Black Friday, your auto-scaling system detects a traffic spike and tries to scale from 10 to 1000 containers, but each container takes 3 minutes to pull and start. By the time your containers are ready, the traffic surge has passed, and you've lost millions in sales. This is where cloud-native Docker optimization becomes critical.
Real-world scenario: A major streaming service regularly deals with sudden traffic spikes during popular events. They need containers that:
- Start in seconds, not minutes, during sudden traffic spikes
- Pull quickly across multiple cloud regions
- Use minimal resources to maximize cost efficiency
- Scale automatically based on real-time demand
The difference between a 500MB optimized image and a 2GB unoptimized image becomes critical when scaling from 10 to 10,000 containers.
Learning Objectives
By the end of this article, you will be able to:
- Design optimized multi-stage builds for cloud-native Go applications
- Implement service-specific optimizations for different deployment patterns
- Create secure, minimal container images that meet production security standards
- Integrate observability patterns for cloud-native monitoring and logging
- Deploy across multiple cloud providers with consistent optimization strategies
- Optimize for auto-scaling scenarios with fast startup and minimal resource usage
Core Concepts - Cloud-Native Docker Philosophy
The Cloud-Native Difference
Cloud-native environments fundamentally change Docker requirements:
Traditional Docker:
- Single region deployment
- Manual scaling decisions
- Large images acceptable
- Manual monitoring and maintenance
Cloud-Native Docker:
- Multi-region deployments - Images must be portable across cloud providers
- Auto-scaling scenarios - Fast image pulls critical for rapid scaling
- Microservice architectures - Many small, specialized containers
- Service mesh integration - Specific networking and security requirements
- Observability needs - Logging, metrics, and tracing integration
💡 Key Insight: Cloud-native Docker isn't just about building small images - it's about building images that work efficiently at scale across distributed systems.
The Optimization Triangle
In cloud-native environments, you must balance three competing factors:
Speed
/\
/ \
/ \
/ \
/ \
/ \
Size <------------> Security
- Size: Smaller images pull faster and scale quicker
- Speed: Optimized for fast startup and shutdown
- Security: Minimal attack surface with only necessary components
You can't maximize all three - you must prioritize based on your use case.
Image Distribution Strategy
When deploying across multiple regions or cloud providers, image distribution becomes critical:
1# docker-compose.cloud.yml - Multi-region deployment strategy
2version: '3.8'
3
4services:
5 api-service:
6 image: ${REGISTRY}/api-service:${VERSION}
7 deploy:
8 replicas: 3
9 resources:
10 limits:
11 cpus: '500m'
12 memory: '512M'
13 reservations:
14 cpus: '250m'
15 memory: '256M'
16 restart_policy:
17 condition: on-failure
18 delay: 5s
19 max_attempts: 3
20 environment:
21 - REGION=${REGION}
22 - ZONE=${ZONE}
23 - CLUSTER=${CLUSTER}
24 labels:
25 - "traefik.enable=true"
26 - "traefik.http.routers.api.rule=Host(`api.${DOMAIN}`)"
27 - "prometheus.scrape=true"
28 - "prometheus.port=8080"
29 - "prometheus.path=/metrics"
30 - "sidecar.istio.io/inject=true"
31 healthcheck:
32 test: ["CMD", "/app/healthcheck"]
33 interval: 30s
34 timeout: 10s
35 retries: 3
36 start_period: 5s
37
38 worker-service:
39 image: ${REGISTRY}/worker-service:${VERSION}
40 deploy:
41 replicas: 5
42 resources:
43 limits:
44 cpus: '200m'
45 memory: '256M'
46 environment:
47 - QUEUE_URL=${REDIS_URL}
48 - WORKER_TYPE=background
49 labels:
50 - "prometheus.scrape=true"
51 - "prometheus.port=9090"
Registry mirroring for faster pulls across regions:
1// /etc/docker/daemon.json - Optimized for cloud-native environments
2{
3 "registry-mirrors": [
4 "https://mirror.gcr.io",
5 "https://registry.mirror.example.com"
6 ],
7 "max-concurrent-downloads": 10,
8 "max-concurrent-uploads": 5,
9 "insecure-registries": [],
10 "debug": false,
11 "experimental": false,
12 "features": {
13 "buildkit": true
14 }
15}
Practical Examples - Building Optimized Images
Example 1: API Service Pattern
API services need fast startup, minimal memory usage, and easy debugging:
1# Dockerfile.api - Optimized for API services
2# Multi-stage build for minimal production image
3
4# Build stage - with all development tools
5FROM golang:1.21-alpine AS builder
6
7# Install build dependencies
8RUN apk add --no-cache git ca-certificates tzdata
9
10# Create non-root user for security
11RUN adduser -D -s /bin/sh appuser
12
13WORKDIR /app
14
15# Copy dependency files first for layer caching
16COPY go.mod go.sum ./
17RUN go mod download && go mod verify
18
19# Copy source code
20COPY . .
21
22# Build with optimizations for production
23# -ldflags="-s -w" strips debug information for smaller binary
24# CGO_ENABLED=0 creates static binary
25RUN CGO_ENABLED=0 GOOS=linux go build \
26 -ldflags='-s -w -extldflags "-static"' \
27 -a -installsuffix cgo \
28 -o api ./cmd/api
29
30# Production stage - minimal runtime
31FROM scratch
32
33# Import CA certificates from builder for HTTPS requests
34COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
35
36# Import timezone data
37COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo
38
39# Import user from builder
40COPY --from=builder /etc/passwd /etc/passwd
41
42# Copy the optimized binary
43COPY --from=builder /app/api /api
44
45# Use non-root user for security
46USER appuser
47
48# Expose port
49EXPOSE 8080
50
51# Health check for Kubernetes/service mesh
52HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
53 CMD ["/api", "healthcheck"]
54
55# Set entrypoint
56ENTRYPOINT ["/api"]
What makes this optimized for cloud-native:
- Scratch base: Minimal attack surface and smallest size
- Static binary: No dependencies to install at runtime
- Non-root user: Security best practice
- Layer caching: Dependencies cached separately from code changes
- Health check: Essential for Kubernetes readiness/liveness probes
Example 2: Background Worker Pattern
Workers need minimal CPU usage but may require additional tools:
1# Dockerfile.worker - Optimized for background workers
2FROM golang:1.21-alpine AS builder
3
4# Install build dependencies including tools for workers
5RUN apk add --no-cache git ca-certificates tzdata curl
6
7WORKDIR /app
8
9# Copy dependency files with caching optimization
10COPY go.mod go.sum ./
11RUN go mod download && go mod verify
12
13# Copy source code
14COPY . .
15
16# Build worker with monitoring capabilities
17RUN CGO_ENABLED=0 GOOS=linux go build \
18 -ldflags='-s -w' \
19 -o worker ./cmd/worker
20
21# Production stage with runtime tools
22FROM alpine:3.18
23
24# Install only runtime dependencies needed for workers
25RUN apk --no-cache add ca-certificates tzdata curl jq && \
26 adduser -D -s /bin/sh workeruser
27
28WORKDIR /app
29
30# Copy certificates for external API calls
31COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
32
33# Copy the binary
34COPY --from=builder /app/worker .
35
36# Copy health check script
37COPY --chown=workeruser:workeruser scripts/healthcheck.sh /usr/local/bin/healthcheck.sh
38RUN chmod +x /usr/local/bin/healthcheck.sh
39
40# Switch to non-root user
41USER workeruser
42
43# Health check for worker monitoring
44HEALTHCHECK --interval=60s --timeout=10s --start-period=10s --retries=3 \
45 CMD ["/usr/local/bin/healthcheck.sh"]
46
47CMD ["./worker"]
Worker-specific optimizations:
- Alpine base: Includes curl and jq for debugging and API calls
- Health check script: Customized for worker queue monitoring
- Longer health intervals: Workers don't need frequent checks
- Debugging tools: curl and jq available for troubleshooting
Example 3: Environment-Specific Builds
Different environments require different optimizations:
1# Dockerfile.multi-env - Environment-optimized builds
2FROM golang:1.21-alpine AS builder
3
4# Install build dependencies
5RUN apk add --no-cache git ca-certificates
6
7WORKDIR /app
8
9# Copy dependency files
10COPY go.mod go.sum ./
11RUN go mod download
12
13# Copy source code
14COPY . .
15
16# Environment-specific build flags
17ARG BUILD_ENV=production
18ARG VERSION=dev
19ARG COMMIT=none
20
21# Build with environment-specific optimizations
22RUN CGO_ENABLED=0 GOOS=linux go build \
23 -ldflags="-s -w -X main.Version=${VERSION} -X main.Commit=${COMMIT} -X main.BuildEnv=${BUILD_ENV}" \
24 -o app ./cmd/app
25
26# Development environment - includes debugging tools
27FROM alpine:3.18 AS development
28RUN apk --no-cache add ca-certificates curl && \
29 adduser -D -s /bin/sh appuser
30COPY --from=builder /app/app .
31COPY --chown=appuser:appuser configs/dev.yaml /app/config.yaml
32USER appuser
33EXPOSE 8080:8080
34CMD ["./app", "--config", "config.yaml", "--debug"]
35
36# Production environment - minimal and secure
37FROM scratch AS production
38COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
39COPY --chown=1000:1000 --from=builder /app/app /app
40USER 1000:1000
41EXPOSE 8080
42HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
43 CMD ["/app", "healthcheck"]
44CMD ["/app"]
45
46# Staging environment - production-like with debugging
47FROM alpine:3.18 AS staging
48RUN apk --no-cache add ca-certificates curl && \
49 adduser -D -s /bin/sh appuser
50COPY --from=builder /app/app .
51COPY --chown=appuser:appuser configs/staging.yaml /app/config.yaml
52USER appuser
53EXPOSE 8080
54HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
55 CMD ["/app", "healthcheck"]
56CMD ["./app", "--config", "config.yaml"]
Build commands for different environments:
1// run
2# Development build
3docker build --target development -t myapp:dev --build-arg BUILD_ENV=dev .
4
5# Production build
6docker build --target production -t myapp:prod --build-arg BUILD_ENV=production \
7 --build-arg VERSION=v1.2.3 --build-arg COMMIT=$(git rev-parse HEAD) .
8
9# Staging build
10docker build --target staging -t myapp:staging --build-arg BUILD_ENV=staging .
Example 4: Service Mesh Integration
Service meshes like Istio require specific optimizations:
1# Dockerfile.istio-ready - Optimized for service mesh environments
2FROM golang:1.21-alpine AS builder
3
4RUN apk add --no-cache git ca-certificates
5
6WORKDIR /app
7COPY go.mod go.sum ./
8RUN go mod download
9COPY . .
10
11# Build with service mesh support
12RUN CGO_ENABLED=0 GOOS=linux go build \
13 -ldflags='-s -w' \
14 -tags=mesh,metrics \
15 -o app ./cmd/app
16
17# Production stage with service mesh optimizations
18FROM gcr.io/distroless/static-debian12:nonroot
19
20# Import CA certificates for service mesh communication
21COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
22
23# Copy application
24COPY --from=builder /app/app /app
25
26# Service mesh specific labels
27LABEL sidecar.istio.io/inject="true"
28 traffic.sidecar.istio.io/includeInboundPorts="8080"
29 traffic.sidecar.istio.io/includeOutboundPorts="*"
30 traffic.sidecar.istio.io/excludeInboundPorts="9090"
31
32# Health check for both app and sidecar
33HEALTHCHECK --interval=30s --timeout=10s --start-period=10s --retries=3 \
34 CMD ["/app", "healthcheck"] || curl -f http://localhost:15020/healthz/ready
35
36USER nonroot:nonroot
37EXPOSE 8080 9090
38CMD ["/app"]
Common Patterns and Pitfalls
Pattern 1: Progressive Layer Optimization
Build layers should be ordered from least to most frequently changing:
1# ❌ BAD: Frequently changing code copied first
2COPY . .
3RUN go mod download
4RUN go build
5
6# ✅ GOOD: Dependencies cached separately
7COPY go.mod go.sum ./
8RUN go mod download
9COPY . .
10RUN go build
Pattern 2: BuildKit Optimization
Use BuildKit for better caching and parallel builds:
1// Enable BuildKit for better caching
2export DOCKER_BUILDKIT=1
3
4// Build with cache mount for better performance
5docker build \
6 --cache-from type=local,src=/path/to/cache \
7 --cache-to type=local,dest=/path/to/cache \
8 -t myapp:optimized .
Pattern 3: Multi-Platform Builds
Build for multiple architectures for cloud flexibility:
1// run
2# Build for multiple platforms
3docker buildx build \
4 --platform linux/amd64,linux/arm64 \
5 -t myregistry/myapp:multiarch \
6 --push .
Common Pitfalls
1. Including development tools in production images
1# ❌ BAD: Large attack surface
2FROM golang:1.21-alpine
3RUN apk add vim curl git # These shouldn't be in production
4
5# ✅ GOOD: Minimal production image
6FROM scratch
7COPY --from=builder /app /app
2. Ignoring security best practices
1# ❌ BAD: Running as root
2USER root
3
4# ✅ GOOD: Non-root user
5RUN adduser -D -s /bin/sh appuser
6USER appuser
3. Not optimizing for layer caching
1# ❌ BAD: Copy everything at once
2COPY . .
3
4# ✅ GOOD: Copy dependencies separately
5COPY go.mod go.sum ./
6RUN go mod download
7COPY . .
Advanced Docker Build Techniques
BuildKit Cache Mounts for Faster Builds
BuildKit provides advanced caching mechanisms for significantly faster builds:
1# Dockerfile.buildkit-advanced - Advanced BuildKit features
2# syntax=docker/dockerfile:1
3
4FROM golang:1.21-alpine AS builder
5
6WORKDIR /app
7
8# Use cache mount for Go modules
9RUN --mount=type=cache,target=/go/pkg/mod \
10 --mount=type=bind,source=go.sum,target=go.sum \
11 --mount=type=bind,source=go.mod,target=go.mod \
12 go mod download -x
13
14# Use cache mount for build cache
15COPY . .
16RUN --mount=type=cache,target=/go/pkg/mod \
17 --mount=type=cache,target=/root/.cache/go-build \
18 CGO_ENABLED=0 go build -o /app/bin/server ./cmd/server
19
20# Production stage
21FROM gcr.io/distroless/static-debian12:nonroot
22
23COPY --from=builder /app/bin/server /server
24
25USER nonroot:nonroot
26EXPOSE 8080
27CMD ["/server"]
Build with BuildKit:
1// run
2# Enable BuildKit
3export DOCKER_BUILDKIT=1
4
5# Build with cache export/import for CI/CD
6docker build \
7 --cache-from type=registry,ref=myregistry/myapp:buildcache \
8 --cache-to type=registry,ref=myregistry/myapp:buildcache,mode=max \
9 -t myregistry/myapp:latest .
Benefits of cache mounts:
- Persistent caching: Go modules and build cache persist across builds
- Parallel builds: Multiple concurrent builds can share cache
- CI/CD optimization: Export cache to registry for reuse across pipelines
Secret Management During Builds
Never embed secrets in images - use BuildKit secret mounts:
1# Dockerfile.secrets - Secure secret handling
2# syntax=docker/dockerfile:1
3
4FROM golang:1.21-alpine AS builder
5
6WORKDIR /app
7
8# Mount secret for private repository access
9RUN --mount=type=secret,id=github_token \
10 git config --global url."https://$(cat /run/secrets/github_token)@github.com/".insteadOf "https://github.com/" && \
11 go mod download
12
13# Mount SSH key for private repository
14RUN --mount=type=ssh \
15 --mount=type=cache,target=/go/pkg/mod \
16 go mod download
17
18COPY . .
19
20# Use build-time secret for API keys (not persisted in image)
21RUN --mount=type=secret,id=api_key \
22 API_KEY=$(cat /run/secrets/api_key) \
23 go build -ldflags="-X main.APIKey=${API_KEY}" -o /app/bin/server ./cmd/server
24
25FROM gcr.io/distroless/static-debian12:nonroot
26COPY --from=builder /app/bin/server /server
27CMD ["/server"]
Build with secrets:
1// run
2# Build with secret from file
3docker build \
4 --secret id=github_token,src=$HOME/.github-token \
5 --secret id=api_key,src=./api-key.txt \
6 -t myapp:secure .
7
8# Build with secret from environment
9export GITHUB_TOKEN=your_token_here
10docker build \
11 --secret id=github_token,env=GITHUB_TOKEN \
12 -t myapp:secure .
13
14# Build with SSH key
15docker build \
16 --ssh default \
17 -t myapp:secure .
Multi-Architecture Images
Build images that work across different CPU architectures:
1# Dockerfile.multiarch - Cross-platform compatible
2FROM --platform=$BUILDPLATFORM golang:1.21-alpine AS builder
3
4ARG TARGETPLATFORM
5ARG BUILDPLATFORM
6ARG TARGETOS
7ARG TARGETARCH
8
9WORKDIR /app
10
11# Copy dependencies
12COPY go.mod go.sum ./
13RUN go mod download
14
15# Copy source
16COPY . .
17
18# Cross-compile for target platform
19RUN CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=$TARGETARCH \
20 go build -ldflags='-s -w' -o /app/bin/server ./cmd/server
21
22# Use platform-specific base image
23FROM --platform=$TARGETPLATFORM gcr.io/distroless/static-debian12:nonroot
24
25COPY --from=builder /app/bin/server /server
26
27USER nonroot:nonroot
28EXPOSE 8080
29CMD ["/server"]
Build for multiple architectures:
1// run
2# Create and use buildx builder
3docker buildx create --name multiarch --use
4docker buildx inspect --bootstrap
5
6# Build and push multi-arch image
7docker buildx build \
8 --platform linux/amd64,linux/arm64,linux/arm/v7 \
9 -t myregistry/myapp:multiarch \
10 --push .
11
12# Inspect multi-arch manifest
13docker buildx imagetools inspect myregistry/myapp:multiarch
When to use multi-arch images:
- Cloud flexibility: Run on AWS Graviton (ARM), Intel, or AMD instances
- Edge computing: Deploy to Raspberry Pi or ARM-based edge devices
- Developer machines: Support developers on M1/M2 Macs (ARM)
- Cost optimization: ARM instances often cheaper in cloud environments
Container Image Scanning and Security
Integrate security scanning into your build process:
1# Dockerfile.security - Security-hardened image
2# syntax=docker/dockerfile:1
3
4FROM golang:1.21-alpine AS builder
5
6# Install security updates
7RUN apk update && apk upgrade && apk add --no-cache \
8 ca-certificates \
9 tzdata
10
11# Create non-root user with specific UID/GID
12RUN addgroup -g 10001 -S appgroup && \
13 adduser -u 10001 -S appuser -G appgroup
14
15WORKDIR /app
16
17# Verify checksums for dependencies
18COPY go.mod go.sum ./
19RUN go mod download && go mod verify
20
21COPY . .
22
23# Build with security flags
24RUN CGO_ENABLED=0 GOOS=linux go build \
25 -ldflags='-w -s -extldflags "-static"' \
26 -trimpath \
27 -o /app/bin/server ./cmd/server
28
29# Minimal runtime with security hardening
30FROM scratch
31
32# Import SSL certificates
33COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
34
35# Import timezone data
36COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo
37
38# Import user/group info
39COPY --from=builder /etc/passwd /etc/passwd
40COPY --from=builder /etc/group /etc/group
41
42# Copy binary
43COPY --from=builder --chown=10001:10001 /app/bin/server /server
44
45# Use non-root user
46USER 10001:10001
47
48# Security labels
49LABEL org.opencontainers.image.source="https://github.com/myorg/myapp" \
50 org.opencontainers.image.vendor="MyOrg" \
51 org.opencontainers.image.licenses="MIT" \
52 org.opencontainers.image.title="MyApp" \
53 org.opencontainers.image.description="Secure Go Application"
54
55# Set read-only root filesystem capability
56# This should be enforced by Kubernetes SecurityContext
57VOLUME /tmp
58
59EXPOSE 8080
60
61ENTRYPOINT ["/server"]
Security scanning integration:
1// run
2# Scan with Trivy
3docker build -t myapp:secure .
4trivy image --severity HIGH,CRITICAL myapp:secure
5
6# Scan with Snyk
7snyk container test myapp:secure --severity-threshold=high
8
9# Scan with Grype
10grype myapp:secure
11
12# Sign image with Cosign (sigstore)
13cosign sign --key cosign.key myregistry/myapp:secure
14
15# Verify signature
16cosign verify --key cosign.pub myregistry/myapp:secure
Cloud-Native Production Patterns
Kubernetes-Optimized Docker Images
Build images specifically optimized for Kubernetes deployments:
1# Dockerfile.k8s-optimized - Kubernetes-ready image
2# syntax=docker/dockerfile:1
3
4FROM golang:1.21-alpine AS builder
5
6WORKDIR /app
7
8# Install build dependencies
9RUN apk add --no-cache git ca-certificates tzdata
10
11# Create user for security
12RUN addgroup -g 1000 appgroup && \
13 adduser -u 1000 -G appgroup -s /bin/sh -D appuser
14
15# Copy and download dependencies
16COPY go.mod go.sum ./
17RUN --mount=type=cache,target=/go/pkg/mod \
18 go mod download
19
20# Copy source code
21COPY . .
22
23# Build with Kubernetes-specific optimizations
24RUN --mount=type=cache,target=/go/pkg/mod \
25 --mount=type=cache,target=/root/.cache/go-build \
26 CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build \
27 -ldflags='-s -w -extldflags "-static"' \
28 -tags=netgo,osusergo \
29 -trimpath \
30 -o /app/bin/server ./cmd/server
31
32# Build health check binary
33RUN --mount=type=cache,target=/go/pkg/mod \
34 --mount=type=cache,target=/root/.cache/go-build \
35 CGO_ENABLED=0 go build \
36 -ldflags='-s -w' \
37 -o /app/bin/healthcheck ./cmd/healthcheck
38
39# Production stage optimized for Kubernetes
40FROM gcr.io/distroless/static-debian12:nonroot
41
42# Import certificates and timezone
43COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
44COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo
45
46# Copy binaries
47COPY --from=builder /app/bin/server /server
48COPY --from=builder /app/bin/healthcheck /healthcheck
49
50# Labels for Kubernetes
51LABEL app.kubernetes.io/name="myapp" \
52 app.kubernetes.io/component="api-server" \
53 app.kubernetes.io/version="1.0.0" \
54 app.kubernetes.io/managed-by="helm"
55
56# Non-root user (matches Kubernetes SecurityContext)
57USER 65532:65532
58
59# Port for service
60EXPOSE 8080
61
62# Health check endpoint for Kubernetes probes
63HEALTHCHECK --interval=10s --timeout=3s --start-period=5s --retries=3 \
64 CMD ["/healthcheck"]
65
66ENTRYPOINT ["/server"]
Kubernetes Deployment with Image:
1apiVersion: apps/v1
2kind: Deployment
3metadata:
4 name: myapp
5 labels:
6 app: myapp
7spec:
8 replicas: 3
9 strategy:
10 type: RollingUpdate
11 rollingUpdate:
12 maxSurge: 1
13 maxUnavailable: 0
14 selector:
15 matchLabels:
16 app: myapp
17 template:
18 metadata:
19 labels:
20 app: myapp
21 annotations:
22 prometheus.io/scrape: "true"
23 prometheus.io/port: "9090"
24 prometheus.io/path: "/metrics"
25 spec:
26 # Match Docker image user
27 securityContext:
28 runAsNonRoot: true
29 runAsUser: 65532
30 runAsGroup: 65532
31 fsGroup: 65532
32 seccompProfile:
33 type: RuntimeDefault
34
35 containers:
36 - name: myapp
37 image: myregistry/myapp:k8s-optimized
38 imagePullPolicy: IfNotPresent
39
40 # Security context
41 securityContext:
42 allowPrivilegeEscalation: false
43 readOnlyRootFilesystem: true
44 runAsNonRoot: true
45 capabilities:
46 drop:
47 - ALL
48
49 # Resource limits for proper scheduling
50 resources:
51 requests:
52 memory: "128Mi"
53 cpu: "100m"
54 limits:
55 memory: "256Mi"
56 cpu: "250m"
57
58 # Ports
59 ports:
60 - name: http
61 containerPort: 8080
62 protocol: TCP
63 - name: metrics
64 containerPort: 9090
65 protocol: TCP
66
67 # Kubernetes probes using health check binary
68 startupProbe:
69 exec:
70 command: ["/healthcheck"]
71 initialDelaySeconds: 0
72 periodSeconds: 1
73 failureThreshold: 30
74
75 livenessProbe:
76 exec:
77 command: ["/healthcheck"]
78 initialDelaySeconds: 30
79 periodSeconds: 10
80 timeoutSeconds: 3
81 failureThreshold: 3
82
83 readinessProbe:
84 exec:
85 command: ["/healthcheck", "--ready"]
86 initialDelaySeconds: 5
87 periodSeconds: 5
88 timeoutSeconds: 2
89 failureThreshold: 3
90
91 # Environment configuration
92 env:
93 - name: APP_ENV
94 value: "production"
95 - name: LOG_LEVEL
96 value: "info"
97 - name: GOMAXPROCS
98 valueFrom:
99 resourceFieldRef:
100 resource: limits.cpu
101
102 # Volume mounts for writable directories
103 volumeMounts:
104 - name: tmp
105 mountPath: /tmp
106 - name: cache
107 mountPath: /cache
108
109 # Volumes for read-only root filesystem
110 volumes:
111 - name: tmp
112 emptyDir: {}
113 - name: cache
114 emptyDir: {}
115
116 # Topology spread for high availability
117 topologySpreadConstraints:
118 - maxSkew: 1
119 topologyKey: kubernetes.io/hostname
120 whenUnsatisfiable: DoNotSchedule
121 labelSelector:
122 matchLabels:
123 app: myapp
124 - maxSkew: 1
125 topologyKey: topology.kubernetes.io/zone
126 whenUnsatisfiable: ScheduleAnyway
127 labelSelector:
128 matchLabels:
129 app: myapp
130
131---
132apiVersion: v1
133kind: Service
134metadata:
135 name: myapp
136 labels:
137 app: myapp
138spec:
139 type: ClusterIP
140 ports:
141 - port: 80
142 targetPort: http
143 protocol: TCP
144 name: http
145 - port: 9090
146 targetPort: metrics
147 protocol: TCP
148 name: metrics
149 selector:
150 app: myapp
151
152---
153apiVersion: policy/v1
154kind: PodDisruptionBudget
155metadata:
156 name: myapp
157spec:
158 minAvailable: 1
159 selector:
160 matchLabels:
161 app: myapp
CI/CD Pipeline Integration
Optimize Docker builds for CI/CD pipelines:
1# .github/workflows/docker-build.yml - GitHub Actions CI/CD
2name: Docker Build and Push
3
4on:
5 push:
6 branches: [ main, develop ]
7 tags: [ 'v*' ]
8 pull_request:
9 branches: [ main ]
10
11env:
12 REGISTRY: ghcr.io
13 IMAGE_NAME: ${{ github.repository }}
14
15jobs:
16 build:
17 runs-on: ubuntu-latest
18 permissions:
19 contents: read
20 packages: write
21 security-events: write
22
23 steps:
24 - name: Checkout code
25 uses: actions/checkout@v4
26
27 - name: Set up Docker Buildx
28 uses: docker/setup-buildx-action@v3
29
30 - name: Log in to Container Registry
31 uses: docker/login-action@v3
32 with:
33 registry: ${{ env.REGISTRY }}
34 username: ${{ github.actor }}
35 password: ${{ secrets.GITHUB_TOKEN }}
36
37 - name: Extract metadata
38 id: meta
39 uses: docker/metadata-action@v5
40 with:
41 images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
42 tags: |
43 type=ref,event=branch
44 type=ref,event=pr
45 type=semver,pattern={{version}}
46 type=semver,pattern={{major}}.{{minor}}
47 type=sha,prefix={{branch}}-
48
49 - name: Build and push Docker image
50 uses: docker/build-push-action@v5
51 with:
52 context: .
53 platforms: linux/amd64,linux/arm64
54 push: ${{ github.event_name != 'pull_request' }}
55 tags: ${{ steps.meta.outputs.tags }}
56 labels: ${{ steps.meta.outputs.labels }}
57 cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:buildcache
58 cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:buildcache,mode=max
59 build-args: |
60 VERSION=${{ steps.meta.outputs.version }}
61 COMMIT=${{ github.sha }}
62 BUILD_DATE=${{ github.event.head_commit.timestamp }}
63
64 - name: Run Trivy vulnerability scanner
65 uses: aquasecurity/trivy-action@master
66 with:
67 image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }}
68 format: 'sarif'
69 output: 'trivy-results.sarif'
70 severity: 'CRITICAL,HIGH'
71
72 - name: Upload Trivy results to GitHub Security
73 uses: github/codeql-action/upload-sarif@v3
74 with:
75 sarif_file: 'trivy-results.sarif'
76
77 - name: Generate SBOM
78 uses: anchore/sbom-action@v0
79 with:
80 image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }}
81 format: spdx-json
82 output-file: sbom.spdx.json
83
84 - name: Sign image with Cosign
85 if: github.event_name != 'pull_request'
86 env:
87 COSIGN_EXPERIMENTAL: "true"
88 run: |
89 cosign sign --yes ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@${{ steps.build.outputs.digest }}
Image Layer Optimization Strategies
Advanced techniques for minimizing layer count and size:
1# Dockerfile.layer-optimized - Optimized layer structure
2# syntax=docker/dockerfile:1
3
4# Stage 1: Base dependencies (rarely changes)
5FROM golang:1.21-alpine AS base
6RUN apk add --no-cache ca-certificates tzdata git
7WORKDIR /app
8
9# Stage 2: Dependencies (changes when dependencies change)
10FROM base AS dependencies
11COPY go.mod go.sum ./
12RUN --mount=type=cache,target=/go/pkg/mod \
13 go mod download
14
15# Stage 3: Builder (changes frequently)
16FROM dependencies AS builder
17COPY . .
18RUN --mount=type=cache,target=/go/pkg/mod \
19 --mount=type=cache,target=/root/.cache/go-build \
20 CGO_ENABLED=0 go build -ldflags='-s -w' -o /app/bin/server ./cmd/server
21
22# Stage 4: Testing (optional, can be skipped in prod builds)
23FROM dependencies AS testing
24COPY . .
25RUN --mount=type=cache,target=/go/pkg/mod \
26 go test -v ./...
27
28# Stage 5: Production (minimal final image)
29FROM scratch AS production
30COPY --from=base /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
31COPY --from=base /usr/share/zoneinfo /usr/share/zoneinfo
32COPY --from=builder /app/bin/server /server
33USER 65532:65532
34EXPOSE 8080
35ENTRYPOINT ["/server"]
Build with target selection:
1// run
2# Development build with testing
3docker build --target testing -t myapp:test .
4
5# Production build skipping tests
6docker build --target production -t myapp:prod .
7
8# Build with specific cache strategy
9docker build \
10 --target production \
11 --cache-from type=registry,ref=myregistry/myapp:cache-base \
12 --cache-from type=registry,ref=myregistry/myapp:cache-deps \
13 --cache-from type=registry,ref=myregistry/myapp:cache-builder \
14 -t myapp:prod .
Integration and Mastery
Observability Integration
Modern cloud-native applications need built-in observability:
1# Dockerfile.observability - With built-in monitoring
2FROM golang:1.21-alpine AS builder
3
4WORKDIR /app
5
6# Copy and build with observability features
7COPY go.mod go.sum ./
8RUN go mod download
9COPY . .
10
11# Build with OpenTelemetry instrumentation
12RUN CGO_ENABLED=0 go build \
13 -ldflags='-s -w' \
14 -tags=otel,metrics \
15 -o app ./cmd/app
16
17# Production with observability
18FROM gcr.io/distroless/static-debian12:nonroot
19
20# Import certificates
21COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
22
23# Copy application with built-in observability
24COPY --from=builder /app/app /app
25
26# Expose metrics and health endpoints
27EXPOSE 8080 9090 9100
28
29# Health check including metrics endpoint
30HEALTHCHECK --interval=30s --timeout=5s --start-period=5s --retries=3 \
31 CMD ["/app", "healthcheck"] && curl -f http://localhost:9090/metrics
32
33USER nonroot:nonroot
34CMD ["/app"]
35
36# Kubernetes deployment with observability annotations
37---
38apiVersion: apps/v1
39kind: Deployment
40metadata:
41 name: myapp
42 annotations:
43 prometheus.io/scrape: "true"
44 prometheus.io/port: "9090"
45 prometheus.io/path: "/metrics"
46 sidecar.istio.io/inject: "true"
47spec:
48 replicas: 3
49 selector:
50 matchLabels:
51 app: myapp
52 template:
53 metadata:
54 labels:
55 app: myapp
56 annotations:
57 prometheus.io/scrape: "true"
58 prometheus.io/port: "9090"
59 prometheus.io/path: "/metrics"
60 spec:
61 containers:
62 - name: myapp
63 image: myregistry/myapp:observability
64 ports:
65 - containerPort: 8080
66 name: http
67 - containerPort: 9090
68 name: metrics
69 resources:
70 requests:
71 memory: "256Mi"
72 cpu: "250m"
73 limits:
74 memory: "512Mi"
75 cpu: "500m"
76 livenessProbe:
77 httpGet:
78 path: /health
79 port: 8080
80 initialDelaySeconds: 30
81 periodSeconds: 10
82 readinessProbe:
83 httpGet:
84 path: /ready
85 port: 8080
86 initialDelaySeconds: 5
87 periodSeconds: 5
88 env:
89 - name: OTEL_EXPORTER_PROMETHEUS_PORT
90 value: "9090"
91 - name: OTEL_SERVICE_NAME
92 value: "myapp"
93 - name: OTEL_RESOURCE_ATTRIBUTES
94 value: "service.name=myapp,service.version=v1.2.3"
Auto-Scaling Optimizations
For environments with frequent scaling:
1# Dockerfile.scaling - Optimized for rapid auto-scaling
2FROM golang:1.21-alpine AS builder
3
4# Optimize build for speed
5RUN apk add --no-cache git ca-certificates
6
7WORKDIR /app
8COPY go.mod go.sum ./
9RUN go mod download
10
11# Build with optimization flags for fast startup
12RUN CGO_ENABLED=0 GOOS=linux go build \
13 -ldflags='-s -w -extldflags "-static"' \
14 -tags=netgo \
15 -o app ./cmd/app
16
17# Minimal runtime for fastest scaling
18FROM scratch
19
20# Copy only essential certificates
21COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
22
23# Copy optimized binary
24COPY --from=builder /app/app /app
25
26# Minimal ports exposed
27EXPOSE 8080
28
29# Fast health check optimized for Kubernetes
30HEALTHCHECK --interval=10s --timeout=2s --start-period=1s --retries=1 \
31 CMD ["/app", "healthcheck"]
32
33USER 1000:1000
34CMD ["/app"]
35
36# Deployment optimized for auto-scaling
37---
38apiVersion: apps/v1
39kind: Deployment
40metadata:
41 name: myapp-scalable
42spec:
43 replicas: 3
44 selector:
45 matchLabels:
46 app: myapp-scalable
47 template:
48 metadata:
49 labels:
50 app: myapp-scalable
51 spec:
52 containers:
53 - name: myapp
54 image: myregistry/myapp:scaling
55 ports:
56 - containerPort: 8080
57 # Fast startup optimizations
58 resources:
59 requests:
60 memory: "128Mi" # Minimal memory request
61 cpu: "100m" # Minimal CPU request
62 limits:
63 memory: "256Mi" # Keep limits low for density
64 cpu: "250m"
65 # Fast health checks for quicker scale readiness
66 startupProbe:
67 httpGet:
68 path: /startup
69 port: 8080
70 failureThreshold: 30
71 periodSeconds: 1
72 readinessProbe:
73 httpGet:
74 path: /ready
75 port: 8080
76 initialDelaySeconds: 1 # Very fast start
77 periodSeconds: 1 # Check frequently
78 timeoutSeconds: 1 # Quick failure
79 livenessProbe:
80 httpGet:
81 path: /health
82 port: 8080
83 initialDelaySeconds: 30 # Give time to start
84 periodSeconds: 10
85
86# Horizontal Pod Autoscaler for intelligent scaling
87---
88apiVersion: autoscaling/v2
89kind: HorizontalPodAutoscaler
90metadata:
91 name: myapp-hpa
92spec:
93 scaleTargetRef:
94 apiVersion: apps/v1
95 kind: Deployment
96 name: myapp-scalable
97 minReplicas: 3
98 maxReplicas: 100
99 metrics:
100 - type: Resource
101 resource:
102 name: cpu
103 target:
104 type: Utilization
105 averageUtilization: 70
106 - type: Resource
107 resource:
108 name: memory
109 target:
110 type: Utilization
111 averageUtilization: 80
112 behavior:
113 scaleDown:
114 stabilizationWindowSeconds: 300
115 policies:
116 - type: Percent
117 value: 50
118 periodSeconds: 60
119 scaleUp:
120 stabilizationWindowSeconds: 60
121 policies:
122 - type: Percent
123 value: 200
124 periodSeconds: 60
Practice Exercises
Exercise 1: Optimize Multi-Stage Docker Build with BuildKit
Objective: Transform a monolithic Dockerfile into a highly optimized multi-stage build using BuildKit features.
Starting Point:
1# Dockerfile.monolithic - Before optimization
2FROM golang:1.21-alpine
3
4WORKDIR /app
5
6# Copy everything
7COPY . .
8
9# Install dependencies and build
10RUN go mod download && go build -o app ./cmd/app
11
12# Install runtime tools
13RUN apk add --no-cache curl vim net-tools
14
15EXPOSE 8080
16
17CMD ["./app"]
Task Requirements:
- Reduce final image size by at least 80% (target: <15MB for Go binary)
- Implement cache mounts for faster rebuilds
- Add security best practices (non-root user, minimal attack surface)
- Include comprehensive health checks
- Support multiple build targets (dev, test, prod)
- Add build-time secret handling for private dependencies
Solution Template:
1# Dockerfile.optimized - Optimized multi-stage build
2# syntax=docker/dockerfile:1
3
4# Stage 1: Base with common dependencies
5FROM golang:1.21-alpine AS base
6RUN apk add --no-cache ca-certificates tzdata git
7WORKDIR /app
8
9# Stage 2: Dependencies layer (cached separately)
10FROM base AS dependencies
11COPY go.mod go.sum ./
12RUN --mount=type=cache,target=/go/pkg/mod \
13 --mount=type=ssh \
14 go mod download && go mod verify
15
16# Stage 3: Builder with testing
17FROM dependencies AS builder
18COPY . .
19
20# Run tests
21RUN --mount=type=cache,target=/go/pkg/mod \
22 --mount=type=cache,target=/root/.cache/go-build \
23 go test -v -race -coverprofile=coverage.out ./...
24
25# Build optimized binary
26RUN --mount=type=cache,target=/go/pkg/mod \
27 --mount=type=cache,target=/root/.cache/go-build \
28 CGO_ENABLED=0 GOOS=linux go build \
29 -ldflags='-s -w -extldflags "-static"' \
30 -trimpath \
31 -o /app/bin/app ./cmd/app
32
33# Build health check binary
34RUN --mount=type=cache,target=/go/pkg/mod \
35 --mount=type=cache,target=/root/.cache/go-build \
36 CGO_ENABLED=0 go build \
37 -ldflags='-s -w' \
38 -o /app/bin/healthcheck ./cmd/healthcheck
39
40# Stage 4: Development target with debugging tools
41FROM base AS development
42RUN apk add --no-cache curl vim
43COPY --from=builder /app/bin/app /app
44COPY --from=builder /app/bin/healthcheck /healthcheck
45RUN adduser -D -s /bin/sh appuser
46USER appuser
47EXPOSE 8080
48CMD ["/app"]
49
50# Stage 5: Production target (minimal and secure)
51FROM scratch AS production
52COPY --from=base /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
53COPY --from=base /usr/share/zoneinfo /usr/share/zoneinfo
54COPY --from=builder /app/bin/app /app
55COPY --from=builder /app/bin/healthcheck /healthcheck
56
57USER 65532:65532
58EXPOSE 8080
59
60HEALTHCHECK --interval=10s --timeout=3s --start-period=5s --retries=3 \
61 CMD ["/healthcheck"]
62
63ENTRYPOINT ["/app"]
Build Commands:
1// run
2# Development build
3DOCKER_BUILDKIT=1 docker build --target development -t myapp:dev .
4
5# Production build with cache
6DOCKER_BUILDKIT=1 docker build \
7 --target production \
8 --ssh default \
9 --cache-from type=registry,ref=myregistry/myapp:cache \
10 --cache-to type=registry,ref=myregistry/myapp:cache,mode=max \
11 -t myapp:prod .
12
13# Verify image size reduction
14docker images | grep myapp
Testing Tasks:
- Measure build time with and without cache
- Compare image sizes before and after optimization
- Verify security scanning results (no HIGH/CRITICAL vulnerabilities)
- Test health check functionality
- Benchmark startup time
Exercise 2: Implement Environment-Specific Multi-Target Build
Objective: Create a single Dockerfile that supports optimized builds for development, staging, and production environments with environment-specific configurations.
Requirements:
- Development: Include debugging tools (delve, pprof), hot reload, verbose logging
- Staging: Production-like with additional monitoring tools and debug symbols
- Production: Minimal size, maximum security, optimized performance
- Common: Share layers between environments for efficient caching
- Automation: Build script that handles all environments
Implementation:
1# Dockerfile.multi-env - Environment-specific builds
2# syntax=docker/dockerfile:1
3
4ARG GO_VERSION=1.21
5ARG ALPINE_VERSION=3.18
6
7# Base stage with common dependencies
8FROM golang:${GO_VERSION}-alpine${ALPINE_VERSION} AS base
9RUN apk add --no-cache ca-certificates tzdata git
10WORKDIR /app
11
12# Dependencies stage
13FROM base AS dependencies
14COPY go.mod go.sum ./
15RUN --mount=type=cache,target=/go/pkg/mod \
16 go mod download
17
18# Builder stage with build arguments
19FROM dependencies AS builder
20ARG BUILD_ENV=production
21ARG VERSION=dev
22ARG COMMIT=none
23ARG BUILD_DATE=unknown
24
25COPY . .
26
27# Build with environment-specific optimizations
28RUN --mount=type=cache,target=/go/pkg/mod \
29 --mount=type=cache,target=/root/.cache/go-build \
30 case "${BUILD_ENV}" in \
31 "development") \
32 echo "Building for development..."; \
33 CGO_ENABLED=0 go build \
34 -gcflags="all=-N -l" \
35 -ldflags="-X main.Version=${VERSION} -X main.Commit=${COMMIT} -X main.BuildEnv=${BUILD_ENV}" \
36 -o /app/bin/app ./cmd/app ;; \
37 "staging") \
38 echo "Building for staging..."; \
39 CGO_ENABLED=0 go build \
40 -ldflags="-s -w -X main.Version=${VERSION} -X main.Commit=${COMMIT} -X main.BuildEnv=${BUILD_ENV}" \
41 -o /app/bin/app ./cmd/app ;; \
42 "production") \
43 echo "Building for production..."; \
44 CGO_ENABLED=0 go build \
45 -ldflags="-s -w -extldflags '-static' -X main.Version=${VERSION} -X main.Commit=${COMMIT} -X main.BuildEnv=${BUILD_ENV}" \
46 -trimpath \
47 -o /app/bin/app ./cmd/app ;; \
48 esac
49
50# Development target with full tooling
51FROM alpine:${ALPINE_VERSION} AS development
52RUN apk add --no-cache \
53 ca-certificates \
54 tzdata \
55 curl \
56 vim \
57 bash \
58 go \
59 delve \
60 && adduser -D -s /bin/sh appuser
61
62COPY --from=builder /app/bin/app /app
63COPY --from=dependencies /go/pkg/mod /go/pkg/mod
64COPY . /src
65
66# Install air for hot reload
67RUN go install github.com/cosmtrek/air@latest
68
69USER appuser
70WORKDIR /src
71EXPOSE 8080 2345 6060
72
73# Hot reload with air
74CMD ["air", "-c", ".air.toml"]
75
76# Staging target with monitoring tools
77FROM alpine:${ALPINE_VERSION} AS staging
78RUN apk add --no-cache \
79 ca-certificates \
80 tzdata \
81 curl \
82 htop \
83 && adduser -D -s /bin/sh appuser
84
85COPY --from=builder /app/bin/app /app
86COPY configs/staging.yaml /etc/app/config.yaml
87
88USER appuser
89EXPOSE 8080 9090 6060
90
91HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
92 CMD curl -f http://localhost:8080/health || exit 1
93
94CMD ["/app", "--config", "/etc/app/config.yaml"]
95
96# Production target (minimal and secure)
97FROM scratch AS production
98COPY --from=base /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
99COPY --from=base /usr/share/zoneinfo /usr/share/zoneinfo
100COPY --from=builder /app/bin/app /app
101
102USER 65532:65532
103EXPOSE 8080
104
105HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
106 CMD ["/app", "healthcheck"]
107
108ENTRYPOINT ["/app"]
Build Automation Script:
1#!/bin/bash
2# build-all.sh - Build script for all environments
3
4set -e
5
6VERSION=${1:-$(git describe --tags --always --dirty)}
7COMMIT=$(git rev-parse HEAD)
8BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')
9REGISTRY=${REGISTRY:-"myregistry.io"}
10IMAGE_NAME=${IMAGE_NAME:-"myapp"}
11
12echo "Building version: ${VERSION}"
13echo "Commit: ${COMMIT}"
14echo "Build date: ${BUILD_DATE}"
15
16# Build development image
17echo "Building development image..."
18docker build \
19 --target development \
20 --build-arg BUILD_ENV=development \
21 --build-arg VERSION="${VERSION}" \
22 --build-arg COMMIT="${COMMIT}" \
23 --build-arg BUILD_DATE="${BUILD_DATE}" \
24 -t "${REGISTRY}/${IMAGE_NAME}:${VERSION}-dev" \
25 -t "${REGISTRY}/${IMAGE_NAME}:dev" \
26 .
27
28# Build staging image
29echo "Building staging image..."
30docker build \
31 --target staging \
32 --build-arg BUILD_ENV=staging \
33 --build-arg VERSION="${VERSION}" \
34 --build-arg COMMIT="${COMMIT}" \
35 --build-arg BUILD_DATE="${BUILD_DATE}" \
36 -t "${REGISTRY}/${IMAGE_NAME}:${VERSION}-staging" \
37 -t "${REGISTRY}/${IMAGE_NAME}:staging" \
38 .
39
40# Build production image
41echo "Building production image..."
42docker build \
43 --target production \
44 --build-arg BUILD_ENV=production \
45 --build-arg VERSION="${VERSION}" \
46 --build-arg COMMIT="${COMMIT}" \
47 --build-arg BUILD_DATE="${BUILD_DATE}" \
48 -t "${REGISTRY}/${IMAGE_NAME}:${VERSION}" \
49 -t "${REGISTRY}/${IMAGE_NAME}:latest" \
50 .
51
52# Display image sizes
53echo -e "\nImage sizes:"
54docker images | grep "${IMAGE_NAME}" | head -6
55
56# Security scan production image
57echo -e "\nScanning production image..."
58trivy image --severity HIGH,CRITICAL "${REGISTRY}/${IMAGE_NAME}:${VERSION}"
59
60echo -e "\nBuild complete!"
Testing Tasks:
- Build all three environments and compare sizes
- Verify development hot reload functionality
- Test staging with monitoring tools
- Validate production security posture
- Measure build times with shared layers
Exercise 3: Create Secure Multi-Registry and Multi-Cloud Deployment
Objective: Implement a comprehensive deployment strategy supporting multiple cloud registries with full security integration.
Requirements:
- Support AWS ECR, GCP GCR, Azure ACR, and Docker Hub
- Implement secure image signing with Cosign
- Generate and attach SBOMs (Software Bill of Materials)
- Environment-specific tagging strategy with promotion workflow
- Automated vulnerability scanning and gate checks
- Multi-region replication
Implementation:
1#!/bin/bash
2# deploy-multicloud.sh - Multi-cloud registry deployment
3
4set -e
5
6VERSION=${1:-$(git describe --tags --always)}
7ENVIRONMENT=${2:-"development"}
8
9# Registry configurations
10AWS_REGISTRY="${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com"
11GCP_REGISTRY="gcr.io/${GCP_PROJECT_ID}"
12AZURE_REGISTRY="${AZURE_REGISTRY_NAME}.azurecr.io"
13DOCKERHUB_REGISTRY="docker.io/${DOCKERHUB_USERNAME}"
14
15IMAGE_NAME="myapp"
16
17# Registry login functions
18login_aws() {
19 echo "Logging into AWS ECR..."
20 aws ecr get-login-password --region ${AWS_REGION} | \
21 docker login --username AWS --password-stdin ${AWS_REGISTRY}
22}
23
24login_gcp() {
25 echo "Logging into GCP GCR..."
26 gcloud auth configure-docker gcr.io
27}
28
29login_azure() {
30 echo "Logging into Azure ACR..."
31 az acr login --name ${AZURE_REGISTRY_NAME}
32}
33
34login_dockerhub() {
35 echo "Logging into Docker Hub..."
36 echo ${DOCKERHUB_TOKEN} | docker login --username ${DOCKERHUB_USERNAME} --password-stdin
37}
38
39# Build multi-arch image
40build_image() {
41 echo "Building multi-arch image..."
42
43 docker buildx build \
44 --platform linux/amd64,linux/arm64 \
45 --build-arg VERSION="${VERSION}" \
46 --build-arg BUILD_DATE="$(date -u +'%Y-%m-%dT%H:%M:%SZ')" \
47 --build-arg COMMIT="$(git rev-parse HEAD)" \
48 --target production \
49 -t ${IMAGE_NAME}:${VERSION} \
50 --cache-from type=registry,ref=${GCP_REGISTRY}/${IMAGE_NAME}:buildcache \
51 --cache-to type=registry,ref=${GCP_REGISTRY}/${IMAGE_NAME}:buildcache,mode=max \
52 --load \
53 .
54}
55
56# Security scanning
57scan_image() {
58 echo "Scanning image for vulnerabilities..."
59
60 # Trivy scan
61 trivy image --severity HIGH,CRITICAL --exit-code 1 ${IMAGE_NAME}:${VERSION}
62
63 # Snyk scan
64 snyk container test ${IMAGE_NAME}:${VERSION} --severity-threshold=high
65
66 # Grype scan
67 grype ${IMAGE_NAME}:${VERSION} --fail-on high
68}
69
70# Generate SBOM
71generate_sbom() {
72 echo "Generating SBOM..."
73
74 syft ${IMAGE_NAME}:${VERSION} -o spdx-json > sbom-${VERSION}.spdx.json
75 syft ${IMAGE_NAME}:${VERSION} -o cyclonedx-json > sbom-${VERSION}.cyclonedx.json
76}
77
78# Tag and push to registry
79push_to_registry() {
80 local registry=$1
81 local tags="${VERSION} ${ENVIRONMENT}"
82
83 if [ "${ENVIRONMENT}" == "production" ]; then
84 tags="${tags} latest"
85 fi
86
87 for tag in ${tags}; do
88 local full_tag="${registry}/${IMAGE_NAME}:${tag}"
89 echo "Pushing to ${full_tag}..."
90
91 docker tag ${IMAGE_NAME}:${VERSION} ${full_tag}
92 docker push ${full_tag}
93
94 # Sign image
95 cosign sign --yes ${full_tag}
96
97 # Attach SBOM
98 cosign attach sbom --sbom sbom-${VERSION}.spdx.json ${full_tag}
99 done
100}
101
102# Replicate across regions (AWS example)
103replicate_aws_regions() {
104 local regions=("us-east-1" "us-west-2" "eu-west-1" "ap-southeast-1")
105 local source_region="${AWS_REGION}"
106 local source_registry="${AWS_ACCOUNT_ID}.dkr.ecr.${source_region}.amazonaws.com"
107
108 for region in "${regions[@]}"; do
109 if [ "${region}" != "${source_region}" ]; then
110 echo "Replicating to ${region}..."
111
112 local target_registry="${AWS_ACCOUNT_ID}.dkr.ecr.${region}.amazonaws.com"
113
114 # Create repository in target region if it doesn't exist
115 aws ecr create-repository \
116 --repository-name ${IMAGE_NAME} \
117 --region ${region} 2>/dev/null || true
118
119 # Pull from source
120 docker pull ${source_registry}/${IMAGE_NAME}:${VERSION}
121
122 # Tag for target
123 docker tag ${source_registry}/${IMAGE_NAME}:${VERSION} \
124 ${target_registry}/${IMAGE_NAME}:${VERSION}
125
126 # Push to target
127 aws ecr get-login-password --region ${region} | \
128 docker login --username AWS --password-stdin ${target_registry}
129 docker push ${target_registry}/${IMAGE_NAME}:${VERSION}
130 fi
131 done
132}
133
134# Main deployment flow
135main() {
136 echo "Starting multi-cloud deployment..."
137 echo "Version: ${VERSION}"
138 echo "Environment: ${ENVIRONMENT}"
139
140 # Build
141 build_image
142
143 # Security checks
144 scan_image
145 generate_sbom
146
147 # Deploy to registries based on environment
148 case "${ENVIRONMENT}" in
149 "development")
150 login_dockerhub
151 push_to_registry ${DOCKERHUB_REGISTRY}
152 ;;
153 "staging")
154 login_gcp
155 push_to_registry ${GCP_REGISTRY}
156 ;;
157 "production")
158 # Deploy to all registries for production
159 login_aws
160 push_to_registry ${AWS_REGISTRY}
161 replicate_aws_regions
162
163 login_gcp
164 push_to_registry ${GCP_REGISTRY}
165
166 login_azure
167 push_to_registry ${AZURE_REGISTRY}
168 ;;
169 *)
170 echo "Unknown environment: ${ENVIRONMENT}"
171 exit 1
172 ;;
173 esac
174
175 echo "Deployment complete!"
176}
177
178# Run main
179main
Registry-Specific Configurations:
1# AWS ECR lifecycle policy
2{
3 "rules": [
4 {
5 "rulePriority": 1,
6 "description": "Keep last 10 production images",
7 "selection": {
8 "tagStatus": "tagged",
9 "tagPrefixList": ["latest", "v"],
10 "countType": "imageCountMoreThan",
11 "countNumber": 10
12 },
13 "action": {
14 "type": "expire"
15 }
16 },
17 {
18 "rulePriority": 2,
19 "description": "Delete untagged images after 7 days",
20 "selection": {
21 "tagStatus": "untagged",
22 "countType": "sinceImagePushed",
23 "countUnit": "days",
24 "countNumber": 7
25 },
26 "action": {
27 "type": "expire"
28 }
29 }
30 ]
31}
Testing Tasks:
- Deploy to all registries and verify image availability
- Validate image signatures with Cosign
- Check SBOM attachments
- Test multi-region replication
- Verify vulnerability gate enforcement
Exercise 4: Implement Comprehensive Observability Integration
Objective: Add full observability stack to Docker images with OpenTelemetry, Prometheus, and structured logging.
Requirements:
- OpenTelemetry instrumentation for traces, metrics, and logs
- Prometheus metrics endpoint with custom business metrics
- Structured JSON logging with correlation IDs
- Health checks with dependency verification (database, cache, APIs)
- Kubernetes deployment with full observability stack
Task: Implement the complete observability integration including instrumentation code, Dockerfile changes, and Kubernetes manifests.
Exercise 5: Create Zero-Downtime Auto-Scaling Architecture
Objective: Build Docker images optimized for rapid horizontal auto-scaling with zero downtime during deployments.
Requirements:
- Startup time under 2 seconds
- Graceful shutdown handling with connection draining
- Pre-warmed caches and connection pools
- Progressive delivery with canary deployments
- Horizontal Pod Autoscaler with custom metrics
- Pod Disruption Budgets for high availability
Task: Implement the complete auto-scaling architecture including optimized Dockerfile, health check implementation, and Kubernetes HPA configuration with custom metrics.
Summary
Key Takeaways
- Cloud-native Docker requires different optimization than traditional Docker
- Multi-stage builds are essential for creating minimal, secure production images
- Layer optimization dramatically improves build and deployment speed
- Service mesh integration requires specific Docker patterns
- Auto-scaling optimization focuses on fast startup and minimal resource usage
Next Steps
- Explore advanced BuildKit features for more efficient caching and parallel builds
- Study container security best practices including runtime security scanning
- Learn about GitOps patterns for automated Docker image deployment
- Investigate serverless container platforms like AWS Fargate and Google Cloud Run
- Consider edge computing optimizations for distributed deployment scenarios
Further Reading
- Docker BuildKit Documentation
- Kubernetes Container Security Guidelines
- OpenTelemetry Go Instrumentation
- Service Mesh Best Practices
- Cloud Native Computing Foundation
You now have the knowledge to create Docker images that are optimized for cloud-native environments, supporting rapid scaling, multi-cloud deployment, and comprehensive observability while maintaining security best practices.