Files
juwan-backend/docs/redis-sentinel-troubleshooting.md
T

22 KiB
Raw Blame History

Redis Sentinel 部署问题诊断与修复报告

问题日期: 2026年2月22日
命名空间: juwan
涉及资源: user-rpc deployment, RedisSentinel


📋 目录

  1. 问题背景
  2. 问题现象
  3. 诊断过程
  4. 根因分析
  5. 解决方案
  6. 修复步骤
  7. 验证结果
  8. 后续建议

🎯 问题背景

部署目标

部署一个简单的三节点 Redis Sentinel 哨兵集群作为缓存服务,供 user-rpc 服务使用。后续如有需要再扩展为分片集群。

初始配置

deploy/k8s/service/user/user-rpc.yaml 中配置了:

  • user-rpc Deployment3副本)
  • user-rpc Service
  • HPACPU和内存)
  • RedisSentinel 资源
  • PostgreSQL Cluster

🔴 问题现象

执行的操作

kubectl apply -f .\deploy\k8s\service\user\user-rpc.yaml

输出结果

deployment.apps/user-rpc configured
service/user-rpc-svc unchanged
horizontalpodautoscaler.autoscaling/user-rpc-hpa-c unchanged
horizontalpodautoscaler.autoscaling/user-rpc-hpa-m unchanged
redissentinel.redis.redis.opstreelabs.in/user-redis unchanged
cluster.postgresql.cnpg.io/user-db unchanged

观察到的异常

查看命名空间资源:

kubectl get all -n juwan

发现:

  • user-api pods 正常运行
  • user-rpc pods 正常运行
  • PostgreSQL clusters 正常运行
  • 没有任何 Redis 相关的 Pod
  • 没有 Redis Service

🔍 诊断过程

步骤 1:检查 RedisSentinel 资源状态

目的: 确认 RedisSentinel 资源是否被成功创建

命令:

kubectl get redissentinel user-redis -n juwan

输出:

NAME         AGE
user-redis   9m56s

分析:

  • RedisSentinel 资源已创建
  • 但没有创建任何 Pod
  • 结论: Operator 没有按照 RedisSentinel 规格创建实际资源

步骤 2:查看 RedisSentinel 详细信息

目的: 检查资源的详细配置和事件

命令:

kubectl describe redissentinel user-redis -n juwan

关键输出:

API Version:  redis.redis.opstreelabs.in/v1beta2
Kind:         RedisSentinel
Metadata:
  Creation Timestamp:  2026-02-22T12:41:47Z
  Finalizers:
    redisSentinelFinalizer
  Generation:        2
Spec:
  Cluster Size:  3
  Redis Sentinel Config:
    Redis Replication Name:   user-redis  # ⚠️ 关键配置
    Master Group Name:        myMaster
    Redis Port:               6379
    Quorum:                   2
Events:  <none>  # ⚠️ 没有任何事件

分析:

  • 配置中引用了 redisReplicationName: user-redis
  • 没有任何 Events,说明 Operator 可能在等待某些前置条件
  • 需要进一步检查依赖资源

步骤 3:检查 Secret 资源

目的: 确认 RedisSentinel 所需的密码 Secret 是否存在

命令:

kubectl get secret user-redis -n juwan

输出:

NAME         TYPE     DATA   AGE
user-redis   Opaque   1      5h48m

分析:

  • Secret 存在,排除认证配置问题

步骤 4:查找 Redis Pods

目的: 确认是否有 Redis Pod 使用不同的命名规则

命令:

kubectl get pods -n juwan | Select-String redis

输出:

(空输出 - 没有找到任何 Redis Pod)

分析:

  • 确认没有任何 Redis Pod 被创建

步骤 5:检查 StatefulSet

目的: Redis 通常使用 StatefulSet 部署,检查是否有相关资源

命令:

kubectl get statefulset -n juwan

输出:

No resources found in juwan namespace.

分析:

  • 没有 StatefulSet 被创建
  • 结论: Operator 完全没有执行创建动作

步骤 6:检查 Redis Operator 状态

目的: 确认 Redis Operator 本身是否正常运行

命令:

kubectl get pods -A | Select-String redis-operator

输出:

default        redis-operator-7dcf4468c9-gfbcm     1/1     Running        0        4h50m
ot-operators   redis-operator-69bb645f5b-6kxkv     0/1     ErrImageNeverPull   0   4h48m

