Deployment: Docker, Kubernetes, Cloud
Deployment is critical for getting applications to production. This article covers containerization and orchestration.
Introduction
Deployment provides:
- Containerization
- Orchestration
- Scalability
- Reliability
- Automation
Understanding deployment helps you:
- Containerize applications
- Orchestrate containers
- Deploy to cloud
- Scale applications
- Automate deployments
Docker Containerization
Dockerfile
# โ
Good: Multi-stage Dockerfile
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Production stage
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY --from=builder /app/dist ./dist
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD node healthcheck.js
CMD ["node", "dist/index.js"]
# โ
Good: .dockerignore
node_modules
npm-debug.log
.git
.env
.DS_Store
dist
coverage
Docker Compose
# โ
Good: docker-compose.yml
version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile
ports:
- "3000:3000"
environment:
NODE_ENV: production
DATABASE_URL: mongodb://db:27017/myapp
REDIS_URL: redis://redis:6379
depends_on:
- db
- redis
restart: unless-stopped
networks:
- app-network
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
db:
image: mongo:5
volumes:
- mongo-data:/data/db
environment:
MONGO_INITDB_ROOT_USERNAME: root
MONGO_INITDB_ROOT_PASSWORD: password
restart: unless-stopped
networks:
- app-network
redis:
image: redis:7-alpine
restart: unless-stopped
networks:
- app-network
volumes:
mongo-data:
networks:
app-network:
driver: bridge
Kubernetes Deployment
Kubernetes Manifests
# โ
Good: Deployment manifest
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-deployment
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: app
image: myapp:1.0.0
ports:
- containerPort: 3000
env:
- name: NODE_ENV
value: "production"
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: app-secrets
key: database-url
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 3000
initialDelaySeconds: 5
periodSeconds: 5
# โ
Good: Service manifest
apiVersion: v1
kind: Service
metadata:
name: app-service
spec:
selector:
app: myapp
ports:
- protocol: TCP
port: 80
targetPort: 3000
type: LoadBalancer
# โ
Good: ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
LOG_LEVEL: "info"
CACHE_TTL: "3600"
# โ
Good: Secret
apiVersion: v1
kind: Secret
metadata:
name: app-secrets
type: Opaque
stringData:
database-url: mongodb://user:password@db:27017/myapp
jwt-secret: your-secret-key
Kubernetes Commands
# โ
Good: Deploy application
kubectl apply -f deployment.yaml
kubectl apply -f service.yaml
# โ
Good: Check deployment status
kubectl get deployments
kubectl get pods
kubectl get services
# โ
Good: View logs
kubectl logs deployment/app-deployment
kubectl logs pod-name
# โ
Good: Scale deployment
kubectl scale deployment app-deployment --replicas=5
# โ
Good: Update image
kubectl set image deployment/app-deployment app=myapp:2.0.0
# โ
Good: Rollback
kubectl rollout undo deployment/app-deployment
# โ
Good: Delete resources
kubectl delete deployment app-deployment
kubectl delete service app-service
Cloud Deployment
AWS Deployment
# โ
Good: Deploy to Elastic Beanstalk
eb init -p node.js-18 my-app
eb create production
eb deploy
# โ
Good: Deploy to ECS
aws ecs create-cluster --cluster-name my-cluster
aws ecs register-task-definition --cli-input-json file://task-definition.json
aws ecs create-service --cluster my-cluster --service-name my-service --task-definition my-task:1 --desired-count 3
# โ
Good: Deploy to Lambda
zip -r lambda-function.zip .
aws lambda create-function --function-name my-function --runtime nodejs18.x --role arn:aws:iam::123456789012:role/lambda-role --handler index.handler --zip-file fileb://lambda-function.zip
Google Cloud Deployment
# โ
Good: Deploy to Cloud Run
gcloud run deploy my-app --source . --platform managed --region us-central1
# โ
Good: Deploy to App Engine
gcloud app deploy
# โ
Good: Deploy to GKE
gcloud container clusters create my-cluster
gcloud container clusters get-credentials my-cluster
kubectl apply -f deployment.yaml
Azure Deployment
# โ
Good: Deploy to App Service
az webapp up --name my-app --resource-group my-group
# โ
Good: Deploy to Container Instances
az container create --resource-group my-group --name my-container --image myapp:1.0.0 --ports 3000
# โ
Good: Deploy to AKS
az aks create --resource-group my-group --name my-cluster
az aks get-credentials --resource-group my-group --name my-cluster
kubectl apply -f deployment.yaml
CI/CD Pipelines
GitHub Actions
# โ
Good: GitHub Actions workflow
name: Deploy
on:
push:
branches: [main]
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
- name: Build
run: npm run build
- name: Build Docker image
run: docker build -t myapp:${{ github.sha }} .
- name: Push to Docker Hub
run: |
echo ${{ secrets.DOCKER_PASSWORD }} | docker login -u ${{ secrets.DOCKER_USERNAME }} --password-stdin
docker push myapp:${{ github.sha }}
- name: Deploy to Kubernetes
run: |
kubectl set image deployment/app-deployment app=myapp:${{ github.sha }}
GitLab CI
# โ
Good: GitLab CI pipeline
stages:
- build
- test
- deploy
build:
stage: build
image: node:18
script:
- npm ci
- npm run build
artifacts:
paths:
- dist/
test:
stage: test
image: node:18
script:
- npm ci
- npm test
deploy:
stage: deploy
image: docker:latest
services:
- docker:dind
script:
- docker build -t myapp:$CI_COMMIT_SHA .
- docker push myapp:$CI_COMMIT_SHA
- kubectl set image deployment/app-deployment app=myapp:$CI_COMMIT_SHA
only:
- main
Monitoring and Logging
Application Monitoring
// โ
Good: Health check endpoint
app.get('/health', (req, res) => {
res.json({
status: 'ok',
timestamp: new Date(),
uptime: process.uptime()
});
});
// โ
Good: Readiness check
app.get('/ready', async (req, res) => {
try {
await checkDatabaseConnection();
res.json({ ready: true });
} catch (err) {
res.status(503).json({ ready: false });
}
});
// โ
Good: Metrics endpoint
app.get('/metrics', (req, res) => {
res.json({
memory: process.memoryUsage(),
uptime: process.uptime(),
requests: requestCount
});
});
Logging
// โ
Good: Structured logging
const winston = require('winston');
const logger = winston.createLogger({
format: winston.format.json(),
transports: [
new winston.transports.File({ filename: 'error.log', level: 'error' }),
new winston.transports.File({ filename: 'combined.log' })
]
});
logger.info('Application started', {
version: '1.0.0',
environment: process.env.NODE_ENV
});
logger.error('Database connection failed', {
error: err.message,
timestamp: new Date()
});
Best Practices
-
Use multi-stage builds:
# โ Good: Multi-stage FROM node:18 AS builder RUN npm ci && npm run build FROM node:18 COPY --from=builder /app/dist ./dist # โ Bad: Single stage FROM node:18 COPY . . RUN npm ci -
Set resource limits:
# โ Good: Resource limits resources: requests: memory: "256Mi" cpu: "250m" limits: memory: "512Mi" cpu: "500m" # โ Bad: No limits -
Use health checks:
# โ Good: Health checks livenessProbe: httpGet: path: /health port: 3000 initialDelaySeconds: 30 # โ Bad: No health checks
Summary
Deployment is essential. Key takeaways:
- Containerize with Docker
- Orchestrate with Kubernetes
- Deploy to cloud platforms
- Implement CI/CD pipelines
- Monitor applications
- Use health checks
- Set resource limits
- Automate deployments
Related Resources
- Docker Documentation
- Kubernetes Documentation
- AWS Documentation
- Google Cloud Documentation
- GitHub Actions
Next Steps
- Learn about Monitoring
- Explore Performance Testing
- Study Testing & QA
- Practice deployment
- Deploy applications
Comments