Skip to content

Portal | Level: L1: Foundations | Topics: Docker / Containers | Domain: Kubernetes

Docker Drills

Remember: Docker exit codes tell you what happened: 0 = clean exit, 1 = application error, 137 = OOM-killed or SIGKILL (128+9), 139 = segfault (128+11), 143 = SIGTERM (128+15). Mnemonic: "137 = Killed, 139 = Segfault" — both are 128 + signal number. Check with docker inspect --format='{{.State.ExitCode}}'.

Gotcha: docker run -v /host/path:/container/path uses a bind mount (host directory). docker run -v myvolume:/container/path uses a named volume (Docker-managed). The syntax looks identical but the behavior is very different. Named volumes persist after docker rm; bind mounts are just host filesystem pointers.

Default trap: docker build uses the entire directory as build context. A missing .dockerignore means Docker sends .git/, node_modules/, test data, and secrets to the daemon. Always create a .dockerignore — it can cut build time from minutes to seconds.

Drill 1: Run and Debug a Container

Difficulty: Easy

Q: Run an nginx container in the background on port 8080, then check its logs and get a shell into it.

Answer
# Run
docker run -d --name web -p 8080:80 nginx:1.25

# Check logs
docker logs web --tail=20 -f

# Get a shell
docker exec -it web bash

# Inside container:
cat /etc/nginx/nginx.conf
curl localhost:80
exit

# Clean up
docker rm -f web

Drill 2: Build a Multi-Stage Image

Difficulty: Medium

Q: Write a Dockerfile that compiles a Go binary in one stage and runs it in a minimal image in the second stage.

Answer
# Build stage
FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o /app/server .

# Runtime stage
FROM gcr.io/distroless/static:nonroot
COPY --from=builder /app/server /server
USER nonroot:nonroot
EXPOSE 8080
CMD ["/server"]
docker build -t myapp:v1 .
docker images myapp:v1    # Should be ~10-20MB, not 1GB+
Key points: - `CGO_ENABLED=0` for static binary (no glibc dependency) - `distroless/static` has no shell, no package manager - Copy `go.mod` first to cache the dependency download layer

Drill 3: Debug a Failing Container

Difficulty: Medium

Q: A container keeps restarting. It exits before you can exec into it. How do you debug?

Answer
# 1. Check logs from the last run
docker logs myapp --tail=50

# 2. Check the exit code
docker inspect myapp --format='{{.State.ExitCode}}'
# Exit 1 = app error, 137 = OOM/SIGKILL, 139 = segfault

# 3. Run interactively with shell override
docker run -it --entrypoint /bin/sh myapp:v1
# Now you can inspect the filesystem, env, etc.

# 4. Check if the image has the right entrypoint
docker inspect myapp:v1 --format='{{.Config.Entrypoint}} {{.Config.Cmd}}'

# 5. Check environment variables
docker inspect myapp --format='{{.Config.Env}}'

# 6. If it's a distroless image (no shell):
docker run -it --entrypoint="" busybox sh
# Copy the binary out and inspect:
docker cp myapp:/server ./server
file ./server

Drill 4: Docker Networking

Difficulty: Medium

Q: Create a custom network and run two containers that can communicate by name.

Answer
# Create network
docker network create mynet

# Run containers on the same network
docker run -d --name api --network mynet nginx:1.25
docker run -d --name db --network mynet postgres:16 \
  -e POSTGRES_PASSWORD=secret

# Test connectivity (api can reach db by name)
docker exec api curl -s http://api:80  # Self
docker exec api apt-get update && apt-get install -y postgresql-client
docker exec api pg_isready -h db -p 5432

# Inspect network
docker network inspect mynet

# Clean up
docker rm -f api db
docker network rm mynet
Default bridge network does NOT support DNS name resolution between containers — you need a custom network.

Drill 5: Volume Management

Difficulty: Easy

Q: Create a named volume, mount it to a container, write data, and verify it persists after container removal.

Answer
# Create volume
docker volume create mydata

# Run container with volume
docker run -d --name app -v mydata:/data alpine sh -c "echo 'hello' > /data/test.txt && sleep 3600"

# Verify data
docker exec app cat /data/test.txt

# Remove container
docker rm -f app

# Data persists — mount to new container
docker run --rm -v mydata:/data alpine cat /data/test.txt
# Output: hello

