Nginx Ingress Controller 工作原理



前言

本文目的是闡述 Nginx Ingress 控制器的工作原理,尤其是 NGINX模型 的構建方式以及為什麼需要這個模型。

Nginx Ingress Controller 工作原理

Nginx 配置

Nginx Ingress 控制器的目標是構建(nginx.conf)配置文件。主要含義是在配置文件中進行任何更改後都需要重新加載 Nginx。不過需要特別注意的是,在只有 upstream 配置變更的時候我們不需要重新加載 Nginx(即當你部署的應用 Endpoints 變更時)。我們使用 lua-nginx-module 實現這一目標。請查看下面的內容,以瞭解有關操作方法的更多信息。

Nginx 模型

通常,Kubernetes 控制器利用同步循環模式來檢查控制器中所需的狀態是否已更新或需要更改。為此,我們需要使用集群中放入不同對象來構建模型,特別是 Ingresses、Services、Endpoints、Secrets 以及 Configmaps 來生成反映集群狀態時間點的配置文件。

為了從集群中獲取該對象,我們使用了Kubernetes Informers,尤其是 FilteredSharedInformer。當一個新的對象添加、修改或者刪除的時候,informers 允許通過 回調 針對單個變更進行響應。不幸的是,沒有辦法知道特定的更改是否會影響最終的配置文件。因此,每次更改時,我們都必須根據集群的狀態從頭開始重建一個新模型,並將其與當前模型進行比較。如果新模型等於當前模型,那麼我們避免生成新的Nginx配置並觸發重新加載。否則,我們就通過 Endpoints 來檢查不同,然後使用 HTTP POST 請求一個新的 Endpoints 列表發送給運行在 Nginx 中的 Lua 程序並且避免重新生成一個新的 NGINX 配置以及觸發重新加載。如果運行的模型和當前的差異不僅僅是 Endpoints,我們則基於新的模型創建一個新的 NGINX 配置文件,替代當前的模型並觸發一次重新加載。

該模型的用途之一是在狀態沒有變化時避免不必要的重新加載,並檢測定義中的衝突。

Nginx 配置的最終表示是從 Go template 生成的,使用新模型作為模板所需變量的輸入。

構建 Nginx 模型

建立模型是一項昂貴的操作,因此,必須使用同步循環。通過使用工作隊列,可以不丟失變更並通過 sync.Mutex 移除來強制執行一次同步循環,此外,還可以在同步循環的開始和結束之間創建一個時間窗口,從而允許我們丟棄不必要的更新。重要的是要理解,集群中的任何變更都會生成事件,然後 informer 會發送給控制器,這也是使用 工作隊列 的原因之一。

建立模型的操作方式:

  • 按 CreationTimestamp 字段對 Ingress 規則排序,即先創建的規則優先。
  • 如果在多個 Ingress 中為同一主機定義了相同路徑,則先創建的規則優先。
  • 如果多個 Ingress 包含同一 host 的 TLS 部分,則先創建的規則優先。
  • 如果多個 Ingress 定義了一個 annotation 影響到 Server 塊配置,則先創建的規則優先。
  • 創建一個 NGINX Servers 列表(按主機名)。
  • 創建一個 NGINX Upstreams 列表。
  • 如果多個 Ingres Servers 定義了同一個 host 的不同路徑,則 Ingress 控制器將合併這些規則。
  • Annotations 被應用於這個 Ingress 的所有路徑。
  • 多個 Ingress 可以定義不同的 Annotations。這些定義在 Ingress 之間不共享。

重新加載

下面的列表描述了需要重新加載的情況:

  • 已創建新的 Ingress 資源。
  • TLS 部分已添加到現有 Ingress。
  • Ingress annotations 的更改不僅僅影響 upstream 配置。例如,load-balance annotation 不需要重新加載。
  • 已從 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 負責其餘的工作。這樣,我們避免在 endpoints 更改時重新加載 Nginx。請注意,這也包括僅影響 Nginx upstream 配置的 annoations 變更。

在具有頻繁部署應用程序的相對較大的群集中,此功能可以節省大量 Nginx 重載,否則會影響響應延遲,負載平衡質量(每次重載Nginx 都會重置負載平衡狀態)等等。

避免因錯誤的配置而中斷

因為 ingress nginx 使用同步循環模式工作,所以它將配置應用於所有匹配的對象。如果某些Ingress 對象的配置損壞,例如


nginx.ingress.kubernetes.io/configuration-snippet annotation 中的語法錯誤,則生成的配置將變得無效,不會重新加載,因此將不再考慮其他 Ingress。

為了防止這種情況的發生,nginx ingress 控制器可以選擇公開一個驗證的 准入Webhook服務器,以確保傳入的ingress對象的可靠性。此 Webhook 將傳入的 ingress 對象追加到 ingresses 列表中,生成配置並調用 nginx 以確保配置沒有語法錯誤。


分享到:


相關文章: