首頁>科技>

背 景

vivo 人工智慧計算平臺小組從 2018 年底開始建設 AI 計算平臺至今,已經在 kubernetes 叢集、以及離線的深度學習模型訓練等方面,積累了眾多寶貴的開發、運維經驗,並逐步打造出穩定的基礎容器平臺 - AI 容器平臺(VContainer)。為了支撐公司 AI 線上業務的發展,滿足公司對算力資源的高效排程管控需求,需要將線上業務,主要包括 C 端、推理等業務,由原來的虛擬機器或物理機遷移至 AI 容器平臺。於是小組從 2020 年初開始,基於線上業務的需求對 AI 容器平臺進行進一步建設,並將平臺與公司的 CMDB、CICD 等基礎模組進行打通,使線上業務能夠順利從虛擬機器、物理機遷移至 AI 容器平臺。

ingress 簡介

kubernetes 將業務執行環境的容器組抽象為 Pod 資源物件,並提供各種各樣的 workload(deployment、statefulset、daemonset 等)來部署 Pod,同時也提供多種資源物件來解決 Pod 之間網路通訊問題,其中,Service 解決 kubernetes 叢集內部網路通訊問題 (東西向流量),Ingress 則透過叢集閘道器形式解決 kubernetes 叢集外訪問 叢集內服務的網路通訊問題 (南北向流量)。

kubernetes Ingress 資源物件定義了叢集內外網路通訊的路由協議規範,目前有多個 Ingress 控制器實現了 Ingress 協議規範,其中 Nginx Ingress 是最常用的 Ingress 控制器。Nginx Ingress 有 2 種實現,其一由 kubernetes 社群提供,另外一個是 Nginx Inc 公司提供的,我們採用是 kubernetes 官方社群提供的 ingress-nginx。

ingress-nginx 包含 2 個元件:ingress controller 透過 watch kubernetes 叢集各種資源(ingress、endpoint、configmap 等)最終將 ingress 定義的路由規則轉換為 nginx 配置;nginx 將叢集外部的 HTTP 訪問請求轉發到對應的業務 Pod。

2.ingress 資源物件:生成 nginx 中 server host、location path 配置,以及根據 ingress annotation 生成服務維度的 nginx 配置;

3.configmap 資源物件:生成全域性維度的 nginx 配置資訊;

4.endpoint 資源物件:根據 Pod IP 動態變更 nginx upstream ip 資訊。

其中針對 ingress nginx upstream 有 2 種路由方案:

方案一:上圖 紅色箭頭所示方案:nginx upstream 設定 Pod IP。由於 Pod IP 不固定,nginx 基於 ngx-lua 模組將實時監聽到的 Pod IP 動態更新到 upstream,nginx 會直接將 HTTP 請求轉發到業務 Pod,這是目前 ingress nginx 預設方案;

方案二:上圖 黑色箭頭所示方案:nginx upstream 設定 Service name。由於 Service name 保持不變,nginx 直接使用 kubernetes 叢集已有的 Service 通訊機制,請求流量會經過 kube-proxy 生成的 iptables,此時 nginx 無需動態更新 upstream,這是 ingress nginx 早期方案。

由於 ngx-lua 動態更新 upstream 這個方案非常穩定,以及 kubernetes Service 透過 iptables 通訊的網路延時、DNAT 和 conntrack 的複雜性,我們採用了方案一。

架 構

kubernetes ingress-nginx 控制器基於 kubernetes 容器化部署,官方提供多種部署方案。

deployment + service 部署方案

方案細節詳見官方文件:

https://kubernetes.github.io/ingress-nginx/deploy/baremetal/#over-a-nodeport-service。

此方案中 ingress 控制器透過 deployment 部署,透過 NodePort service 暴露服務,公司接入層 Nginx 透過 NodePort 訪問叢集內 ingress 服務,每個業務都獨立部署完全隔離的 ingress 叢集。

方案優點:每個服務可以獨立部署 ingress 叢集,ingress 叢集彼此完全隔離。

方案缺點:

