MSSQL Always On + Kubernetes:为什么 2/3 Ready 不会触发主备切换?

目录标题

V1.0

非常好,你提到你的数据库是 MSSQL,而高可用机制使用的是 Always On Availability Groups,那么我们就可以针对 Kubernetes 上部署的 Always On MSSQL 环境,解释为什么:

  • Pod 2/3 就绪(Ready),但不会触发主备切换;
  • 以及探活/就绪探针在 Always On 中的作用和你看到的 SQLCMD 报错。

🧠 一、为什么 2/3 Ready 不会触发主备切换?

✅ 原因总结:

Always On 主备切换的决策 是由 SQL Server 自身 + Windows/Linux Cluster 或 AG Listener 控制的,而不是由 Kubernetes 控制。

就算 Kubernetes 检测到 Pod 的某个容器探针失败(变为 2/3 Ready),它不会影响 MSSQL Always On 的主备角色


🔎 二、你当前的问题简析

你看到的报错是:

Readiness probe failed: Sqlcmd: Error: Microsoft ODBC Driver 17 for SQL Server : 
Client unable to establish connection due to prelogin failure.

🚨 原因可能包括:

可能原因说明
🔒 SQL Server 端口未开放 / 启动缓慢Probe 在 SQL Server 完全就绪前执行连接失败
📉 SQL Server 连接数达到限制Too many connections 错误导致连接失败
🔌 网络端口不可达Probe 尝试连接 1433 失败
🧪 Probe 配置有误readinessProbe 中的 tcpSocketexec 命令不适配

🔁 三、探针在 Always On 环境中的角色

探针类型建议用途注意点
Liveness Probe用于重启挂掉的 SQL 容器不会触发 Always On 切换,但会重启 Pod
Readiness Probe控制 Service 是否将请求路由至此 Pod适合配置 SQLCMD 检查是否响应

✅ 推荐 Readiness Probe 方式(exec 方式举例):

readinessProbe:
  exec:
    command:
    - /bin/sh
    - -c
    - /opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P <password> -Q "SELECT 1"
  initialDelaySeconds: 60
  periodSeconds: 10
  timeoutSeconds: 5
  failureThreshold: 3

⚠️ 注意:

  • initialDelaySeconds 要设置得足够大,SQL Server 冷启动时间很长。
  • sqlcmd 必须可执行,容器中必须有 /opt/mssql-tools/bin/sqlcmd

📌 四、为什么 2/3 Ready 不触发主备切换?

再次强调:

  • Always On 的角色切换机制 是 SQL Server 与其配置的 AG(Availability Group)或 Listener 控制的;
  • Kubernetes 只控制 Pod 层面的探针和调度,不知道哪个是主库;
  • 即便探针失败,只是代表流量不再进这个 Pod,并不会影响 MSSQL 的主备角色。

✅ 建议操作步骤

  1. 修复 readinessProbe 中 SQLCMD 失败问题

    • 检查是否为端口未开放、连接数限制、探针触发太早。
  2. 检查 SQL Server 容器内日志

    kubectl logs mssql-b8afbcd101-0 -c <sqlserver-container>
    
  3. 通过 SQL Server Management Studio (SSMS) 检查 AG 状态

    SELECT replica_server_name, role_desc FROM sys.dm_hadr_availability_replica_states;
    
  4. 设置合理的资源限制(避免 OOM)
    SQL Server 非常吃内存和 CPU,资源不足会导致失败。


🧩 如果你想要主动控制 AG 主备切换?

你需要调用 T-SQL 命令如:

ALTER AVAILABILITY GROUP [YourAG] FAILOVER;

而不是依赖 Kubernetes 触发。


如你能提供:

  • 当前 deployment/statefulset YAML
  • 探针完整配置
  • SQL Server 日志或 AG 配置

V2.0

在这里插入图片描述

你提供的 /probe/mssqlprobe 是一个用于 Kubernetes 中 readiness/liveness probe 的自定义脚本,用于检查 MSSQL Always On 节点的状态。这是个相对完善的健康探针脚本,判断逻辑合理,但你遇到的 2/3 Ready 且不会触发主备切换的问题 本质上与以下几点有关。


