OpenNJet KIC v1.0 发布!K8s Ingress Controller

By 刘琪 2023-12-27

OpenNJet KIC v1.0 发布!K8s Ingress Controller

NGINX 向云原生演进,All in OpenNJet 概述


OpenNJet KIC(K ubernetes Ingress Controller) 基于 OpenNJet proxy 的动态特性、高性能实现。弥补 nginx 在云原生场景中应用的不足。提供了丰富的流量管理能力,如动态 location、host/path 路由、负载均衡、动态 upstream、金丝雀发布、TLS Termination/SNI 等。

本版本主要特性:

  • 支持 Ingress API、支持 path/host 路由
  • 支持自定义资源 VirtualServer,支持 path/host、高级 (header、请求方法等) 路由
  • 支持动态 Upstream
  • 支持 Upstream 负载均衡,支持 round-robin 及 consitent hash 算法
  • 支持 Upstream 主动健康检查
  • 支持 TLS SNI
  • 支持 Prometheus 指标采集

架构图如下:

img

新特性概览

Ingress API

OpenNJet KIC 采用动态 API 方式实现基本路由 / TLS 的变化的更改,当 Ingress 资源变化时,而不需要 reload OpenNJet 配置文件。我们采用单 server 多 location 的方式实现 HTTP host 头匹配,和 path 匹配。如下图所示:

img

当 Ingress 资源中 host 或者 path 变化时,通过动态 location API 来更新 OpenNJet 的配置信息,而不是 reload OpenNJet 配置文件。

当 Ingress 资源中关联的 service 发生改变或者 service 关联的 pod 进行了动态扩缩容时,我们通过动态 Upstream API (目前基于 lua 实现) 来更新 OpenNJet 的配置信息,而不是 reload OpenNJet 配置文件。资源变更应用如下表所述:

资源变化 OpenNJet 配置信息变化方式
Ingress 资源变化 删除新建 Ingress 动态 location API动态 Upstream API
内容 host 动态 location API
path 动态 location API
service 动态 Upstream API
pod 动态扩缩容 endpoint 动态 Upstream API

VirtualServer CR API

VirtualServer 是一个自定义资源,在 OpenNJet KIC 中用来替代 Ingress 资源,是一个替代方案。VirtualServer 除了具备 Ingress 的能力,还提供了更丰富的功能,比如 advanced content-based routing 等,可以灵活的配置匹配策略实现灰度发布。

VS 在处理一个路由时,高级路由匹配由 spec 中的 matches 定义。conditions 在匹配中定义条件,支持 headercookieargumentvariable

以下是一个 VS 的示例:

apiVersion: k8s.njet.org/v1
kind: VirtualServer
metadata:
  name: cafe
  namespace: default
spec:
  host: cafe.example.com.vs
  routes:
  - action:
      pass: details
    matches:
    - action:
        pass: tea-post
      conditions:
      - value: POST
        variable: $request_method
    path: ~* \.html$
  - action:
      pass: ratings
    matches:
    - action:
        pass: productpage
      conditions:
      - cookie: version
        value: v2
      - value: GET
        variable: $request_method
    path: /productpage
  upstreams:
  - name: ratings
    port: 9080
    service: ratings
  - name: productpage
    port: 9080
    service: productpage
  - name: tea-post
    port: 80
    service: tea-post-svc
  - name: details
    port: 9080
    service: details

上图 VS 中,配置了两个路由:

  1. path 为 .html$ 的正则匹配,匹配以.html 结尾的请求,实现高级路由 (请求方法为 POST 的请求被路由到 tea-post upstream,其他请求被路由到 details upstream (默认处理))
  2. path 为 /productpage 的前缀匹配,实现高级路由 (请求方法为 GET 且 cookie 为 version=v2 的请求被路由到 productpage upstream,其他请求被路由到 ratings upstream (默认处理))

实现方式与 Ingress 基本一致。

动态 Upstream

在 Upstream 配置更新方面,OpenNJet KIC 使用 lua 实现 Upstream 动态配置,来应对云原生场景。在云原生场景中,upstrem 变更是常态,比如集群部署了新的服务、某服务进行了动态扩缩容、Pod 被重新调度等,这都导致 upstream 相关配置的变更。

OpenNJet KIC 配置当中会生成一个默认的被称为 “upstream_balancer” 的 upstream,此 upstream 会处理所有路由。当真实流量到来时,会交由内部 lua 上下文处理。“upstream_balancer” 配置如下:

 upstream upstream_balancer {
        ### Attention!!!
        #
        # We no longer create "upstream" section for every backend.
        # Backends are handled dynamically using Lua.
        #
        ###

        server 0.0.0.1; # placeholder

        balancer_by_lua_block {
            balancer.balance()
        }

        keepalive 320;
        keepalive_time 1h;
        keepalive_timeout  120s;
        keepalive_requests 10000;
    }

