# ARTICLE_LOADED
> cat docker-multi-stage.md
> rendering content...
status: READING_MODE
security_level: PUBLIC
engagement_tracking: ENABLED
article@tektik:/var/blog/docker-multi-stage$
cat metadata.json
"category": "DevOps"
"date": "2025-10-03"
"read_time": "7 dk okuma"
"author": "TekTık Yazılım DevOps Ekibi"
DevOps3 Ekim 2025

Docker Multi-stage Build Optimizasyonu

echo"Docker image boyutunu azaltmak ve build süresini optimize etmek için multi-stage build teknikleri. Layer caching, security best practices ve production-ready containerization."
#Docker#Optimization#DevOps#Containerization
7 dk okuma
TekTık Yazılım DevOps Ekibi
content@tektik:/var/articles/docker-multi-stage.md$
./render-article --format=html --style=cyber

Docker Multi-stage Build Optimizasyonu

Docker multi-stage build'ler, container image boyutunu dramatik olarak azaltmanın ve güvenliği artırmanın en etkili yöntemlerinden biridir. Bu rehberde, production-ready container image'ları oluşturmak için advanced Docker tekniklerini öğreneceksiniz.

İçindekiler

  1. Multi-stage Build Temelleri
  2. Layer Caching Optimizasyonu
  3. Security Best Practices
  4. Build Performance Tuning
  5. Advanced Patterns ve Teknikler
  6. CI/CD Pipeline Entegrasyonu
  7. Troubleshooting ve Debugging

Multi-stage Build Temelleri {#multi-stage-temelleri}

Single-stage vs Multi-stage Build Karşılaştırması

Problemli Single-stage Build:

text
# ❌ Kötü örnek - Single-stage build
FROM node:18

WORKDIR /app

# Tüm development dependencies dahil
COPY package*.json ./
RUN npm install

# Source code kopyala
COPY . .

# Build
RUN npm run build

# Production dependencies (gereksiz)
RUN npm install --only=production

EXPOSE 3000
CMD ["npm", "start"]

# Sonuç: ~800MB image boyutu
# İçerik: Build tools, development dependencies, source code, compiled code

Optimize Edilmiş Multi-stage Build:

text
# ✅ İyi örnek - Multi-stage build
# Stage 1: Build environment
FROM node:18-alpine AS builder

WORKDIR /app

# Package files'ları önce kopyala (cache optimization)
COPY package*.json ./

# Install all dependencies (dev + prod)
RUN npm ci --only=production && npm cache clean --force

# Source code'u kopyala
COPY . .

# Build the application
RUN npm run build

# Stage 2: Production environment
FROM node:18-alpine AS production

# Security: Create non-root user
RUN addgroup -g 1001 -S nodejs && \
    adduser -S nextjs -u 1001

WORKDIR /app

# Copy production dependencies
COPY --from=builder /app/node_modules ./node_modules

# Copy built application
COPY --from=builder /app/.next ./.next
COPY --from=builder /app/public ./public
COPY --from=builder /app/package.json ./package.json

# Set ownership
RUN chown -R nextjs:nodejs /app

# Switch to non-root user
USER nextjs

EXPOSE 3000

# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
    CMD curl -f http://localhost:3000/api/health || exit 1

CMD ["npm", "start"]

# Sonuç: ~150MB image boyutu (80% azalma)
# İçerik: Sadece runtime dependencies ve compiled code

Real-world Örnek: Go Application

text
# Go Multi-stage Build Example
# Stage 1: Build environment
FROM golang:1.21-alpine AS builder

# Install build dependencies
RUN apk add --no-cache git ca-certificates tzdata

# Create non-root user for final stage
RUN adduser -D -s /bin/sh -u 1001 appuser

WORKDIR /src

# Copy go mod files first (for caching)
COPY go.mod go.sum ./

# Download dependencies
RUN go mod download && go mod verify

# Copy source code
COPY . .

# Build the binary
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build \
    -ldflags="-w -s -extldflags '-static'" \
    -a -installsuffix cgo \
    -o app ./cmd/server

# Stage 2: Scratch-based minimal image
FROM scratch AS production

# Copy timezone data
COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo

# Copy CA certificates
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/

# Copy user information
COPY --from=builder /etc/passwd /etc/passwd

# Copy the binary
COPY --from=builder /src/app /app

# Use non-root user
USER appuser

EXPOSE 8080

# Health check endpoint
HEALTHCHECK --interval=30s --timeout=3s CMD ["/app", "healthcheck"]

ENTRYPOINT ["/app"]

# Sonuç: ~10MB image boyutu (from scratch)
# İçerik: Sadece statically compiled binary

Layer Caching Optimizasyonu {#layer-caching}

Optimal Layer Ordering

text
# ✅ Optimized layer ordering
FROM node:18-alpine AS base

# 1. Install system dependencies (rarely changes)
RUN apk add --no-cache \
    libc6-compat \
    openssl \
    && rm -rf /var/cache/apk/*

WORKDIR /app

# 2. Copy package manager files (changes less frequently)
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./

# 3. Install dependencies
RUN \
  if [ -f yarn.lock ]; then yarn --frozen-lockfile; \
  elif [ -f package-lock.json ]; then npm ci; \
  elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm && pnpm i; \
  else echo "Lockfile not found." && exit 1; \
  fi

# 4. Copy source code (changes most frequently)
COPY . .

# Build stage
FROM base AS builder
RUN npm run build

# Production stage  
FROM base AS production
COPY --from=builder /app/.next ./.next
CMD ["npm", "start"]

Build Context Optimization

text
# .dockerignore - Exclude unnecessary files
node_modules
npm-debug.log
Dockerfile*
docker-compose*
.dockerignore
.git
.gitignore
README.md
.env
.nyc_output
coverage
.nyc_output

# Development files
src/**/*.test.js
src/**/*.spec.js
**/__tests__
**/.coverage
**/coverage

# IDE files
.vscode
.idea

# OS generated files
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db

# Logs
logs
*.log
lerna-debug.log

# Build artifacts (if copying from host)
dist
build
.next

Layer Caching Strategies

bash
#!/bin/bash
# Build with cache optimization

# 1. Use BuildKit for advanced caching
export DOCKER_BUILDKIT=1

# 2. Build with cache mount for package managers
docker build \
  --target production \
  --cache-from myregistry.com/myapp:cache \
  --tag myregistry.com/myapp:latest \
  --tag myregistry.com/myapp:cache \
  .

# 3. Push cache layer
docker push myregistry.com/myapp:cache

# 4. Multi-platform build with cache
docker buildx build \
  --platform linux/amd64,linux/arm64 \
  --cache-from type=registry,ref=myregistry.com/myapp:cache \
  --cache-to type=registry,ref=myregistry.com/myapp:cache,mode=max \
  --tag myregistry.com/myapp:latest \
  --push .

Security Best Practices {#security-practices}

Distroless Images

text
# Python application with distroless
FROM python:3.11-slim AS builder

WORKDIR /app

# Install build dependencies
RUN apt-get update && apt-get install -y \
    build-essential \
    && rm -rf /var/lib/apt/lists/*

# Install Python dependencies
COPY requirements.txt .
RUN pip install --user --no-cache-dir -r requirements.txt

# Copy application
COPY . .

# Build/compile if needed
RUN python -m compileall .

# Final stage with distroless
FROM gcr.io/distroless/python3-debian11

# Copy Python packages
COPY --from=builder /root/.local /root/.local

# Copy application
COPY --from=builder /app /app

WORKDIR /app

# Update PATH
ENV PATH=/root/.local/bin:$PATH

EXPOSE 8000

CMD ["app.py"]

# Security benefits:
# - No shell access
# - No package manager
# - Minimal attack surface
# - Non-root user by default

Security Scanning Integration

text
# Dockerfile with security annotations
FROM node:18-alpine AS base

# Vulnerability: Use specific version tags
# hadolint ignore=DL3018
RUN apk add --no-cache \
    # Pin package versions for security
    curl=8.0.1-r0 \
    ca-certificates=20230506-r0

# Security: Create user with specific UID/GID
RUN addgroup -g 1001 -S nodejs && \
    adduser -S nextjs -u 1001 -G nodejs

WORKDIR /app

# Security: Copy with specific ownership
COPY --chown=nextjs:nodejs package*.json ./

USER nextjs

RUN npm ci --only=production && npm cache clean --force

COPY --chown=nextjs:nodejs . .

# Security: Don't run as root
USER nextjs

EXPOSE 3000

# Security: Use exec form for proper signal handling
CMD ["node", "server.js"]
bash
#!/bin/bash
# Security scanning pipeline
set -e

IMAGE_NAME="myapp:latest"

echo "🔍 Building image..."
docker build -t "$IMAGE_NAME" .

echo "🔍 Scanning with Trivy..."
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
  aquasec/trivy image --exit-code 1 --severity HIGH,CRITICAL "$IMAGE_NAME"

echo "🔍 Scanning with Snyk..."
snyk container test "$IMAGE_NAME" --severity-threshold=high

echo "🔍 Scanning with Grype..."
grype "$IMAGE_NAME" --fail-on high

echo "✅ Security scans passed!"

Build Performance Tuning {#performance-tuning}

Parallel Builds

text
# Parallel build example
FROM node:18-alpine AS base

WORKDIR /app
COPY package*.json ./

# Stage for dependencies
FROM base AS deps
RUN npm ci --only=production && npm cache clean --force

# Stage for dev dependencies and build
FROM base AS build-deps
RUN npm ci && npm cache clean --force

# Build stage
FROM build-deps AS builder
COPY . .
RUN npm run build

# Test stage (runs in parallel with builder)
FROM build-deps AS tester
COPY . .
RUN npm run test
RUN npm run lint

# Final stage
FROM base AS production
RUN addgroup -g 1001 -S nodejs && adduser -S nextjs -u 1001

# Copy from multiple stages
COPY --from=deps --chown=nextjs:nodejs /app/node_modules ./node_modules
COPY --from=builder --chown=nextjs:nodejs /app/.next ./.next
COPY --from=builder --chown=nextjs:nodejs /app/public ./public

USER nextjs
CMD ["npm", "start"]

BuildKit Advanced Features

text
# syntax=docker/dockerfile:1

FROM node:18-alpine AS base

# Mount caches for package managers
RUN --mount=type=cache,target=/root/.npm \
    --mount=type=bind,source=package.json,target=package.json \
    --mount=type=bind,source=package-lock.json,target=package-lock.json \
    npm ci --only=production

# Secret mount for private repos
RUN --mount=type=secret,id=npmrc,target=/root/.npmrc \
    npm ci --only=production

# Bind mount for source (zero copy)
RUN --mount=type=bind,source=.,target=/src,rw \
    --mount=type=cache,target=/app/.next/cache \
    cd /src && npm run build && cp -r .next /app/

COPY --from=base /app/.next ./.next
CMD ["npm", "start"]

Advanced Patterns ve Teknikler {#advanced-patterns}

Multi-Architecture Support

text
# syntax=docker/dockerfile:1

# Multi-arch base selection
FROM --platform=$BUILDPLATFORM node:18-alpine AS base

ARG TARGETPLATFORM
ARG BUILDPLATFORM

RUN echo "Building on $BUILDPLATFORM for $TARGETPLATFORM"

# Architecture-specific optimizations
RUN case "$TARGETPLATFORM" in \
      "linux/amd64") \
        echo "Optimizing for amd64" && \
        apk add --no-cache libc6-compat ;; \
      "linux/arm64") \
        echo "Optimizing for arm64" && \
        apk add --no-cache libc6-compat ;; \
      *) \
        echo "Unknown platform $TARGETPLATFORM" ;; \
    esac

WORKDIR /app

# Build stage
FROM base AS builder

COPY package*.json ./
RUN npm ci

COPY . .
RUN npm run build

# Production stage
FROM base AS production

COPY --from=builder /app/.next ./.next
COPY --from=builder /app/package.json ./

RUN npm ci --only=production

CMD ["npm", "start"]

Dynamic Stage Selection

text
# syntax=docker/dockerfile:1

ARG BUILD_ENV=production

# Development stage
FROM node:18-alpine AS development
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
CMD ["npm", "run", "dev"]

# Production build stage  
FROM node:18-alpine AS build-production
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build

# Production runtime stage
FROM node:18-alpine AS production
WORKDIR /app
COPY --from=build-production /app/.next ./.next
COPY --from=build-production /app/node_modules ./node_modules
CMD ["npm", "start"]

# Final stage selection based on BUILD_ENV
FROM ${BUILD_ENV} AS final

Shared Layer Optimization

text
# Base layer for multiple services
FROM node:18-alpine AS node-base

# Common system dependencies
RUN apk add --no-cache \
    curl \
    ca-certificates \
    tzdata

# Common user setup
RUN addgroup -g 1001 -S nodejs && \
    adduser -S appuser -u 1001 -G nodejs

WORKDIR /app

# Service A
FROM node-base AS service-a-builder
COPY service-a/package*.json ./
RUN npm ci
COPY service-a/ .
RUN npm run build

FROM node-base AS service-a
COPY --from=service-a-builder /app/dist ./dist
COPY --from=service-a-builder /app/node_modules ./node_modules
USER appuser
CMD ["node", "dist/index.js"]

# Service B
FROM node-base AS service-b-builder
COPY service-b/package*.json ./
RUN npm ci
COPY service-b/ .
RUN npm run build

FROM node-base AS service-b
COPY --from=service-b-builder /app/dist ./dist
COPY --from=service-b-builder /app/node_modules ./node_modules
USER appuser
CMD ["node", "dist/server.js"]

CI/CD Pipeline Entegrasyonu {#cicd-integration}

GitHub Actions ile Docker Build

yaml
# .github/workflows/docker-build.yml
name: Docker Build and Push

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

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

jobs:
  build-and-push:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
    
    steps:
    - name: Checkout
      uses: actions/checkout@v4
    
    - name: Set up Docker Buildx
      uses: docker/setup-buildx-action@v3
    
    - name: Log in to Container Registry
      uses: docker/login-action@v3
      with:
        registry: ${{ env.REGISTRY }}
        username: ${{ github.actor }}
        password: ${{ secrets.GITHUB_TOKEN }}
    
    - name: Extract metadata
      id: meta
      uses: docker/metadata-action@v5
      with:
        images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
        tags: |
          type=ref,event=branch
          type=ref,event=pr
          type=sha,prefix={{branch}}-
          type=raw,value=latest,enable={{is_default_branch}}
    
    - name: Build and push
      uses: docker/build-push-action@v5
      with:
        context: .
        platforms: linux/amd64,linux/arm64
        push: true
        tags: ${{ steps.meta.outputs.tags }}
        labels: ${{ steps.meta.outputs.labels }}
        cache-from: type=gha
        cache-to: type=gha,mode=max
        build-args: |
          BUILD_ENV=production
          GIT_SHA=${{ github.sha }}
    
    - name: Run Trivy vulnerability scanner
      uses: aquasecurity/trivy-action@master
      with:
        image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
        format: 'sarif'
        output: 'trivy-results.sarif'
    
    - name: Upload Trivy scan results to GitHub Security tab
      uses: github/codeql-action/upload-sarif@v2
      if: always()
      with:
        sarif_file: 'trivy-results.sarif'

Docker Compose ile Development Environment

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

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
      target: development
      args:
        BUILD_ENV: development
    ports:
      - "3000:3000"
    volumes:
      - .:/app
      - /app/node_modules
      - /app/.next
    environment:
      - NODE_ENV=development
      - CHOKIDAR_USEPOLLING=true
    depends_on:
      - postgres
      - redis
  
  postgres:
    image: postgres:15-alpine
    environment:
      POSTGRES_DB: myapp
      POSTGRES_USER: user
      POSTGRES_PASSWORD: password
    volumes:
      - postgres_data:/var/lib/postgresql/data
    ports:
      - "5432:5432"
  
  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    volumes:
      - redis_data:/data

volumes:
  postgres_data:
  redis_data:

# Production override
# docker-compose.prod.yml
version: '3.8'

services:
  app:
    build:
      target: production
      args:
        BUILD_ENV: production
    ports:
      - "80:3000"
    environment:
      - NODE_ENV=production
    volumes: []
    restart: unless-stopped

Troubleshooting ve Debugging {#troubleshooting}

Debug Multi-stage Builds

bash
#!/bin/bash
# Debug script for multi-stage builds

IMAGE_NAME="myapp"

echo "🔍 Inspecting build stages..."

# Build and tag each stage
docker build --target builder --tag "${IMAGE_NAME}:builder" .
docker build --target production --tag "${IMAGE_NAME}:production" .

# Inspect each stage
echo "📊 Builder stage size:"
docker images "${IMAGE_NAME}:builder" --format "table {{.Repository}}:{{.Tag}}\t{{.Size}}"

echo "📊 Production stage size:"
docker images "${IMAGE_NAME}:production" --format "table {{.Repository}}:{{.Tag}}\t{{.Size}}"

# Analyze layers
echo "🔍 Analyzing layers..."
docker history "${IMAGE_NAME}:production"

# Run interactive debug session
echo "🐛 Starting debug session..."
docker run -it --rm "${IMAGE_NAME}:builder" /bin/sh

Common Issues ve Solutions

text
# Issue: Large node_modules in final image
# ❌ Problem
FROM node:18-alpine
COPY . .
RUN npm install
CMD ["npm", "start"]

# ✅ Solution: Multi-stage build
FROM node:18-alpine AS builder
COPY package*.json ./
RUN npm ci --only=production

FROM node:18-alpine
COPY --from=builder /app/node_modules ./node_modules
COPY . .
CMD ["npm", "start"]

# Issue: Cache invalidation on every build
# ❌ Problem
FROM node:18-alpine
COPY . .
RUN npm install

# ✅ Solution: Optimize copy order
FROM node:18-alpine
COPY package*.json ./
RUN npm ci
COPY . .

# Issue: Security vulnerabilities
# ❌ Problem
FROM node:18

# ✅ Solution: Use minimal base + security scanning
FROM node:18-alpine
RUN apk add --no-cache dumb-init
USER node
ENTRYPOINT ["dumb-init", "--"]
CMD ["npm", "start"]

Performance Profiling

bash
#!/bin/bash
# Docker build performance profiling

# Enable BuildKit experimental features
export DOCKER_BUILDKIT=1
export BUILDKIT_PROGRESS=plain

# Build with detailed timing
time docker build \
  --progress=plain \
  --no-cache \
  -t myapp:test .

# Analyze build context size
echo "📊 Build context analysis:"
tar -czf - . | wc -c | numfmt --to=iec

# Check .dockerignore effectiveness
echo "📊 Files being sent to Docker daemon:"
tar -czf - . --exclude-from=.dockerignore | wc -c | numfmt --to=iec

# Layer size analysis
echo "📊 Layer size analysis:"
docker history myapp:test --format "{{.CreatedBy}}: {{.Size}}"

Sonuç

Docker multi-stage build optimizasyonu, modern containerization stratejisinin temelini oluşturur. Bu rehberde ele aldığımız teknikler:

  • Image Size Reduction: %80'e varan boyut azaltması
  • Security Enhancement: Minimal attack surface ve distroless images
  • Build Performance: Layer caching ve parallel builds
  • Production Readiness: Health checks, non-root users, proper signal handling

Bu stratejileri uygulayarak, güvenli, hızlı ve efficient container image'ları oluşturabilirsiniz.


TekTık Yazılım olarak, Docker containerization ve DevOps optimizasyon stratejileri konularında profesyonel danışmanlık hizmeti vermekteyiz. İletişim sayfamızdan bizimle iletişime geçebilirsiniz.