天天看點

通過阿裡雲K8S Ingress Controller實作路由配置的動态更新

簡介

在Kubernetes叢集中,Ingress作為叢集内服務對外暴露的通路接入點,其幾乎承載着叢集内服務通路的所有流量。我們知道,Nginx Ingress Controller是Kubernetes社群很重要的一個子項目,其内部主要依托于高性能的負載均衡軟體Nginx,将Kubernetes Ingress資源對象實時地自動化轉換為Nginx配置規則來對外提供期望的授權通路入口。

現實問題

當随着Kubernetes叢集中部署的微服務越來越多,對外暴露的路由規則越來越複雜,服務後端Endpoint變化的越來越頻繁,那麼對應地都會引起Nginx Ingress Controller元件内Nginx配置檔案的變化越來越頻繁;而我們知道,任何一行Nginx配置的變化,都需要Reload Nginx才能生效,這在變化頻率較低的場景下索性還能接受,但在高頻率變化的場景下就會引起Nginx的頻繁Reload。

而Nginx頻繁Reload帶來的問題,這已是一個老生常談的話題了,其問題本質主要還是源于Nginx本身最初的架構設計模型:

通過阿裡雲K8S Ingress Controller實作路由配置的動态更新

一般在Linux伺服器中,我們會配置使用Nginx的EPOLL多程序模式;當我們修改了Nginx配置檔案後,需要通過

nginx -s reload           

指令來重新熱加載新的Nginx配置規則;

當Nginx Master程序接收到reload signal後,其會從指定路徑重新加載新的Nginx配置檔案内容,并校驗配置規則的有效性,若檢驗為有效的配置檔案,則會依據新的配置檔案中的worker_processes值fork出指定數量的新的Nginx Worker程序,此時新fork出來的子程序完全繼承了父程序的記憶體資料ngx_cycle(其包含了新的解析後的Nginx配置規則),同時将配置中的每一個Listen Socket FD注冊到核心的EPOLL事件監聽中,此時這些新的Nginx Worker程序可以接收處理來自用戶端的請求;

同時Nginx Master程序會發送QUIT signal通知老的Nginx Worker程序平滑退出,當老的Nginx Worker程序接收到QTUI信号後,将其之前注冊到EPOLL中的監聽Event移除,至此不再接收處理新的用戶端請求,并依據老配置檔案中設定的worker_shutdown_timeout值來設定定時器,然後繼續處理完已接收的用戶端請求;若在worker_shutdown_timeout之前處理完已有的用戶端請求,則自動退出,若未處理完,則被強制Kill退出,此時就會導緻該用戶端請求響應異常。

是以,對于在高頻率變化的場景下,Nginx頻繁Reload會帶來較明顯的請求通路問題:

  1. 造成一定的QPS抖動和通路失敗情況
  2. 對于長連接配接服務會被頻繁斷掉
  3. 造成大量的處于shutting down的Nginx Worker程序,進而引起記憶體膨脹

動态更新

為緩解Nginx頻繁Reload帶來的影響,我們需要通過動态更新的方式來加載Nginx配置規則,即在不Fork新Nginx Worker程序的情況下來實時更新已加載到記憶體中的Nginx配置規則。

首先我們看下Nginx的配置檔案樣式,主要包含下面幾部配置設定置章節:

# 1. main configuration
daemon off;
worker_processes 4;

events {
  # 2. event configuration
  multi_accept        on;
  worker_connections  1024;
  use                 epoll;
}

http {
  # 3. http main configuration
  access_log /var/log/nginx/access.log;
  error_log  /var/log/nginx/error.log;

  upstream {
    # 4. upstream configuration
    server 0.0.0.1;
  }

  server {
    # 5. server configuration
    server_name _ ;
    listen 80 default_server;

    location / {
      # 6. location configuration
      proxy_pass http://upstream_balancer;
    }
}           

而在Kubernetes叢集中,一個Ingress資源對象主要被解析映射到Nginx的HTTP Main Block、Server Block、Upstream Block和Location Block等章節的配置規則上,是以我們可以将這幾部分頻繁變化的配置内容以Shared Memory的方式統一維持在記憶體中,同時在Ingress Controller内部暴露出管控端口,通過API的方式來實時管理Nginx路由規則配置:

通過阿裡雲K8S Ingress Controller實作路由配置的動态更新

當K8S Ingress Controller監控到叢集内Ingress及相關聯的資源發生變化時,均可通過Internal API将最新的Nginx配置規則推送到統一的共享記憶體配置中,而不用再通過Reload Nginx的方式來使新配置生效,至此當Nginx處理任何新接收的用戶端請求時,都可以基于最新的共享記憶體中的配置進行規則比對和路由轉發;

配置說明

1、目前阿裡雲容器服務Kubernetes叢集中

最新版本

的Nginx Ingress Controller元件預設已開啟Upstream的動态更新,同時支援應用服務的灰階釋出和藍綠釋出功能,具體配置說明可參考

這裡

我們可以通過如下指令來檢視目前共享記憶體中的Nginx Upstream的配置清單:

kubectl -n kube-system exec -it <NGINX-INGRESS-CONOTROLLER-POD-NAME> -- curl http://127.0.0.1:18080/configuration/backends           

2、同時也支援HTTPS證書的動态更新,可通過修改nginx-ingress-controller deployment的如下參數配置來開啟Nginx Ingress Controller的證書動态更新:

- args:
  - /nginx-ingress-controller
  - --configmap=$(POD_NAMESPACE)/nginx-configuration
  - --tcp-services-configmap=$(POD_NAMESPACE)/tcp-services
  - --udp-services-configmap=$(POD_NAMESPACE)/udp-services
  - --annotations-prefix=nginx.ingress.kubernetes.io
  - --publish-service=$(POD_NAMESPACE)/nginx-ingress-lb
  - --enable-dynamic-certificates=true  ### 添加該配置
  - --v=2           

當開啟HTTPS證書的動态更新後,Ingress的TLS證書都統一維護在Nginx的共享記憶體中,我們可通過如下指令來檢視目前共享記憶體中配置的證書清單:

kubectl -n kube-system exec -it <NGINX-INGRESS-CONOTROLLER-POD-NAME> -- curl http://127.0.0.1:18080/configuration/certs           

3、進一步地我們也支援Nginx Server和Location配置的動态更新,可通過修改nginx-ingress-controller deployment的如下參數配置來開啟Nginx Ingress Controller的Server和Location的動态更新:

- args:
  - /nginx-ingress-controller
  - --configmap=$(POD_NAMESPACE)/nginx-configuration
  - --tcp-services-configmap=$(POD_NAMESPACE)/tcp-services
  - --udp-services-configmap=$(POD_NAMESPACE)/udp-services
  - --annotations-prefix=nginx.ingress.kubernetes.io
  - --publish-service=$(POD_NAMESPACE)/nginx-ingress-lb
  - --enable-dynamic-certificates=true  ### 添加該配置
  - --enable-dynamic-servers=true ### 添加該配置,同時也要enable-dynamic-certificates
  - --v=2           

同樣地,當我們開啟了Nginx Ingress Controller的Server動态更新後,所有Nginx Server和Location的配置都統一維護在共享記憶體中,我們同樣可以通過如下指令來檢視目前共享記憶體中的Server配置清單:

kubectl -n kube-system exec -it <NGINX-INGRESS-CONOTROLLER-POD-NAME> -- curl http://127.0.0.1:18080/configuration/servers           

注意說明:當開啟Server的動态更新特性後,部分Ingress Annotation配置暫不支援,正在逐漸優化支援中,相應地您可直接通過

ConfigMap

方式來進行配置;

繼續閱讀