分析:

  • default namespace 中的 Operator 正常运行
  • ⚠️ ot-operators namespace 中的 Operator 镜像拉取失败(但不影响我们的场景)

步骤 7:查看 Operator 日志

目的: 从 Operator 日志中寻找线索

命令:

kubectl logs redis-operator-7dcf4468c9-gfbcm -n default --tail=50

关键输出:

{"level":"info","ts":"2026-02-22T08:01:56Z","msg":"Starting Controller","controller":"redissentinel"}
{"level":"info","ts":"2026-02-22T08:01:56Z","msg":"Starting workers","controller":"redissentinel","worker count":1}

分析:

  • RedisSentinel Controller 已启动
  • 没有错误日志
  • 但也没有处理 user-redis 资源的日志
  • 推测: Operator 在等待某个依赖资源

步骤 8:检查 RedisReplication 资源(关键发现)

目的: 根据 RedisSentinel 配置中的 redisReplicationName: user-redis,检查对应的 RedisReplication 是否存在

命令:

kubectl get redisreplication -n juwan

输出:

No resources found in juwan namespace.

分析:

  • RedisReplication 资源不存在!
  • 🔎 这就是问题的根本原因

💡 根因分析

问题根源

RedisSentinel 依赖 RedisReplication,但配置中只创建了 RedisSentinel,没有创建 RedisReplication。

Redis Operator 架构理解

在 OpsTree Redis Operator 中,资源之间的关系如下:

┌─────────────────────────────────────────┐
│        RedisSentinel (哨兵层)            │
│  - 3个 Sentinel 节点                     │
│  - 负责监控和自动故障转移                 │
│  - 引用: redisReplicationName           │
└──────────────┬──────────────────────────┘
               │ 监控
               ↓
┌─────────────────────────────────────────┐
│     RedisReplication (数据层)            │
│  - 1个 Master + N个 Replica              │
│  - 提供实际的缓存服务                     │
│  - 主从复制                              │
└─────────────────────────────────────────┘

错误配置的问题

原始配置直接创建了 RedisSentinel,但:

  1. 缺少被监控对象: Sentinel 需要监控一个 RedisReplication 集群
  2. 引用不存在的资源: redisReplicationName: user-redis 指向一个不存在的 RedisReplication
  3. Operator 行为: Operator 发现依赖的 RedisReplication 不存在,因此不会创建 Sentinel Pod

为什么没有错误提示?

  • CRD 验证只检查语法和字段类型
  • 资源引用关系由 Operator 运行时检查
  • Operator 采用了"等待依赖"策略,而不是报错

解决方案

正确的部署顺序

  1. 先创建 RedisReplication(建立 Redis 主从复制集群)
  2. 再创建 RedisSentinel(监控上述复制集群)

配置结构

# 第一步:创建 Redis 主从复制(数据层)
apiVersion: redis.redis.opstreelabs.in/v1beta2
kind: RedisReplication
metadata:
  name: user-redis  # Sentinel 将引用这个名称
  namespace: juwan
spec:
  clusterSize: 3  # 1 Master + 2 Replicas
  kubernetesConfig:
    image: quay.io/opstree/redis:v7.0.12
    resources:
      requests:
        cpu: 100m
        memory: 128Mi
      limits:
        cpu: 500m
        memory: 512Mi
    redisSecret:
      name: user-redis
      key: password
  storage:
    volumeClaimTemplate:
      spec:
        accessModes: ["ReadWriteOnce"]
        resources:
          requests:
            storage: 1Gi  # 每个 Redis 节点 1GB 存储

---
# 第二步:创建 Sentinel 监控(监控层)
apiVersion: redis.redis.opstreelabs.in/v1beta2
kind: RedisSentinel
metadata:
  name: user-redis-sentinel  # 使用不同的名称避免混淆
  namespace: juwan
spec:
  clusterSize: 3  # 3个 Sentinel 节点(推荐奇数)
  kubernetesConfig:
    image: quay.io/opstree/redis-sentinel:v7.0.12  # 使用 Sentinel 专用镜像
  redisSentinelConfig:
    redisReplicationName: user-redis  # 引用上面的 RedisReplication
    masterGroupName: mymaster
    quorum: "2"  # 需要 2 个 Sentinel 同意才能进行故障转移

