请教一个在 k8s 里面使用 nginx 代理 headless service 的问题

2024 年 11 月 21 日
 eephee

背景

如题,我目前在 k8s 里面部署了一个 nginx 服务( Deployment )和一个后端服务 backend ( StatefulSet ),backend 创建了三个副本:backend-0, backend-1, backend-2 。

nginx 服务目前充当 API 网关的角色,其核心配置如下:

upstream backend {
    hash $request_uri;
    server backend-0.backend-headless.default.svc;
    server backend-1.backend-headless.default.svc;
    server backend-2.backend-headless.default.svc;
}


server {
   ...
   location /api/ {
       proxy_pass backend;
   }
}

配置说明:nginx 反代 backend 的三个 headless service ,并且使用一致性哈希以达到 对于特定的 URL 的请求,固定转发到唯一的副本。


问题

目前我面临的问题如下:

  1. 当 backend 更新或重启后,nginx 反代会出错,报错 504 (记不太清了应该是 504 )

这是因为 backend 重启之后,Pod 变了,其 headless service 域名对应的 IP 也变了。但是 nginx 默认只会在启动时解析一遍域名,无法做到动态解析(动态解析功能要商业版 nginx 才有),因此 backend 重启之后 nginx 无法与 backend 建立连接,所以报错。

我在网上搜了一圈,我目前是在用这个第三方模块 + 自己编译 nginx ,但是并不喜欢这个做法,而且该项目兼容的 nginx 版本较低。

我在想有没有其他更好的办法,再不行就换 tengine ,但是 tengine 我还没用过也许可以试一试

  1. 无法灵活地增减 backend 的副本数

目前 backend 是三个副本,假如我要增加到 5 个副本,我需要手动修改 nginx 配置里面的 upstream server 。虽然我目前是通过环境变量来配置 upstream server 的,但是仍然无法避免需要手动修改环境变量的麻烦。

如果要解决这个问题,似乎只能自己开发和部署一个专门更新配置的小服务,除此之外还有更方便的解决办法吗?

另外想咨询一下有人知道 Kong 能否在上面两个场景中派上用场吗?我打算抽空也去调研一下 Kong

3393 次点击
所在节点    Kubernetes
41 条回复
KagurazakaNyaa
2024 年 11 月 21 日
有试过直接用 ingress 而非自己部署 nginx 服务进行服务的暴露吗
rrfeng
2024 年 11 月 21 日
watch service 写 upstream 然后 reload nginx ,这是最简单的方法。
eephee
2024 年 11 月 21 日
@XiLingHost

是因为我们有一些 rewrite/hash 之类的需求,所以得用 nginx 来做。

至于为什么不用 ingress-nginx ,是因为我们部署在华为云上面,就用了华为云的集群自带的 cce ingress controller 了,而 cce 除了路由好像没有提供更多的功能。
Pbe2RsrnVcPrF
2024 年 11 月 21 日
1.加就绪探针
2. backend StatefulSet 加一个 service ,proxy_pass 直接配置这个 service 。
3.用 headless service 的用意是?
eephee
2024 年 11 月 21 日
@dropdatabase
> 用 headless service 的用意是?

我们有一个需求,就是 “对于特定的 URL 的请求,需要固定转发到唯一的副本”,如果只用一个 service 的话,就没法达到这个目的
eephee
2024 年 11 月 21 日
@rrfeng 嗯嗯,我也在考虑这个方法,就是感觉有点太耦合了
defunct9
2024 年 11 月 21 日
nginx-dynamic-upstream ,后端服务加 sidecar ,重启第一步就通知 nginx 去刷一下配置。
eephee
2024 年 11 月 21 日
@defunct9 感谢 ssh 哥,我也再考虑考虑这个做法
winglight2016
2024 年 11 月 21 日
@dropdatabase #4 我们也是用类似的方式,还使用了 pre-stop 去调用内部接口触发 spring 的退出。

根据 url 分发到特定 pod ,这个需求很奇怪,可以使用 gateway 做转发规则或者 nacos 的服务发现。
hejw19970413
2024 年 11 月 21 日
如果你是单个服务一对一的配置 nginx 的推荐你用 watch 去重启 nginx ,如果是一对多的情况下不建议这么干,因为 nginx 频繁重启会有问题。目前对于 k8s 来说最好的代理是 envoy ,支持动态配置,只不过就是对接起来有点困难,但是简单的用是可以的。
NoobPhper
2024 年 11 月 21 日
等下 你们的 ingress 是什么, 按理说 这个不用再 套一层的
eephee
2024 年 11 月 21 日
@winglight2016

> 可以使用 gateway 做转发规则或者 nacos 的服务发现

我们就是拿 nginx 做 api gateway 的,所以这一层转发就打算在 nginx 这里做
eephee
2024 年 11 月 21 日
@hejw19970413 不瞒你说,我们有 3 个类似 backend 这样的 StatefulSet 服务,而且有 3 个 nginx 这样的服务。也就是说 3x3=9 的场景...
Pbe2RsrnVcPrF
2024 年 11 月 21 日
@winglight2016 细说根据 url 分发到特定 pod ??

在流量接入层配置按 URL 转发就好了吧
eephee
2024 年 11 月 21 日
@NoobPhper 我们是用的华为云集群的 cce ingress controller ,然后集群内部再用 nginx 做请求分发到各个后端服务
defunct9
2024 年 11 月 21 日
噢,也可以用 openresty 做分发器,lua 读取 redis 的配置往后分发,我们就是这样搞的灰度
ser3w
2024 年 11 月 21 日
@eephee 最简单的方法 改为变量类型的 proxy_pass
resolver <coredns svc ip> valid=5 ipv6=off;

set $wx_upstream "";
set $wx_host "";
location / {
proxy_pass $wx_upstream;
}
nothingLeft
2024 年 11 月 21 日
我不明白,你都用 k8s 了,为什么还用 nginx 的 upstream
defunct9
2024 年 11 月 21 日
ser3w 是正解
justdoit123
2024 年 11 月 21 日
楼主可以再深入描述一下,业务的细节,这样其他人可以给更好的建议。

另外,想请教一下 "一致性哈希以达到 对于特定的 URL 的请求,固定转发到唯一的副本。" 这个需求,在扩容或缩容之后,如何保证之前的请求,依然分流到之前的副本?

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1091538

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX