不能直接用django admin或/health路由做k8s探针,因其默认触发中间件、查数据库、校验缓存等重操作,响应慢且依赖外部服务,易导致探针超时失败、误判重启;应手写独立wsgi健康端点,绕过django全栈,仅验证进程存活与配置加载,并通过端口隔离、网络过滤和startupprobe保障启动安全。

为什么不能直接用Django Admin或/health路由做K8s探针
很多团队把Django Admin的/admin/或自定义的/health/(比如django-health-check默认路径)直接塞进livenessProbe,结果上线后频繁重启。根本原因是:这些端点默认会触发中间件链(如AuthenticationMiddleware、SessionMiddleware)、查数据库表结构、甚至调用CacheBackend.check()——而K8s探针要求毫秒级响应,且不能依赖任何外部服务。
- django-health-check的
/health/默认包含数据库连接测试,DB慢或临时不可用就返回500,readinessProbe立刻失败,流量被切走 - Admin路径需要登录态,K8s探针没带Cookie或CSRF token,必然403
- 未关闭DEBUG时,堆栈信息可能泄露敏感路径或配置片段
如何手写一个真正轻量、隔离、可部署的健康端点
最稳的方式是绕过Django整个请求生命周期,用纯WSGI callable实现一个独立端点。不走URL路由、不加载中间件、不碰ORM和缓存——只确认Python进程活着、Django settings已加载。
- 在项目根目录新建
health.py,内容为:def health_app(environ, start_response): status = '200 OK' headers = [('Content-type', 'application/json')] start_response(status, headers) return [b'{"status": "ok", "uptime": "true"}'] - 修改
asgi.py或wsgi.py,在顶层import并挂载:from .health import health_app <h1>在application定义下方追加</h1><p>health_application = health_app </p>
- 用Uvicorn启动时显式暴露该应用:
uvicorn myproject.asgi:health_application --host 0.0.0.0 --port 8081
K8s配置里怎么引用这个独立端点
关键不是“能不能访问”,而是“是否指向容器内真实监听的端口”。Django主服务跑在8000,健康端点跑在8081,但containerPort必须显式声明两者,否则K8s网络层压根不通。
- Deployment中
ports字段要列出两个端口:ports: - containerPort: 8000 name: http - containerPort: 8081 name: health
-
livenessProbe和readinessProbe都指向8081,且必须设initialDelaySeconds: 30(Django冷启动耗时长):livenessProbe: httpGet: path: / port: 8081 initialDelaySeconds: 30 periodSeconds: 10 readinessProbe: httpGet: path: / port: 8081 initialDelaySeconds: 30 periodSeconds: 5 - 千万别漏掉
startupProbe——它专治Django加载慢导致的误杀:startupProbe: httpGet: path: / port: 8081 failureThreshold: 30 periodSeconds: 10
安全边界必须卡死的三个地方
即使端点本身无状态,暴露在公网或集群内网仍可能被滥用。Django没做防护时,这个/健康端点会被当成攻击入口试探。
- 在
health.py里加最简校验,拒绝非本地或非K8s Service CIDR的请求(比如只允许10.96.0.0/12):def health_app(environ, start_response): remote_addr = environ.get('REMOTE_ADDR', '') if not remote_addr.startswith(('127.0.', '10.96.')): status = '403 Forbidden' headers = [('Content-type', 'text/plain')] start_response(status, headers) return [b'Forbidden'] # ... rest - 确保
ALLOWED_HOSTS不包含通配符*,且health_app不读取settings.ALLOWED_HOSTS——它必须完全脱离Django配置体系运行 - 镜像构建时删掉
health.py以外所有调试文件,避免被curl http://pod-ip:8081/manage.py之类误扫到
Django健康端点真正的难点不在“写出来”,而在“和主应用彻底解耦”——只要它还依赖django.conf.settings或任意models.py,就随时可能因数据库迁移失败、Redis连接超时而崩掉探针。独立WSGI应用+端口隔离+网络层过滤,才是生产环境敢开的方案。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











