【k8s】web服务优雅关闭用户连接
1. 背景
在 Kubernetes 中,如果直接滚动更新或重启 Pod,默认行为是:
- K8s 会先发送
SIGTERM
给容器。 - 如果应用没处理这个信号,Django 进程(通常运行在 gunicorn/daphne/uwsgi 等 WSGI/ASGI server 下)会立刻被杀掉,正在处理的请求会被中断。
因此,我们需要:
- 在 K8s 配置层面,让 Pod 先摘流量,再关机。
- 在应用层面(Django + Gunicorn/Uvicorn),优雅处理 SIGTERM,等现有请求完成再退出。
2. Kubernetes 配置
一个推荐的 Deployment 片段:
apiVersion: apps/v1
kind: Deployment
metadata:name: django-app
spec:replicas: 5selector:matchLabels:app: django-apptemplate:metadata:labels:app: django-appspec:terminationGracePeriodSeconds: 60 # 给应用 60s 优雅关闭containers:- name: djangoimage: my-django-app:latestports:- containerPort: 8000lifecycle:preStop:exec:command: ["/bin/sh", "-c", "sleep 10"] # 先等 10s,确保从 LB 摘掉readinessProbe:httpGet:path: /healthzport: 8000initialDelaySeconds: 5periodSeconds: 5
解释:
- terminationGracePeriodSeconds: Pod 收到 SIGTERM 后最多等待 60 秒再强制杀死。
- preStop hook: Pod 被删除/更新时,先 sleep 10s,让 kube-proxy/Service 彻底摘掉流量。
- readinessProbe: 当应用返回非 200 时,K8s 不会再将请求调度到这个 Pod。
3. 应用层优雅关闭
3.1 Gunicorn 配置 (WSGI)
如果你用 Gunicorn + Django:
Gunicorn 默认支持 优雅关闭,它在接收 SIGTERM
后:
- 停止接收新请求。
- 等待已有请求完成后退出(受
timeout
参数影响)。
启动示例:
gunicorn myproject.wsgi:application \--bind 0.0.0.0:8000 \--workers 4 \--graceful-timeout 30 \--timeout 60
说明:
--graceful-timeout 30
:收到SIGTERM
后,worker 会等待 30s 完成请求再退出。--timeout 60
:强制超时限制,避免死锁。
3.2 Daphne / Uvicorn (ASGI)
如果你用 Daphne / Uvicorn(常见于 Django Channels 或 ASGI 模式),也要支持优雅关闭。
例如 Uvicorn:
uvicorn myproject.asgi:application \--host 0.0.0.0 --port 8000 \--lifespan=on
Uvicorn 内部会处理 SIGTERM
,自动触发 lifespan.shutdown
事件。
3.3 Django 中的健康检查视图
K8s 的 ReadinessProbe 需要健康检查接口:
# myproject/views.py
from django.http import JsonResponsedef healthz(request):return JsonResponse({"status": "ok"})
在 urls.py
中加:
from django.urls import path
from . import viewsurlpatterns = [path("healthz", views.healthz),
]
当服务准备下线时,可以通过 关闭监听 socket 或让探针返回非 200,使 Pod 迅速从负载均衡中摘掉。
3.4 捕获 SIGTERM(可选增强)
如果你需要 更精细控制(比如管理 WebSocket 长连接),可以在 Django 启动时加一个信号处理器:
# myproject/wsgi.py 或 asgi.py
import signal
import sys
import threadingshutdown_event = threading.Event()def handle_sigterm(*args):print("SIGTERM received, starting graceful shutdown...")shutdown_event.set()# TODO: 这里可以做清理,比如关闭 WebSocket,等待后台任务结束sys.exit(0)signal.signal(signal.SIGTERM, handle_sigterm)
这样你能在 Pod 下线时,做一些自定义处理(比如通知客户端断线重连)。
4. 请求生命周期示意图
5. 总结
为了保证 Pod 重启时连接不中断,需要:
-
K8s 配置
terminationGracePeriodSeconds
preStop
hookreadinessProbe
-
Django 服务层
- Gunicorn/Uvicorn 支持优雅关闭
- 健康检查接口
/healthz
- 可选的 SIGTERM 捕获,处理长连接
这样配置后,即使 Pod 滚动升级,已有请求也能顺利完成,不会被中断。