应返回200且仅做内存级检查:/health用于liveness(进程存活),/ready用于readiness(依赖就绪);避免DB、Redis、日志、外部调用,显式声明methods=["GET","HEAD"],K8s探针需分开配置initialDelaySeconds等参数。

Flask里怎么写一个符合K8s readiness/liveness要求的health路由
K8s健康检查不关心业务逻辑,只看HTTP状态码和响应时间。Flask里最稳妥的做法是返回200 OK且响应体极简,避免引入数据库、缓存、外部API等依赖。
常见错误是把/health写成调用db.session.execute("SELECT 1")或redis.ping()——这会让liveness探针因短暂网络抖动反复重启Pod。
- 只做进程存活检查:返回
{"status": "ok"}+200 - 如果必须检查依赖,单独暴露
/healthz/ready(readiness),并设更宽松的timeoutSeconds和failureThreshold - 不要在health路由里记录日志、打点或触发任何副作用
为什么不能直接用Flask内置的debug模式或/health端点
Flask默认没提供/health路由,有人会随手加个@app.route("/health")但忘了设methods=["GET"],导致K8s用HEAD请求时返回405 Method Not Allowed——而K8s默认发的就是HEAD(除非显式指定httpGet: { scheme: HTTP, port: 5000, path: "/health", httpHeaders: [...] })。
另一个坑是用了flask-debugtoolbar或werkzeug.middleware.proxy_fix后,health响应被中间件拦截、重定向或加了额外Header,导致K8s判定失败。
立即学习“Python免费学习笔记(深入)”;
- 显式声明
methods=["GET", "HEAD"] - 确保路由在所有中间件之前注册(比如放在
app = Flask(__name__)之后立即定义) - 用
curl -I http://localhost:5000/health验证响应头只有200和Content-Length
如何配置K8s YAML让Flask健康检查真正生效
很多人写了路由却没配对YAML字段,结果liveness一直用默认的TCP探针,根本没走HTTP路径。
关键点是:readiness和liveness必须分开配,且initialDelaySeconds要大于Flask冷启动时间(特别是用了gunicorn时,worker加载可能耗时1–3秒)。
- livenessProbe应设
httpGet: { path: "/health", port: 5000 },initialDelaySeconds: 10,periodSeconds: 10 - readinessProbe可设相同路径,但
initialDelaySeconds: 5,failureThreshold: 3(避免启动瞬间误判) - 确保容器端口名是
http或显式写port: 5000,否则K8s找不到目标端口 - 别信“默认就是HTTP”,K8s不会猜你的Flask端口,
port字段必须存在
生产环境绕不开的细节:Gunicorn + Flask组合下的超时陷阱
Flask开发服务器能立刻响应/health,但Gunicorn worker可能卡在慢请求里,导致新进来的health请求排队等待——K8s探针超时(默认1秒)就判定失败。
这不是代码问题,是并发模型冲突:health应该走独立线程或绕过worker队列。
- 给Gunicorn加
--preload参数,让health逻辑在master进程加载,不依赖worker - 或者用
gunicorn --workers=2 --worker-class=gevent降低阻塞概率 - 最彻底的解法:用
subprocess.run(["pgrep", "-f", "gunicorn"], capture_output=True)这类OS级检查替代HTTP路由(需挂载hostPID: true,慎用) - 永远在
livenessProbe里设timeoutSeconds: 2,别用默认1秒
/health路由本身很简单,难的是让它在K8s各种超时、重试、中间件、进程模型下始终稳定返回200。多数故障不是路由写错了,而是没意识到K8s探针和Python WSGI服务器之间那几毫秒的竞态。



















