部署微服务应用
部署微服务应用
本章将通过一个完整的微服务应用,实践 Kubernetes 的各种特性。
项目架构
我们将部署一个电商微服务系统:
┌─────────────┐
│ Ingress │
└──────┬──────┘
│
┌───┴────────────────┬──────────────┬──────────────┐
│ │ │ │
┌──▼──────┐ ┌───────▼────┐ ┌─────▼─────┐ ┌────▼─────┐
│ Frontend│ │ API │ │ Product │ │ Order │
│ (React) │ │ Gateway │ │ Service │ │ Service │
└─────────┘ └────────────┘ └───────────┘ └──────────┘
│ │ │
┌───┴──────────────┴──────────────┴───┐
│ │
┌──────▼──────┐ ┌──────▼──────┐
│ MongoDB │ │ Redis │
└─────────────┘ └─────────────┘
准备工作
创建 Namespace
apiVersion: v1
kind: Namespace
metadata:
name: ecommerce
labels:
name: ecommerce
env: production
kubectl apply -f namespace.yaml
kubectl config set-context --current --namespace=ecommerce
配置 Secret
apiVersion: v1
kind: Secret
metadata:
name: db-secret
namespace: ecommerce
type: Opaque
stringData:
mongodb-root-password: "SecurePassword123"
redis-password: "RedisPass456"
---
apiVersion: v1
kind: Secret
metadata:
name: jwt-secret
namespace: ecommerce
type: Opaque
stringData:
jwt-key: "your-secret-jwt-key-here"
部署数据库
MongoDB
apiVersion: v1
kind: ConfigMap
metadata:
name: mongodb-config
namespace: ecommerce
data:
mongo.conf: |
storage:
dbPath: /data/db
systemLog:
destination: file
path: /var/log/mongodb/mongod.log
logAppend: true
net:
port: 27017
bindIp: 0.0.0.0
---
apiVersion: v1
kind: Service
metadata:
name: mongodb
namespace: ecommerce
spec:
clusterIP: None
ports:
- port: 27017
name: mongodb
selector:
app: mongodb
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mongodb
namespace: ecommerce
spec:
serviceName: mongodb
replicas: 3
selector:
matchLabels:
app: mongodb
template:
metadata:
labels:
app: mongodb
spec:
containers:
- name: mongodb
image: mongo:6.0
ports:
- containerPort: 27017
name: mongodb
env:
- name: MONGO_INITDB_ROOT_USERNAME
value: "admin"
- name: MONGO_INITDB_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: db-secret
key: mongodb-root-password
volumeMounts:
- name: data
mountPath: /data/db
- name: config
mountPath: /etc/mongo
resources:
requests:
cpu: 500m
memory: 1Gi
limits:
cpu: 1
memory: 2Gi
livenessProbe:
exec:
command:
- mongo
- --eval
- "db.adminCommand('ping')"
initialDelaySeconds: 30
periodSeconds: 10
volumes:
- name: config
configMap:
name: mongodb-config
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 20Gi
Redis
apiVersion: v1
kind: ConfigMap
metadata:
name: redis-config
namespace: ecommerce
data:
redis.conf: |
maxmemory 256mb
maxmemory-policy allkeys-lru
save ""
---
apiVersion: v1
kind: Service
metadata:
name: redis
namespace: ecommerce
spec:
ports:
- port: 6379
name: redis
selector:
app: redis
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis
namespace: ecommerce
spec:
replicas: 1
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis
spec:
containers:
- name: redis
image: redis:7.0-alpine
ports:
- containerPort: 6379
command:
- redis-server
- /usr/local/etc/redis/redis.conf
- --requirepass
- $(REDIS_PASSWORD)
env:
- name: REDIS_PASSWORD
valueFrom:
secretKeyRef:
name: db-secret
key: redis-password
volumeMounts:
- name: config
mountPath: /usr/local/etc/redis
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
cpu: 200m
memory: 512Mi
volumes:
- name: config
configMap:
name: redis-config
部署微服务
Product Service
apiVersion: v1
kind: ConfigMap
metadata:
name: product-config
namespace: ecommerce
data:
APP_ENV: "production"
LOG_LEVEL: "info"
DB_HOST: "mongodb-0.mongodb.ecommerce.svc.cluster.local"
DB_PORT: "27017"
DB_NAME: "products"
CACHE_HOST: "redis.ecommerce.svc.cluster.local"
CACHE_PORT: "6379"
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: product-service
namespace: ecommerce
spec:
replicas: 3
selector:
matchLabels:
app: product-service
template:
metadata:
labels:
app: product-service
version: v1
spec:
containers:
- name: product-service
image: ecommerce/product-service:v1.0.0
ports:
- containerPort: 8080
name: http
envFrom:
- configMapRef:
name: product-config
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-secret
key: mongodb-root-password
- name: CACHE_PASSWORD
valueFrom:
secretKeyRef:
name: db-secret
key: redis-password
resources:
requests:
cpu: 200m
memory: 256Mi
limits:
cpu: 500m
memory: 512Mi
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
name: product-service
namespace: ecommerce
spec:
selector:
app: product-service
ports:
- port: 8080
targetPort: 8080
name: http
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: product-service-hpa
namespace: ecommerce
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: product-service
minReplicas: 3
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
Order Service
apiVersion: v1
kind: ConfigMap
metadata:
name: order-config
namespace: ecommerce
data:
APP_ENV: "production"
LOG_LEVEL: "info"
DB_HOST: "mongodb-0.mongodb.ecommerce.svc.cluster.local"
DB_PORT: "27017"
DB_NAME: "orders"
PRODUCT_SERVICE_URL: "http://product-service:8080"
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
namespace: ecommerce
spec:
replicas: 3
selector:
matchLabels:
app: order-service
template:
metadata:
labels:
app: order-service
version: v1
spec:
containers:
- name: order-service
image: ecommerce/order-service:v1.0.0
ports:
- containerPort: 8080
name: http
envFrom:
- configMapRef:
name: order-config
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-secret
key: mongodb-root-password
resources:
requests:
cpu: 200m
memory: 256Mi
limits:
cpu: 500m
memory: 512Mi
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 30
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
---
apiVersion: v1
kind: Service
metadata:
name: order-service
namespace: ecommerce
spec:
selector:
app: order-service
ports:
- port: 8080
targetPort: 8080
API Gateway
apiVersion: v1
kind: ConfigMap
metadata:
name: gateway-config
namespace: ecommerce
data:
nginx.conf: |
events {
worker_connections 1024;
}
http {
upstream product_service {
server product-service:8080;
}
upstream order_service {
server order-service:8080;
}
server {
listen 80;
location /api/products {
proxy_pass http://product_service;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
location /api/orders {
proxy_pass http://order_service;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
location /health {
return 200 "OK";
}
}
}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-gateway
namespace: ecommerce
spec:
replicas: 2
selector:
matchLabels:
app: api-gateway
template:
metadata:
labels:
app: api-gateway
spec:
containers:
- name: nginx
image: nginx:alpine
ports:
- containerPort: 80
volumeMounts:
- name: config
mountPath: /etc/nginx/nginx.conf
subPath: nginx.conf
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 200m
memory: 256Mi
volumes:
- name: config
configMap:
name: gateway-config
---
apiVersion: v1
kind: Service
metadata:
name: api-gateway
namespace: ecommerce
spec:
selector:
app: api-gateway
ports:
- port: 80
targetPort: 80
Frontend
apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend
namespace: ecommerce
spec:
replicas: 2
selector:
matchLabels:
app: frontend
template:
metadata:
labels:
app: frontend
spec:
containers:
- name: frontend
image: ecommerce/frontend:v1.0.0
ports:
- containerPort: 80
env:
- name: API_BASE_URL
value: "http://api-gateway"
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 200m
memory: 256Mi
---
apiVersion: v1
kind: Service
metadata:
name: frontend
namespace: ecommerce
spec:
selector:
app: frontend
ports:
- port: 80
targetPort: 80
配置 Ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ecommerce-ingress
namespace: ecommerce
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-prod"
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/rate-limit: "100"
spec:
ingressClassName: nginx
tls:
- hosts:
- ecommerce.example.com
- api.ecommerce.example.com
secretName: ecommerce-tls
rules:
- host: ecommerce.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: frontend
port:
number: 80
- host: api.ecommerce.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: api-gateway
port:
number: 80
部署流程
1. 按顺序部署
# 1. 创建 Namespace 和 Secrets
kubectl apply -f namespace.yaml
kubectl apply -f secrets.yaml
# 2. 部署数据库
kubectl apply -f mongodb.yaml
kubectl apply -f redis.yaml
# 等待数据库就绪
kubectl wait --for=condition=ready pod -l app=mongodb --timeout=300s
kubectl wait --for=condition=ready pod -l app=redis --timeout=300s
# 3. 部署微服务
kubectl apply -f product-service.yaml
kubectl apply -f order-service.yaml
# 等待服务就绪
kubectl wait --for=condition=ready pod -l app=product-service --timeout=180s
kubectl wait --for=condition=ready pod -l app=order-service --timeout=180s
# 4. 部署网关和前端
kubectl apply -f api-gateway.yaml
kubectl apply -f frontend.yaml
# 5. 配置 Ingress
kubectl apply -f ingress.yaml
2. 验证部署
# 查看所有资源
kubectl get all -n ecommerce
# 查看 Pod 状态
kubectl get pods -n ecommerce
# 查看服务
kubectl get svc -n ecommerce
# 查看 Ingress
kubectl get ingress -n ecommerce
3. 测试服务
# 测试 API Gateway
kubectl port-forward -n ecommerce svc/api-gateway 8080:80
curl http://localhost:8080/health
# 测试 Product Service
kubectl port-forward -n ecommerce svc/product-service 8081:8080
curl http://localhost:8081/health
# 测试前端
kubectl port-forward -n ecommerce svc/frontend 8082:80
监控和日志
配置 Service Monitor
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: ecommerce-services
namespace: ecommerce
spec:
selector:
matchLabels:
monitoring: enabled
endpoints:
- port: http
path: /metrics
interval: 30s
查看日志
# 查看特定服务日志
kubectl logs -n ecommerce -l app=product-service --tail=100 -f
# 查看所有服务日志
kubectl logs -n ecommerce --all-containers=true --tail=100
# 查看特定 Pod 日志
kubectl logs -n ecommerce product-service-abc123 -f
更新和回滚
滚动更新
# 更新镜像
kubectl set image deployment/product-service \
product-service=ecommerce/product-service:v1.1.0 \
-n ecommerce
# 查看更新状态
kubectl rollout status deployment/product-service -n ecommerce
# 查看更新历史
kubectl rollout history deployment/product-service -n ecommerce
回滚
# 回滚到上一版本
kubectl rollout undo deployment/product-service -n ecommerce
# 回滚到指定版本
kubectl rollout undo deployment/product-service --to-revision=2 -n ecommerce
最佳实践总结
1. 资源组织
- 使用 Namespace 隔离环境
- 使用 Labels 标记资源
- 使用 ConfigMap 管理配置
- 使用 Secret 管理敏感信息
2. 高可用
- 多副本部署(至少 3 个)
- 配置 Pod 反亲和性
- 使用 HPA 自动扩缩容
- 配置健康检查
3. 安全
- 使用 RBAC 控制权限
- Secret 加密存储
- 网络策略隔离
- 定期更新镜像
4. 可观测性
- 配置日志收集
- 添加监控指标
- 配置告警规则
- 分布式追踪
5. 性能优化
- 合理设置资源限制
- 使用缓存(Redis)
- 配置连接池
- 优化数据库查询
小结
本章通过完整的微服务应用实践了:
- 架构设计:微服务拆分
- 数据库部署:StatefulSet
- 服务部署:Deployment + Service
- 网关配置:API Gateway
- 流量管理:Ingress
- 弹性伸缩:HPA
- 配置管理:ConfigMap + Secret
下一章我们将学习 CI/CD 集成,实现自动化部署。