Hands-on Lab

#Lab: Dockerfile Optimization

Reduce image size and improve build performance.

#๐ŸŽฏ Objectives

  • Implement multi-stage builds
  • Minimize image layers
  • Choose optimal base images
  • Measure size improvements

#๐Ÿ“‹ Prerequisites

  • Docker installed

#โฑ๏ธ Duration: 30 minutes


#Task 1: Measure Baseline (5 min)

Create an unoptimized Dockerfile:

bash
1mkdir ~/optimize-lab && cd ~/optimize-lab
2
3cat << 'EOF' > app.go
4package main
5import "fmt"
6func main() { fmt.Println("Hello, Docker!") }
7EOF
8
9# Unoptimized Dockerfile
10cat << 'EOF' > Dockerfile.unoptimized
11FROM golang:1.21
12WORKDIR /app
13COPY . .
14RUN go build -o app
15CMD ["./app"]
16EOF
17
18docker build -t app:unoptimized -f Dockerfile.unoptimized .
19docker images app:unoptimized

Note the image size (~800MB+).


#Task 2: Multi-Stage Build (10 min)

bash
1cat << 'EOF' > Dockerfile.multistage
2# Build stage
3FROM golang:1.21-alpine AS builder
4WORKDIR /app
5COPY . .
6RUN CGO_ENABLED=0 go build -ldflags="-s -w" -o app
7
8# Runtime stage
9FROM alpine:3.18
10COPY --from=builder /app/app /app
11ENTRYPOINT ["/app"]
12EOF
13
14docker build -t app:multistage -f Dockerfile.multistage .
15docker images app:multistage

Compare sizes:

bash
docker images | grep "app"

#Task 3: Scratch Base (5 min)

bash
1cat << 'EOF' > Dockerfile.scratch
2FROM golang:1.21-alpine AS builder
3WORKDIR /app
4COPY . .
5RUN CGO_ENABLED=0 go build -ldflags="-s -w" -o app
6
7FROM scratch
8COPY --from=builder /app/app /app
9ENTRYPOINT ["/app"]
10EOF
11
12docker build -t app:scratch -f Dockerfile.scratch .
13docker images app:scratch

#Task 4: Compare All Sizes (5 min)

bash
echo "=== Image Size Comparison ==="
docker images --format "table {{.Repository}}:{{.Tag}}\t{{.Size}}" | grep "app:"

Expected results:

ImageApproximate Size
app:unoptimized~800MB
app:multistage~10MB
app:scratch~2MB

#Task 5: Node.js Optimization (5 min)

bash
1cat << 'EOF' > package.json
2{"name":"test","dependencies":{"express":"^4.18.2"}}
3EOF
4
5cat << 'EOF' > server.js
6const express = require('express');
7const app = express();
8app.get('/', (req, res) => res.send('OK'));
9app.listen(3000);
10EOF
11
12cat << 'EOF' > Dockerfile.node
13FROM node:20-alpine
14WORKDIR /app
15COPY package*.json ./
16RUN npm ci --only=production && npm cache clean --force
17COPY server.js .
18USER node
19EXPOSE 3000
20CMD ["node", "server.js"]
21EOF
22
23docker build -t app:node -f Dockerfile.node .
24docker images app:node

#โœ… Success Criteria

  • Unoptimized image built
  • Multi-stage build reduces size 90%+
  • Scratch image is smallest
  • Node.js optimized correctly

#๐Ÿงน Cleanup

bash
docker rmi app:unoptimized app:multistage app:scratch app:node 2>/dev/null
cd ~ && rm -rf optimize-lab