🔧 修复步骤

步骤 1:删除错误的 RedisSentinel 资源

命令:

kubectl delete redissentinel user-redis -n juwan

输出:

redissentinel.redis.redis.opstreelabs.in "user-redis" deleted

说明: 删除仅创建了 CRD 实例但未创建实际 Pod 的资源


步骤 2:更新配置文件

修改 deploy/k8s/service/user/user-rpc.yaml,将单独的 RedisSentinel 替换为:

  1. RedisReplication(数据层)
  2. RedisSentinel(监控层)

变更内容:

  • 添加 RedisReplication 资源定义
  • 添加 storage.volumeClaimTemplate 配置
  • 修改 RedisSentinel 的 metadata.nameuser-redis-sentinel
  • 使用正确的 Sentinel 镜像:quay.io/opstree/redis-sentinel:v7.0.12
  • 完善 Sentinel 配置参数

步骤 3:应用更新后的配置

命令:

kubectl apply -f .\deploy\k8s\service\user\user-rpc.yaml

输出:

deployment.apps/user-rpc configured
service/user-rpc-svc unchanged
horizontalpodautoscaler.autoscaling/user-rpc-hpa-c unchanged
horizontalpodautoscaler.autoscaling/user-rpc-hpa-m unchanged
redisreplication.redis.redis.opstreelabs.in/user-redis created  ✅
redissentinel.redis.redis.opstreelabs.in/user-redis-sentinel created  ✅
cluster.postgresql.cnpg.io/user-db unchanged

分析:

  • RedisReplication 成功创建
  • RedisSentinel 成功创建
  • 🎯 两个资源都是新创建(created),符合预期

验证结果

验证 1:检查 Pod 创建情况(等待 30 秒)

命令:

kubectl get statefulset,pods -n juwan | Select-String -Pattern "user-redis|NAME"

输出:

NAME                                            READY   AGE
statefulset.apps/user-redis                     3/3     81s   ✅
statefulset.apps/user-redis-sentinel-sentinel   3/3     24s   ✅

NAME                                 READY   STATUS    RESTARTS   AGE
pod/user-redis-0                     2/2     Running   0          80s   ✅
pod/user-redis-1                     2/2     Running   0          52s   ✅
pod/user-redis-2                     2/2     Running   0          47s   ✅
pod/user-redis-sentinel-sentinel-0   1/1     Running   0          24s   ✅
pod/user-redis-sentinel-sentinel-1   1/1     Running   0          8s    ✅
pod/user-redis-sentinel-sentinel-2   1/1     Running   0          5s    ✅

分析:

  • RedisReplication 创建了 3 个 Poduser-redis-0/1/2
    • 每个 Pod 有 2 个容器(2/2):Redis + Exporter
    • 所有 Pod 处于 Running 状态
  • RedisSentinel 创建了 3 个 Poduser-redis-sentinel-sentinel-0/1/2
    • 每个 Pod 有 1 个容器(1/1):Sentinel
    • 所有 Pod 处于 Running 状态
  • 创建了 2 个 StatefulSetREADY 状态为 3/3

验证 2:检查 Service 资源

命令:

kubectl get svc -n juwan | Select-String -Pattern "redis|NAME"

输出:

NAME                                      TYPE        CLUSTER-IP       PORT(S)             AGE
user-redis                                ClusterIP   10.103.91.84     6379/TCP,9121/TCP   95s   ✅
user-redis-additional                     ClusterIP   10.107.228.48    6379/TCP            95s
user-redis-headless                       ClusterIP   None             6379/TCP            95s   ✅
user-redis-master                         ClusterIP   10.97.120.76     6379/TCP            95s   ✅
user-redis-replica                        ClusterIP   10.100.213.103   6379/TCP            95s   ✅
user-redis-sentinel-sentinel              ClusterIP   10.105.28.231    26379/TCP           40s   ✅
user-redis-sentinel-sentinel-additional   ClusterIP   10.97.111.42     26379/TCP           39s
user-redis-sentinel-sentinel-headless     ClusterIP   None             26379/TCP           41s

Service 功能说明:

Redis 数据层 Service(端口 6379

  • user-redis-master: 主节点服务,用于写操作
  • user-redis-replica: 从节点服务,用于读操作
  • user-redis: 通用访问入口(负载均衡到所有节点)
  • user-redis-headless: 无头服务,用于 StatefulSet Pod 间通信
  • user-redis-additional: 额外的访问入口

Sentinel 监控层 Service(端口 26379

  • user-redis-sentinel-sentinel: Sentinel 访问入口
  • user-redis-sentinel-sentinel-headless: Sentinel 节点间通信
  • user-redis-sentinel-sentinel-additional: 额外的 Sentinel 访问入口

验证 3:检查完整的集群状态

命令:

kubectl get all -n juwan

最终状态统计:

资源类型 名称 数量 状态
Deployment user-api 3/3 Running
Deployment user-rpc 3/3 Running
StatefulSet cluster-example (PostgreSQL) 3/3 Running
StatefulSet user-db (PostgreSQL) 3/3 Running
StatefulSet user-redis (Redis 数据) 3/3 Running
StatefulSet user-redis-sentinel-sentinel 3/3 Running

Pod 总计: 18 个(全部 Running Service 总计: 13 个 HPA 总计: 6 个


📊 架构图

部署后的 Redis 架构

┌────────────────────────────────────────────────────────────┐
│                    应用层 (user-rpc)                        │
│                                                            │
│              [需要添加 Redis 连接配置]                       │
└──────────┬─────────────────────────────┬───────────────────┘
           │                             │
           │ 写操作                       │ 读操作
           ↓                             ↓
    ┌─────────────┐              ┌─────────────┐
    │ user-redis- │              │ user-redis- │
    │   master    │              │   replica   │
    │ Service     │              │  Service    │
    └─────────────┘              └─────────────┘
           │                             │
           └──────────┬──────────────────┘
                      ↓
    ┌──────────────────────────────────────────┐
    │      RedisReplication (数据层)            │
    │                                          │
    │   ┌──────────┐  ┌──────────┐  ┌───────┐ │
    │   │ Master   │→ │ Replica  │→ │Replica│ │
    │   │ redis-0  │  │ redis-1  │  │redis-2│ │
    │   └──────────┘  └──────────┘  └───────┘ │
    └──────────────────────────────────────────┘
                      ↑
                      │ 监控 & 故障转移
                      │
    ┌──────────────────────────────────────────┐
    │     RedisSentinel (监控层)               │
    │                                          │
    │   ┌──────────┐  ┌──────────┐  ┌───────┐ │
    │   │Sentinel-0│  │Sentinel-1│  │Sentinel-2│
    │   └──────────┘  └──────────┘  └───────┘ │
    │                                          │
    │   Quorum: 2/3 (多数派决策)                │
    └──────────────────────────────────────────┘

📝 后续建议

1. 应用集成 Redis

user-rpc 服务目前还没有配置 Redis 连接,需要:

修改配置文件 app/users/rpc/etc/pb.yaml

Name: pb.rpc
ListenOn: 0.0.0.0:8080

# 添加 Redis 配置(使用 Sentinel 模式)
Redis:
  - Host: user-redis-sentinel-sentinel:26379
    Type: sentinel
    MasterName: mymaster
    Pass: ${REDIS_PASSWORD}

# 或使用主从模式
# Redis:
#   - Host: user-redis-master:6379  # 写
#     Type: node
#     Pass: ${REDIS_PASSWORD}
#   - Host: user-redis-replica:6379  # 读
#     Type: node
#     Pass: ${REDIS_PASSWORD}

Etcd:
  Hosts:
  - etcd-service:2379  # 需要配置实际的 Etcd 地址
  Key: pb.rpc

修改 Config 结构 app/users/rpc/internal/config/config.go

package config

import (
    "github.com/zeromicro/go-zero/core/stores/redis"
    "github.com/zeromicro/go-zero/zrpc"
)

type Config struct {
    zrpc.RpcServerConf
    Redis redis.RedisConf  // 添加 Redis 配置
}

初始化 Redis 客户端 app/users/rpc/internal/svc/serviceContext.go

package svc

import (
    "github.com/zeromicro/go-zero/core/stores/redis"
    "juwan-backend/app/users/rpc/internal/config"
)

type ServiceContext struct {
    Config config.Config
    Redis  *redis.Redis  // 添加 Redis 客户端
}

func NewServiceContext(c config.Config) *ServiceContext {
    return &ServiceContext{
        Config: c,
        Redis:  redis.MustNewRedis(c.Redis),  // 初始化 Redis
    }
}

更新 Deployment 环境变量

# deploy/k8s/service/user/user-rpc.yaml
env:
  - name: DB_URI
    valueFrom:
      secretKeyRef:
        name: user-db-app
        key: uri
  - name: REDIS_PASSWORD  # 添加 Redis 密码
    valueFrom:
      secretKeyRef:
        name: user-redis
        key: password

2. Redis 性能监控

已启用 Redis Exporter(端口 9121),可以配置 Prometheus 监控:

apiVersion: v1
kind: ServiceMonitor
metadata:
  name: user-redis-metrics
  namespace: juwan
spec:
  selector:
    matchLabels:
      app: user-redis
  endpoints:
    - port: redis-exporter
      interval: 30s

监控指标:

  • redis_up: 实例状态
  • redis_connected_clients: 连接数
  • redis_memory_used_bytes: 内存使用
  • redis_commands_processed_total: 命令处理数
  • redis_master_repl_offset: 复制偏移量

3. 高可用性测试

测试主节点故障转移

# 1. 查找当前主节点
kubectl exec -it user-redis-sentinel-sentinel-0 -n juwan -- redis-cli -p 26379 SENTINEL get-master-addr-by-name mymaster

# 2. 模拟主节点故障
kubectl delete pod user-redis-0 -n juwan

# 3. 观察 Sentinel 的故障转移过程
kubectl logs -f user-redis-sentinel-sentinel-0 -n juwan

# 4. 确认新主节点
kubectl exec -it user-redis-sentinel-sentinel-0 -n juwan -- redis-cli -p 26379 SENTINEL get-master-addr-by-name mymaster

预期结果

  • Sentinel 检测到主节点下线(5 秒)
  • 2/3 Sentinel 节点达成共识(quorum=2
  • 自动提升一个从节点为主节点
  • 客户端自动重连到新主节点

4. 扩展为分片集群(未来)

当缓存数据量增长需要横向扩展时,可以迁移到 RedisCluster

apiVersion: redis.redis.opstreelabs.in/v1beta2
kind: RedisCluster
metadata:
  name: user-redis-cluster
  namespace: juwan
spec:
  clusterSize: 6  # 3 主 + 3 从
  kubernetesConfig:
    image: quay.io/opstree/redis:v7.0.12
  redisLeader:
    replicas: 3
  redisFollower:
    replicas: 3
  storage:
    volumeClaimTemplate:
      spec:
        accessModes: ["ReadWriteOnce"]
        resources:
          requests:
            storage: 5Gi

迁移步骤:

  1. 部署新的 RedisCluster
  2. 使用 redis-cli --cluster import 迁移数据
  3. 更新应用配置指向新集群
  4. 下线旧的 Sentinel 集群

5. 备份策略

Redis Operator 不提供自动备份,建议配置定时任务:

# 创建 CronJob 定期执行 BGSAVE
apiVersion: batch/v1
kind: CronJob
metadata:
  name: redis-backup
  namespace: juwan
spec:
  schedule: "0 2 * * *"  # 每天凌晨 2 点
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: backup
            image: redis:7.0.12
            command:
            - /bin/sh
            - -c
            - |
              redis-cli -h user-redis-master -a $REDIS_PASSWORD BGSAVE
              # 将 /data/dump.rdb 上传到对象存储
          restartPolicy: OnFailure

📚 总结

关键经验

  1. 理解资源依赖关系: RedisSentinel 依赖 RedisReplication,部署顺序很重要
  2. 资源命名规范: 使用清晰的名称区分不同层次的资源(如 user-redis 和 user-redis-sentinel
  3. 诊断思路:
    • 从现象(Pod 缺失)→ 资源状态(CRD 存在)→ Operator 日志 → 依赖检查
    • 逐层排查,最终定位到 RedisReplication 缺失
  4. 验证完整性: 不仅要检查 Pod,还要验证 Service、StatefulSet 等所有相关资源

文档价值

本文档可用于:

  • 团队知识传承
  • 类似问题的快速排查手册
  • 新成员的 Redis Operator 学习资料
  • 事后复盘和经验总结

最后更新时间: 2026年2月22日
文档状态: 问题已解决,Redis 集群运行正常
下一步行动: 配置应用连接 Redis