(1)接入層 Nginx 需要透過 NodePort 方式訪問叢集內 ingress 服務,NodePort 會繞經 kubernetes 內部的 iptables 負載均衡,涉及 DNAT、conntrack 核心機制,存在埠資源耗盡風險,網路延時較高並且複雜性非常高,穩定性較差。

(2)每個業務都獨立部署 ingress 叢集,由於 ingress 必須高可用部署多副本,由此在部署諸多長尾小業務時會極大浪費叢集資源。

daemonset + hostNetwork 部署方案

方案細節詳見官方文件:

https://kubernetes.github.io/ingress-nginx/deploy/baremetal/#via-the-host-network

此方案中 ingress 控制器透過 daemonset 部署,並且採用宿主機網路,每個 node 機器只部署 1 個 ingresspod,並且 ingress pod 獨佔 node 整機資源。

方案優點:

(1)ingress pod 使用宿主機網路,網路通訊繞過 iptables、DNAT、conntrack 眾多核心元件邏輯,網路延時低並且複雜性大為降低,穩定性較好;

(2)多業務共享 ingress 叢集,極大節省叢集資源。

方案缺點:

(1)多業務共享 ingress 叢集,由於隔離不徹底可能會互相干擾造成影響;

(2)ingress daemonset 部署需要提前規劃 node 節點,增刪 ingress nginx 控制器之後需要更新接入層 Nginx upstream 資訊。

經過仔細考量和對比兩種部署方案之後,我們採用方案二,針對方案二存在的兩個問題,我們提供了針對性解決方案。

針對缺點 1,我們提供了 ingress 叢集隔離方案,詳見 3.3 章節。

針對缺點 2,我們提供 ingress 一鍵部署指令碼、並且結合公司接入層 Nginx 變更工單實現流程自動化。

ingress 多叢集隔離部署

kubernetes 叢集部署業務時,某些場景下需要將 ingress 隔離部署,例如預發環境和生產環境的業務流量需要嚴格區分開,某些大流量業務、重保核心業務也需要進行隔離。

ingress nginx 官方提供多叢集部署方案,詳見官方文件:

https://kubernetes.github.io/ingress-nginx/user-guide/multiple-ingress/。

事實上,多業務共享 ingress 叢集的原因在於 ingresscontroller 在監聽 kubernetes apiserver 時 拉取了所有 ingress 資源物件,沒有做過濾和區分。所幸 ingress nginx 官方提供“kubernetes.io/ingress.class”機制將 ingress 資源物件進行歸類。

最終達到的效果是:

ingress controllerA 僅僅只 watch"屬於業務 A 的 ingress 資源物件"

nginx controllerB 僅僅只 watch “屬於業務 A 的 ingress 資源物件”

我們在 3.2 章節 daemonset + hostNetwork 部署方案的基礎之上,結合 ingress 叢集隔離部署方案,最終實現了 ingress 叢集的高可用、高效能、簡單易維護、特殊業務可隔離的部署架構。

效能最佳化

ingress-nginx 叢集作為 kubernetes 叢集內外通訊的流量閘道器,需要最佳化效能滿足業務需求,我們在 nginx 和核心配置層面做了相應的最佳化工作。

宿主機中斷最佳化

ingress-nginx 物理機執行 top 命令發現每個 CPU 的 si 指標不均衡,針對此問題 我們開啟了網絡卡多佇列機制 以及中斷最佳化。

開啟網絡卡多佇列:

ethtool -l eth0  // 檢視網絡卡可以支援的多佇列配置ethtool -L eth0 combined 8 // 開啟網絡卡多佇列機制

中斷打散最佳化:

service irqbalance stop  // 首先關閉irqbalance系統服務sh set_irq_affinity -X all eth0  // Intel提供中斷打散指令碼:https://github.com/majek/ixgbe/blob/master/scripts/set_irq_affinity

我們針對核心引數也做了最佳化工作以提升 nginx 效能,主要基於 nginx 官方提供的效能最佳化方案:https://www.nginx.com/blog/tuning-nginx/

