Docker Containerization Best Practices
What is Docker?
Docker is an open-source containerization platform that allows developers to package applications and their dependencies into lightweight, portable containers.
Basic Concepts
Image
An image is a template for containers, containing all files and configurations needed to run an application.
Container
A container is a running instance of an image, providing an isolated, lightweight runtime environment.
Dockerfile
A Dockerfile is a text file used to build Docker images.
Writing Efficient Dockerfiles
Basic Structure
# Use official base image
FROM node:18-alpine
# Set working directory
WORKDIR /app
# Copy package files
COPY package*.json ./
# Install dependencies
RUN npm ci --only=production
# Copy application code
COPY . .
# Expose port
EXPOSE 3000
# Start command
CMD ["node", "server.js"]
Best Practices
1. Use Multi-Stage Builds
Reduce final image size by separating build and runtime environments.
# Build stage
FROM node:18 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Runtime stage
FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY package*.json ./
RUN npm ci --only=production
CMD ["node", "dist/server.js"]
2. Optimize Layer Caching
Place instructions that change less frequently earlier, leveraging Docker’s layer caching mechanism.
# Good practice: Copy dependency files first
COPY package*.json ./
RUN npm ci
# Then copy application code (changes frequently)
COPY . .
3. Use .dockerignore
Create a .dockerignore file to exclude unnecessary files:
node_modules
npm-debug.log
.git
.env
*.md
.vscode
4. Reduce Image Size
- Use lightweight base images like Alpine
- Clean up unnecessary files
- Combine RUN instructions
RUN apt-get update && \
apt-get install -y --no-install-recommends curl && \
rm -rf /var/lib/apt/lists/*
Docker Compose
For multi-container applications, use Docker Compose to simplify management.
docker-compose.yml Example
version: "3.8"
services:
web:
build: .
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- DATABASE_URL=postgresql://db:5432/myapp
depends_on:
- db
restart: unless-stopped
db:
image: postgres:15-alpine
volumes:
- pgdata:/var/lib/postgresql/data
environment:
- POSTGRES_PASSWORD=secret
- POSTGRES_DB=myapp
restart: unless-stopped
volumes:
pgdata:
Common Commands
# Start all services
docker-compose up -d
# View logs
docker-compose logs -f
# Stop services
docker-compose down
# Rebuild
docker-compose build
Security Best Practices
1. Don’t Use root User
RUN addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001
USER nodejs
2. Scan for Vulnerabilities
# Use Trivy to scan images
docker run aquasec/trivy image myapp:latest
3. Use Secrets for Sensitive Information
Don’t hardcode passwords and keys in Dockerfile.
Performance Optimization
1. Resource Limits
services:
web:
deploy:
resources:
limits:
cpus: "0.5"
memory: 512M
reservations:
memory: 256M
2. Health Checks
HEALTHCHECK --interval=30s --timeout=3s \
CMD node healthcheck.js || exit 1
3. Log Management
services:
web:
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
Continuous Integration/Continuous Deployment
GitHub Actions Example
name: Build and Push Docker Image
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Build Docker image
run: docker build -t myapp:latest .
- name: Push to Registry
run: |
docker login -u ${{ secrets.DOCKER_USER }} -p ${{ secrets.DOCKER_PASS }}
docker push myapp:latest
Common Troubleshooting
Container Won’t Start
# View container logs
docker logs <container-id>
# Enter container for debugging
docker exec -it <container-id> sh
Network Issues
# Check network configuration
docker network ls
docker network inspect bridge
Storage Space Issues
# Clean up unused resources
docker system prune -a
# Check disk usage
docker system df
Summary
Docker containerization provides a consistent, reliable way to deploy applications. Following best practices helps you build efficient and secure containerized applications.