Master Container Deployment: Docker & Kubernetes Best Practices for Production
This comprehensive guide walks you through containerizing applications, optimizing Docker images, securing containers, designing Kubernetes high‑availability clusters, implementing observability with Prometheus and ELK, automating CI/CD pipelines, applying RBAC and network policies, and cutting costs with autoscaling and resource tuning, all backed by real‑world code examples.
Container Deployment Practices: Docker and Kubernetes in Production
Why Containerize?
Frequent production incidents caused by environment drift and "it works on my machine" problems can be eliminated by adopting containerization, which ensures consistency across development, testing, and production.
Docker: From Basics to Production
Image Optimization
Reduce image size from >1 GB to <100 MB by using multi‑stage builds and Alpine base images.
# Optimized Dockerfile (image size: 85 MB)
FROM python:3.9‑alpine AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --user -r requirements.txt
FROM python:3.9‑alpine
RUN apk add --no-cache libpq
COPY --from=builder /root/.local /root/.local
COPY --from=builder /app /app
WORKDIR /app
COPY . .
ENV PATH=/root/.local/bin:$PATH
CMD ["python","app.py"]Use Alpine Linux as the base image.
Apply multi‑stage builds to drop build‑time dependencies.
Combine RUN commands to minimise layers.
Clean caches and temporary files.
Leverage a .dockerignore file.
Production‑grade Docker Security
# docker‑compose.yml security configuration example
version: '3.8'
services:
app:
image: myapp:latest
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE
read_only: true
tmpfs:
- /tmp
user: "1000:1000"
networks:
- internal
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M
reservations:
cpus: '0.25'
memory: 256MRun containers as non‑root users.
Drop unnecessary Linux capabilities.
Mount the filesystem read‑only.
Set CPU and memory limits.
Regularly scan images for vulnerabilities.
Docker Network Design
# Create a custom bridge network
docker network create --driver bridge \
--subnet=172.20.0.0/16 \
--ip-range=172.20.240.0/20 \
--gateway=172.20.0.1 \
production-network
# Connect backend and frontend containers
docker run -d --name backend \
--network production-network \
--network-alias api-server \
myapp:backend
docker run -d --name frontend \
--network production-network \
-e API_URL=http://api-server:8080 \
myapp:frontendKubernetes: Building an Enterprise‑grade Cluster
High‑availability Master Nodes
# kubeadm-config.yaml (high‑availability control plane)
apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
kubernetesVersion: v1.28.0
controlPlaneEndpoint: "k8s-api.example.com:6443"
networking:
serviceSubnet: "10.96.0.0/12"
podSubnet: "10.244.0.0/16"
etcd:
external:
endpoints:
- https://etcd-0.example.com:2379
- https://etcd-1.example.com:2379
- https://etcd-2.example.com:2379
caFile: /etc/kubernetes/pki/etcd/ca.crt
certFile: /etc/kubernetes/pki/etcd/client.crt
keyFile: /etc/kubernetes/pki/etcd/client.keyApplication Deployment Workflow
# ConfigMap for application configuration
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
namespace: production
data:
database.conf: |
host=db.example.com
port=5432
pool_size=20
redis.conf: |
host=redis.example.com
port=6379
---
apiVersion: v1
kind: Secret
metadata:
name: app-secrets
namespace: production
type: Opaque
data:
db-password: cGFzc3dvcmQxMjM=
api-key: YWJjZGVmZ2hpams= # Deployment example for an API server
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-server
namespace: production
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
selector:
matchLabels:
app: api-server
template:
metadata:
labels:
app: api-server
spec:
containers:
- name: api-server
image: registry.example.com/api-server:v2.1.0
ports:
- containerPort: 8080
name: http
- containerPort: 9090
name: metrics
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: app-secrets
key: db-password
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5 # Service & Ingress for the API server
apiVersion: v1
kind: Service
metadata:
name: api-server-service
namespace: production
spec:
type: ClusterIP
selector:
app: api-server
ports:
- port: 80
targetPort: 8080
name: http
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: api-ingress
namespace: production
annotations:
nginx.ingress.kubernetes.io/rewrite-target: "/"
nginx.ingress.kubernetes.io/ssl-redirect: "true"
cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
ingressClassName: nginx
tls:
- hosts:
- api.example.com
secretName: api-tls-secret
rules:
- host: api.example.com
http:
paths:
- path: "/"
pathType: Prefix
backend:
service:
name: api-server-service
port:
number: 80Autoscaling Strategies
# HorizontalPodAutoscaler (CPU & memory based)
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: api-server-hpa
namespace: production
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: api-server
minReplicas: 3
maxReplicas: 20
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" # VerticalPodAutoscaler for fine‑grained resource tuning
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: api-server-vpa
namespace: production
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: api-server
updatePolicy:
updateMode: "Auto"
resourcePolicy:
containerPolicies:
- containerName: api-server
minAllowed:
cpu: 100m
memory: 128Mi
maxAllowed:
cpu: 2
memory: 2GiObservability
Prometheus + Grafana Monitoring Stack
# prometheus-config.yaml (ConfigMap)
apiVersion: v1
kind: ConfigMap
metadata:
name: prometheus-config
namespace: monitoring
data:
prometheus.yml: |
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: 'kubernetes-pods'
kubernetes_sd_configs:
- role: pod
relabel_configs:
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
action: keep
regex: true
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]
action: replace
target_label: __metrics_path__
regex: (.+)
- source_labels: [__address__, __meta_kubernetes_pod_annotation_prometheus_io_port]
action: replace
regex: ([^:]+)(?::\d+)?;(\d+)
replacement: $1:$2
target_label: __address__ELK Log Collection
# Fluentd ConfigMap example
apiVersion: v1
kind: ConfigMap
metadata:
name: fluentd-config
namespace: kube-system
data:
fluent.conf: |
<source>
@type tail
path /var/log/containers/*.log
pos_file /var/log/fluentd-containers.log.pos
tag kubernetes.*
<parse>
@type json
time_format %Y-%m-%dT%H:%M:%S.%NZ
</parse>
</source>
<filter kubernetes.**>
@type kubernetes_metadata
</filter>
<match **>
@type elasticsearch
host elasticsearch.elastic-system.svc.cluster.local
port 9200
logstash_format true
logstash_prefix kubernetes
<buffer>
@type file
path /var/log/fluentd-buffers/kubernetes.system.buffer
flush_interval 5s
retry_type exponential_backoff
retry_max_interval 30
chunk_limit_size 2M
queue_limit_length 8
overflow_action block
</buffer>
</match>CI/CD Integration
# .gitlab-ci.yml pipeline example
stages:
- build
- test
- security
- deploy
variables:
DOCKER_DRIVER: overlay2
DOCKER_TLS_CERTDIR: ""
REGISTRY: registry.example.com
IMAGE_TAG: $CI_COMMIT_SHORT_SHA
build:
stage: build
image: docker:20.10
services:
- docker:20.10-dind
script:
- docker build -t $REGISTRY/$CI_PROJECT_NAME:$IMAGE_TAG .
- docker push $REGISTRY/$CI_PROJECT_NAME:$IMAGE_TAG
only:
- main
- develop
test:
stage: test
image: $REGISTRY/$CI_PROJECT_NAME:$IMAGE_TAG
script:
- pytest tests/ --cov=app --cov-report=xml
- coverage report
artifacts:
reports:
coverage_report:
coverage_format: cobertura
path: coverage.xml
security-scan:
stage: security
image: aquasec/trivy:latest
script:
- trivy image --severity HIGH,CRITICAL $REGISTRY/$CI_PROJECT_NAME:$IMAGE_TAG
allow_failure: false
deploy-production:
stage: deploy
image: bitnami/kubectl:latest
script:
- kubectl set image deployment/api-server api-server=$REGISTRY/$CI_PROJECT_NAME:$IMAGE_TAG -n production
- kubectl rollout status deployment/api-server -n production
environment:
name: production
url: https://api.example.com
only:
- main
when: manualSecurity Hardening
RBAC Permissions
# ServiceAccount, Role, and RoleBinding for read‑only access
apiVersion: v1
kind: ServiceAccount
metadata:
name: readonly-user
namespace: production
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: readonly-role
namespace: production
rules:
- apiGroups: ["", "apps", "batch"]
resources: ["pods", "services", "deployments", "jobs"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: readonly-binding
namespace: production
subjects:
- kind: ServiceAccount
name: readonly-user
namespace: production
roleRef:
kind: Role
name: readonly-role
apiGroup: rbac.authorization.k8s.ioNetworkPolicy Isolation
# NetworkPolicy allowing only frontend pods to reach the API server
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: api-server-netpol
namespace: production
spec:
podSelector:
matchLabels:
app: api-server
policyTypes:
- Ingress
- Egress
ingress:
- from:
- namespaceSelector:
matchLabels:
name: production
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 8080
egress:
- to:
- namespaceSelector:
matchLabels:
name: production
ports:
- protocol: TCP
port: 5432 # PostgreSQL
- protocol: TCP
port: 6379 # Redis
- {} # Allow DNS
- podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53Cost Optimization
Resource Utilisation with VPA
# VerticalPodAutoscaler to automatically adjust CPU/memory requests
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: api-server-vpa
namespace: production
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: api-server
updatePolicy:
updateMode: "Auto"
resourcePolicy:
containerPolicies:
- containerName: api-server
minAllowed:
cpu: 100m
memory: 128Mi
maxAllowed:
cpu: 2
memory: 2GiNode Tainting and Tolerations
# Taint a GPU node and add tolerations to pods that need it
kubectl taint nodes gpu-node-1 gpu=true:NoSchedule
# Example tolerations in a pod spec
tolerations:
- key: "gpu"
operator: "Equal"
value: "true"
effect: "NoSchedule"By following these practices you can build a reliable, secure, and cost‑effective containerised platform that scales from a few services to thousands of pods handling billions of requests per day.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
MaGe Linux Operations
Founded in 2009, MaGe Education is a top Chinese high‑end IT training brand. Its graduates earn 12K+ RMB salaries, and the school has trained tens of thousands of students. It offers high‑pay courses in Linux cloud operations, Python full‑stack, automation, data analysis, AI, and Go high‑concurrency architecture. Thanks to quality courses and a solid reputation, it has talent partnerships with numerous internet firms.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
