Docker Fundamentals¶
Category: DevOps Tags: docker, containers, images, dockerfile, registry
Container Fundamentals¶
What it is: A lightweight, standalone package that includes everything needed to run an application: code, runtime, system tools, libraries, and settings.
Why it matters: Containers solve the "it works on my machine" problem by providing consistent environments across development, testing, and production. They're the foundation of modern application deployment and microservices architecture.
Key characteristics: - Isolation - Containers are isolated from each other and the host system - Portability - Run anywhere Docker is installed, regardless of underlying OS - Efficiency - Share the host OS kernel, much lighter than virtual machines - Immutable - Containers don't change; new versions create new containers - Stateless - Data should be stored outside the container for persistence
Container vs Virtual Machine: - Containers - Share OS kernel, faster startup, less resource usage - VMs - Full OS per instance, stronger isolation, more overhead - Use cases - Containers for applications, VMs for different OS requirements
Container lifecycle: - Created - Container exists but hasn't started - Running - Container is executing - Paused - Container processes are suspended - Stopped - Container has finished executing - Deleted - Container and its filesystem are removed
Common commands:
# Running containers
docker run nginx # Run nginx container
docker run -d nginx # Run in background (detached)
docker run -p 8080:80 nginx # Map host port 8080 to container port 80
docker run -it ubuntu bash # Interactive terminal
docker run --name my-nginx nginx # Assign custom name
docker run -e ENV_VAR=value myapp # Set environment variable
# Managing containers
docker ps # List running containers
docker ps -a # List all containers (including stopped)
docker stop <container-id> # Stop running container
docker start <container-id> # Start stopped container
docker restart <container-id> # Restart container
docker rm <container-id> # Remove container
docker rm -f <container-id> # Force remove running container
# Interacting with containers
docker exec -it <container-id> bash # Shell into running container
docker logs <container-id> # View container logs
docker logs -f <container-id> # Follow logs in real-time
docker inspect <container-id> # Detailed container information
docker stats # Real-time resource usage
docker top <container-id> # Running processes in container
Resource management:
# Limit CPU and memory
docker run --cpus="1.5" --memory="512m" nginx
# Set restart policy
docker run --restart=always nginx # Always restart
docker run --restart=unless-stopped nginx # Restart unless manually stopped
docker run --restart=on-failure:3 nginx # Restart up to 3 times on failure
When you'll use it: Every Docker workflow involves running and managing containers. Daily operations include starting, stopping, monitoring, and troubleshooting containers.
Image Management¶
What it is: Read-only template used to create containers, containing application code, runtime, system tools, libraries, and configuration files.
Why it matters: Images are the blueprints for containers. They ensure consistency across environments, enable easy sharing and distribution of applications, and form the basis of container deployment strategies.
Image structure: - Layers - Images are built in layers for efficiency and reusability - Base layer - Starting point (often OS like Ubuntu, Alpine, or scratch) - Application layers - Code, dependencies, configuration added on top - Metadata - Information about the image (creation date, author, etc.)
Image naming:
- Repository - Name of the image (nginx, ubuntu, myapp)
- Tag - Version or variant (latest, v1.0, alpine)
- Registry - Where image is stored (docker.io, gcr.io, private registry)
- Full name - registry/repository:tag (docker.io/nginx:1.21)
Image layers and caching: - Layer sharing - Multiple images can share common layers - Copy-on-write - Containers share image layers until they write to them - Build cache - Docker reuses layers when building similar images - Layer optimization - Order Dockerfile commands to maximize cache hits
Common commands:
# Image management
docker images # List local images
docker images -a # List all images (including intermediate)
docker pull nginx:1.21 # Download image from registry
docker push myapp:v1.0 # Upload image to registry
docker rmi <image-id> # Remove image
docker rmi $(docker images -q) # Remove all images
# Image information
docker inspect <image-id> # Detailed image information
docker history <image-id> # Show image layer history
docker image ls --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}" # Custom format
# Building images
docker build -t myapp:v1.0 . # Build image from Dockerfile
docker build -t myapp:v1.0 -f Custom.dockerfile . # Use custom Dockerfile
docker build --no-cache -t myapp:v1.0 . # Build without cache
docker build --target production -t myapp:v1.0 . # Multi-stage build target
# Image cleanup
docker image prune # Remove unused images
docker image prune -a # Remove all unused images
docker system prune # Remove unused containers, images, networks
Image registry operations:
# Docker Hub (default registry)
docker login # Login to Docker Hub
docker logout # Logout
docker search nginx # Search for images
docker pull nginx:alpine # Pull specific tag
# Private registries
docker login registry.company.com # Login to private registry
docker tag myapp:v1.0 registry.company.com/myapp:v1.0 # Tag for private registry
docker push registry.company.com/myapp:v1.0 # Push to private registry
When you'll use it: Images are central to Docker - you'll pull base images, build custom images, manage image versions, and distribute images for deployment.
Dockerfile Best Practices¶
What it is: Text file containing instructions to build a Docker image automatically, defining the environment and steps needed to create a container image.
Why it matters: Dockerfiles provide reproducible, version-controlled way to build images. They document exactly how an application environment is constructed and enable automated image builds in CI/CD pipelines.
Common Dockerfile instructions: - FROM - Specify base image - RUN - Execute commands during build - COPY/ADD - Copy files from host to image - WORKDIR - Set working directory - EXPOSE - Document which ports the container listens on - ENV - Set environment variables - CMD - Default command when container starts - ENTRYPOINT - Configures container to run as executable
Dockerfile best practices: - Use specific base image tags - Avoid 'latest' for predictability - Minimize layers - Combine RUN commands with && - Order instructions by change frequency - Least changing first - Use .dockerignore - Exclude unnecessary files from build context - Don't run as root - Create and use non-root user - Multi-stage builds - Reduce final image size
Example Dockerfile (Node.js app):
# Use specific Node.js version
FROM node:16-alpine
# Set working directory
WORKDIR /app
# Copy package files first (for better caching)
COPY package*.json ./
# Install dependencies
RUN npm ci --only=production
# Create non-root user
RUN addgroup -g 1001 -S nodejs && \
adduser -S nextjs -u 1001
# Copy application code
COPY . .
# Change ownership to non-root user
RUN chown -R nextjs:nodejs /app
USER nextjs
# Expose port
EXPOSE 3000
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:3000/health || exit 1
# Default command
CMD ["npm", "start"]
Multi-stage Dockerfile example:
# Build stage
FROM node:16-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Production stage
FROM node:16-alpine AS production
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production && npm cache clean --force
COPY --from=builder /app/dist ./dist
USER node
CMD ["npm", "start"]
Common commands:
# Building images
docker build -t myapp:v1.0 . # Build from Dockerfile in current directory
docker build -t myapp:v1.0 -f prod.dockerfile . # Use specific Dockerfile
docker build --build-arg VERSION=1.0 -t myapp . # Pass build arguments
docker build --target builder -t myapp:build . # Build specific stage
# Build with context
docker build -t myapp https://github.com/user/repo.git # Build from Git repo
docker build -t myapp - < Dockerfile # Build from stdin
# Debugging builds
docker build --no-cache -t myapp . # Build without cache
docker build --progress=plain -t myapp . # Show build output
Dockerfile optimization tips: - Layer caching - Structure commands to maximize cache reuse - Minimize image size - Use Alpine base images, remove unnecessary packages - Security - Don't include secrets, use non-root users, scan for vulnerabilities - Build context - Use .dockerignore to exclude unnecessary files
When you'll use it: Every custom Docker image needs a Dockerfile. You'll write Dockerfiles for applications, modify existing ones, and optimize them for build speed and image size.
Registry Operations¶
What it is: Centralized storage and distribution system for Docker images, allowing teams to share, version, and deploy container images.
Why it matters: Registries enable image sharing across teams and environments, provide version control for container images, and integrate with CI/CD pipelines for automated deployment.
Types of registries: - Public registries - Docker Hub, Quay.io, GitHub Container Registry - Private cloud registries - AWS ECR, Google GCR, Azure ACR - Self-hosted registries - Harbor, GitLab Container Registry, Sonatype Nexus - Local registries - For development and testing
Docker Hub (default registry): - Official images - Maintained by Docker (nginx, ubuntu, postgres) - Verified publishers - Images from trusted organizations - Community images - User-contributed images - Automated builds - Build images from GitHub/Bitbucket repositories - Webhooks - Trigger actions when images are pushed
Registry concepts: - Repository - Collection of related images (nginx repository contains various nginx versions) - Tag - Version or variant identifier (latest, v1.0, alpine) - Manifest - Metadata about image layers and configuration - Digest - Immutable identifier for specific image version (sha256:abc123...)
Common commands:
# Registry authentication
docker login # Login to Docker Hub
docker login registry.company.com # Login to private registry
docker logout # Logout from registry
# Image operations
docker pull nginx:1.21 # Pull specific image version
docker pull nginx # Pull latest version
docker push myapp:v1.0 # Push image to registry
docker search nginx # Search Docker Hub
# Image naming and tagging
docker tag myapp:v1.0 myregistry.com/myapp:v1.0 # Tag for different registry
docker tag myapp:v1.0 myapp:latest # Add additional tag
# Registry inspection
docker inspect nginx:1.21 # View image details
docker manifest inspect nginx:1.21 # View manifest (if supported)
Private registry setup (simple):
# Run local registry
docker run -d -p 5000:5000 --name registry registry:2
# Tag and push to local registry
docker tag myapp:v1.0 localhost:5000/myapp:v1.0
docker push localhost:5000/myapp:v1.0
# Pull from local registry
docker pull localhost:5000/myapp:v1.0
Registry security: - Authentication - Username/password, tokens, certificates - Authorization - Role-based access control (RBAC) - Image scanning - Vulnerability detection in images - Content trust - Digital signing of images - Network security - TLS encryption, private networks
CI/CD integration: - Automated builds - Trigger builds on code changes - Image promotion - Move images through environments (dev → staging → prod) - Deployment triggers - Automatically deploy when new images are pushed - Security scanning - Scan images before deployment
When you'll use it: Every Docker workflow involves registries - pulling base images, pushing custom images, managing image versions, and distributing images for deployment across environments.