Kubernetes Deployment Strategies and CI/CD: Production-Ready Practices
Kubernetes Deployment Strategies and CI/CD: Production-Ready Practices
Deploying applications to Kubernetes in production requires sophisticated strategies to ensure zero-downtime updates, reliable rollbacks, and seamless integration with CI/CD pipelines. This comprehensive guide explores advanced deployment patterns, GitOps principles, and production-ready CI/CD implementations for Kubernetes environments.
Table of Contents
- Introduction to Kubernetes Deployments
- Core Deployment Strategies
- Rolling Update Strategy
- Blue-Green Deployments
- Canary Deployments
- Feature Flags and Progressive Delivery
- GitOps and Declarative Deployments
- CI/CD Pipeline Integration
- Advanced Deployment Patterns
- Monitoring and Rollback Strategies
- Security and Compliance
- Production Best Practices
Introduction to Kubernetes Deployments
Kubernetes provides powerful primitives for application deployment, but choosing the right strategy depends on your application's requirements, risk tolerance, and operational maturity. Modern deployment strategies minimize risk while maximizing velocity.
Why Deployment Strategies Matter
🚀 Key Benefits:
- Zero-Downtime Updates: Keep applications available during updates
- Risk Mitigation: Test changes with real traffic before full rollout
- Fast Rollbacks: Quickly revert problematic deployments
- Progressive Delivery: Gradually roll out features to users
- Automated Operations: Reduce manual intervention and human error
Kubernetes Deployment Architecture
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-deployment
labels:
app: myapp
spec:
replicas: 3
selector:
matchLabels:
app: myapp
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
template:
metadata:
labels:
app: myapp
version: v1.0.0
spec:
containers:
- name: app
image: myapp:v1.0.0
ports:
- containerPort: 8080
readinessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
Core Deployment Strategies
Strategy Comparison Matrix
Strategy | Downtime | Resource Cost | Rollback Speed | Testing Capability | Complexity |
---|---|---|---|---|---|
Recreate | Yes | Low | Fast | None | Low |
Rolling Update | No | Low | Medium | Limited | Low |
Blue-Green | No | High | Instant | Full | Medium |
Canary | No | Medium | Fast | Progressive | High |
A/B Testing | No | Medium | Fast | Targeted | High |
Choosing the Right Strategy
graph TD
A[Deployment Strategy Selection] --> B{Downtime Acceptable?}
B -->|Yes| C[Recreate Strategy]
B -->|No| D{Need Instant Rollback?}
D -->|Yes| E[Blue-Green]
D -->|No| F{Progressive Testing?}
F -->|Yes| G[Canary]
F -->|No| H[Rolling Update]
Rolling Update Strategy
Rolling updates are Kubernetes' default deployment strategy, gradually replacing old pods with new ones.
Basic Rolling Update
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app
spec:
replicas: 10
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 2 # Maximum pods above desired replicas
maxUnavailable: 1 # Maximum pods that can be unavailable
selector:
matchLabels:
app: web-app
template:
metadata:
labels:
app: web-app
spec:
containers:
- name: app
image: web-app:v2.0.0
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
Advanced Rolling Update Configuration
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-service
spec:
replicas: 20
progressDeadlineSeconds: 600 # Timeout for deployment
revisionHistoryLimit: 10 # Number of old ReplicaSets to keep
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 25% # Percentage-based configuration
maxUnavailable: 25%
selector:
matchLabels:
app: api-service
template:
metadata:
labels:
app: api-service
version: v2.1.0
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "9090"
spec:
containers:
- name: api
image: api-service:v2.1.0
ports:
- containerPort: 8080
- containerPort: 9090 # Metrics port
env:
- name: VERSION
value: "v2.1.0"
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
timeoutSeconds: 3
successThreshold: 1
failureThreshold: 3
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "sleep 15"]
Rolling Update with PodDisruptionBudget
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: api-pdb
spec:
minAvailable: 80%
selector:
matchLabels:
app: api-service
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-service
spec:
replicas: 10
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 2
maxUnavailable: 0 # Ensure high availability
selector:
matchLabels:
app: api-service
template:
metadata:
labels:
app: api-service
spec:
containers:
- name: api
image: api-service:v2.0.0
Blue-Green Deployments
Blue-green deployments maintain two complete environments, switching traffic between them for instant rollbacks.
Blue-Green with Services
# Blue Deployment (Current)
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-blue
labels:
version: blue
spec:
replicas: 3
selector:
matchLabels:
app: myapp
version: blue
template:
metadata:
labels:
app: myapp
version: blue
spec:
containers:
- name: app
image: myapp:v1.0.0
ports:
- containerPort: 8080
---
# Green Deployment (New)
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-green
labels:
version: green
spec:
replicas: 3
selector:
matchLabels:
app: myapp
version: green
template:
metadata:
labels:
app: myapp
version: green
spec:
containers:
- name: app
image: myapp:v2.0.0
ports:
- containerPort: 8080
---
# Service (Switch between blue/green)
apiVersion: v1
kind: Service
metadata:
name: myapp-service
spec:
selector:
app: myapp
version: blue # Change to 'green' to switch
ports:
- port: 80
targetPort: 8080
type: LoadBalancer
Blue-Green Automation Script
#!/bin/bash
# blue-green-deploy.sh
NAMESPACE="production"
APP_NAME="myapp"
NEW_VERSION="v2.0.0"
# Deploy green version
kubectl set image deployment/${APP_NAME}-green \
app=${APP_NAME}:${NEW_VERSION} \
-n ${NAMESPACE}
# Wait for green deployment to be ready
kubectl rollout status deployment/${APP_NAME}-green -n ${NAMESPACE}
# Run smoke tests
echo "Running smoke tests on green deployment..."
GREEN_POD=$(kubectl get pod -n ${NAMESPACE} -l version=green -o jsonpath='{.items[0].metadata.name}')
kubectl exec ${GREEN_POD} -n ${NAMESPACE} -- /app/smoke-test.sh
if [ $? -eq 0 ]; then
echo "Smoke tests passed. Switching traffic to green..."
# Switch service to green
kubectl patch service ${APP_NAME}-service -n ${NAMESPACE} \
-p '{"spec":{"selector":{"version":"green"}}}'
echo "Traffic switched to green. Monitoring for 5 minutes..."
sleep 300
# If everything is stable, update blue to match green
kubectl set image deployment/${APP_NAME}-blue \
app=${APP_NAME}:${NEW_VERSION} \
-n ${NAMESPACE}
else
echo "Smoke tests failed. Keeping traffic on blue."
exit 1
fi
Blue-Green with Ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myapp-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: myapp.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: myapp-blue # or myapp-green
port:
number: 80
Canary Deployments
Canary deployments gradually roll out changes to a small subset of users before full deployment.
Basic Canary with Deployments
# Stable Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-stable
spec:
replicas: 9 # 90% of traffic
selector:
matchLabels:
app: myapp
version: stable
template:
metadata:
labels:
app: myapp
version: stable
spec:
containers:
- name: app
image: myapp:v1.0.0
---
# Canary Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-canary
spec:
replicas: 1 # 10% of traffic
selector:
matchLabels:
app: myapp
version: canary
template:
metadata:
labels:
app: myapp
version: canary
spec:
containers:
- name: app
image: myapp:v2.0.0
---
# Service routing to both
apiVersion: v1
kind: Service
metadata:
name: myapp-service
spec:
selector:
app: myapp # Matches both stable and canary
ports:
- port: 80
targetPort: 8080
Advanced Canary with Flagger
# Install Flagger
# kubectl apply -k github.com/fluxcd/flagger/kustomize/linkerd
apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
name: myapp
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: myapp
progressDeadlineSeconds: 600
service:
port: 80
targetPort: 8080
gateways:
- public-gateway.istio-system.svc.cluster.local
hosts:
- myapp.example.com
analysis:
interval: 1m
threshold: 5
maxWeight: 50
stepWeight: 10
metrics:
- name: request-success-rate
thresholdRange:
min: 99
interval: 1m
- name: request-duration
thresholdRange:
max: 500
interval: 1m
webhooks:
- name: acceptance-test
type: pre-rollout
url: http://flagger-loadtester.test/
timeout: 30s
metadata:
type: bash
cmd: "curl -s http://myapp-canary.test:80/health"
- name: load-test
type: rollout
url: http://flagger-loadtester.test/
metadata:
cmd: "hey -z 2m -q 10 -c 2 http://myapp-canary.test:80/"
Canary with Istio
# VirtualService for traffic splitting
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: myapp
spec:
hosts:
- myapp.example.com
http:
- match:
- headers:
canary:
exact: "true"
route:
- destination:
host: myapp-canary
port:
number: 80
- route:
- destination:
host: myapp-stable
port:
number: 80
weight: 90
- destination:
host: myapp-canary
port:
number: 80
weight: 10
---
# DestinationRule
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
name: myapp-stable
spec:
host: myapp-stable
subsets:
- name: v1
labels:
version: v1
---
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
name: myapp-canary
spec:
host: myapp-canary
subsets:
- name: v2
labels:
version: v2
Feature Flags and Progressive Delivery
Combine deployment strategies with feature flags for fine-grained control over feature rollouts.
Feature Flag Implementation
apiVersion: v1
kind: ConfigMap
metadata:
name: feature-flags
data:
flags.json: |
{
"features": {
"new-ui": {
"enabled": true,
"percentage": 10,
"allowedUsers": ["beta-tester-1", "beta-tester-2"]
},
"advanced-analytics": {
"enabled": false
},
"payment-v2": {
"enabled": true,
"percentage": 100,
"regions": ["us-east", "eu-west"]
}
}
}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-with-flags
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: app
image: myapp:v2.0.0
env:
- name: FEATURE_FLAGS_PATH
value: /etc/feature-flags/flags.json
volumeMounts:
- name: feature-flags
mountPath: /etc/feature-flags
volumes:
- name: feature-flags
configMap:
name: feature-flags
Progressive Delivery with Argo Rollouts
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: myapp-rollout
spec:
replicas: 10
strategy:
canary:
canaryService: myapp-canary
stableService: myapp-stable
trafficRouting:
istio:
virtualServices:
- name: myapp-vsvc
routes:
- primary
maxSurge: "25%"
maxUnavailable: 0
steps:
- setWeight: 10
- pause: {duration: 1m}
- setWeight: 20
- pause: {duration: 2m}
- setWeight: 40
- pause: {duration: 2m}
- setWeight: 60
- pause: {duration: 2m}
- setWeight: 80
- pause: {duration: 2m}
analysis:
templates:
- templateName: success-rate
startingStep: 2
args:
- name: service-name
value: myapp-canary
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: app
image: myapp:v2.0.0
ports:
- containerPort: 8080
GitOps and Declarative Deployments
GitOps uses Git as the single source of truth for declarative infrastructure and applications.
ArgoCD Implementation
# Application definition
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: myapp
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/myorg/myapp-k8s
targetRevision: HEAD
path: environments/production
destination:
server: https://kubernetes.default.svc
namespace: production
syncPolicy:
automated:
prune: true
selfHeal: true
allowEmpty: false
syncOptions:
- Validate=true
- CreateNamespace=true
retry:
limit: 5
backoff:
duration: 5s
factor: 2
maxDuration: 3m
Flux CD Configuration
# GitRepository source
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: GitRepository
metadata:
name: myapp
namespace: flux-system
spec:
interval: 1m
ref:
branch: main
url: https://github.com/myorg/myapp-k8s
---
# Kustomization
apiVersion: kustomize.toolkit.fluxcd.io/v1beta2
kind: Kustomization
metadata:
name: myapp
namespace: flux-system
spec:
interval: 10m
path: "./environments/production"
prune: true
sourceRef:
kind: GitRepository
name: myapp
validation: client
postBuild:
substitute:
cluster_name: production
region: us-east-1
substituteFrom:
- kind: ConfigMap
name: cluster-config
GitOps Repository Structure
myapp-k8s/
├── base/
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── configmap.yaml
│ └── kustomization.yaml
├── environments/
│ ├── development/
│ │ ├── kustomization.yaml
│ │ ├── replica-patch.yaml
│ │ └── resource-patch.yaml
│ ├── staging/
│ │ ├── kustomization.yaml
│ │ ├── replica-patch.yaml
│ │ └── ingress.yaml
│ └── production/
│ ├── kustomization.yaml
│ ├── replica-patch.yaml
│ ├── resource-patch.yaml
│ ├── hpa.yaml
│ └── pdb.yaml
└── scripts/
├── promote.sh
└── rollback.sh
CI/CD Pipeline Integration
Jenkins Pipeline for Kubernetes
pipeline {
agent {
kubernetes {
yaml """
apiVersion: v1
kind: Pod
spec:
containers:
- name: docker
image: docker:20.10.21-dind
securityContext:
privileged: true
- name: kubectl
image: bitnami/kubectl:latest
command:
- cat
tty: true
- name: helm
image: alpine/helm:3.10.3
command:
- cat
tty: true
"""
}
}
environment {
DOCKER_REGISTRY = 'registry.example.com'
APP_NAME = 'myapp'
NAMESPACE = 'production'
}
stages {
stage('Build') {
steps {
container('docker') {
script {
def tag = "${env.BUILD_NUMBER}"
sh """
docker build -t ${DOCKER_REGISTRY}/${APP_NAME}:${tag} .
docker push ${DOCKER_REGISTRY}/${APP_NAME}:${tag}
"""
}
}
}
}
stage('Test') {
steps {
container('docker') {
sh """
docker run --rm ${DOCKER_REGISTRY}/${APP_NAME}:${BUILD_NUMBER} npm test
"""
}
}
}
stage('Security Scan') {
steps {
container('docker') {
sh """
trivy image ${DOCKER_REGISTRY}/${APP_NAME}:${BUILD_NUMBER}
"""
}
}
}
stage('Deploy to Staging') {
steps {
container('kubectl') {
withCredentials([file(credentialsId: 'kubeconfig', variable: 'KUBECONFIG')]) {
sh """
kubectl set image deployment/${APP_NAME} \
${APP_NAME}=${DOCKER_REGISTRY}/${APP_NAME}:${BUILD_NUMBER} \
-n staging
kubectl rollout status deployment/${APP_NAME} -n staging
"""
}
}
}
}
stage('Integration Tests') {
steps {
sh """
npm run test:integration -- --url=https://staging.example.com
"""
}
}
stage('Deploy to Production') {
when {
branch 'main'
}
input {
message "Deploy to production?"
ok "Deploy"
}
steps {
container('helm') {
withCredentials([file(credentialsId: 'kubeconfig', variable: 'KUBECONFIG')]) {
sh """
helm upgrade --install ${APP_NAME} ./helm-chart \
--namespace ${NAMESPACE} \
--set image.tag=${BUILD_NUMBER} \
--set image.repository=${DOCKER_REGISTRY}/${APP_NAME} \
--wait
"""
}
}
}
}
}
post {
success {
slackSend(
color: 'good',
message: "Deployment successful: ${env.JOB_NAME} - ${env.BUILD_NUMBER}"
)
}
failure {
slackSend(
color: 'danger',
message: "Deployment failed: ${env.JOB_NAME} - ${env.BUILD_NUMBER}"
)
}
}
}
GitLab CI/CD Pipeline
# .gitlab-ci.yml
variables:
DOCKER_REGISTRY: registry.gitlab.com
APP_NAME: myapp
CONTAINER_IMAGE: $DOCKER_REGISTRY/$CI_PROJECT_PATH
stages:
- build
- test
- security
- deploy-staging
- deploy-production
build:
stage: build
image: docker:20.10.21
services:
- docker:20.10.21-dind
script:
- docker build -t $CONTAINER_IMAGE:$CI_COMMIT_SHA .
- docker tag $CONTAINER_IMAGE:$CI_COMMIT_SHA $CONTAINER_IMAGE:latest
- docker push $CONTAINER_IMAGE:$CI_COMMIT_SHA
- docker push $CONTAINER_IMAGE:latest
test:
stage: test
image: $CONTAINER_IMAGE:$CI_COMMIT_SHA
script:
- npm test
- npm run test:coverage
coverage: '/Coverage: \d+\.\d+%/'
artifacts:
reports:
junit: test-results.xml
coverage_report:
coverage_format: cobertura
path: coverage.xml
security-scan:
stage: security
image: aquasec/trivy:latest
script:
- trivy image --severity HIGH,CRITICAL $CONTAINER_IMAGE:$CI_COMMIT_SHA
allow_failure: true
deploy-staging:
stage: deploy-staging
image: bitnami/kubectl:latest
environment:
name: staging
url: https://staging.example.com
script:
- kubectl config use-context staging
- |
kubectl set image deployment/$APP_NAME \
$APP_NAME=$CONTAINER_IMAGE:$CI_COMMIT_SHA \
-n staging
- kubectl rollout status deployment/$APP_NAME -n staging
only:
- develop
- main
deploy-production:
stage: deploy-production
image: alpine/helm:3.10.3
environment:
name: production
url: https://example.com
script:
- helm upgrade --install $APP_NAME ./helm-chart
--namespace production
--set image.tag=$CI_COMMIT_SHA
--set image.repository=$CONTAINER_IMAGE
--atomic
--timeout 10m
when: manual
only:
- main
GitHub Actions Workflow
# .github/workflows/deploy.yml
name: Build and Deploy
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
outputs:
image-tag: ${{ steps.meta.outputs.tags }}
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Log in to Container Registry
uses: docker/login-action@v2
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v4
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=sha,prefix={{branch}}-
- name: Build and push
uses: docker/build-push-action@v4
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
test:
needs: build
runs-on: ubuntu-latest
container:
image: ${{ needs.build.outputs.image-tag }}
steps:
- name: Run tests
run: |
npm test
npm run test:integration
deploy-staging:
needs: [build, test]
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/develop'
environment:
name: staging
url: https://staging.example.com
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Deploy to Kubernetes
uses: azure/k8s-deploy@v4
with:
namespace: staging
manifests: |
k8s/deployment.yaml
k8s/service.yaml
images: ${{ needs.build.outputs.image-tag }}
deploy-production:
needs: [build, test]
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
environment:
name: production
url: https://example.com
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Deploy with Helm
uses: deliverybot/helm@v1
with:
release: myapp
namespace: production
chart: ./helm-chart
values: |
image:
repository: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tag: ${{ github.sha }}
atomic: true
env:
KUBECONFIG_FILE: ${{ secrets.KUBECONFIG }}
Advanced Deployment Patterns
Multi-Region Deployment
# Global Traffic Manager
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: global-ingress
annotations:
external-dns.alpha.kubernetes.io/hostname: app.example.com
external-dns.alpha.kubernetes.io/ttl: "60"
spec:
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: regional-router
port:
number: 80
---
# Regional Deployment Template
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-${REGION}
labels:
app: myapp
region: ${REGION}
spec:
replicas: ${REPLICAS}
selector:
matchLabels:
app: myapp
region: ${REGION}
template:
metadata:
labels:
app: myapp
region: ${REGION}
spec:
nodeSelector:
topology.kubernetes.io/region: ${REGION}
containers:
- name: app
image: myapp:v2.0.0
env:
- name: REGION
value: ${REGION}
- name: DB_HOST
value: db-${REGION}.example.com
Shadow Deployment
# Shadow deployment for testing with production traffic
apiVersion: v1
kind: Service
metadata:
name: myapp-shadow
spec:
selector:
app: myapp
version: shadow
ports:
- port: 80
targetPort: 8080
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-shadow
spec:
replicas: 1
selector:
matchLabels:
app: myapp
version: shadow
template:
metadata:
labels:
app: myapp
version: shadow
spec:
containers:
- name: app
image: myapp:experimental
env:
- name: SHADOW_MODE
value: "true"
---
# Istio VirtualService for traffic mirroring
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: myapp
spec:
hosts:
- myapp.example.com
http:
- match:
- uri:
prefix: "/"
route:
- destination:
host: myapp-stable
port:
number: 80
mirror:
host: myapp-shadow
port:
number: 80
mirrorPercentage:
value: 10.0
Dark Launch Pattern
apiVersion: v1
kind: ConfigMap
metadata:
name: dark-launch-config
data:
features.yaml: |
darkFeatures:
- name: new-recommendation-engine
enabled: true
percentage: 0 # Feature is deployed but not active
metrics:
- recommendation-accuracy
- processing-time
- name: enhanced-search
enabled: true
percentage: 5 # 5% of users see this feature
userGroups:
- beta-testers
- employees
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-dark-launch
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: app
image: myapp:v2.0.0-beta
volumeMounts:
- name: dark-launch
mountPath: /etc/features
env:
- name: DARK_LAUNCH_ENABLED
value: "true"
volumes:
- name: dark-launch
configMap:
name: dark-launch-config
Monitoring and Rollback Strategies
Automated Rollback with Prometheus
apiVersion: v1
kind: ConfigMap
metadata:
name: rollback-rules
data:
rules.yaml: |
groups:
- name: deployment_health
interval: 30s
rules:
- alert: HighErrorRate
expr: |
rate(http_requests_total{status=~"5.."}[5m]) > 0.05
for: 2m
labels:
severity: critical
action: rollback
annotations:
summary: "High error rate detected"
description: "Error rate is above 5% for 2 minutes"
- alert: LowSuccessRate
expr: |
rate(http_requests_total{status="200"}[5m]) /
rate(http_requests_total[5m]) < 0.95
for: 3m
labels:
severity: warning
action: investigate
annotations:
summary: "Low success rate"
description: "Success rate below 95%"
- alert: HighLatency
expr: |
histogram_quantile(0.95, http_request_duration_seconds_bucket) > 0.5
for: 5m
labels:
severity: warning
action: scale
annotations:
summary: "High latency detected"
description: "95th percentile latency above 500ms"
---
# Rollback automation script
apiVersion: v1
kind: ConfigMap
metadata:
name: rollback-script
data:
rollback.sh: |
#!/bin/bash
set -e
DEPLOYMENT=$1
NAMESPACE=$2
# Get current and previous revision
CURRENT=$(kubectl get deployment $DEPLOYMENT -n $NAMESPACE -o jsonpath='{.metadata.annotations.deployment\.kubernetes\.io/revision}')
PREVIOUS=$((CURRENT - 1))
echo "Rolling back $DEPLOYMENT from revision $CURRENT to $PREVIOUS"
# Perform rollback
kubectl rollout undo deployment/$DEPLOYMENT -n $NAMESPACE --to-revision=$PREVIOUS
# Wait for rollback to complete
kubectl rollout status deployment/$DEPLOYMENT -n $NAMESPACE
# Verify health
sleep 30
kubectl get pods -n $NAMESPACE -l app=$DEPLOYMENT
# Send notification
curl -X POST $SLACK_WEBHOOK -d "{\"text\": \"Rollback completed for $DEPLOYMENT\"}"
Deployment Monitoring Dashboard
apiVersion: v1
kind: ConfigMap
metadata:
name: grafana-dashboard
data:
deployment-dashboard.json: |
{
"dashboard": {
"title": "Kubernetes Deployment Monitor",
"panels": [
{
"title": "Deployment Status",
"targets": [
{
"expr": "kube_deployment_status_replicas{deployment=\"$deployment\"}"
}
]
},
{
"title": "Error Rate",
"targets": [
{
"expr": "rate(http_requests_total{status=~\"5..\",deployment=\"$deployment\"}[5m])"
}
]
},
{
"title": "Response Time",
"targets": [
{
"expr": "histogram_quantile(0.95, http_request_duration_seconds_bucket{deployment=\"$deployment\"})"
}
]
},
{
"title": "Pod Restarts",
"targets": [
{
"expr": "rate(kube_pod_container_status_restarts_total{deployment=\"$deployment\"}[15m])"
}
]
}
]
}
}
Security and Compliance
Secure Deployment Pipeline
# Security scanning in deployment
apiVersion: batch/v1
kind: Job
metadata:
name: security-scan-${BUILD_ID}
spec:
template:
spec:
containers:
- name: scanner
image: aquasec/trivy:latest
command:
- sh
- -c
- |
# Scan image for vulnerabilities
trivy image --severity HIGH,CRITICAL ${IMAGE}
# Check for secrets
trivy image --security-checks secret ${IMAGE}
# Policy compliance check
trivy image --policy /policies ${IMAGE}
# Generate SBOM
trivy image --format cyclonedx ${IMAGE} > sbom.json
volumeMounts:
- name: policies
mountPath: /policies
volumes:
- name: policies
configMap:
name: security-policies
restartPolicy: Never
RBAC for Deployments
# ServiceAccount for CI/CD
apiVersion: v1
kind: ServiceAccount
metadata:
name: cicd-deployer
namespace: production
---
# Role with minimal permissions
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: cicd-deployer
namespace: production
rules:
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "update", "patch"]
- apiGroups: ["apps"]
resources: ["replicasets"]
verbs: ["get", "list"]
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list"]
- apiGroups: [""]
resources: ["pods/log"]
verbs: ["get"]
---
# RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: cicd-deployer
namespace: production
subjects:
- kind: ServiceAccount
name: cicd-deployer
namespace: production
roleRef:
kind: Role
name: cicd-deployer
apiGroup: rbac.authorization.k8s.io
Pod Security Standards
apiVersion: v1
kind: Namespace
metadata:
name: production
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/audit: restricted
pod-security.kubernetes.io/warn: restricted
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: secure-app
namespace: production
spec:
replicas: 3
selector:
matchLabels:
app: secure-app
template:
metadata:
labels:
app: secure-app
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 2000
seccompProfile:
type: RuntimeDefault
containers:
- name: app
image: myapp:v2.0.0
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
volumeMounts:
- name: tmp
mountPath: /tmp
- name: cache
mountPath: /app/cache
volumes:
- name: tmp
emptyDir: {}
- name: cache
emptyDir: {}
Production Best Practices
Complete Production Deployment
# Production-ready deployment configuration
apiVersion: apps/v1
kind: Deployment
metadata:
name: production-app
labels:
app: myapp
env: production
spec:
replicas: 10
revisionHistoryLimit: 10
progressDeadlineSeconds: 600
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 2
maxUnavailable: 0
selector:
matchLabels:
app: myapp
env: production
template:
metadata:
labels:
app: myapp
env: production
version: v2.0.0
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "9090"
prometheus.io/path: "/metrics"
spec:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- myapp
topologyKey: kubernetes.io/hostname
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: node-type
operator: In
values:
- production
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: myapp
priorityClassName: high-priority
serviceAccountName: myapp-sa
securityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 2000
containers:
- name: app
image: registry.example.com/myapp:v2.0.0
imagePullPolicy: Always
ports:
- containerPort: 8080
name: http
protocol: TCP
- containerPort: 9090
name: metrics
protocol: TCP
env:
- name: APP_VERSION
value: "v2.0.0"
- name: ENVIRONMENT
value: "production"
- name: LOG_LEVEL
value: "info"
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: db-credentials
key: url
resources:
requests:
cpu: 500m
memory: 512Mi
limits:
cpu: 1000m
memory: 1Gi
readinessProbe:
httpGet:
path: /ready
port: 8080
httpHeaders:
- name: X-Probe-Type
value: Readiness
initialDelaySeconds: 10
periodSeconds: 5
timeoutSeconds: 3
successThreshold: 1
failureThreshold: 3
livenessProbe:
httpGet:
path: /health
port: 8080
httpHeaders:
- name: X-Probe-Type
value: Liveness
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
startupProbe:
httpGet:
path: /startup
port: 8080
initialDelaySeconds: 0
periodSeconds: 10
timeoutSeconds: 3
failureThreshold: 30
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "sleep 15 && /app/graceful-shutdown.sh"]
volumeMounts:
- name: config
mountPath: /etc/config
readOnly: true
- name: secrets
mountPath: /etc/secrets
readOnly: true
- name: cache
mountPath: /app/cache
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
add:
- NET_BIND_SERVICE
initContainers:
- name: migration
image: registry.example.com/myapp:v2.0.0
command: ["/app/migrate.sh"]
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: db-credentials
key: url
volumes:
- name: config
configMap:
name: app-config
- name: secrets
secret:
secretName: app-secrets
defaultMode: 0400
- name: cache
emptyDir:
sizeLimit: 1Gi
imagePullSecrets:
- name: registry-credentials
terminationGracePeriodSeconds: 60
---
# HorizontalPodAutoscaler
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: production-app-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: production-app
minReplicas: 10
maxReplicas: 50
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
- type: Pods
pods:
metric:
name: http_requests_per_second
target:
type: AverageValue
averageValue: "1000"
behavior:
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Percent
value: 10
periodSeconds: 60
- type: Pods
value: 2
periodSeconds: 60
selectPolicy: Min
scaleUp:
stabilizationWindowSeconds: 60
policies:
- type: Percent
value: 50
periodSeconds: 60
- type: Pods
value: 5
periodSeconds: 60
selectPolicy: Max
---
# PodDisruptionBudget
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: production-app-pdb
spec:
minAvailable: 80%
selector:
matchLabels:
app: myapp
env: production
unhealthyPodEvictionPolicy: AlwaysAllow
GitOps Production Workflow
#!/bin/bash
# promote-to-production.sh
set -euo pipefail
SOURCE_ENV="staging"
TARGET_ENV="production"
APP_NAME="myapp"
GIT_REPO="[email protected]:myorg/k8s-deployments.git"
# Clone GitOps repository
git clone $GIT_REPO /tmp/gitops
cd /tmp/gitops
# Get current staging version
STAGING_VERSION=$(yq eval '.spec.template.spec.containers[0].image' \
environments/$SOURCE_ENV/$APP_NAME/deployment.yaml | cut -d: -f2)
echo "Promoting version $STAGING_VERSION from $SOURCE_ENV to $TARGET_ENV"
# Update production manifests
yq eval -i ".spec.template.spec.containers[0].image = \"myapp:$STAGING_VERSION\"" \
environments/$TARGET_ENV/$APP_NAME/deployment.yaml
# Create pull request
git checkout -b promote-$STAGING_VERSION
git add environments/$TARGET_ENV/
git commit -m "Promote $APP_NAME $STAGING_VERSION to $TARGET_ENV
- Source: $SOURCE_ENV
- Version: $STAGING_VERSION
- Automated promotion"
git push origin promote-$STAGING_VERSION
# Create PR using GitHub CLI
gh pr create \
--title "Promote $APP_NAME $STAGING_VERSION to $TARGET_ENV" \
--body "Automated promotion from $SOURCE_ENV" \
--base main \
--label "promotion" \
--label "production"
Conclusion
Kubernetes deployment strategies and CI/CD integration form the backbone of modern cloud-native applications. By mastering these concepts and patterns, you can:
- ✅ Deploy applications with zero downtime
- ✅ Implement sophisticated rollout strategies
- ✅ Build robust CI/CD pipelines
- ✅ Practice GitOps for declarative deployments
- ✅ Ensure security and compliance
- ✅ Monitor and rollback deployments automatically
Key Takeaways
- Choose the Right Strategy: Match deployment strategy to your risk tolerance and requirements
- Automate Everything: Use CI/CD pipelines and GitOps for consistent deployments
- Monitor Proactively: Implement comprehensive monitoring and automated rollbacks
- Security First: Integrate security scanning and compliance checks
- Practice Progressive Delivery: Use canary deployments and feature flags
- Embrace GitOps: Git as single source of truth simplifies operations
Next Steps
- Implement service mesh for advanced traffic management
- Explore progressive delivery tools like Flagger and Argo Rollouts
- Study chaos engineering for resilience testing
- Learn multi-cluster deployment strategies
- Master observability with distributed tracing
Remember, successful Kubernetes deployments combine the right strategies, automation, and monitoring to deliver reliable applications at scale.
Additional Resources
- Kubernetes Deployment Documentation
- Argo Project for GitOps tools
- Flagger for progressive delivery
- CNCF CI/CD Landscape
- Kubernetes Production Best Practices
Master these deployment strategies, and you'll be ready to operate Kubernetes at scale with confidence! 🚀⚓