Master the art of containerization and orchestration using Docker and Kubernetes. Learn how to build, deploy, and manage containerized applications at scale. 🚀
Docker Basics 🐳
# Multi-stage build example
# Build stage
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Production stage
FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY package*.json ./
RUN npm ci --production
EXPOSE 3000
CMD ["npm", "start"]
# Development stage with hot reload
FROM node:20-alpine AS development
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["npm", "run", "dev"]
# Testing stage
FROM node:20-alpine AS test
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run test
Docker Compose 📦
# docker-compose.yml
version: '3.8'
services:
api:
build:
context: ./api
target: development
volumes:
- ./api:/app
- /app/node_modules
ports:
- "3000:3000"
environment:
- NODE_ENV=development
- DATABASE_URL=postgres://user:pass@db:5432/mydb
depends_on:
- db
- redis
web:
build:
context: ./web
target: development
volumes:
- ./web:/app
- /app/node_modules
ports:
- "8080:8080"
environment:
- VITE_API_URL=http://api:3000
depends_on:
- api
db:
image: postgres:15-alpine
volumes:
- postgres_data:/var/lib/postgresql/data
environment:
- POSTGRES_USER=user
- POSTGRES_PASSWORD=pass
- POSTGRES_DB=mydb
ports:
- "5432:5432"
redis:
image: redis:7-alpine
volumes:
- redis_data:/data
ports:
- "6379:6379"
nginx:
image: nginx:alpine
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
ports:
- "80:80"
depends_on:
- api
- web
volumes:
postgres_data:
redis_data:
Kubernetes Deployment 🎯
# Namespace
apiVersion: v1
kind: Namespace
metadata:
name: myapp
labels:
name: myapp
---
# ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
namespace: myapp
data:
NODE_ENV: production
API_URL: http://api-service
REDIS_HOST: redis-service
---
# Secret
apiVersion: v1
kind: Secret
metadata:
name: app-secrets
namespace: myapp
type: Opaque
data:
DB_PASSWORD: cGFzc3dvcmQxMjM=
JWT_SECRET: c2VjcmV0MTIz
---
# API Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-deployment
namespace: myapp
spec:
replicas: 3
selector:
matchLabels:
app: api
template:
metadata:
labels:
app: api
spec:
containers:
- name: api
image: myapp/api:latest
ports:
- containerPort: 3000
env:
- name: NODE_ENV
valueFrom:
configMapKeyRef:
name: app-config
key: NODE_ENV
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: app-secrets
key: DB_PASSWORD
resources:
limits:
cpu: "500m"
memory: "512Mi"
requests:
cpu: "200m"
memory: "256Mi"
readinessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 15
periodSeconds: 20
---
# API Service
apiVersion: v1
kind: Service
metadata:
name: api-service
namespace: myapp
spec:
selector:
app: api
ports:
- port: 80
targetPort: 3000
type: ClusterIP
---
# Web Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-deployment
namespace: myapp
spec:
replicas: 2
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: web
image: myapp/web:latest
ports:
- containerPort: 80
env:
- name: API_URL
valueFrom:
configMapKeyRef:
name: app-config
key: API_URL
resources:
limits:
cpu: "300m"
memory: "256Mi"
requests:
cpu: "100m"
memory: "128Mi"
---
# Web Service
apiVersion: v1
kind: Service
metadata:
name: web-service
namespace: myapp
spec:
selector:
app: web
ports:
- port: 80
targetPort: 80
type: ClusterIP
---
# Ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-ingress
namespace: myapp
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: myapp.example.com
http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: api-service
port:
number: 80
- path: /
pathType: Prefix
backend:
service:
name: web-service
port:
number: 80
---
# HorizontalPodAutoscaler
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: api-hpa
namespace: myapp
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: api-deployment
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
Monitoring and Logging 📊
# Prometheus ServiceMonitor
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: app-monitor
namespace: myapp
spec:
selector:
matchLabels:
app: api
endpoints:
- port: metrics
interval: 15s
---
# Grafana Dashboard ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
name: grafana-dashboard
namespace: monitoring
data:
dashboard.json: |
{
"dashboard": {
"title": "Application Metrics",
"panels": [
{
"title": "CPU Usage",
"type": "graph",
"datasource": "Prometheus",
"targets": [
{
"expr": "container_cpu_usage_seconds_total{namespace=\"myapp\"}"
}
]
},
{
"title": "Memory Usage",
"type": "graph",
"datasource": "Prometheus",
"targets": [
{
"expr": "container_memory_usage_bytes{namespace=\"myapp\"}"
}
]
}
]
}
}
---
# Fluentd ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
name: fluentd-config
namespace: logging
data:
fluent.conf: |
<source>
@type tail
path /var/log/containers/*.log
pos_file /var/log/fluentd-containers.log.pos
tag kubernetes.*
read_from_head true
<parse>
@type json
time_format %Y-%m-%dT%H:%M:%S.%NZ
</parse>
</source>
<match kubernetes.**>
@type elasticsearch
host elasticsearch-service
port 9200
logstash_format true
logstash_prefix k8s
<buffer>
@type file
path /var/log/fluentd-buffers/kubernetes.system.buffer
flush_mode interval
retry_type exponential_backoff
flush_interval 5s
retry_forever false
retry_max_interval 30
chunk_limit_size 2M
queue_limit_length 8
overflow_action block
</buffer>
</match>
CI/CD Pipeline 🔄
# .github/workflows/deploy.yml
name: Deploy to Kubernetes
on:
push:
branches: [main]
pull_request:
branches: [main]
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push API image
uses: docker/build-push-action@v5
with:
context: ./api
push: true
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/api:${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Build and push Web image
uses: docker/build-push-action@v5
with:
context: ./web
push: true
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/web:${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
deploy:
needs: build-and-push
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install kubectl
uses: azure/setup-kubectl@v3
- name: Set Kubernetes context
uses: azure/k8s-set-context@v3
with:
kubeconfig: ${{ secrets.KUBE_CONFIG }}
- name: Deploy to Kubernetes
run: |
# Update image tags
kubectl set image deployment/api-deployment \
api=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/api:${{ github.sha }} \
-n myapp
kubectl set image deployment/web-deployment \
web=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/web:${{ github.sha }} \
-n myapp
# Wait for rollout
kubectl rollout status deployment/api-deployment -n myapp
kubectl rollout status deployment/web-deployment -n myapp
- name: Verify deployment
run: |
kubectl get pods -n myapp
kubectl get services -n myapp
kubectl get ingress -n myapp
Best Practices 📝
- Use multi-stage builds
- Implement proper health checks
- Use resource limits
- Implement auto-scaling
- Use proper monitoring
- Implement proper logging
- Use proper security
- Implement CI/CD
- Use proper networking
- Follow Kubernetes best practices
Additional Resources
Docker and Kubernetes are essential tools for modern application deployment. This guide covers the fundamentals and best practices for containerizing and orchestrating applications in production.