目录标题
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 中的 tcpSocket 或 exec 命令不适配 |
🔁 三、探针在 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 的主备角色。
✅ 建议操作步骤
-
修复 readinessProbe 中 SQLCMD 失败问题
- 检查是否为端口未开放、连接数限制、探针触发太早。
-
检查 SQL Server 容器内日志
kubectl logs mssql-b8afbcd101-0 -c <sqlserver-container>
-
通过 SQL Server Management Studio (SSMS) 检查 AG 状态
SELECT replica_server_name, role_desc FROM sys.dm_hadr_availability_replica_states;
-
设置合理的资源限制(避免 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
如你需要我帮你:
- 生成完整优化版脚本
/probe/mssqlprobe
- 修改后的 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 容器探针失败,就触发主备切换
✅ 建议做法:
-
让
leader
容器中的 HA 脚本检查 Pod 所有容器的就绪状态- 用 Kubernetes API 或 Downward API
- 检测所有容器状态,如果不都是
ready == true
,就触发 MSSQL 主备切换逻辑(FAILOVER
)
-
将这个检查加入
/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 容器或一个专用的探测容器去执行