Welcome to from-docker-to-kubernetes

Images

Learn about Docker images, Dockerfiles, and image management

Docker Images

Docker images are the building blocks of containers. They are read-only templates containing:

  • Application code
  • Runtime environment
  • Dependencies
  • Configuration

Docker images function like a complete filesystem snapshot that provides everything needed to run an application. They are:

  • Immutable: Once built, the content doesn't change
  • Layered: Composed of multiple filesystem layers for efficiency
  • Cacheable: Layers can be reused across multiple images
  • Distributable: Can be shared via registries
  • Versioned: Tagged with version information for tracking changes

Each image is identified by a unique SHA256 digest and can have multiple human-readable tags (like nginx:latest or ubuntu:20.04). The image's architecture determines what CPU architectures it can run on (x86_64, ARM64, etc.).

Working with Dockerfiles

A Dockerfile is a text document containing all commands to build an image. It provides a declarative, reproducible way to define your container environment:

# Use an official Python runtime as the base image
FROM python:3.9-slim

# Set working directory
WORKDIR /app

# Copy requirements file
COPY requirements.txt .

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

# Copy application code
COPY . .

# Command to run the application
CMD ["python", "app.py"]

Common Dockerfile Instructions

InstructionPurposeExample
FROMSpecifies the base imageFROM ubuntu:20.04
WORKDIRSets the working directoryWORKDIR /app
COPYCopies files from host to imageCOPY . /app
ADDCopies files with extra features (URL support, tar extraction)ADD https://example.com/file.tar.gz /app
RUNExecutes commands in a new layerRUN apt-get update && apt-get install -y curl
ENVSets environment variablesENV NODE_ENV=production
EXPOSEDocuments which ports are intended to be publishedEXPOSE 8080
CMDProvides default command to runCMD ["node", "app.js"]
ENTRYPOINTConfigures container to run as executableENTRYPOINT ["nginx", "-g", "daemon off;"]
VOLUMECreates a mount pointVOLUME /data
USERSets the user for subsequent instructionsUSER node
ARGDefines build-time variablesARG VERSION=latest

Each instruction in a Dockerfile creates a new layer in the image, which affects both the build time and the final image size.

Building Images

Basic Build Command

docker build -t my-app:1.0 .

The -t flag tags the image with a name and version. The . specifies the build context (the directory containing your Dockerfile and application files). When you run this command, Docker:

  1. Sends the build context to the Docker daemon
  2. Executes each instruction in the Dockerfile sequentially
  3. Creates intermediate containers for each step
  4. Commits each step as a new image layer
  5. Removes intermediate containers
  6. Tags the final image with the specified name

Build with Different Dockerfile

docker build -f Dockerfile.dev -t my-app:dev .

The -f flag allows you to specify a custom Dockerfile name. This is useful when maintaining multiple Dockerfiles for different environments (development, testing, production) or platforms.

Build with Build Arguments

docker build --build-arg ENV=production -t my-app:prod .

Build arguments allow you to pass variables to the build process, which can be used in your Dockerfile with the ARG instruction. This enables parameterized builds that can produce different images based on the provided arguments.

Advanced Build Options

# Build with no cache
docker build --no-cache -t my-app:latest .

# Build for multiple platforms
docker buildx build --platform linux/amd64,linux/arm64 -t my-app:multi .

# Build with BuildKit enabled (faster, more features)
DOCKER_BUILDKIT=1 docker build -t my-app:fast .

# Build with build-time secrets
docker build --secret id=mysecret,src=./secret.txt -t my-app:secure .

Image Management

# List images
docker images

# List images with detailed format
docker images --format "table {{.ID}}\t{{.Repository}}\t{{.Tag}}\t{{.Size}}"

# Remove image
docker rmi <image-id>

# Remove all unused images
docker image prune -a

# Pull image from registry
docker pull <image-name>

# Pull specific version of an image
docker pull <image-name>:<tag>

# Pull image for specific platform
docker pull --platform linux/arm64 <image-name>

# Push image to registry
docker push <image-name>

# Tag image
docker tag <source-image> <target-image>

# Inspect image details
docker inspect <image-id>

# View image history (layers)
docker history <image-id>

# Save image to tar file
docker save -o myimage.tar <image-name>

# Load image from tar file
docker load -i myimage.tar

Image Naming Conventions

Docker images follow a specific naming convention:

  • Registry domain: (Optional) Defaults to Docker Hub if not specified
  • Repository name: The name of the image
  • Tag: (Optional) Defaults to "latest" if not specified
  • Digest: (Optional) SHA256 hash that uniquely identifies the image

Examples:

  • nginx:1.21.0 (Docker Hub, nginx repository, tag 1.21.0)
  • gcr.io/project/image:v1 (Google Container Registry, project/image repository, tag v1)
  • localhost:5000/myapp:dev (Local registry on port 5000, myapp repository, tag dev)
  • ubuntu@sha256:a56414c82641178a5628fc1a4d281cd39e4fb0f1b94455c693dbc4ae0a7c310e (Image by digest)

Multi-stage Builds

Multi-stage builds help create smaller production images by separating the build environment from the runtime environment:

# Build stage
FROM node:16 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build

# Production stage
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

Benefits of Multi-stage Builds

  1. Smaller final images: Only include what's needed for runtime
  2. Better security posture: Fewer dependencies mean smaller attack surface
  3. Separation of concerns: Build tools don't contaminate production image
  4. Improved performance: Smaller images load faster and use less bandwidth
  5. Reduced complexity: Single Dockerfile instead of multiple build scripts

Advanced Multi-stage Techniques

# Base stage with common dependencies
FROM python:3.9-slim AS base
RUN apt-get update && apt-get install -y --no-install-recommends gcc

# Development stage
FROM base AS development
RUN pip install pytest pytest-cov
COPY . /app/
WORKDIR /app
CMD ["pytest"]

# Build stage
FROM base AS builder
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . /app/
WORKDIR /app
RUN python setup.py bdist_wheel

# Production stage
FROM python:3.9-slim AS production
COPY --from=builder /app/dist/*.whl /tmp/
RUN pip install --no-cache-dir /tmp/*.whl && rm /tmp/*.whl
CMD ["python", "-m", "myapp"]

You can target specific stages during build with --target:

docker build --target development -t myapp:dev .

Best Practices