lua 上下文怎么区分不同流量该由谁处理呢?

首先,OpenNJet KIC 会通过动态 upstream API 接口创建所有 upstream 信息。

其次,每个路由 (location) 都会关联实际处理自己的 upstream 名称。

最后,实际处理流量的 upstream 名称会被传递到 lua 上下文,最终保证流量被正确处理。

路由与 upstream 关联如下所示:

up-b0e7be65e5c23bb09da783dacd9b6abba11.png

Upstream 的更新流程

img

Upstream 负载均衡

Upstream 可以设置对应的负载均衡策略,目前支持默认的 round_robin,及一致性 hash。round_robin 使用轮询的方式获取 peer。一致性 hash 根据配置的 hash key 值来进行负载,相同的 key 值,将始终访问同一个后端 peer。常用的 hash key 有:

hash key 描述
$arg_{VAR} 根据 url 传递的参数 VAR 做一致性 hash
$http_{NAME} 根据 HEADER 传递的参数 NAME 做一致性 hash
$cookie_{NAME} 根据 Cookie 传递的参数 NAME 做一致性 hash
$remote_addr 根据客户端的 IP 做一致性 hash

OpenNJet 可使用的内部变量与 Nginx 一致,可以参考文档:https://nginx.org/en/docs/varindex.html

Ingress 与 VirtualServer CR 都支持 Upstream 负载均衡策略配置。

下面给出一个 VS 配置 Upstream 负载均衡的一个例子:

img

上面的例子通过 lb-method: “chash $arg_uu” 进行显式的声明 Upstream 负载均衡算法为 chash 且以请求携带的参数 uu 为 hash key。

Upstream 主动健康检查

OpenNJet KIC 提供了 Upstream 主动健康检查的能力,确保所有请求都能被健康的上游后端处理,提高用户体验度。

通过 Ingress、VirtualServer CR 配置 Upstream 的主动健康检查, OpenNJet KIC 会通过单独的一个 priviliege agent 进程对 upstream 的各 peer 进行检查, 如果 peer 检查失败,并且失败次数达到预先配置的阈值,健康检查程序会将对应的 peer 从 upstream peer 列表中移除。被移除的 peer, 在之后的检查中如果为健康状态,并达到配置的阈值,将会触发重新上线的操作。

下图为健康检查架构图:

img

  • 更新 Upstream 数据时,生成一份 “raw hc backends” 的副本, 定时器中的健康检查使用此副本中的数据进行。 当健康检查结果需要触发 peers 变更时,更新共享内存中的 upstream backends。
  • 目前健康检查模块的定时器时间间隔是 5 秒。策略中的健康检查间隔需 >=5s 。

下面给出一个 VS 配置主动健康检查的一个例子:

img

img

TLS Termination/SNI

OpenNJet KIC 处理 TLS 流量由内部端口 443 负责,支持 TLS Termination/SNI 功能,根据主机名在同一端口上进行多路复用。

Ingress、VirtualServer CR 都支持 TLS Termination/SNI 配置,且 OpenNJet KIC 支持配置动态更新,不进行 reload,这一能力得益于 OpenNJet 提供的动态 map 能力。host 与证书的对应关系通过动态 map HTTP 接口进行更新。

下面给出一个 VS 配置 TLS 的一个例子:

img

上面的例子配置期望把 vstest.example.coma.test.com 对应的证书进行关联。

Prometheus 指标采集

为了满足用户对业务的监控,OpenNJet KIC 目前提供了 VTS (virtual host traffic status) 指标采集,OpenNJet 使用定制的 vts 模块,采集 upstream 的相关指标。

OpenNJet KIC 容器中的 OpenNJet 进程通过 vts 模块记录 Upstream 的相关指标信息,并且 OpenNJet 提供 HTTP 接口获取 Prometheus 格式的指标信息。

KIC 服务中通过注解 Annotations 声明 Prometheus 指标的采集端口及路径。配置如下:

apiVersion: v1
kind: Service
metadata:
  annotations:
    prometheus.io/port: "12001"
    prometheus.io/scheme: http
    prometheus.io/scrape: "true"
    prometheus.io/path: "/stats"
  name: njet-ingress
  namespace: njet-ingress
spec:
  type: NodePort
  ports:
  - port: 80
    targetPort: 80
    protocol: TCP
    name: http
  - port: 443
    targetPort: 443
    protocol: TCP
    name: https
  selector:
    app: njet-ingress

img

参考链接

[OpenNJet KIC 用户手册](https://gitee.com/njet-rd/docs#/njet-rd/docs/blob/master/zh-cn/OpenNJet-K8s Ingress Controller V1.0-用户使用手册.md)

OpenNJet KIC 源码地址