# Clean up
docker volume rm mydata

Drill 6: Image Layer Analysis

Difficulty: Medium

Q: You have a 2GB Docker image. How do you find which layers are large and reduce the image size?

Answer
# View layers and sizes
docker history myapp:v1

# Use dive for interactive layer inspection
dive myapp:v1

# Common size reduction techniques:
# 1. Multi-stage build (don't ship build tools)
# 2. Combine RUN commands to reduce layers:
RUN apt-get update && \
    apt-get install -y --no-install-recommends curl && \
    rm -rf /var/lib/apt/lists/*

# 3. Use .dockerignore
cat > .dockerignore << 'EOF'
.git
node_modules
*.md
Dockerfile
docker-compose.yml
EOF

# 4. Use smaller base image
# FROM ubuntu → FROM alpine (5MB vs 80MB)
# FROM node:20 → FROM node:20-slim → FROM node:20-alpine

# 5. Remove package manager cache in same layer
RUN apk add --no-cache curl

Drill 7: Docker Compose Multi-Service

Difficulty: Medium

Q: Write a docker-compose.yml for an app with API, PostgreSQL, and Redis. The API should wait for DB to be healthy.

Answer
services:
  api:
    build: .
    ports: ["8080:8080"]
    environment:
      DB_HOST: db
      DB_PORT: "5432"
      REDIS_HOST: redis
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_healthy

  db:
    image: postgres:16
    volumes:
      - pgdata:/var/lib/postgresql/data
    environment:
      POSTGRES_DB: app
      POSTGRES_USER: app
      POSTGRES_PASSWORD: secret
    healthcheck:
      test: pg_isready -U app
      interval: 5s
      retries: 5

  redis:
    image: redis:7-alpine
    healthcheck:
      test: redis-cli ping
      interval: 5s
      retries: 5

volumes:
  pgdata:
docker compose up -d
docker compose ps         # Check health status
docker compose logs -f api

Drill 8: Resource Limits

Difficulty: Medium

Q: Run a container with memory and CPU limits. Verify the limits are enforced.

Answer
# Run with limits
docker run -d --name limited \
  --memory 256m \
  --cpus 0.5 \
  nginx:1.25

# Verify limits
docker inspect limited --format='Memory: {{.HostConfig.Memory}} CPU: {{.HostConfig.NanoCpus}}'
# Memory: 268435456 (bytes = 256MB)
# CPU: 500000000 (nano = 0.5 cores)

# Monitor usage
docker stats limited --no-stream

# Test memory limit (container will be OOM-killed)
docker run --rm --memory 50m alpine sh -c \
  'dd if=/dev/zero of=/dev/null bs=100m count=1'
In Kubernetes, these map to: - `--memory` → `resources.limits.memory` - `--cpus` → `resources.limits.cpu`

Drill 9: Security Scanning

Difficulty: Easy

Q: Scan a Docker image for vulnerabilities and check if it runs as root.

Answer
# Scan with Trivy
trivy image myapp:v1
trivy image --severity CRITICAL,HIGH myapp:v1
trivy image --exit-code 1 --severity CRITICAL myapp:v1  # Fail CI on critical

# Check if image runs as root
docker inspect myapp:v1 --format='User: {{.Config.User}}'
# Empty = root (bad)

# Check at runtime
docker run --rm myapp:v1 whoami
docker run --rm myapp:v1 id

# Force non-root
docker run --rm --user 1000:1000 myapp:v1 id

Drill 10: Cleanup and Maintenance

Difficulty: Easy

Q: Your Docker host is running low on disk. Reclaim space safely.

Answer
# Check what's using space
docker system df
docker system df -v    # Verbose

# Safe cleanup (only unused resources)
docker container prune       # Stopped containers
docker image prune           # Dangling images
docker volume prune          # Unused volumes
docker network prune         # Unused networks

# Nuclear option (remove ALL unused)
docker system prune -a --volumes

# Find large images
docker images --format '{{.Size}}\t{{.Repository}}:{{.Tag}}' | sort -rh | head -10

# Check if builder cache is large
docker builder prune
Warning: `docker volume prune` removes unnamed volumes — data may be lost if containers are stopped.

Wiki Navigation

Prerequisites

  • Docker Exercises (Quest Ladder) (CLI) (Exercise Set, L0)