š 4.6 Interpret Contents of a Dockerfile
What is a Dockerfile?
A Dockerfile is a text file containing instructions to build a Docker image. It defines the base image, application code, dependencies, configuration, and runtime behavior.
Complete Dockerfile Example
# Dockerfile - Complete Python Web Application Example
# FROM - Specify base image
FROM python:3.9-slim
# LABEL - Add metadata to image
LABEL maintainer="developer@example.com"
LABEL version="1.0"
LABEL description="Python Flask API application"
# ENV - Set environment variables
ENV APP_HOME=/app
ENV FLASK_APP=app.py
ENV FLASK_ENV=production
ENV PYTHONUNBUFFERED=1
# WORKDIR - Set working directory (creates if doesn't exist)
WORKDIR ${APP_HOME}
# COPY - Copy files from host to container
# Copy requirements first (for layer caching)
COPY requirements.txt .
# RUN - Execute commands during build
# Install Python dependencies
RUN pip install --no-cache-dir -r requirements.txt
# COPY application code
COPY . .
# RUN - Create non-root user for security
RUN useradd -m -u 1000 appuser && \
chown -R appuser:appuser ${APP_HOME}
# USER - Switch to non-root user
USER appuser
# EXPOSE - Document which port app uses (doesn't publish)
EXPOSE 5000
# HEALTHCHECK - Define health check command
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:5000/health || exit 1
# VOLUME - Create mount point for persistent data
VOLUME ["/app/data"]
# CMD - Default command when container starts (can be overridden)
CMD ["python", "app.py"]
# Alternative: ENTRYPOINT - Command that always runs (harder to override)
# ENTRYPOINT ["python", "app.py"]
Dockerfile Instructions Explained
FROM Base Image
Specifies the parent image to build upon. Every Dockerfile must start with FROM.
# Official images FROM python:3.9 FROM node:18-alpine FROM nginx:latest FROM ubuntu:22.04 # Specific version (recommended for production) FROM python:3.9.16-slim # Multi-stage builds FROM golang:1.19 AS builder FROM alpine:latest
RUN Execute Commands
Executes commands during image build. Creates a new layer in the image.
# Install packages
RUN apt-get update && apt-get install -y \
curl \
vim \
git
# Install Python packages
RUN pip install --no-cache-dir flask requests
# Chain commands with && to reduce layers
RUN apt-get update && \
apt-get install -y curl && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
# Create directories
RUN mkdir -p /app/logs
COPY vs ADD
Both copy files from host to container, but ADD has extra features.
# COPY - Simple file copy (recommended) COPY requirements.txt /app/ COPY src/ /app/src/ COPY . /app/ # ADD - Copy with extra features (auto-extract tar, download URLs) ADD archive.tar.gz /app/ # Auto-extracts ADD https://example.com/file.zip /app/ # Downloads file # Best practice: Use COPY unless you need ADD features COPY app.py /app/
WORKDIR Working Directory
Sets the working directory for subsequent instructions.
# Set working directory WORKDIR /app # All subsequent commands run in /app RUN pip install -r requirements.txt # Runs in /app COPY . . # Copies to /app # Can use multiple times WORKDIR /app WORKDIR logs # Now in /app/logs
ENV - Environment Variables
Sets environment variables available during build and runtime.
# Set environment variables
ENV APP_PORT=5000
ENV DB_HOST=localhost
ENV DEBUG=False
# Use in other commands
ENV APP_DIR=/app
WORKDIR ${APP_DIR}
# Multiple variables
ENV APP_PORT=5000 \
DB_HOST=localhost \
LOG_LEVEL=INFO
EXPOSE Port Documentation
Documents which ports the application uses. Doesn't actually publish ports.
# Document ports EXPOSE 80 # HTTP EXPOSE 443 # HTTPS EXPOSE 5000 # Flask default EXPOSE 3000 # Node.js common # Multiple ports EXPOSE 8080 8443 # Note: Still need -p flag when running: # docker run -p 5000:5000 myapp
CMD vs ENTRYPOINT
Define what command runs when container starts.
# CMD - Can be overridden CMD ["python", "app.py"] # Override: docker run myimage python other.py # CMD - Shell form CMD python app.py # ENTRYPOINT - Main command (harder to override) ENTRYPOINT ["python", "app.py"] # Can only add args: docker run myimage --debug # Combined ENTRYPOINT + CMD ENTRYPOINT ["python"] CMD ["app.py"] # Default: python app.py # Override CMD: docker run myimage other.py
Dockerfile Instructions Reference
| Instruction | Purpose | Example |
|---|---|---|
| FROM | Base image | FROM python:3.9 |
| RUN | Execute command (build time) | RUN pip install flask |
| COPY | Copy files from host | COPY app.py /app/ |
| ADD | Copy with extract/download | ADD file.tar.gz /app/ |
| WORKDIR | Set working directory | WORKDIR /app |
| ENV | Set environment variable | ENV PORT=5000 |
| EXPOSE | Document port | EXPOSE 8080 |
| CMD | Default command (runtime) | CMD ["python", "app.py"] |
| ENTRYPOINT | Main executable | ENTRYPOINT ["python"] |
| USER | Set user context | USER appuser |
| VOLUME | Create mount point | VOLUME ["/data"] |
| LABEL | Add metadata | LABEL version="1.0" |
| ARG | Build-time variable | ARG VERSION=1.0 |
| HEALTHCHECK | Container health check | HEALTHCHECK CMD curl localhost |
Best Practices for Dockerfiles
- Use .dockerignore: Exclude unnecessary files (like .gitignore)
- Minimize Layers: Combine RUN commands with &&
- Order Matters: Put changing instructions last (leverage cache)
- Specific Base Images: Use version tags, not latest
- Security: Don't run as root, use USER instruction
- Clean Up: Remove temporary files in same RUN command
- Multi-Stage Builds: Reduce final image size
- Small Images: Use alpine or slim variants
Multi-Stage Dockerfile Example
# Multi-stage build - reduces final image size # Stage 1: Build stage FROM python:3.9 AS builder WORKDIR /app COPY requirements.txt . RUN pip install --user --no-cache-dir -r requirements.txt COPY . . # Stage 2: Production stage (final) FROM python:3.9-slim WORKDIR /app # Copy only necessary files from builder COPY --from=builder /root/.local /root/.local COPY --from=builder /app . # Update PATH ENV PATH=/root/.local/bin:$PATH # Run application CMD ["python", "app.py"] # Result: Smaller final image (no build tools, only runtime)
š³ 4.7 Utilize Docker Images in Local Development
What are Docker Images?
Docker images are read-only templates containing application code, libraries, and dependencies. Images are built from Dockerfiles and used to create containers.
Essential Docker Commands
1. Building Images
# Build image from Dockerfile in current directory docker build -t myapp:1.0 . # Build with specific Dockerfile docker build -f Dockerfile.prod -t myapp:prod . # Build with build arguments docker build --build-arg VERSION=1.0 -t myapp:1.0 . # Build without cache (force rebuild) docker build --no-cache -t myapp:1.0 . # Tag image after build docker tag myapp:1.0 myapp:latest docker tag myapp:1.0 registry.example.com/myapp:1.0
2. Managing Images
# List all images docker images docker image ls # List with filters docker images --filter "dangling=true" # Untagged images docker images python # Images matching "python" # Remove image docker rmi myapp:1.0 docker image rm myapp:1.0 # Remove all unused images docker image prune # Remove all images (careful!) docker rmi $(docker images -q) # Inspect image details docker inspect myapp:1.0 # View image history (layers) docker history myapp:1.0 # Save image to tar file docker save -o myapp.tar myapp:1.0 # Load image from tar docker load -i myapp.tar
3. Working with Docker Hub / Registries
# Login to Docker Hub docker login docker login registry.example.com # Pull image from registry docker pull python:3.9 docker pull nginx:alpine docker pull registry.example.com/myapp:1.0 # Push image to registry docker push username/myapp:1.0 docker push registry.example.com/myapp:1.0 # Search Docker Hub docker search python # Tag for registry docker tag myapp:1.0 username/myapp:1.0 docker tag myapp:1.0 username/myapp:latest
4. Running Containers from Images
# Basic run
docker run myapp:1.0
# Run in detached mode (background)
docker run -d myapp:1.0
# Run with name
docker run --name my-container myapp:1.0
# Run with port mapping
docker run -p 5000:5000 myapp:1.0
docker run -p 8080:80 nginx
# Run with environment variables
docker run -e DB_HOST=localhost -e DB_PORT=5432 myapp:1.0
# Run with volume mount
docker run -v /host/path:/container/path myapp:1.0
docker run -v $(pwd):/app myapp:1.0
# Run interactive with terminal
docker run -it ubuntu:22.04 /bin/bash
# Run and remove after exit
docker run --rm myapp:1.0
# Complete example with multiple options
docker run -d \
--name flask-app \
-p 5000:5000 \
-e FLASK_ENV=development \
-v $(pwd)/data:/app/data \
--restart unless-stopped \
myapp:1.0
5. Container Management
# List running containers docker ps # List all containers (including stopped) docker ps -a # Stop container docker stop container_name docker stop container_id # Start stopped container docker start container_name # Restart container docker restart container_name # Remove container docker rm container_name # Remove running container (force) docker rm -f container_name # View container logs docker logs container_name docker logs -f container_name # Follow logs # Execute command in running container docker exec container_name ls -la docker exec -it container_name /bin/bash # View container stats docker stats # Inspect container docker inspect container_name
Docker Compose - Multi-Container Applications
docker-compose.yml Example
# docker-compose.yml - Define multi-container application
version: '3.8'
services:
# Web application
web:
build: .
image: myapp:1.0
container_name: flask-app
ports:
- "5000:5000"
environment:
- FLASK_ENV=development
- DB_HOST=database
volumes:
- ./app:/app
- logs:/app/logs
depends_on:
- database
- redis
networks:
- app-network
restart: unless-stopped
# Database
database:
image: postgres:14
container_name: postgres-db
environment:
- POSTGRES_USER=admin
- POSTGRES_PASSWORD=secret
- POSTGRES_DB=myapp
volumes:
- db-data:/var/lib/postgresql/data
networks:
- app-network
# Cache
redis:
image: redis:7-alpine
container_name: redis-cache
ports:
- "6379:6379"
networks:
- app-network
volumes:
db-data:
logs:
networks:
app-network:
driver: bridge
Docker Compose Commands
# Start all services docker-compose up # Start in detached mode docker-compose up -d # Build and start docker-compose up --build # Stop all services docker-compose down # Stop and remove volumes docker-compose down -v # View logs docker-compose logs docker-compose logs -f web # List running services docker-compose ps # Execute command in service docker-compose exec web bash # Scale service docker-compose up -d --scale web=3
Local Development Workflow
- Write Dockerfile: Define application environment
- Build Image:
docker build -t myapp:dev . - Test Locally:
docker run -p 5000:5000 myapp:dev - Iterate: Make changes, rebuild, test
- Use Volumes: Mount code for live reload
- Docker Compose: Multi-container setup (app + db + cache)
- Tag for Registry:
docker tag myapp:dev user/myapp:1.0 - Push to Registry:
docker push user/myapp:1.0
Debugging and Troubleshooting
# Check if Docker is running docker info docker version # View detailed container info docker inspect container_name # View container logs docker logs container_name docker logs --tail 100 container_name # Access container shell docker exec -it container_name /bin/bash docker exec -it container_name /bin/sh # For alpine # View running processes in container docker top container_name # View resource usage docker stats container_name # Export container filesystem docker export container_name > container.tar # Copy files from container docker cp container_name:/app/config.txt ./ # View port mappings docker port container_name
- Port already in use: Use different port or stop conflicting service
- Permission denied: Check file permissions or run as root (not recommended)
- Image not found: Check image name/tag, pull from registry first
- Container exits immediately: Check logs, CMD might be wrong
- Out of disk space: Prune unused images/containers
š Interactive Python Interpreter
- Write Python code in the editor below
- Click "ā¶ Run Code" to execute
- Learn Docker concepts and workflows