(1)調整連線佇列大小

nginx 程序監聽 socket 套接字的 連線佇列大小預設為 511 ,在高併發場景下 預設的佇列大小不足以快速處理業務流量洪峰,連線佇列過小會造成佇列溢位、部分請求無法建立 TCP 連線,因此我們調整了 nginx 程序連線佇列大小。

sysctl -w net.core.somaxconn=32768

nginx 程序充當反向代理時 會作為客戶端與 upstream 服務端建立 TCP 連線,此時會佔用臨時埠,Linux 預設的埠使用範圍是 32768-60999,在高併發場景下,預設的源埠過少會造成埠資源耗盡,nginx 無法與 upstream 服務端建立連線,因此我們調整了預設埠使用範圍。

sysctl -w net.ipv4.ip_local_port_range="1024 65000"

nginx 程序充當反向代理時 會作為服務端與接入層 nginx 建立 TCP 連線,同時作為客戶端與 upstream 服務端建立 TCP 連線,即 1 個 HTTP 請求在 nginx 側會耗用 2 條連線,也就佔用 2 個檔案描述符。在高併發場景下,為了同時處理海量請求,我們調整了最大檔案描述符數限制。

sysctl -w fs.file-max=1048576

nginx 程序充當反向代理時 會作為客戶端與 upstream 服務端建立 TCP 連線,連線會超時回收和主動釋放,nginx 側作為 TCP 連線釋放的發起方,會存在 TIME_WAIT 狀態的 TCP 連線,這種狀態的 TCP 連線會長時間 (2MSL 時長) 佔用埠資源,當 TIME_WAIT 連線過多時 會造成 nginx 無法與 upstream 建立連線。

處理 TIME_WAIT 連線通常有 2 種解決辦法:

net.ipv4.tcp_tw_reuse:複用TIME_WAIT狀態的socket用於新建連線net.ipv4.tcp_tw_recycle:快速回收TIME_WAIT狀態連線

由於 tcp_tw_recycle 的高危性,4.12 核心已經廢棄此選項,tcp_tw_reuse 則相對安全,nginx 作為 TCP 連線的發起方,因此啟用此選項。

sysctl -w net.ipv4.tcp_tw_reuse=1

以上核心引數都使用 kubernetes 提供的 initContainer 機制進行設定。

initContainers:

initContainers:      - name: sysctl        image: alpine:3.10        securityContext:          privileged: true        command:        - sh        - -c        - sysctl -w net.core.somaxconn=32768; sysctl -w net.ipv4.ip_local_port_range='1024 65000'; sysctl -w fs.file-max=1048576; sysctl -w net.ipv4.tcp_tw_reuse=1

nginx 連線數

nginx 作為服務端與接入層 nginx 建立 TCP 連線,同時作為客戶端與 upstream 服務端建立 TCP 連線,由此兩個方向的 TCP 連線數都需要調優。

(1)nginx 充當服務端,調整 keep-alive 連線超時和最大請求數

ingress-nginx 使用 keep-alive 選項設定 接入層 nginx 和 ingress nginx 之間的連線超時時間(預設超時時間為 75s)。

使用 keep-alive-requests 選項設定 接入層 nginx 和 ingress nginx 之間 單個連線可承載的最大請求數(預設情況下單連線處理 100 個請求之後就會斷開釋放)。

在高併發場景下,我們調整了這兩個選項值,對應到 ingress-nginx 全域性 configmap 配置。

keep-alive: "75"keep-alive-requests: "10000"

(2)nginx 充當客戶端,調整 upstream-keepalive 連線超時和最大空閒連線數

ingress-nginx 使用 upstream-keepalive-connections 選項 設定 ingress nginx 和 upstream pod 之間 最大空閒連線快取數(預設情況下最多快取 32 個空閒連線)。

使用 upstream-keepalive-timeout 選項 設定 ingress nginx 和 upstream pod 之間的連線超時時間(預設超時時間為 60s)。

