๐ข Docker Production Best Practices
The Art of Building Ships That Never Sink
Imagine youโre building a toy boat. If you stuff too many toys inside, use heavy wood, and forget to make it waterproofโit sinks! ๐ถ๐ฆ
Docker containers are like little boats carrying your apps across the ocean of the internet. Letโs learn how to build unsinkable containers that run smoothly in production!
๐ฏ The Golden Rule: One Process Per Container
Think of It Like a Lunchbox ๐ฑ
Each compartment in a lunchbox holds ONE type of food:
- ๐ Rice in one section
- ๐ฅฆ Veggies in another
- ๐ Chicken in its own spot
Containers work the same way! Each container should do ONE job.
โ Wrong Way (The Messy Lunchbox)
# BAD: Multiple processes crammed together
CMD ["sh", "-c", "nginx & php-fpm & mysql"]
โ Right Way (The Organized Lunchbox)
# Container 1: Web Server
CMD ["nginx", "-g", "daemon off;"]
# Container 2: App Server
CMD ["php-fpm"]
# Container 3: Database
CMD ["mysqld"]
Why Does This Matter?
| One Process | Multiple Processes |
|---|---|
| Easy to fix if broken | Hard to find problems |
| Restarts cleanly | Zombie processes appear |
| Scales independently | All or nothing scaling |
| Clear logs | Mixed up logs |
๐ฆ Minimal Base Images
The Backpack Story ๐
Going on a trip? You pack only what you need!
- Heavy backpack = slow, tiring, might break your back
- Light backpack = fast, easy, comfortable
Docker images work the same way!
Size Comparison
graph TD A["ubuntu:latest<br/>77MB"] --> B["debian:slim<br/>22MB"] B --> C["alpine:latest<br/>5MB"] C --> D["distroless<br/>2MB"] style A fill:#ff6b6b style B fill:#ffa502 style C fill:#7bed9f style D fill:#2ed573
Real Example: Python App
# โ Heavy (900MB+)
FROM python:3.11
# โ
Light (50MB)
FROM python:3.11-alpine
# โ
Even Lighter (40MB)
FROM python:3.11-slim
Why Minimal Images Win
| Benefit | Explanation |
|---|---|
| ๐ Faster deploys | Less data to download |
| ๐ More secure | Fewer tools = fewer vulnerabilities |
| ๐พ Less storage | Saves disk space |
| ๐ Easier debugging | Less noise, clearer problems |
๐ค Non-Root Container Users
The Superhero Problem ๐ฆธ
Imagine every kid in school had superhero powers. Chaos, right? One angry kid could destroy the whole building!
Root user = Superhero powers
If a hacker breaks into a root container, they can escape and damage your whole server!
Creating a Safe User
FROM node:20-alpine
# Create a regular user (no superpowers!)
RUN addgroup -S appgroup && \
adduser -S appuser -G appgroup
# Set ownership of app files
COPY --chown=appuser:appgroup . /app
# Switch to the safe user
USER appuser
CMD ["node", "server.js"]
The Safety Shield
graph TD A["Hacker Breaks In"] --> B{Running as Root?} B -->|Yes| C["๐ Can Escape Container<br/>Access Host System!"] B -->|No| D["๐ Trapped in Container<br/>Limited Damage"] style C fill:#ff6b6b style D fill:#2ed573
๐ก Proper Signal Handling
The Fire Drill ๐
When the fire alarm rings at school:
- Teachers give instructions
- Students line up calmly
- Everyone exits safely
Your container needs to understand โfire alarmsโ too!
Container Signals
| Signal | Meaning | Action |
|---|---|---|
SIGTERM |
โPlease stop nicelyโ | Save work, close connections |
SIGKILL |
โSTOP NOW!โ | Immediate termination |
SIGHUP |
โReload configโ | Refresh settings |
Node.js Signal Handler
// Listen for the "please stop" signal
process.on('SIGTERM', () => {
console.log('Received SIGTERM...');
// Close database connections
db.close();
// Stop accepting new requests
server.close(() => {
console.log('Graceful shutdown complete');
process.exit(0);
});
});
๐ Graceful Container Shutdown
The Restaurant Closing Analogy ๐ฝ๏ธ
Bad closing: Kick everyone out mid-meal! Good closing:
- Stop accepting new customers
- Let current diners finish
- Clean up tables
- Lock the doors
How Containers Shutdown
graph TD A["Docker sends SIGTERM"] --> B["App receives signal"] B --> C["Stop new connections"] C --> D["Finish current work"] D --> E["Close databases"] E --> F["Exit cleanly"] F --> G["โ Container Stops"] style G fill:#2ed573
The 10-Second Rule
Docker waits 10 seconds after SIGTERM. If your app doesnโt stop, Docker sends SIGKILL (the rude kick-out).
# Give your app more time to cleanup
STOPSIGNAL SIGTERM
# In docker-compose or run command:
# docker stop --time 30 mycontainer
Python Graceful Shutdown
import signal
import sys
def graceful_exit(signum, frame):
print("Shutting down gracefully...")
# Save state
save_to_database()
# Close connections
close_all_connections()
sys.exit(0)
signal.signal(signal.SIGTERM, graceful_exit)
๐ Container Init Process
The Orphan Problem ๐ถ
When a parent process dies, child processes become โorphans.โ Without someone to care for them, they become zombie processesโdead but still taking up space!
What is an Init Process?
An init process (PID 1) is like a babysitter:
- Adopts orphan processes
- Cleans up zombies
- Forwards signals properly
Using Tini (The Best Babysitter)
FROM python:3.11-alpine
# Install tini
RUN apk add --no-cache tini
# Set tini as the entrypoint
ENTRYPOINT ["/sbin/tini", "--"]
# Your actual command
CMD ["python", "app.py"]
Dockerโs Built-in Option
# Run with init flag
docker run --init myapp
# In docker-compose
services:
myapp:
init: true
image: myapp:latest
graph TD A["Main Process Dies"] --> B{Init Process?} B -->|No| C["๐ป Zombie Children"] B -->|Yes| D["๐งน Tini Cleans Up"] style C fill:#ff6b6b style D fill:#2ed573
๐ซ Container Anti-Patterns
Things That Make Containers Sad ๐ข
1. Storing Data Inside Containers
# โ BAD: Data dies when container dies
RUN mkdir /data
COPY mydata.db /data/
# โ
GOOD: Use volumes
# docker run -v mydata:/data myapp
2. Hardcoding Secrets
# โ NEVER DO THIS
ENV DATABASE_PASSWORD=supersecret123
# โ
Use secrets or environment variables
# docker run -e DB_PASS_FILE=/run/secrets/db_pass
3. Running as Root (We Covered This!)
# โ Dangerous default
FROM node:20
CMD ["node", "app.js"]
# โ
Safe with USER
FROM node:20
USER node
CMD ["node", "app.js"]
4. Fat Images with Dev Tools
# โ Production image with debugging tools
FROM ubuntu:latest
RUN apt-get install -y vim curl wget gcc make
# โ
Multi-stage build - dev stays in build stage
FROM node:20 AS builder
RUN npm install && npm run build
FROM node:20-alpine
COPY --from=builder /app/dist /app
5. Ignoring Health Checks
# โ
Always add health checks
HEALTHCHECK --interval=30s \
--timeout=3s \
--retries=3 \
CMD curl -f http://localhost:8080/health || exit 1
โ๏ธ Docker vs Kubernetes Overview
The Captain vs The Admiral ๐ข
Docker = One shipโs captain
- Controls a single container
- Great for one boat
Kubernetes = Fleet admiral
- Commands hundreds of ships
- Coordinates the whole navy
When to Use What
graph TD A["Your App"] --> B{How Many Containers?} B -->|1-5| C["Docker + Docker Compose"] B -->|6-20| D["Docker Swarm"] B -->|20+| E["Kubernetes"] style C fill:#7bed9f style D fill:#ffa502 style E fill:#ff6b6b
Feature Comparison
| Feature | Docker | Kubernetes |
|---|---|---|
| Learning Curve | ๐ Easy | ๐ Complex |
| Scaling | Manual | Automatic |
| Self-Healing | No | Yes |
| Load Balancing | Basic | Advanced |
| Best For | Small apps | Enterprise |
Simple Truth
| Scenario | Choose |
|---|---|
| Side project | Docker |
| Small startup | Docker Compose |
| Growing team | Docker Swarm |
| Big company | Kubernetes |
๐ฏ Quick Summary
| Best Practice | Remember This |
|---|---|
| One Process | ๐ฑ One food per lunchbox section |
| Minimal Images | ๐ Pack light for easy travel |
| Non-Root User | ๐ฆธ No superpowers = safer |
| Signal Handling | ๐ Listen to the fire alarm |
| Graceful Shutdown | ๐ฝ๏ธ Close the restaurant properly |
| Init Process | ๐ถ Hire a babysitter for orphans |
| Anti-Patterns | ๐ซ Avoid the container sins |
| Docker vs K8s | ๐ข Captain vs Admiral |
๐ You Did It!
You now understand how to build production-ready containers that are:
- โ Safe and secure
- โ Fast and efficient
- โ Reliable and stable
- โ Easy to manage
Your containers are ready to sail the production seas! ๐๐ข
Remember: A well-built container is like a well-built boatโit carries your precious cargo safely through any storm!