✅ 一、你这个探针脚本的核心逻辑是干什么的?

逻辑块功能说明
update_heartbeat()更新心跳数据库中的 heartbeat_table,保证数据库可写且响应正常
is_failover_ready()检查当前节点是否满足主库接管的状态
is_slave_ready()检查从库的同步状态是否为 SYNCHRONIZED 或 SYNCHRONIZING
判断 $DBROLE根据角色执行不同逻辑:Master/Replica 会执行心跳和 failover 检查;Slave 只检查同步状态
初始化标志 .init_process判断数据库初始化是否完成,作为 readiness 的基本条件

⚠️ 二、你遇到的问题分析(为什么 Pod 2/3 且没有主备切换)

🔍 1. readinessProbe 脚本返回非 0(导致 2/3 Ready)

查看你贴图的错误:

Readiness probe failed: Sqlcmd: Error: Microsoft ODBC Driver 17 for SQL Server : 
Client unable to establish connection due to prelogin failure.

说明:

  • 脚本最早执行时(如 SQL Server 启动慢、未初始化 .init_process),会立即失败(exit 1);
  • 或者 $MASTER_MSSQL_CON 执行失败,比如身份认证问题、SQL Server 未监听 127.0.0.1:1433;
  • 会导致 kubelet 标记这个容器未 ready → 2/3

❗ 2. 这个 probe 脚本不会也不能触发主备切换

原因如下:

项目原因
Kubernetes 探针的职责检测容器是否可接受流量,不能控制 Always On
MSSQL Always On 切换条件必须由 SQL Server 自身判断失败、或者通过 Listener 失败转移
你这个脚本最多是防止 Kubernetes 把流量转给故障副本,但不会影响 MSSQL AG 本身角色切换

🔧 三、改进建议

✅ 1. 确保探针脚本稳定执行

  • 检查 sqlcmd 是否使用正确 IP 和认证方式;
  • 避免连接 admin:127.0.0.1,直接使用 localhost 或容器内部服务名试试;
  • 或者增加 logging:
$MASTER_MSSQL_CON "select 1" || { echo "sqlcmd failed"; exit 1; }

✅ 2. 初始探针延迟增加

建议在 readinessProbe 中加上如下配置:

readinessProbe:
  exec:
    command: ["/probe/mssqlprobe"]
  initialDelaySeconds: 90
  periodSeconds: 10
  timeoutSeconds: 5
  failureThreshold: 5

避免 SQL Server 启动缓慢时被误判为不健康


✅ 四、总结:你要怎么理解和应对这个现象?

问题原因建议
Pod 显示 2/3某个容器探针失败(通常是 readiness)检查 probe 脚本逻辑和初始延迟设置
没有主备切换K8s 不管理 MSSQL AG 的角色这是 MSSQL 的责任,非 K8s
是否有副作用?是,K8s 不会将流量路由给这个 Pod,但 MSSQL 本身依然可主库可以配合 Service + readiness 避免错误访问

✅ 如果你想让主备切换更智能一点…

你可以通过这些方式主动控制 Always On 切换:

  • 利用 SQL Server 的 ALTER AVAILABILITY GROUP ... FAILOVER 脚本;
  • 设置 AG Listener 在主库切换时自动漂移;
  • 搭配 Kubernetes Operator(如 KubeAG、Agility AG)来增强 MSSQL AG 感知能力。

V3.0

非常清楚,现在我们可以综合你的 mssql 容器 readinessProbe 探针配置和实际 Ready: False 状态,来定位问题。


✅ 当前状态总结

你提供的信息说明:

  • mssql 容器启动成功,但 readinessProbe 报错,导致 Pod 状态为 Ready: False
  • 探针配置是:
readinessProbe:
  exec:
    command: ["/probe/mssqlprobe"]
  failureThreshold: 3
  initialDelaySeconds: 60
  periodSeconds: 10
  timeoutSeconds: 8
  • 环境变量中 DBROLE=Replica
  • /probe/mssqlprobe 中对 Replica 的逻辑会执行:
is_failover_ready

🔎 为什么会 Ready: False?从探针脚本看原因

你的探针脚本中这部分逻辑是关键:

