·26 分钟
Docker + Kubernetes 容器化部署实战
DockerKubernetes容器化部署DevOpsDevOps
Docker 基础
Dockerfile 最佳实践
dockerfile
# 多阶段构建
# 阶段 1: 构建
FROM node:20-alpine AS builder
WORKDIR /app
# 利用缓存层
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
# 阶段 2: 运行
FROM node:20-alpine AS runner
WORKDIR /app
# 创建非 root 用户
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
# 复制构建产物
COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjs
EXPOSE 3000
ENV PORT 3000
ENV NODE_ENV production
CMD ["node", "server.js"]
Docker Compose
yaml
# docker-compose.yml
version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile
ports:
- "3000:3000"
environment:
- DATABASE_URL=postgresql://postgres:password@db:5432/mydb
- REDIS_URL=redis://redis:6379
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
restart: unless-stopped
db:
image: postgres:16-alpine
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=password
- POSTGRES_DB=mydb
volumes:
- postgres_data:/var/lib/postgresql/data
ports:
- "5432:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redis_data:/data
volumes:
postgres_data:
redis_data:
Docker 网络
bash
# 创建自定义网络
docker network create mynetwork
# 使用自定义网络
docker run -d --name app --network mynetwork myapp
docker run -d --name db --network mynetwork postgres
# 容器间可以通过服务名访问
# app 容器中可以使用 db 作为主机名连接数据库
Kubernetes 基础
核心概念
yaml
# Pod - 最小部署单元
apiVersion: v1
kind: Pod
metadata:
name: myapp
labels:
app: myapp
spec:
containers:
- name: myapp
image: myapp:latest
ports:
- containerPort: 3000
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 3000
initialDelaySeconds: 5
periodSeconds: 5
Deployment
yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
replicas: 3
selector:
matchLabels:
app: myapp
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: myapp:latest
ports:
- containerPort: 3000
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: db-secret
key: url
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "500m"
Service
yaml
# ClusterIP Service - 集群内部访问
apiVersion: v1
kind: Service
metadata:
name: myapp-service
spec:
selector:
app: myapp
ports:
- protocol: TCP
port: 80
targetPort: 3000
type: ClusterIP
---
# NodePort Service - 节点端口暴露
apiVersion: v1
kind: Service
metadata:
name: myapp-nodeport
spec:
selector:
app: myapp
ports:
- protocol: TCP
port: 80
targetPort: 3000
nodePort: 30080
type: NodePort
---
# LoadBalancer Service - 负载均衡器
apiVersion: v1
kind: Service
metadata:
name: myapp-lb
spec:
selector:
app: myapp
ports:
- protocol: TCP
port: 80
targetPort: 3000
type: LoadBalancer
配置管理
ConfigMap
yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: myapp-config
data:
APP_ENV: "production"
APP_DEBUG: "false"
LOG_LEVEL: "info"
config.json: |
{
"apiUrl": "https://api.example.com",
"timeout": 5000
}
Secret
yaml
apiVersion: v1
kind: Secret
metadata:
name: db-secret
type: Opaque
data:
url: cG9zdGdyZXNxbDovL3VzZXI6cGFzc3dvcmRAaG9zdDo1NDMyL215ZGI= # base64 编码
password: cGFzc3dvcmQ=
---
# 使用 Secret
apiVersion: apps/v1
kind: Deployment
spec:
template:
spec:
containers:
- name: myapp
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: db-secret
key: url
持久化存储
PersistentVolume
yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: myapp-pv
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: standard
hostPath:
path: /data/myapp
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: myapp-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
storageClassName: standard
---
# 在 Deployment 中使用
apiVersion: apps/v1
kind: Deployment
spec:
template:
spec:
volumes:
- name: data
persistentVolumeClaim:
claimName: myapp-pvc
containers:
- name: myapp
volumeMounts:
- name: data
mountPath: /app/data
Ingress 配置
yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myapp-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
ingressClassName: nginx
tls:
- hosts:
- myapp.example.com
secretName: myapp-tls
rules:
- host: myapp.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: myapp-service
port:
number: 80
- path: /api
pathType: Prefix
backend:
service:
name: api-service
port:
number: 80
自动伸缩
Horizontal Pod Autoscaler
yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: myapp-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: myapp
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
behavior:
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Percent
value: 10
periodSeconds: 60
scaleUp:
stabilizationWindowSeconds: 0
policies:
- type: Percent
value: 100
periodSeconds: 15
CI/CD 流水线
GitHub Actions
yaml
# .github/workflows/deploy.yml
name: Deploy
on:
push:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- run: npm ci
- run: npm test
build:
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Login to DockerHub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push
uses: docker/build-push-action@v5
with:
push: true
tags: myapp:${{ github.sha }}
deploy:
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup kubectl
uses: azure/setup-kubectl@v3
- name: Deploy to K8s
run: |
kubectl set image deployment/myapp myapp=myapp:${{ github.sha }}
kubectl rollout status deployment/myapp
监控与日志
Prometheus + Grafana
yaml
# ServiceMonitor for Prometheus
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: myapp-monitor
spec:
selector:
matchLabels:
app: myapp
endpoints:
- port: metrics
interval: 30s
path: /metrics
日志收集
yaml
# Fluentd DaemonSet
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluentd
spec:
selector:
matchLabels:
name: fluentd
template:
metadata:
labels:
name: fluentd
spec:
containers:
- name: fluentd
image: fluent/fluentd-kubernetes-daemonset:v1.16
volumeMounts:
- name: varlog
mountPath: /var/log
- name: containers
mountPath: /var/lib/docker/containers
readOnly: true
volumes:
- name: varlog
hostPath:
path: /var/log
- name: containers
hostPath:
path: /var/lib/docker/containers
总结
容器化部署的关键要点:
- Docker 最佳实践:多阶段构建、最小镜像、非 root 用户
- Kubernetes 核心资源:Pod、Deployment、Service、ConfigMap、Secret
- 网络与存储:Ingress、PersistentVolume
- 自动伸缩:HPA 根据资源使用率自动调整
- CI/CD:自动化构建、测试、部署流水线
- 监控日志:Prometheus + Grafana 监控,EFK 日志收集
掌握这些技术栈,可以构建出高可用、可扩展的现代化应用部署方案。