主题
高可用架构
业务场景
云咖啡公司的在线点单系统越来越受欢迎,用户量快速增长。为了应对日益增长的流量,我们需要提升系统的性能和可用性。
需求:
- 部署 Redis 缓存,提升查询性能
- 配置 HPA 自动扩缩容,应对流量波动
- 实现多副本负载均衡,提升可用性
- 进行性能测试和优化
学习目标
完成本课程后,你将掌握:
- HPA(Horizontal Pod Autoscaler)的配置和使用
- Redis 缓存的部署和集成
- 负载均衡策略
- 性能测试方法
- 系统优化技巧
前置准备
1. 确认环境
bash
# 检查命名空间
kubectl get namespace cloud-cafe
# 检查现有资源
kubectl get all -n cloud-cafe
# 检查 metrics-server(HPA 需要)
kubectl get pods -n kube-system | grep metrics2. 安装 metrics-server(如果未安装)
bash
# 检查 metrics-server 是否已安装
kubectl get pods -n kube-system | grep metrics
# 如果没有安装,执行以下命令安装
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
# 等待 metrics-server 就绪
kubectl wait --namespace kube-system \
--for=condition=ready pod \
--selector=k8s-app=metrics-server \
--timeout=120s
# 查看 metrics-server
kubectl get pods -n kube-system | grep metrics
# 测试 metrics-server
kubectl top nodes
kubectl top pods -n cloud-cafe实战步骤
Step 1: 部署 Redis 缓存
概念: Redis 是一个高性能的键值存储系统,常用于缓存、会话存储等场景。
1.1 创建 Redis 配置
bash
# 创建 Redis ConfigMap
kubectl create configmap redis-config \
--from-literal=redis.conf='
maxmemory 256mb
maxmemory-policy allkeys-lru
save 900 1
save 300 10
save 60 10000
appendonly yes
' \
-n cloud-cafe
# 查看 ConfigMap
kubectl get configmap redis-config -n cloud-cafe1.2 创建 Redis PVC
bash
# 创建 Redis 数据 PVC YAML 文件
cat > redis-pvc.yaml << 'EOF'
# Redis 数据持久化卷声明
# 用途:为 Redis 提供持久化存储
# 容量:1Gi
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: redis-pvc
namespace: cloud-cafe
labels:
app: redis
spec:
accessModes:
- ReadWriteOnce # 单节点读写
resources:
requests:
storage: 1Gi # 申请 1GB 存储空间
storageClassName: local-path # K3s 默认存储类
EOF
# 应用 PVC
kubectl apply -f redis-pvc.yaml
# 查看 PVC
kubectl get pvc -n cloud-cafe1.3 部署 Redis
bash
# 创建 Redis Deployment YAML 文件
cat > redis-deployment.yaml << 'EOF'
# Redis 缓存服务 Deployment
# 用途:部署高性能键值存储缓存服务
# 特点:
# - 使用自定义配置文件
# - 持久化存储(AOF + RDB)
# - 健康检查
# 前置依赖:需要 redis-config ConfigMap 和 redis-pvc 已创建
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis
namespace: cloud-cafe
labels:
app: redis
spec:
replicas: 1 # Redis 单节点部署(集群模式需要多个实例)
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis
spec:
containers:
- name: redis
image: redis:7-alpine # Redis 7 Alpine 精简版镜像
command: ["redis-server", "/etc/redis/redis.conf"] # 使用自定义配置启动
ports:
- containerPort: 6379
name: redis # 端口名称
# 存储挂载
volumeMounts:
- name: redis-data
mountPath: /data # Redis 数据目录(持久化文件存储位置)
- name: redis-config
mountPath: /etc/redis # Redis 配置文件目录
# 资源限制
resources:
requests:
memory: "128Mi" # 最低内存要求
cpu: "100m" # 最低 CPU(0.1核)
limits:
memory: "256Mi" # 最大内存限制(与 maxmemory 配置对应)
cpu: "200m" # 最大 CPU(0.2核)
# 健康检查
livenessProbe:
exec:
# 使用 redis-cli ping 检查 Redis 是否存活
command: ["redis-cli", "ping"]
initialDelaySeconds: 30 # 首次检查延迟(Redis 启动需要时间)
periodSeconds: 10 # 检查间隔
readinessProbe:
exec:
# 使用 redis-cli ping 检查 Redis 是否就绪
command: ["redis-cli", "ping"]
initialDelaySeconds: 5 # 首次检查延迟
periodSeconds: 5 # 检查间隔
# 卷配置
volumes:
- name: redis-data
persistentVolumeClaim:
claimName: redis-pvc # 引用 Redis 数据 PVC
- name: redis-config
configMap:
name: redis-config # 引用 Redis 配置 ConfigMap
items:
- key: redis.conf # ConfigMap 中的键
path: redis.conf # 挂载后的文件名
EOF
# 应用 Deployment
kubectl apply -f redis-deployment.yaml
# 等待 Redis 就绪
kubectl rollout status deployment/redis -n cloud-cafe
# 查看 Pod
kubectl get pods -n cloud-cafe1.4 创建 Redis Service
bash
# 创建 Redis Service
kubectl expose deployment redis \
--port=6379 \
--target-port=6379 \
--name=redis-svc \
-n cloud-cafe
# 查看 Service
kubectl get svc redis-svc -n cloud-cafe1.5 测试 Redis
bash
# 获取 Redis Pod 名称
REDIS_POD=$(kubectl get pod -l app=redis -n cloud-cafe -o jsonpath='{.items[0].metadata.name}')
# 测试 Redis 连接
kubectl exec -it $REDIS_POD -n cloud-cafe -- redis-cli ping
# 测试 Redis 读写
kubectl exec -it $REDIS_POD -n cloud-cafe -- redis-cli SET test "Hello Redis"
kubectl exec -it $REDIS_POD -n cloud-cafe -- redis-cli GET test
# 测试 Redis 持久化
kubectl exec -it $REDIS_POD -n cloud-cafe -- redis-cli SAVEStep 2: 集成 Redis 缓存到后端服务
现在我们更新后端服务,集成 Redis 缓存。
首先,更新 order-backend-deployment.yaml 文件:
bash
# 创建/编辑后端服务 Deployment 文件(集成 Redis 缓存)
cat > order-backend-deployment.yaml << 'EOF'
# 订单后端服务 Deployment(集成 Redis 缓存版)
# 本次修改:添加 Redis 缓存支持
# 修改位置:
# 1. pip install 添加 redis 依赖
# 2. Python 代码中添加 Redis 客户端初始化和缓存逻辑
# 3. 新增 /cache/stats 端点用于查看缓存统计
# 缓存策略:
# - GET /orders:先查缓存,缓存未命中再查数据库,结果写入缓存(TTL 60秒)
# - POST /orders:写入数据库后,删除缓存(保证数据一致性)
# 前置依赖:需要 Redis、MySQL 和相关 ConfigMap/Secret 已创建
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-backend
namespace: cloud-cafe
labels:
app: order-backend
spec:
replicas: 2
selector:
matchLabels:
app: order-backend
template:
metadata:
labels:
app: order-backend
spec:
containers:
- name: order-backend
image: python:3.9-slim
command: ["/bin/sh", "-c"]
args:
- |
# [修改开始] 添加 redis 依赖
pip install flask pymysql flask-cors redis
# [修改结束]
cat > /app/app.py << 'PYEOF'
from flask import Flask, request, jsonify
from flask_cors import CORS
import pymysql
# [新增开始] 导入 Redis 客户端
import redis
# [新增结束]
import os
import json
from datetime import timedelta
app = Flask(__name__)
CORS(app)
# 数据库配置
db_config = {
'host': os.getenv('DB_HOST', 'mysql-service'),
'port': int(os.getenv('DB_PORT', 3306)),
'user': os.getenv('DB_USER', 'cafeadmin'),
'password': os.getenv('DB_PASSWORD', 'userpassword123'),
'database': os.getenv('DB_NAME', 'cloudcafe')
}
# [新增开始] Redis 客户端配置
# decode_responses=True:自动将字节解码为字符串
redis_client = redis.Redis(
host=os.getenv('REDIS_HOST', 'redis-svc'),
port=int(os.getenv('REDIS_PORT', 6379)),
decode_responses=True
)
# [新增结束]
def get_db_connection():
return pymysql.connect(**db_config)
@app.route('/health')
def health():
# [新增] 检查 Redis 连接状态
return jsonify({
'status': 'healthy',
'redis': 'connected' if redis_client.ping() else 'disconnected'
})
@app.route('/orders', methods=['GET'])
def get_orders():
try:
# [新增开始] 缓存查询逻辑
cache_key = 'orders:all'
# 尝试从缓存获取
cached_orders = redis_client.get(cache_key)
if cached_orders:
# 缓存命中,直接返回
app.logger.info('Orders retrieved from cache')
return jsonify(json.loads(cached_orders))
# [新增结束]
# 缓存未命中,从数据库获取
conn = get_db_connection()
cursor = conn.cursor(pymysql.cursors.DictCursor)
cursor.execute('SELECT * FROM orders ORDER BY order_time DESC LIMIT 20')
orders = cursor.fetchall()
conn.close()
# [新增开始] 写入缓存
# setex:设置键值并指定过期时间(单位:秒)
redis_client.setex(cache_key, 60, json.dumps(orders))
app.logger.info('Orders retrieved from database and cached')
# [新增结束]
return jsonify(orders)
except Exception as e:
app.logger.error(f'Error getting orders: {str(e)}')
return jsonify({'error': str(e)}), 500
@app.route('/orders', methods=['POST'])
def create_order():
try:
data = request.json
conn = get_db_connection()
cursor = conn.cursor()
cursor.execute(
'INSERT INTO orders (customer_name, coffee_type, quantity, total_price) VALUES (%s, %s, %s, %s)',
(data['customer_name'], data['coffee_type'], data['quantity'], data['total_price'])
)
conn.commit()
order_id = cursor.lastrowid
conn.close()
# [新增开始] 删除缓存(保证数据一致性)
redis_client.delete('orders:all')
app.logger.info(f'Order {order_id} created, cache cleared')
# [新增结束]
return jsonify({'order_id': order_id, 'message': 'Order created successfully'}), 201
except Exception as e:
app.logger.error(f'Error creating order: {str(e)}')
return jsonify({'error': str(e)}), 500
# [新增开始] 缓存统计端点
@app.route('/cache/stats', methods=['GET'])
def cache_stats():
"""查看 Redis 缓存统计信息"""
try:
info = redis_client.info('stats')
return jsonify({
'total_commands_processed': info.get('total_commands_processed', 0),
'total_connections_received': info.get('total_connections_received', 0),
'keyspace_hits': info.get('keyspace_hits', 0),
'keyspace_misses': info.get('keyspace_misses', 0)
})
except Exception as e:
return jsonify({'error': str(e)}), 500
# [新增结束]
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
PYEOF
python /app/app.py
ports:
- containerPort: 5000
env:
- name: DB_HOST
valueFrom:
configMapKeyRef:
name: app-config
key: DB_HOST
- name: DB_PORT
valueFrom:
configMapKeyRef:
name: app-config
key: DB_PORT
- name: DB_USER
valueFrom:
configMapKeyRef:
name: mysql-config
key: MYSQL_USER
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secret
key: MYSQL_PASSWORD
- name: DB_NAME
valueFrom:
configMapKeyRef:
name: mysql-config
key: MYSQL_DATABASE
- name: REDIS_HOST
valueFrom:
configMapKeyRef:
name: app-config
key: REDIS_HOST
- name: REDIS_PORT
valueFrom:
configMapKeyRef:
name: app-config
key: REDIS_PORT
- name: FLASK_ENV
valueFrom:
configMapKeyRef:
name: order-backend-config
key: FLASK_ENV
- name: FLASK_DEBUG
valueFrom:
configMapKeyRef:
name: order-backend-config
key: FLASK_DEBUG
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "200m"
livenessProbe:
httpGet:
path: /health
port: 5000
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /health
port: 5000
initialDelaySeconds: 10
periodSeconds: 5
volumeMounts:
- name: app-logs
mountPath: /app/logs
volumes:
- name: app-logs
persistentVolumeClaim:
claimName: app-log-pvc
EOF
# [新增] 更新 app-config,添加 Redis 连接配置
kubectl create configmap app-config \
--from-literal=DB_HOST=mysql-service \
--from-literal=DB_PORT=3306 \
--from-literal=REDIS_HOST=redis-svc \
--from-literal=REDIS_PORT=6379 \
--from-literal=LOG_LEVEL=info \
-n cloud-cafe \
--dry-run=client -o yaml | kubectl apply -f -
# 应用 Deployment
kubectl apply -f order-backend-deployment.yaml
# 等待后端服务更新完成
kubectl rollout status deployment/order-backend -n cloud-cafe
# 查看 Pod
kubectl get pods -n cloud-cafeStep 3: 配置 HPA 自动扩缩容
概念: HPA(Horizontal Pod Autoscaler)根据 CPU 使用率或其他指标自动调整 Pod 副本数。
3.1 创建 HPA
bash
# 为后端服务创建 HPA
kubectl autoscale deployment order-backend \
--cpu-percent=50 \
--min=2 \
--max=10 \
-n cloud-cafe
# 查看 HPA
kubectl get hpa -n cloud-cafe
# 查看 HPA 详细信息
kubectl describe hpa order-backend -n cloud-cafe📌 关于
kubectl autoscale参数的解释
kubectl autoscale用于创建 HorizontalPodAutoscaler(HPA),实现 Pod 的自动扩缩容。参数说明:
参数 说明 示例 --cpu-percent目标 CPU 使用率阈值 --cpu-percent=50表示 CPU 超过 50% 时扩容--min最小副本数 即使负载很低也保持的最小 Pod 数量 --max最大副本数 即使负载很高也不会超过的 Pod 数量 扩缩容逻辑:
- 当实际 CPU > 目标 CPU 时,自动扩容(增加 Pod 数量)
- 当实际 CPU < 目标 CPU 时,自动缩容(减少 Pod 数量)
- 副本数始终在
--min和--max之间其他常用参数:
bash# 基于内存自动扩缩容 kubectl autoscale deployment myapp --memory-percent=80 --min=2 --max=10 # 查看 HPA 状态 kubectl get hpa myapp -w
观察要点:
- HPA 的目标 CPU 使用率是 50%
- 最小副本数是 2,最大副本数是 10
- 当前副本数是 2
3.2 为前端服务创建 HPA
bash
# 为前端服务创建 HPA
kubectl autoscale deployment frontend \
--cpu-percent=70 \
--min=2 \
--max=5 \
-n cloud-cafe
# 查看 HPA
kubectl get hpa -n cloud-cafeStep 4: 测试自动扩缩容
4.1 压力测试
bash
# 创建压力测试 Pod
kubectl run load-generator --image=busybox --rm -it -n cloud-cafe -- /bin/sh
# 在 Pod 内执行压力测试
# 获取后端服务地址
BACKEND_SVC="order-backend-svc"
# 持续发送请求
while true; do
wget -q -O- http://$BACKEND_SVC:5000/orders > /dev/null
echo "Request sent at $(date)"
sleep 0.1
done在另一个终端窗口中观察 HPA 和 Pod:
bash
# 实时查看 HPA 状态
kubectl get hpa -n cloud-cafe -w
# 实时查看 Pod 状态
kubectl get pods -n cloud-cafe -w
# 查看 Pod 的 CPU 使用率
kubectl top pods -n cloud-cafe观察要点:
- 随着 CPU 使用率上升,HPA 会自动增加副本数
- 当负载降低时,HPA 会自动减少副本数
- 副本数不会低于最小值,也不会高于最大值
4.2 停止压力测试
bash
# 停止压力测试(在 load-generator Pod 中按 Ctrl+C)
# 等待一段时间,观察副本数是否自动减少
kubectl get hpa -n cloud-cafe -w
kubectl get pods -n cloud-cafe -wStep 5: 测试缓存效果
5.1 测试缓存命中
bash
# 获取 Ingress 访问地址
INGRESS_PORT=$(kubectl get svc -n ingress-nginx ingress-nginx-controller -o jsonpath='{.spec.ports[0].nodePort}')
NODE_IP=$(kubectl get nodes -o jsonpath='{.items[0].status.addresses[?(@.type=="InternalIP")].address}')
# 第一次请求(缓存未命中)
time curl -H "Host: cloudcafe.local" http://$NODE_IP:$INGRESS_PORT/api/orders > /dev/null
# 第二次请求(缓存命中)
time curl -H "Host: cloudcafe.local" http://$NODE_IP:$INGRESS_PORT/api/orders > /dev/null
# 第三次请求(缓存命中)
time curl -H "Host: cloudcafe.local" http://$NODE_IP:$INGRESS_PORT/api/orders > /dev/null观察要点:
- 第一次请求应该较慢(从数据库读取)
- 后续请求应该更快(从缓存读取)
5.2 查看缓存统计
bash
# 查看缓存统计信息
curl -H "Host: cloudcafe.local" http://$NODE_IP:$INGRESS_PORT/api/cache/stats
# 查看 Redis 信息
REDIS_POD=$(kubectl get pod -l app=redis -n cloud-cafe -o jsonpath='{.items[0].metadata.name}')
kubectl exec -it $REDIS_POD -n cloud-cafe -- redis-cli INFO stats5.3 测试缓存失效
bash
# 创建新订单(会清除缓存)
curl -X POST -H "Host: cloudcafe.local" -H "Content-Type: application/json" \
-d '{"customer_name":"缓存测试","coffee_type":"美式咖啡","quantity":1,"total_price":18.00}' \
http://$NODE_IP:$INGRESS_PORT/api/orders
# 再次查询订单(缓存已失效,从数据库读取)
time curl -H "Host: cloudcafe.local" http://$NODE_IP:$INGRESS_PORT/api/orders > /dev/nullStep 6: 性能测试和优化
6.1 使用 Apache Bench 进行性能测试
bash
# 安装 Apache Bench(如果未安装)
sudo apt-get install -y apache2-utils
# 测试后端 API 性能(无缓存)
ab -n 1000 -c 10 -H "Host: cloudcafe.local" http://$NODE_IP:$INGRESS_PORT/api/orders
# 测试后端 API 性能(有缓存)
# 先预热缓存
for i in {1..10}; do
curl -H "Host: cloudcafe.local" http://$NODE_IP:$INGRESS_PORT/api/orders > /dev/null
done
# 再次测试
ab -n 1000 -c 10 -H "Host: cloudcafe.local" http://$NODE_IP:$INGRESS_PORT/api/orders观察要点:
- 有缓存的请求应该更快
- 比较两次测试的 RPS(Requests Per Second)
6.2 优化建议
根据性能测试结果,可以考虑以下优化:
- 增加缓存时间: 根据业务需求调整缓存过期时间
- 增加副本数: 根据负载情况调整最小和最大副本数
- 优化数据库查询: 添加索引、优化 SQL 语句
- 使用连接池: 减少数据库连接开销
- 启用压缩: 减少网络传输数据量
验证和测试
1. 检查所有资源状态
bash
# 查看 Deployment
kubectl get deployment -n cloud-cafe
# 查看 StatefulSet
kubectl get statefulset -n cloud-cafe
# 查看 Pod
kubectl get pods -n cloud-cafe
# 查看 Service
kubectl get svc -n cloud-cafe
# 查看 HPA
kubectl get hpa -n cloud-cafe
# 查看 PVC
kubectl get pvc -n cloud-cafe2. 测试完整流程
bash
# 1. 测试健康检查
curl -H "Host: cloudcafe.local" http://$NODE_IP:$INGRESS_PORT/api/health
# 2. 测试缓存效果
for i in {1..5}; do
time curl -H "Host: cloudcafe.local" http://$NODE_IP:$INGRESS_PORT/api/orders > /dev/null
done
# 3. 测试创建订单
curl -X POST -H "Host: cloudcafe.local" -H "Content-Type: application/json" \
-d '{"customer_name":"性能测试","coffee_type":"拿铁","quantity":2,"total_price":50.00}' \
http://$NODE_IP:$INGRESS_PORT/api/orders
# 4. 查看缓存统计
curl -H "Host: cloudcafe.local" http://$NODE_IP:$INGRESS_PORT/api/cache/stats3. 测试自动扩缩容
bash
# 启动压力测试
kubectl run load-test --image=busybox --rm -it -n cloud-cafe -- /bin/sh
# 在 Pod 内执行: while true; do wget -q -O- http://order-backend-svc:5000/orders > /dev/null; done
# 在另一个终端观察
watch -n 1 'kubectl get hpa -n cloud-cafe && kubectl get pods -n cloud-cafe'
# 停止压力测试后,观察副本数是否自动减少4. 查看资源使用情况
bash
# 查看节点资源使用情况
kubectl top nodes
# 查看 Pod 资源使用情况
kubectl top pods -n cloud-cafe
# 查看 HPA 详细信息
kubectl describe hpa -n cloud-cafe📝 总结和思考
本课程学到的知识点
- Redis 缓存: 提升查询性能,减少数据库负载
- HPA 自动扩缩容: 根据负载自动调整副本数
- 负载均衡: 多副本实现负载均衡
- 性能测试: 使用工具测试系统性能
- 系统优化: 根据测试结果进行优化
关键概念
- 缓存策略: 合理设置缓存过期时间
- 自动扩缩容: 根据负载自动调整资源
- 负载均衡: 多副本分散请求压力
- 性能监控: 持续监控系统性能
思考题
- Redis 缓存和数据库有什么区别?分别在什么场景下使用?
- HPA 的扩缩容策略是什么?如何调整扩缩容的灵敏度?
- 如何实现 Redis 的高可用?(提示:Redis Sentinel、Redis Cluster)
- 缓存穿透、缓存击穿、缓存雪崩是什么?如何解决?
- 如何实现蓝绿部署或金丝雀发布?
最佳实践
- 合理设置缓存时间: 根据业务需求调整缓存过期时间
- 监控缓存命中率: 确保缓存有效
- 设置合理的 HPA 参数: 避免频繁扩缩容
- 定期进行性能测试: 及时发现性能瓶颈
- 使用连接池: 减少数据库连接开销
下一步
本课程学习了如何添加缓存、配置自动扩缩容,提升系统性能和可用性。
下一课程将学习如何添加监控和日志,实现系统可观测性。
下一课程: 06-监控和日志.md
清理环境
如果你想清理本课程创建的资源:
bash
# 删除 HPA
kubectl delete hpa order-backend frontend -n cloud-cafe
# 删除 Redis
kubectl delete deployment redis -n cloud-cafe
kubectl delete svc redis-svc -n cloud-cafe
kubectl delete pvc redis-pvc -n cloud-cafe
kubectl delete configmap redis-config -n cloud-cafe
# 删除其他资源
kubectl delete ingress cloud-cafe-ingress -n cloud-cafe
kubectl delete deployment frontend order-backend -n cloud-cafe
kubectl delete svc frontend-svc order-backend-svc -n cloud-cafe
kubectl delete statefulset mysql -n cloud-cafe
kubectl delete svc mysql-service -n cloud-cafe
kubectl delete pvc mysql-pvc app-log-pvc -n cloud-cafe
kubectl delete configmap frontend-html mysql-config app-config order-backend-config -n cloud-cafe
kubectl delete secret mysql-secret mysql-secret-manual -n cloud-cafe
# 删除命名空间
kubectl delete namespace cloud-cafe提示: 如果你要继续学习下一个课程,建议保留这些资源,因为下一个课程会在此基础上进行。