if $MASTER_MSSQL_CON "select 1"; then
    ...
    update_heartbeat
    if [ "$STRATEGY" == "MssqlRWCluster" ]; then
        is_failover_ready
    fi
else
    exit 1
fi

也就是说:

  • 如果 sqlcmd -S admin:127.0.0.1 ... 连接不上,就会直接 exit 1,探针失败;
  • 只要 SQL Server 还没 ready 就执行探针,是一定会失败的。

🔥 核心问题:SQL Server 启动还没就绪,你的探针已经触发了

即使设置了 initialDelaySeconds: 60,但 MSSQL 在 AlwaysOn 模式下(尤其是副本节点),有时会启动超过 60 秒,甚至 2-3 分钟。

另外你的探针内部并没有对 SQL Server 启动慢进行“忽略或宽容”。


✅ 建议改进(分三类)

✅ 1. 探针脚本中加一个 TCP 检查作为快速失败前提

加入:

# 检查端口是否监听,避免直接 SQL 失败
if ! nc -z 127.0.0.1 1433; then
  echo "SQL Server port not ready"
  exit 1
fi

放在最前面,可以避免早期连接失败。


✅ 2. SQL Server 启动前跳过 probe

修改脚本,检测服务是否还没起来,提供一个“宽容等待期”:

# 尝试连接 SQL Server 判断状态是否 up
$MASTER_MSSQL_CON "select 1" > /dev/null 2>&1
if [ $? -ne 0 ]; then
  echo "SQL Server not ready"
  exit 1
fi

✅ 3. 适当调大 readinessProbe 延迟配置

你目前的配置:

initialDelaySeconds: 60
timeoutSeconds: 8
periodSeconds: 10
failureThreshold: 3

可以改为:

initialDelaySeconds: 120  # → SQL Server 启动慢时有更长缓冲
timeoutSeconds: 10        # → MSSQL 响应慢时留余地
failureThreshold: 5       # → 多失败几次再判定失败

🔄 可选增强:是否初始化完成检查

你的脚本中有:

if [ ! -f "$DATADIR/.init_process" ]; then
  exit 1
fi

确保 .init_process 文件真正存在,否则每次 pod 重建都会导致 probe fail。


✅ 小结

问题建议
探针脚本过早执行,SQL 未就绪加 TCP 端口判断或 sqlcmd "select 1" 检查
SQL Server 启动太慢initialDelaySeconds 提高到 120 秒
探针失败次数太紧failureThreshold: 5 可以宽容一点
.init_process 文件未落地保证 SQL 初始化脚本或 entrypoint 落地这个文件

总结

好的,我们来系统整理一下刚才的内容,重点回答你的核心问题:


❓问题现象

在使用 MSSQL Always On + Kubernetes 构建的高可用架构中,发现:

  • 有时 Pod 状态为 Ready: False
  • 明明容器是 Running,但 Readiness Probe 执行失败
  • 2个副本中,2/3 没有切换成功(未就绪)

🔍 根本原因分析

1. 探针脚本 /probe/mssqlprobe 执行失败

  • 探针用的是 exec 模式,脚本直接运行 SQL 命令:

    /opt/mssql-tools/bin/sqlcmd -S admin:127.0.0.1 -U $MSSQL_ROOT_USER -P $MSSQL_ROOT_PASSWORD -Q "select 1"
    
  • 但在 SQL Server 启动未完成、数据库尚未加入 AlwaysOn 集群时,该命令会失败 → 探针失败 → Ready: False


2. SQL Server 启动慢,探针太早触发

  • 当前 readinessProbe 设置为:

    initialDelaySeconds: 60
    periodSeconds: 10
    failureThreshold: 3
    
  • 在某些节点上,SQL Server 尤其在 Replica 模式下,启动时间可能 超过 60 秒,导致探针执行过早。


3. 探针脚本没有足够的“容错逻辑”

例如,没有判断:

  • SQL Server 服务是否已经监听端口(如 1433)
  • 连接超时或网络抖动等临时错误
  • 数据库是否初始化完成(靠 .init_process 判断,但这也可能延迟或丢失)

✅ 为什么 1/3 会切换成功?

