问题描述
最近,我们在 GKE 上运行的 Kubernetes 部署遇到了问题。 看起来,随机地,我们的 Nginx 前端容器,服务于我们的前端应用程序,似乎死了。这引起了相当大的骚动,因为 nginx-ingress 只会告诉我们存在 HTTP2 协议错误。经过大约一周的混乱,我们终于在 FE 容器的日志中注意到,每当我们遇到 HTTP2 协议错误(在 chrome 中)时都会发生这种输出:
一旦我们将 nginx-ingress 切换到 HTTP1,我们就会得到 ERR_CONTENT_LENGTH_MISMATCH 200 的错误,但这仍然是一个误导性错误。
这是我们所有配置的要点,供感兴趣的人使用: gist
至于Nginx版本,我尝试了以下:
稳定的高山
稳定
mainline-alpine
1.17.10-高山
所有结果都在同一组日志中。
我尝试过的事情:
- 更改 FE 的 Nginx 版本
- 告诉 nginx-ingress 使用 HTTP 1
- 告诉 nginx-ingress 不要使用 GZIP
- 使用了本网站的所有内容tencent high availability in nginx blog post
- 为整个 nginx-ingress 和每个单独的 child-ingress 打开和关闭代理缓冲
- 在 nginx-ingress 中将 max-temp-file-size 设置为 0
- 在 nginx-ingress 中将 max-temp-file-size 设置为 10M
- 从对上游的请求中删除了接受编码、内容长度和内容类型
- 为 FE 容器启用 Gzip
- 将工作进程设置为自动,将 FE 容器中的工作进程设置为 1
- 将 keepalive-timeout 设置为 65,在 FE 容器中将其设置为 15
- 更新了 FE 部署上的生命周期 preStop
- 从 FE 部署中将 terminateGracePeriodSeconds 设置为 60(然后将其删除)
在任何人问之前:到目前为止,对 nginx-ingress 所做的所有配置都是为了解决 HTTP2 协议错误。显然没有这些工作的,因为如果上游服务器关闭,这并不重要。
我可以推断出,虽然 Nginx 正在关闭(为什么,我仍然不知道),但容器本身并没有重新启动,因此该 pod 实际上是一个僵尸案例。我如何 A. 强制重启或 B. 强制 Pod 死亡并重生?
当然,如果有人首先回答为什么 Nginx 容器被告知关闭,那也会有帮助。
另一个可能相关的问题是,有时此部署的副本无法启动,容器已准备就绪,但没有日志或连接。
手动杀死 Pod,似乎可以解决此问题,但这不是真正的解决方案。
集群正在运行 n1-standard-2 个节点,并且我们启用了自动缩放,因此 cpu/内存/存储不是(不应该是,永远不要说永远)问题。
提前致谢!如果我可以改进这个问题,请发表评论。
编辑 #1:包括我们在 GKE 上。
编辑 #2:我添加了就绪和活跃度探测器。我已经使用健康检查路由更新了 Nginx FE 服务器。这似乎起到了故障保护的作用,以确保如果内部 Nginx 进程停止或什至没有启动,容器将重新启动。但是,如果有人有更好的选择或根本原因,我很想知道!也许我应该为每个 Pod 设置特定的 cpu 和内存请求?
解决方法
我建议您做的是将图像本身从 apine 更改为 debian,或者将 nginx 从 1.17 更新到更新版本。我记得很多人在这张 alpine expecialy 图像中的网络问题中挣扎着反应应用程序和 php 应用程序。如果您正在使用 supervisord 运行 nginx 来检查该子进程或 stopasgroup 等上的停止信号,我建议您做的另一件事是,因为该配置有几个配置。 另一件可能改进的事情是正常关闭,您可以在 deployment.yml 中这样做 如果您遇到系统 OOM,还要检查 kubernetes 事件。
,好的!经过大量反复试验和错误,这最终对我们有用。
1.我们在前端/上游 nginx 容器和部署中设置了活跃度和准备情况探测。
nginx.conf
# default server block
location /healthz {
auth_basic off;
allow all;
return 200;
}
deployment.yaml
#spec.containers
livenessProbe:
failureThreshold: 1
httpGet:
path: /healthz
port: 80
readinessProbe:
failureThreshold: 3
httpGet:
path: /healthz
port: 80
毫无疑问,这确保了当我们的 nginx 容器失败时,无论出于何种原因,它们都会重新启动。
2.我们为 nginx 容器设置了平滑关闭
deployment.yaml
#spec.containers
lifecycle:
preStop:
exec:
command: ["/bin/sh","-c","sleep 3; nginx -s quit; while pgrep -x nginx; do sleep 1; done"]
这确保了当重启发生时,nginx 容器实际上关闭而不会丢失连接(尽管公平地说,这种情况很少见)。
3.最低 CPU 和内存请求
有了上述两个变化,感觉我们处于一个很好的位置。在此之前,我们将每个前端部署扩展到 3 个副本。仅仅一个晚上之后,似乎是随机选择的,几个 FE 部署中的一些副本(我们已经设置了几个用于测试,dev-1 到 dev-20)重新启动了 800 次!疯了!
我们不知道是什么导致了这种情况。但一时兴起,我们最终添加了一些最小 CPU 和内存请求,最终解决了重启/失败循环。
# spec.containers
resources:
requests:
cpu: 0.001
memory: 4M
最后的笔记
从上面可以看出,我们确实在现有的部署设置中添加了大量配置和工作。但这些都与 Ingress 无关,而是我们所有的上游 nginx 容器。
活性探测非常严格,因为一旦容器开始起作用,该容器就结束了。我们没有看到容器自行修复,所以一旦活跃度探测器发现健康检查失败,我们就可以强制重启。
preStop 命令大致是 kubernetes 文档中关于 preStop 命令的推荐为 nginx 的默认命令,如 here 所示。在另一个 stackOverflow question 中找到了 sleep 3,而在这个 medium article 中使用了 pgrep。这绝对是一个拼凑而成的停止命令,但它似乎对我们有用。
最后,我们遇到的最小 CPU 请求和内存请求基于我们所有 FE 部署的指标。 0.001 CPU 是高负载时的平均使用率,4M 是高负载时的平均内存使用率。认为最好在这里对冲下注并使用比我们需要的略多的 CPU/内存(尽管显然这个 CPU 使用率很小)。
我不完全确定为什么设置请求最终解决了重启问题。如果我不得不猜测,给出请求允许 kubernetes 找到更好的节点来放置这些 pod。也许经常出现故障的 Pod 被塞进了一个或另一个节点的极限,因此任何实际使用都会导致故障。
我希望这对未来的某人有所帮助,或者这可能是我们自己制造的怪物,每个人都已经知道要执行上述所有操作。
祝找到这个的人好运!