Docker Guide for Developers
Docker is a platform for developing, shipping, and running applications in containers. Containers package software with all its dependencies, ensuring consistency across different environments.
Core Concepts
What is Docker?
- Container: Lightweight, standalone executable package
- Image: Read-only template for creating containers
- Dockerfile: Script with instructions to build an image
- Registry: Repository for storing and distributing images (Docker Hub)
Installation
Linux
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
sudo usermod -aG docker $USER
Verify Installation
docker --version
docker run hello-world
Basic Commands
Images
# List images
docker images
# Pull an image
docker pull nginx:latest
# Build an image
docker build -t myapp:1.0 .
# Remove an image
docker rmi image_name
# Tag an image
docker tag myapp:1.0 username/myapp:1.0
# Push to registry
docker push username/myapp:1.0
Containers
# Run a container
docker run -d -p 8080:80 --name webserver nginx
# List running containers
docker ps
# List all containers
docker ps -a
# Stop a container
docker stop webserver
# Start a container
docker start webserver
# Remove a container
docker rm webserver
# View logs
docker logs webserver
# Execute command in container
docker exec -it webserver bash
# Inspect container
docker inspect webserver
Dockerfile
Basic Structure
# Base image
FROM openjdk:17-jdk-slim
# Set working directory
WORKDIR /app
# Copy files
COPY target/myapp.jar app.jar
# Expose port
EXPOSE 8080
# Run command
CMD ["java", "-jar", "app.jar"]
Multi-stage Build (Java)
# Build stage
FROM maven:3.8-openjdk-17 AS build
WORKDIR /app
COPY pom.xml .
COPY src ./src
RUN mvn clean package -DskipTests
# Runtime stage
FROM openjdk:17-jdk-slim
WORKDIR /app
COPY --from=build /app/target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
Node.js Application
FROM node:18-alpine
WORKDIR /app
# Copy package files
COPY package*.json ./
# Install dependencies
RUN npm ci --only=production
# Copy application code
COPY . .
# Expose port
EXPOSE 3000
# Start application
CMD ["node", "server.js"]
Python Application
FROM python:3.11-slim
WORKDIR /app
# Install dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copy application
COPY . .
EXPOSE 5000
CMD ["python", "app.py"]
Docker Compose
Basic docker-compose.yml
version: '3.8'
services:
web:
build: .
ports:
- "8080:8080"
environment:
- DATABASE_URL=postgresql://db:5432/mydb
depends_on:
- db
volumes:
- ./logs:/app/logs
db:
image: postgres:15
environment:
POSTGRES_DB: mydb
POSTGRES_USER: user
POSTGRES_PASSWORD: password
volumes:
- postgres_data:/var/lib/postgresql/data
ports:
- "5432:5432"
volumes:
postgres_data:
Full Stack Application
version: '3.8'
services:
frontend:
build: ./frontend
ports:
- "3000:3000"
depends_on:
- backend
backend:
build: ./backend
ports:
- "8080:8080"
environment:
- SPRING_DATASOURCE_URL=jdbc:postgresql://db:5432/appdb
- SPRING_DATASOURCE_USERNAME=postgres
- SPRING_DATASOURCE_PASSWORD=password
- REDIS_HOST=redis
depends_on:
- db
- redis
db:
image: postgres:15
environment:
POSTGRES_DB: appdb
POSTGRES_USER: postgres
POSTGRES_PASSWORD: password
volumes:
- db_data:/var/lib/postgresql/data
redis:
image: redis:7-alpine
ports:
- "6379:6379"
nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
depends_on:
- frontend
- backend
volumes:
db_data:
Docker Compose Commands
# Start services
docker-compose up
# Start in detached mode
docker-compose up -d
# Stop services
docker-compose down
# View logs
docker-compose logs -f
# Rebuild images
docker-compose build
# Scale services
docker-compose up -d --scale backend=3
# Execute command
docker-compose exec backend bash
Networking
Create Network
docker network create mynetwork
Run Container on Network
docker run -d --name db --network mynetwork postgres
docker run -d --name app --network mynetwork myapp
Network Types
- bridge: Default network for containers
- host: Container uses host's network
- none: No networking
- overlay: Multi-host networking (Swarm)
Volumes
Named Volumes
# Create volume
docker volume create mydata
# Use volume
docker run -v mydata:/app/data myapp
# List volumes
docker volume ls
# Remove volume
docker volume rm mydata
Bind Mounts
# Mount host directory
docker run -v /host/path:/container/path myapp
# Read-only mount
docker run -v /host/path:/container/path:ro myapp
Best Practices
1. Use Official Base Images
FROM node:18-alpine # Good
FROM ubuntu:latest # Avoid
2. Minimize Layers
# Bad
RUN apt-get update
RUN apt-get install -y package1
RUN apt-get install -y package2
# Good
RUN apt-get update && apt-get install -y \
package1 \
package2 \
&& rm -rf /var/lib/apt/lists/*
3. Use .dockerignore
node_modules
npm-debug.log
.git
.env
*.md
.DS_Store
4. Don't Run as Root
FROM node:18-alpine
# Create user
RUN addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001
# Set ownership
COPY --chown=nodejs:nodejs . .
# Switch to user
USER nodejs
CMD ["node", "server.js"]
5. Use Multi-stage Builds
Reduces final image size by separating build and runtime environments.
6. Leverage Build Cache
# Copy dependency files first
COPY package*.json ./
RUN npm install
# Then copy source code
COPY . .
7. Health Checks
HEALTHCHECK --interval=30s --timeout=3s \
CMD curl -f http://localhost:8080/health || exit 1
Security
1. Scan Images
docker scan myapp:latest
2. Use Specific Tags
FROM node:18.16.0-alpine # Good
FROM node:latest # Avoid
3. Limit Resources
docker run -m 512m --cpus=1 myapp
4. Read-only Filesystem
docker run --read-only myapp
Debugging
View Container Logs
docker logs -f container_name
Inspect Container
docker inspect container_name
Execute Shell
docker exec -it container_name sh
View Resource Usage
docker stats
Copy Files
# From container to host
docker cp container_name:/app/file.txt ./
# From host to container
docker cp ./file.txt container_name:/app/
CI/CD Integration
GitHub Actions
name: Docker Build and Push
on:
push:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push
uses: docker/build-push-action@v4
with:
context: .
push: true
tags: username/myapp:latest
Common Patterns
Database Initialization
services:
db:
image: postgres:15
volumes:
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
- db_data:/var/lib/postgresql/data
Environment Variables
services:
app:
image: myapp
env_file:
- .env
environment:
- NODE_ENV=production
Restart Policies
services:
app:
image: myapp
restart: unless-stopped
Troubleshooting
Container Exits Immediately
# Check logs
docker logs container_name
# Run interactively
docker run -it myapp sh
Port Already in Use
# Find process using port
sudo lsof -i :8080
# Use different port
docker run -p 8081:8080 myapp
Out of Disk Space
# Remove unused containers
docker container prune
# Remove unused images
docker image prune -a
# Remove unused volumes
docker volume prune
# Clean everything
docker system prune -a --volumes
Production Deployment
Docker Swarm
# Initialize swarm
docker swarm init
# Deploy stack
docker stack deploy -c docker-compose.yml myapp
# Scale service
docker service scale myapp_web=3
Kubernetes
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: username/myapp:latest
ports:
- containerPort: 8080
- Go back to Frameworks
- Return to Home