因为那 1/3 的副本 在探针触发前 SQL Server 已经 ready

  • 启动时间更快
  • 网络或负载条件更理想
  • 恰好初始化文件 .init_process 存在

所以通过了探针 → 状态变为 Ready=True → 成为候选主节点或可用节点。

而另外 2/3 因为探针失败次数超过了阈值 failureThreshold: 3,被判定为不健康,不能参与切换。


✅ 解决建议汇总

问题建议
SQL Server 启动慢,探针太早initialDelaySeconds 增加到 120
探针脚本逻辑太严格加入 TCP 检查(nc -z 127.0.0.1 1433) 和更宽容的 SQL 错误判断
.init_process 文件缺失确保初始化逻辑里始终创建这个文件
容错不足failureThreshold 增加到 5,避免因短暂错误被标记为失败
Replica 判断逻辑不合理在脚本中区分主/从节点状态检查逻辑,或者只对主节点执行关键判断

✅ 示例优化后的探针脚本片段

# 判断 SQL Server 是否监听
if ! nc -z 127.0.0.1 1433; then
  echo "SQL port not open"
  exit 1
fi

# 判断 SQL Server 是否响应
$MASTER_MSSQL_CON "select 1" > /dev/null 2>&1
if [ $? -ne 0 ]; then
  echo "SQL not responding"
  exit 1
fi

✅ 修改后的 readinessProbe 推荐配置

readinessProbe:
  exec:
    command:
    - /probe/mssqlprobe
  initialDelaySeconds: 120
  timeoutSeconds: 10
  periodSeconds: 10
  failureThreshold: 5
  successThreshold: 1

如你需要我帮你:

  1. 生成完整优化版脚本 /probe/mssqlprobe
  2. 修改后的 YAML readiness 配置

如何实现主备切换的需求呢?

明白了,你的 MSSQL 主库 Pod 中有:

  • mssql:SQL Server 主进程
  • leader:负责高可用逻辑,如协调主备切换
  • exporter:采集监控指标(如 Prometheus exporter)

针对你之前的问题(主库 Pod 中容器 2/3 Ready 不触发切换)

这种部署结构下,出现容器探针失败但主容器 mssql 还正常时,会有如下情况:

  • Kubernetes 认为整个 Pod Not Ready(因为一个容器探针失败)
  • 但 MSSQL 内部仍认为自己是 Primary,leader 容器可能也未触发切换

✅ 想实现:只要主库 Pod 有非 mssql 容器探针失败,就触发主备切换

✅ 建议做法:

  1. leader 容器中的 HA 脚本检查 Pod 所有容器的就绪状态

    • 用 Kubernetes API 或 Downward API
    • 检测所有容器状态,如果不都是 ready == true,就触发 MSSQL 主备切换逻辑(FAILOVER
  2. 将这个检查加入 /probe/mssqlprobe 或 leader 自己的周期任务


示例逻辑(在 leader 容器中执行):

# 设置变量(自动获取当前 Pod 和命名空间)
POD_NAME=$(hostname)
NAMESPACE=$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace)

# 容器总数
total_containers=$(kubectl get pod "$POD_NAME" -n "$NAMESPACE" -o json | jq '.status.containerStatuses | length')

# 就绪的容器数
ready_containers=$(kubectl get pod "$POD_NAME" -n "$NAMESPACE" -o json | jq '[.status.containerStatuses[] | select(.ready == true)] | length')

echo "Ready: $ready_containers / $total_containers"

# 如果有任何一个容器不 Ready,则触发主备切换
if [ "$ready_containers" -lt "$total_containers" ]; then
    echo "Not all containers are ready. Triggering failover..."
    # 在此执行你 MSSQL 的 failover 逻辑,比如调用 ALTER AVAILABILITY GROUP
    # /opt/mssql-tools/bin/sqlcmd -S ... -Q "ALTER AVAILABILITY GROUP ..."
fi

你也可以通过 Kubernetes Downward API 把容器名、Pod 名等挂载进来,避免使用 kubectl


补充建议

  • mssqlprobe 的探针继续保留:它负责自身状态
  • 将容器探针失败 → 主动 failover 的判断 交由 leader 容器或一个专用的探测容器去执行

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值