Lab 1: The Foundation (Basic Web Server)
Goal: Create a simple Nginx server that displays a custom HTML page. This introduces FROM, COPY, and EXPOSE.
Step 1: Create a file named index.html
<h1>Hello from Docker!</h1>
Step 2: Create a file named Dockerfile
# Step 1: Base Image (The OS)
FROM nginx:alpine
# Step 2: Metadata (Good practice)
LABEL maintainer="devsecops-guru"
# Step 3: Copy your web page to the server's default folder
COPY index.html /usr/share/nginx/html/index.html
# Step 4: Documentation (Tell humans this port is needed)
EXPOSE 80
# Note: Nginx has a default CMD, so we don't need to write it!
Step 3: Build and Run
docker build -t my-web-server .
docker run -p 8080:80 my-web-server
- Test: Open your browser to
localhost:8080. You should see “Hello from Docker!”.
Lab 2: The Optimizer (Layer Caching)
Goal: Understand why the order of instructions matters for speed.
Step 1: Create a python file app.py
print("Hello World")
Step 2: Create a requirement file requirements.txt
flask
Step 3: The “Bad” Dockerfile (Write this first)
FROM python:3.9-slim
WORKDIR /app
COPY . .
# ^ PROBLEM: If you change code, cache breaks here!
RUN pip install -r requirements.txt
CMD ["python", "app.py"]
Step 4: The “Good” Dockerfile (Write this second)
FROM python:3.9-slim
WORKDIR /app
# 1. Copy only files needed for installation first
COPY requirements.txt .
# 2. Install dependencies (This layer is now cached!)
RUN pip install -r requirements.txt
# 3. NOW copy the rest of the source code
COPY . .
CMD ["python", "app.py"]
Activity:
- Build the “Good” Dockerfile.
- Change one word in
app.py. - Build it again.
- Notice that
RUN pip installsays---> Using cache. It didn’t run again! This saves huge time in big projects.
Lab 3: The Architect (Multi-Stage Build)
Goal: Build a Go application. Go needs a compiler (heavy), but the final app is just a binary (tiny). We will throw away the compiler.
Step 1: Create a Go program main.go
package main
import "fmt"
func main() {
fmt.Println("I am a tiny container!")
}
Step 2: Create the Multi-Stage Dockerfile
# ----------------------------
# STAGE 1: The Builder (Heavy)
# ----------------------------
FROM golang:1.19-alpine AS builder
WORKDIR /app
# Copy source code
COPY main.go .
# Compile the code to a binary named "my-app"
RUN go build -o my-app main.go
# ----------------------------
# STAGE 2: The Runner (Tiny)
# ----------------------------
FROM alpine:latest
WORKDIR /root/
# Copy ONLY the binary from the "builder" stage
COPY --from=builder /app/my-app .
# Command to run the binary
CMD ["./my-app"]
Step 3: Verify the Size
- Build it:
docker build -t tiny-go . - Check size:
docker images - Result: You will see the image is roughly 10MB. If you didn’t use multi-stage, it would be 800MB+.