使用 upstream-keepalive-requests 選項 設定 ingress nginx 和 upstream pod 之間 單個連線可承載的最大請求數(預設情況下單連線處理 100 個請求之後就會斷開釋放)。

在高併發場景下,我們也調整了這 3 個選項值,使得 nginx 儘可能快速處理 HTTP 請求(儘量少釋放並重建 TCP 連線),同時控制 nginx 記憶體使用量。

upstream-keepalive-connections: "200"upstream-keepalive-requests: "10000"upstream-keepalive-timeout: "100"

閘道器超時

ingress nginx 與 upstream pod 建立 TCP 連線並進行通訊,其中涉及 3 個超時配置,我們也相應進行調優。

proxy-connect-timeout 選項 設定 nginx 與 upstream pod 連線建立的超時時間,ingress nginx 預設設定為 5s,由於在 nginx 和業務均在內網同機房通訊,我們將此超時時間縮短到 1s。

proxy-read-timeout 選項 設定 nginx 與 upstream pod 之間讀操作的超時時間,ingress nginx 預設設定為 60s,當業務方服務異常導致響應耗時飆漲時,異常請求會長時間鎖住 ingress 閘道器,我們在拉取所有服務正常請求的 P99.99 耗時之後,將閘道器與 upstream pod 之間讀寫超時均縮短到 3s,使得 nginx 可以及時掐斷異常請求,避免長時間被夯住。

proxy-connect-timeout: "1"proxy-read-timeout: "3"proxy-send-timeout: "3"

如果某個業務需要單獨調整讀寫超時,可以設定

ingress annotation(nginx.ingress.kubernetes.io/proxy-read-timeout 和 nginx.ingress.kubernetes.io/proxy-send-timeout)進行調整。

穩定性建設

ingress-nginx 作為 kubernetes 叢集內外通訊的流量閘道器,而且多服務共享 ingress 叢集,因此 ingress 叢集必須保證極致的高可用性,我們在穩定性建設方面做了大量工作,未來會持續提升 ingress 叢集穩定性。

健康檢查

接入層 nginx 將 ingress nginx worker IP 作為 upstream,早期接入層 nginx 使用預設的被動健康檢查機制,當 kubernetes 叢集某個服務異常時,這種健康檢查機制會影響其他正常業務請求。

upstream ingress-backend {    server 10.192.168.1 max_fails=3 fail_timeout=30s;    server 10.192.168.2 max_fails=3 fail_timeout=30s;}

如果某個業務 A 出現大量 HTTP error,接入層 nginx 在預設的健康檢查機制之下會將 ingress nginx 例項遮蔽,但是此時業務 B 的請求是正常的,理應被 ingress nginx 正常轉發。

針對此問題,我們配合接入層 nginx 使用 nginx_upstream_check_module 模組來做主動健康檢查,使用 /healthz 介面來反饋 ingress-nginx 執行的健康狀況,這種情況下接入層 nginx 不依賴於實際業務請求做 upstream ip 健康檢查,因此 kubernetes 叢集每個業務請求彼此獨立,不會互相影響。

upstream ingress-backend {      server 10.192.168.1 max_fails=0 fail_timeout=10s;      server 10.192.168.2 max_fails=0 fail_timeout=10s;      check  interval=1000 rise=2 fall=2 timeout=1000 type=http default_down=false;      check_keepalive_requests 1;      check_http_send "GET /healthz HTTP/1.0\r\n\r\n";      check_http_expect_alive http_2xx;      zone ingress-backend 1M;}

無損釋出

ingress nginx 社群活躍,版本迭代較快,我們也在持續跟蹤社群最新進展,因此不可避免涉及 ingress nginx 自身的部署更新。在 ingress nginx 控制器部署更新的過程中必須保證流量完全無損。

接入層 nginx 基於 openresty 開發了 upstream 動態註冊和解綁介面,在此之上,我們結合 kubernetes 提供的 Pod lifecycle 機制,確保在解綁 ingress nginx ip 之後進行實際的更新部署操作,確保在 ingress nginx 新例項健康檢查透過之後動態註冊到接入層 nginx。

