8.3 Container Fundamentals

Mastering the Container Lifecycle
Now that you can run basic containers, it’s time to understand how containers really work. This section covers essential container operations, networking concepts, data persistence, and debugging techniques you’ll use daily in development and production environments.
Think of this as your “driver’s license” for containers - you’ll learn not just what to do, but why and when to do it.
Learning Objectives
By the end of this section, you will:
Master container lifecycle management (create, start, stop, remove)
Configure container networking and port mapping effectively
Manage data persistence with volumes and bind mounts
Debug containers using logs, exec, and inspection tools
Optimize resource usage and container performance
Understand when to use interactive vs. detached modes
Prerequisites: Basic container concepts and successful “hello world” container runs
Container Execution Modes
Interactive vs. Detached: When to Use Each
Understanding execution modes is crucial for effective container management:
Interactive Mode (-it):
When to use: Development, debugging, one-time tasks, learning
Characteristics: Keeps terminal attached, captures input/output
Best for: Database administration, file editing, testing commands
Detached Mode (-d):
When to use: Production services, long-running processes, web servers
Characteristics: Runs in background, returns control immediately
Best for: Web applications, databases, background workers
Practical Examples:
# Interactive: Perfect for debugging and exploration
docker run -it ubuntu:22.04 /bin/bash
# You get a shell inside the container
# Interactive with cleanup: One-time tasks
docker run --rm -it python:3.11 python
# Python REPL, container removed when you exit
# Detached: Long-running services
docker run -d --name web-server nginx:alpine
# Returns immediately, container runs in background
# Detached with port mapping: Production-ready web service
docker run -d -p 8080:80 --name my-web nginx:alpine
Combining Modes:
# Start detached, then connect interactively
docker run -d --name debug-container ubuntu:22.04 sleep 3600
docker exec -it debug-container /bin/bash
# View output from detached container
docker logs -f debug-container
Container Networking Mastery
Port Mapping Deep Dive
Container networking can be confusing at first, but understanding the concepts makes everything click:
Basic Port Mapping:
# Syntax: -p HOST_PORT:CONTAINER_PORT
docker run -d -p 8080:80 nginx:alpine
# Maps host port 8080 to container port 80
# Multiple port mappings
docker run -d \
-p 8080:80 \
-p 8443:443 \
nginx:alpine
# Bind to specific interface
docker run -d -p 127.0.0.1:8080:80 nginx:alpine
# Only accessible from localhost
# Random port assignment
docker run -d -P nginx:alpine
# Docker assigns random available ports
Network Modes:
# Default bridge network
docker run -d --name web1 nginx:alpine
# Host networking (container uses host's network stack)
docker run -d --network host nginx:alpine
# Container port 80 = host port 80 directly
# No networking
docker run -d --network none alpine sleep 3600
# Complete network isolation
# Custom networks (recommended for multi-container apps)
docker network create my-app-network
docker run -d --network my-app-network --name web nginx:alpine
docker run -d --network my-app-network --name db postgres:15
Service Discovery:
# Create custom network
docker network create app-tier
# Start database
docker run -d \
--network app-tier \
--name postgres-db \
-e POSTGRES_PASSWORD=mypassword \
postgres:15
# Start web app (can connect to 'postgres-db' by name)
docker run -d \
--network app-tier \
--name web-app \
-p 8080:8080 \
-e DATABASE_URL=postgresql://postgres:mypassword@postgres-db:5432/postgres \
my-web-app:latest
Data Persistence Strategies
Understanding Container Storage
Containers are ephemeral by design, but applications need persistent data. Here’s how to handle it:
Volume Types Comparison:
Type |
Use Case |
Performance |
Portability |
---|---|---|---|
Volumes |
Production data |
Excellent |
High (Docker-managed) |
Bind Mounts |
Development |
Native |
Medium (host-dependent) |
tmpfs |
Temporary data |
Excellent |
Low (memory-only) |
Docker Volumes (Recommended for Production):
# Create named volume
docker volume create postgres-data
# Use volume in container
docker run -d \
--name postgres-db \
-v postgres-data:/var/lib/postgresql/data \
-e POSTGRES_PASSWORD=securepassword \
postgres:15
# Inspect volume
docker volume inspect postgres-data
# List all volumes
docker volume ls
# Backup volume data
docker run --rm \
-v postgres-data:/source:ro \
-v $(pwd):/backup \
ubuntu tar czf /backup/postgres-backup.tar.gz -C /source .
Bind Mounts (Great for Development):
# Mount current directory for live code editing
docker run -d \
--name dev-server \
-v $(pwd):/app \
-w /app \
-p 3000:3000 \
node:18-alpine \
npm run dev
# Mount config files
docker run -d \
--name web-server \
-v $(pwd)/nginx.conf:/etc/nginx/nginx.conf:ro \
-p 8080:80 \
nginx:alpine
tmpfs Mounts (Memory-based, Fast):
# Mount memory filesystem for temporary data
docker run -d \
--name fast-cache \
--tmpfs /cache:rw,noexec,nosuid,size=1g \
redis:alpine
Database Deployment Best Practices
Production-Ready Database Container
Let’s deploy a PostgreSQL database following best practices:
# Create dedicated network for database
docker network create db-network
# Create volume for persistent data
docker volume create postgres-data
# Deploy PostgreSQL with proper configuration
docker run -d \
--name production-postgres \
--network db-network \
-p 5432:5432 \
-v postgres-data:/var/lib/postgresql/data \
-v $(pwd)/init-scripts:/docker-entrypoint-initdb.d:ro \
-e POSTGRES_DB=appdb \
-e POSTGRES_USER=appuser \
-e POSTGRES_PASSWORD=securepassword \
-e POSTGRES_INITDB_ARGS="--auth-host=scram-sha-256" \
--restart unless-stopped \
postgres:15-alpine
Database Initialization Script:
-- init-scripts/01-create-tables.sql
CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
username VARCHAR(50) UNIQUE NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS posts (
id SERIAL PRIMARY KEY,
user_id INTEGER REFERENCES users(id),
title VARCHAR(200) NOT NULL,
content TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Create indexes for performance
CREATE INDEX idx_users_username ON users(username);
CREATE INDEX idx_posts_user_id ON posts(user_id);
CREATE INDEX idx_posts_created_at ON posts(created_at);
Database Connection Testing:
# Test connection using psql
docker exec -it production-postgres psql -U appuser -d appdb
# Run SQL commands from host
docker exec production-postgres psql -U appuser -d appdb -c "SELECT version();"
# Import data from SQL file
docker exec -i production-postgres psql -U appuser -d appdb < data.sql
Container Management Commands
Essential Container Operations
Lifecycle Management:
# Create container without starting
docker create --name my-app nginx:alpine
# Start a stopped container
docker start my-app
# Stop container gracefully (SIGTERM, then SIGKILL after timeout)
docker stop my-app
# Force stop container immediately (SIGKILL)
docker kill my-app
# Restart container
docker restart my-app
# Pause/unpause container processes
docker pause my-app
docker unpause my-app
# Remove container (must be stopped first)
docker rm my-app
# Remove running container forcefully
docker rm -f my-app
Container Inspection and Debugging:
# View detailed container information
docker inspect my-app
# Get specific information using format
docker inspect --format='{{.State.Status}}' my-app
docker inspect --format='{{.NetworkSettings.IPAddress}}' my-app
# View container processes
docker top my-app
# Monitor resource usage
docker stats my-app
docker stats --no-stream # One-time snapshot
# View port mappings
docker port my-app
Working with Container Filesystems:
# Copy files between host and container
docker cp local-file.txt my-app:/app/
docker cp my-app:/app/logs/ ./logs/
# View filesystem changes
docker diff my-app
# Export container filesystem as tar
docker export my-app > my-app-backup.tar
Logging and Monitoring
Container Log Management
# View container logs
docker logs my-app
# Follow logs in real-time
docker logs -f my-app
# Show last 100 lines
docker logs --tail 100 my-app
# Show logs since specific time
docker logs --since "2024-01-01T00:00:00" my-app
# Show logs with timestamps
docker logs -t my-app
# Configure log rotation
docker run -d \
--log-driver json-file \
--log-opt max-size=10m \
--log-opt max-file=3 \
nginx:alpine
Resource Monitoring:
# Real-time resource usage
docker stats
# Historical resource usage (requires monitoring setup)
# Use tools like cAdvisor, Prometheus, Grafana for production
# Check disk usage
docker system df
docker system df -v # Verbose output
Development Workflows
Hot Reloading Development Setup
# Python development with hot reloading
docker run -d \
--name python-dev \
-v $(pwd):/app \
-w /app \
-p 5000:5000 \
-e FLASK_ENV=development \
-e FLASK_DEBUG=1 \
python:3.11-slim \
bash -c "pip install flask && flask run --host=0.0.0.0"
# Node.js development with nodemon
docker run -d \
--name node-dev \
-v $(pwd):/app \
-w /app \
-p 3000:3000 \
node:18-alpine \
sh -c "npm install -g nodemon && nodemon app.js"
Database Development Workflow:
# Start development database
docker run -d \
--name dev-postgres \
-p 5433:5432 \
-v dev-postgres-data:/var/lib/postgresql/data \
-e POSTGRES_DB=dev_app \
-e POSTGRES_USER=dev_user \
-e POSTGRES_PASSWORD=dev_pass \
postgres:15-alpine
# Run database migrations
docker exec dev-postgres psql -U dev_user -d dev_app -f /migrations/001_initial.sql
# Backup development data
docker exec dev-postgres pg_dump -U dev_user dev_app > dev_backup.sql
# Reset development database
docker exec dev-postgres psql -U dev_user -c "DROP DATABASE dev_app; CREATE DATABASE dev_app;"
Troubleshooting Guide
Common Issues and Solutions
Container Won’t Start:
# Check container status and exit code
docker ps -a
# View error logs
docker logs container-name
# Run with interactive mode to debug
docker run -it --entrypoint /bin/sh image-name
# Check if port is already in use
netstat -tulpn | grep :8080 # Linux
lsof -i :8080 # macOS
Performance Issues:
# Check resource constraints
docker stats
# Inspect container configuration
docker inspect container-name | jq '.[].HostConfig.Resources'
# Check host system resources
free -h # Memory usage
df -h # Disk usage
top # CPU usage
Network Connectivity Problems:
# Test container networking
docker exec container-name ping google.com
# Check DNS resolution
docker exec container-name nslookup example.com
# Inspect network configuration
docker network ls
docker network inspect bridge
# Test port connectivity
docker exec container-name nc -zv hostname port
Permission Issues:
# Check file ownership in container
docker exec container-name ls -la /app
# Fix ownership for bind mounts
sudo chown -R $(id -u):$(id -g) ./app-directory
# Run container with specific user
docker run --user $(id -u):$(id -g) image-name
Production Considerations
Resource Limits and Constraints
# Set memory limit
docker run -m 512m nginx:alpine
# Set CPU limit
docker run --cpus="1.5" nginx:alpine
# Set both memory and CPU limits
docker run -m 512m --cpus="1.0" nginx:alpine
# Limit other resources
docker run \
--memory=512m \
--cpus="1.0" \
--pids-limit=100 \
--ulimit nofile=1024:2048 \
nginx:alpine
Health Checks in Production:
# Run container with health check
docker run -d \
--name healthy-app \
--health-cmd="curl -f http://localhost:8080/health || exit 1" \
--health-interval=30s \
--health-timeout=10s \
--health-retries=3 \
--health-start-period=60s \
my-web-app:latest
# Check health status
docker ps # Shows health status
docker inspect healthy-app | jq '.[].State.Health'
Restart Policies:
# Restart policies for production
docker run -d --restart=unless-stopped nginx:alpine # Recommended
docker run -d --restart=always nginx:alpine # Always restart
docker run -d --restart=on-failure:3 nginx:alpine # Restart on failure, max 3 times
docker run -d --restart=no nginx:alpine # Never restart (default)
Practical Examples
Example 1: Complete Web Application Stack
# Create network
docker network create webapp-network
# Start database
docker run -d \
--name webapp-db \
--network webapp-network \
-v webapp-db-data:/var/lib/postgresql/data \
-e POSTGRES_DB=webapp \
-e POSTGRES_USER=webapp \
-e POSTGRES_PASSWORD=securepassword \
postgres:15-alpine
# Start Redis cache
docker run -d \
--name webapp-cache \
--network webapp-network \
redis:7-alpine
# Start web application
docker run -d \
--name webapp-api \
--network webapp-network \
-p 8080:8080 \
-e DATABASE_URL=postgresql://webapp:securepassword@webapp-db:5432/webapp \
-e REDIS_URL=redis://webapp-cache:6379 \
my-webapp:latest
# Start reverse proxy
docker run -d \
--name webapp-proxy \
--network webapp-network \
-p 80:80 \
-v $(pwd)/nginx.conf:/etc/nginx/nginx.conf:ro \
nginx:alpine
Example 2: Development Environment
# Development database with sample data
docker run -d \
--name dev-db \
-p 5432:5432 \
-v $(pwd)/dev-data:/docker-entrypoint-initdb.d:ro \
-e POSTGRES_DB=myapp_dev \
-e POSTGRES_USER=developer \
-e POSTGRES_PASSWORD=devpass \
postgres:15-alpine
# Development app with hot reloading
docker run -d \
--name dev-app \
-p 3000:3000 \
-v $(pwd)/src:/app/src:ro \
-v $(pwd)/public:/app/public:ro \
-e NODE_ENV=development \
node:18-alpine \
sh -c "cd /app && npm run dev"
What’s Next?
You now have solid fundamentals for working with containers in any environment. In the next section, we’ll learn how to create your own custom container images using Dockerfiles, giving you complete control over your application environments.
Key takeaways:
Interactive mode for development and debugging, detached for production
Port mapping enables network access to containerized services
Volumes provide persistent data storage across container lifecycles
Networks enable secure communication between containers
Proper resource limits prevent containers from impacting system performance
Health checks and restart policies ensure application reliability
Note
Practice Makes Perfect: The commands and concepts in this section form the foundation of daily container operations. Practice them until they become second nature.