Ingress-Nginx-Controller 工作原理

官方文档说明:https://kubernetes.github.io/ingress-nginx/how-it-works/

ingress-nginx 控制器工作原理

Nginx 配置

Ingress-nginx-controller 的目的是构造配置文件 nginx.conf ,主要用途是在配置文件有任何变更后都需要重新加载 Nginx。不过需要特别注意的是,在只有 upstream 配置变更的时候我们不需要重新加载 Nginx(即当你部署的应用 Endpoints 变更时),可以使用 lua-nginx-module 来达到这个目的。

Nginx 模型

通常,一个 Kubernetes 控制器采用 synchronization loop pattern(同步循环模式) 来检查控制器中所需的状态是否已经更新或者需要变更。为了达到这个目的,需要在集群中放入不同的对象来构建模型,特别是 Ingresses、Services、Endpoints、Secrets 以及 Configmaps 来生成反映集群状态时间点的配置文件。

为了从集群获取这些对象,Ingress-nginx-controller 使用了 Kubernetes Informers, 特别是 FilteredSharedInformer。 当一个新的对象添加、修改或者删除的时候,informers 允许通过 回调 针对单个变更进行响应。不过没办法知道一个特定的变更是否会影响最终的配置文件,在每次变更时,Ingress-nginx-controller 都必须基于集群的状态重新构建一个新模型,并将其和当前的模型进行比较

  • 如果新模型等于当前模型,那么 Ingress-nginx-controller 就可以避免生成新的 Nginx 配置并触发重新加载
  • 如果新模型不等于当前模型,Ingress-nginx-controller 就先通过 Endpoints 来检查不同,然后使用 HTTP POST 请求一个新的 Endpoints 列表发送给运行 Nginx 中的 Lua 程序并且避免重新生成一个新的 Nginx 配置以及触发重新加载。
  • 如果运行的模型和当前的差异不仅仅是 Endpoints,Ingress-nginx-controller 则基于新的模型创建一个新的 Nginx 配置文件,替代当前的模型并触发一次重新加载。

该模型的用途之一是当状态没有变化的时候避免不必要的重新加载,并检测定义中的冲突。 生成的 Nginx 配置最终是从一个 Go template 生成的,使用新模型作为这个模板所需要的变量输入。

构建 Nginx 模型

建立模型是一项成本较高的操作,所以必须使用同步循环,通过使用 work queue,可以不丢失变更并通过使用 sync.Mutex 移除来强制执行一次同步循环,此外还可以在同步循环的开始和结束之间创建一个时间窗口,从而允许 Ingress-nginx-controller 摒弃不必要的更新。 重要的是要理解,集群中的任何变更都会生成事件,然后 informer 会发送给控制器,这也是使用 world queue 的原因之一。

建立模型的操作方式:

  • 通过 CreationTimestamp 字段对 Ingress 规则排序,即先创建的规则优先;
  • 如果相同 host 的相同路径被多个 Ingress 定义,那么先创建的规则优先;
  • 如果多个 Ingress 包含相同 host 的 TLS 部分,那么闲创建的规则优先;
  • 如果多个 Ingresses 定义了 一个 annotation 影响到 Server 块配置,那么先创建的规则优先;
  • 创建一个 Nginx Servers 列表(按主机名)
  • 创建一个 Nginx Upstreams 列表
  • 如果多个 Ingresses 定义了同一个 host 的不同路径,Ingress 控制器会合并这些规则
  • Annotation 被应用于这个 Ingress 的所有路径
  • 多个 Ingresses 可以定义不同的 annotations,这些定义不会在 Ingresses 之间共享

重新加载

下面描述了一些需要重新加载的场景:

  • 新的 Ingress 资源创建
  • 添加 TLS 部分到现有的 Ingress
  • 变更 Ingress annotations 不仅仅影响 upstream 配置
  • 从 Ingress 中添加/移除路径
  • 一个 Ingress、Service、Secret 被移除
  • 一些 Ingress 缺少可用的引用对象时,比如 Service 或者 Secret
  • Secret 对象被更新

避免重新加载

在某些情况下,可以避免重新加载,尤其是在 Endpoints 发生变化的时候,如 Pod 启动或者被重建时。完全移除重新加载这超过了 Ingress 控制器的范围,这将需要大量的工作,并且有时没有任何意义。 仅当 Nginx 变更了读取新配置的方式时,才会进行变更,基本上,新的更改不会替代工作进程。

避免 Endpoints 变更时重新加载

在每个 endpoint 对象变更时,控制器从所有能看到的服务上获取 endpoints 并生成相应的后端对象。然后将这些对象发送给运行在 Nginx 内部的 Lua 处理程序。Lua 程序将这些后端对象存储在共享内存区域,然后对于在 balancer_by_lua 上下文运行的每个请求,Lua 代码检测到 endpoints 选择对应的 upstream 并应用已经配置的负载均衡算法,Nginx 负责其余的工作。

这样,Ingress-nginx-controller 可以避免在 endpoint 变更时重新加载 Nginx。 注意:这也包括仅影响 Nginx upstream 配置的 annotations 变更。

在频繁部署应用的较大集群中,这个特性可以避免大量的 Nginx 重新加载,否则会影响响应延迟,负载均衡(每次重新加载 Nginx 都会重置负载均衡状态)等等。

避免因错误的配置而中断

因为 Ingress-nginx-controller 使用 synchronization loop pattern (同步循环模式),它对所有匹配到的对象应用配置。 如果某些 Ingress 对象配置损坏,如 nginx.ingress.kubernetes.io/configuration-snippet 这个 annotation 语法错误,生成的配置变得无效,将不会重新加载,并不考虑其它 Ingress。

为了防止这种情况发生,Ingress-nginx-controller 选择暴露一个 validationg admission webhook server(验证准入 webhook 服务器) 以确保传入的 Ingress 对象可用性。这个 webhook 把传入的 ingress 对象追加到 Ingresses 列表上,生成配置并调用 nginx 以确保配置没有语法错误。


Ingress-Nginx-Controller 工作原理
http://www.qiqios.cn/2022/01/12/ingress-nginx-controller-工作原理/
作者
一亩三分地
发布于
2022年1月12日
许可协议