webhook 校驗配置

ingress nginx 提供 main-snippet、http-snippet 和 location-snippet 機制使得上層應用可以自定義官方仍未支援的 nginx 配置,但是預設情況下,ingress nginx 不會校驗這些自定義配置的正確性,如果某個應用自定義了錯誤的 nginx 配置,nginx 讀取該錯誤配置之後 reload 操作會失敗,控制器 pod 會一直 crash 並不斷重啟。由於一個應用使用了錯誤的 ingress 配置導致整個 ingress 叢集受損,這種問題非常嚴重,所幸 ingress nginx 官方提供了相應的 vadatingwebhook 來主動校驗應用的 ingress 配置,如果配置錯誤就直接攔截,由此保護了 ingress 叢集的穩定。

containers:      - args:        - --validating-webhook=:9090        - --validating-webhook-certificate=/usr/local/certificates/validating-webhook.pem        - --validating-webhook-key=/usr/local/certificates/validating-webhook-key.pem

監控告警

ingress nginx 官方提供 grafana 模板來做視覺化監控,基於官方提供的監控指標,我們使用內部的告警平臺配置了 ingress 相關的告警項,包括 ingress controller HTTP 請求成功率、響應延時、CPU 使用率、記憶體使用率、Pod 狀態異常、Pod 重啟、Reload 失敗等。基於這些監控告警項,會第一時間獲知 ingress 叢集的執行狀態,並迅速排查和解決問題。

最佳化重試機制

nginx 提供了預設的 upstream 請求重試機制,預設情況下,當 upstream 服務返回 error 或者超時,nginx 會自動重試異常請求,並且沒有重試次數限制。由於接入層 nginx 和 ingress nginx 本質都是 nginx,兩層 nginx 都啟用了預設的重試機制,異常請求時會出現大量重試,最差情況下會導致叢集閘道器雪崩。我們和接入層 nginx 一起解決了這個問題:接入層 nginx 必須使用 proxy_next_upstream_tries 嚴格限制重試次數,ingress nginx 則使用 proxy-next-upstream="off"直接關閉預設的重試機制。

總結展望

vivo AI 計算平臺 kubernetes 叢集 ingress 閘道器目前承擔了人工智慧 AI 業務的大部分流量,隨著業務不斷容器化部署,ingress 閘道器需要在功能豐富性、效能、穩定性方面進一步提升。展望後續工作,我們計劃在以下方面著手,進一步完善 ingress 閘道器。

服務限流

支援 grpc

目前有個別業務希望直接暴露 GRPC 服務供其他服務呼叫,但是 ingress nginx 只支援 GRPCS(grpcbase tls-http2),這其中帶來了證書分發和管理的複雜性、加解密的效能損耗,而且內網可信環境下 普通業務無需加密通訊,因此需要支援 GRPC(grpc base plaintext http2(non-TLS)),社群也迫切希望具備這個能力,相關的 issue 和 PR 數不勝數,但是均被專案創始人回絕了,主要是目前 ingress nginx 不能以 ingress 維護開啟 http2 特性 以及現有機制無法自動探測和分發 HTTP1.x 和 HTTP2 流量。

日誌分析

ingress nginx 預設日誌方案是標準的雲原生方式,還不具備日誌收集以及日誌分析運營的能力。如果 ingress nginx 日誌能對接公司內部的日誌中心(日誌檔案落盤並由 agent 程式收集至統一的日誌中心),那麼 ingress 叢集的排錯易用性、服務質量分析運營能力會大幅提升。鑑於 ingress 叢集流量巨大、日誌檔案落盤的效能損耗、日誌中心單服務的寫入限制,我們會謹慎推進這項工作,一切工作都需要在保證 ingress 叢集的穩定性前提之下有序開展。

- END -

12
最新評論
  • 整治雙十一購物亂象,國家再次出手!該跟這些套路說再見了
  • 未來智慧演算法將控制世界?大量的人將被淘汰?專家說:不會