V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
eephee
V2EX  ›  Kubernetes

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

  •  
  •   eephee · 2 天前 · 1189 次点击

    背景

    如题,我目前在 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

    38 条回复    2024-11-22 17:01:28 +08:00
    XiLingHost
        1
    XiLingHost  
       2 天前
    有试过直接用 ingress 而非自己部署 nginx 服务进行服务的暴露吗
    rrfeng
        2
    rrfeng  
       2 天前
    watch service 写 upstream 然后 reload nginx ,这是最简单的方法。
    eephee
        3
    eephee  
    OP
       2 天前
    @XiLingHost

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

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

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

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

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

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

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

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

    另外,想请教一下 "一致性哈希以达到 对于特定的 URL 的请求,固定转发到唯一的副本。" 这个需求,在扩容或缩容之后,如何保证之前的请求,依然分流到之前的副本?
    eephee
        21
    eephee  
    OP
       1 天前
    @ser3w 是的这个我也有查到,但是这个只针对单个 url 生效(即 wx_upstream 是一个 url ),无法对 upstream 生效,根据 nginx 的说法的话 https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_pass
    eephee
        22
    eephee  
    OP
       1 天前
    @justdoit123

    > 业务的细节,这样其他人可以给更好的建议

    这个说来话长了,关键词是 协同编辑、websocket 之类的。最开始制定技术方案的时候定下来的这个需求。

    > 如何保证之前的请求,依然分流到之前的副本

    我们目前也在考虑这块问题
    kennylam777
        23
    kennylam777  
       1 天前
    反正都 hardcode 的 nginx config, 即是 3 個副本是固定數量的。

    1 的 504 問題很簡單, 三個副本獨立各自有 ClusterIP 的 service 即可解決問題, ClusterIP 是固定 IP 不會跟隨 Pod IP 變動。


    2 的 hash 問題, 我是用 Istio 解決的, Istio 有自己的 resolving 機制不跟隨 k8s services 做法, 它會自行更新 Pod IP 比較有彈性。
    winglight2016
        24
    winglight2016  
       1 天前
    @eephee 大部分网关都提供 url 对下游服务器的配置,kong 还提供 route by header 功能,我们一般都是用 service 作下游服务,lz 这种指定 pod 方式比较少见,但是依然可以通过 ip 的方式配置。


    @eephee 自己配 nginx 有点难以维护,用现成的网关服务还能通过 API 动态配置
    ser3w
        25
    ser3w  
       1 天前
    @eephee 要是需要 url 亲和性的话我建议你上网关 apisix 之类的,nginx 不好维护, 但如果你把 有状态的 3 副本应用调整为 3 个单副本的 deployment + 3 个 svc 就可以实现你这种,不过有点麻烦了
    justdoit123
        26
    justdoit123  
       1 天前
    @eephee 按你的描述,感觉应该先解决这个分布式有状态扩缩容问题。 然后你这个问题可能就不是什么问题。
    kennylam777
        27
    kennylam777  
       1 天前
    @ser3w 3 個 service 的方法就是我說過的 1, 但問題還是 2 的 load balancing 。

    其實我自己有這種 hash 指定 backend 場景, 解決方法也很簡單, 沒有用多個 service 這麼麻煩, Istio 會參考 service 的配置但不觸及 ClusterIP, 這個我研究過。

    直接上 Istio, EnvoyFilter 用 lua 加一個"x-hash-key"的 HTTP header, 然後在 DestinationRule.spec.trafficPolicy.loadBalancer.consistentHash.httpHeaderName 設成"x-hash-key"就好

    ChatGPT 就能給出代碼細節。
    buffzty
        28
    buffzty  
       1 天前
    我有个服务跟你场景一样, 长链接服务器有 2-n 个,动态变化
    我的做法 一个 ingress+ 多个 service + 一个 statusSet
    比如 a.com/1/ 路由到 a-svc-01 a.com/$n/ 路由到 a-svc-$n
    a-svc-01 指向 a-pod-01, a-svc-$n 指向 a-pod-$n

    我觉得 前面不用放 nginx,也不用 headless service. 你这是把简单问题复杂化了.

    动态更新也很简单,pod 根据 hpa 动态调整, svc ingress 预生成 n 个,不用人为干预
    smallparking
        29
    smallparking  
       1 天前 via Android
    那个 我们公司有个开原的 kic 不确定是不是满足你的需求,可以看看 https://gitee.com/njet-rd/open-njet-kic
    bay1
        30
    bay1  
       1 天前
    我们感觉跟 28 楼差不多,也是一个 StatefulSet 对应 多个 svc ,svc 端口号和 pod index 联动,联动部分是通过 helm 部署直接自动填充的。就可以针对多个 svc 做路由 分发
    nivalxer
        31
    nivalxer  
       1 天前   ❤️ 1
    @eephee 楼主的有状态服务是为了根据$request_uri 进行 hash 做会话亲和,还是有其他特殊业务?

    如果只是会话亲和的话,推荐用 27 楼的方法上 Istio ,也能解决后续扩容缩容的问题。

    同华为云 CCE ,早期在用华为云的 cce ingress ,后面陆续换成 Istio gateway ,配合 EnvoyFilter ,实现了 WebSocket 前期握手( signalr )和后续链接的会话亲和。
    li24361
        32
    li24361  
       1 天前
    楼主还是用传统架构的思维去思考的,k8s 不是这么用的,它本身就带了服务编排。
    最简单的就是使用 service ,service 自己会对应 deployment ,无论增减 pod 都可以自动负载均衡
    复杂的可以参考 27 、31 使用 istio 或者安装自己的 lb
    wenche
        33
    wenche  
       1 天前
    根本原因是:proxy_pass 中如果配置的是域名地址,Nginx 只有在 start / restart / reload 时,才会连接一次域名服务器解析域名,缓存解析的结果,后续则不会根据解析结果的 TTL 进行自动更新。增加一个 svc 就好了
    oaa
        34
    oaa  
       1 天前
    你需要一个 ingress-controller
    https://github.com/alauda/alb
    Serino
        35
    Serino  
       1 天前
    我就是因为 nginx 的 header 搞不定,换了 caddy
    feedcode
        36
    feedcode  
       1 天前
    你要做的是 watch endpointslice, 然后去更新 uppstream, 这不就是 nginx ingress controller 吗,加上 annotations 就是你现在实现的效果
    nginx.ingress.kubernetes.io/upstream-hash-by: "$request_uri"

    对于这个 “至于为什么不用 ingress-nginx ,是因为我们部署在华为云上面,就用了华为云的集群自带的 cce ingress controller 了,而 cce 除了路由好像没有提供更多的功能。” 看来不了解 ingress controller, k8s 里可以安装多个的啊,谁告诉你只能用一个? 你用 ingressClass 区分就好了
    hejw19970413
        37
    hejw19970413  
       1 天前
    @eephee 那你这种服务变动还好,不是很大,如果不频繁重启 pod 的情况下,对现有的架构不做重大的调整,那就 watch 动态变更 nginx 。还有一种方式不知道你可以不,就是根据具体需求建立不同的 service ,然后 watch pod, 不要自动的添加 endpoint 中 IP ,而是要手动更新 endpoint IP ,这样你不用频繁重启 nginx,因为 nginx 中的地址都是 service ip, 通过 service IP 就能动态找到 POD IP 了。这种情况就是针对你这种很少的服务可以,因为无论多少都是固定的。
    n0bug
        38
    n0bug  
       1 天前
    用 envoy 替换 nginx 就行,我做过类似的场景,当时 PoC 就发现 nginx 不好做
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2755 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 22ms · UTC 09:38 · PVG 17:38 · LAX 01:38 · JFK 04:38
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.