阿里雲容器服務Kubernetes實踐系列 - Ingress篇

做者:榮濱,酷劃在線後端架構師,關注微服務治理,容器化技術,Service Mesh等技術領域html

開源項目推薦

Pepper Metrics是我與同事開發的一個開源工具(github.com/zrbcool/pep…),其經過收集jedis/mybatis/httpservlet/dubbo/motan的運行性能統計,並暴露成prometheus等主流時序數據庫兼容數據,經過grafana展現趨勢。其插件化的架構也很是方便使用者擴展並集成其餘開源組件。
請你們給個star,同時歡迎你們成爲開發者提交PR一塊兒完善項目。nginx

背景篇

現狀

前面一篇文章主要是落地容器化以前對基礎網絡組件的調研及性能測試,感興趣的同窗請參考:阿里雲開源K8S CNI插件terway網絡性能測試git

目前公司的後端架構基本上是微服務的架構模式,以下圖,全部的入站流量均經過API網關進入到後端服務,API網關起到一個「保護神」的做用,監控着全部進入的請求,並具有防刷,監控接口性能,報警等重要功能,流量經過網關後服務間的調用均爲RPC調用。 github

過渡期

在經歷完微服務改造後,雖然微服務架構給咱們帶來了很多紅利,可是也難免引入一些問題:數據庫

  • 服務拆分致使機器數增多
  • 爲了下降資源浪費,多個服務部署到一臺主機形成的端口管理成本
  • 不一致的交付環境,形成的排查問題成本

爲了解決上述問題,通過調查,咱們將目光鎖定容器服務Kubernetes,以解決咱們的問題,本篇文章主要關注nginx-ingress-controller(後面統一簡稱NGINX IC)部分,因此下面的架構主要突出API網關及IC。 下圖是咱們的過渡期方案: 經過引入一個內網的SLB來解決IC作爲咱們API網關upstream時,服務發現的問題。而且,能夠經過逐步切換接口到SLB上達到漸進式遷移的效果,粒度能夠作到接口+百分比級別。 過渡期間架構圖以下所示:json

終態

所有遷移完成後,全部的機器回收,以下圖所示: 後端

其實兩層SLB是會有浪費的,但因爲咱們的API網關目前承擔着重要的做用,必須找到替代方案才能去掉。 咱們也嘗試調研了用定製IC,或者Service Mesh架構的Istio方案來解決,因爲當時Istio 1.1還沒發佈,而且大規模使用的案例太少,因此咱們保守的選擇了目前的這種折中方案,後續要切換到Istio上也不是很麻煩。

按業務線分組IC

默認Kubernetes全部的流量都由一組默認的IC來承擔,這個方案咱們從一開始就是否認的,因此經過部署多組IC來達到必定的隔離是必要的。最終會是上面圖的那個形態。

實踐篇

如何啓用多IC

查看IC的啓動命令參數,能夠找到: api

其意思是隻關注攜帶annotation爲"kubernetes.io/ingress.class",而且與IC啓動參數參數--ingress-class的值相同的Ingress定義,不符合的會被忽略,這樣就作到了多組IC,Ingress定義之間的隔離。 例如:

containers:
 - args:
 - /nginx-ingress-controller
 - --ingress-class=xwz #看這裏
 - --configmap=$(POD_NAMESPACE)/xwz-nginx-configuration
 - --tcp-services-configmap=$(POD_NAMESPACE)/xwz-tcp-services
 - --udp-services-configmap=$(POD_NAMESPACE)/xwz-udp-services
 - --annotations-prefix=nginx.ingress.kubernetes.io
 - --publish-service=$(POD_NAMESPACE)/xwz-nginx-ingress-lb
 - --v=2
複製代碼

以及網絡

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
 annotations:
    kubernetes.io/ingress.class: "xwz" #看這裏
 labels:
 app: tap-revision-ing
 wayne-app: tap-revision
 wayne-ns: coohua
 name: tap-revision-ing
 namespace: coohua
spec:
 rules:
 - host: staging.coohua.com
 http:
 paths:
 - backend:
 serviceName: tap-revision-stable
 servicePort: 80
 path: /
複製代碼

接下來咱們來看下IC在Kubernetes內部署的資源結構,如圖所示: mybatis

能夠看到一組IC其實由如下幾部分組成:

  • ServiceAccount,ClusterRole,ClusterRoleBinding:權限RBAC定義
  • Deployment:控制controller的部署,並依賴ServiceAccount,Configmap,Service
  • ConfigMap:三個configmap都是保存自定義的controller配置用的
  • Service:這裏使用type爲LoadBalancer的svc主要是利用阿里雲基礎服務實現的自動綁定到SLB實例的功能

咱們的場景是不須要特殊的權限配置的,因此咱們就簡單的把紅框裏面的幾個資源複製一份修改下其中的幾個配置(例如--ingress-class=xwz),而後直接引用默認IC的ServiceAccount,就完成了一組全新IC的部署,而且與Kubernetes自帶的IC是互相隔離的。 這裏我把我寫好的配置放到個人github上,讀者能夠參考:ingress resources

建立好新IC後,若是發現IC有以下錯誤

E0416 11:31:50.831279       6 leaderelection.go:304] Failed to update lock: configmaps "ingress-controller-leader-xwz" is forbidden: User "system:serviceaccount:kube-system:nginx-ingress-controller" cannot update resource "configmaps" in API group "" in the namespace "kube-system"
複製代碼

參考issue,須要修改clusterrole:nginx-ingress-controller,增長以下內容

...
- apiGroups:
 - ""
 resourceNames:
 - ingress-controller-leader-nginx
 - ingress-controller-leader-xwz #將新增長的configmap增長進來,否則會報上面提到的錯誤
 resources:
 - configmaps
...
複製代碼

擴容的IC實例如何自動添加到SLB的後端服務列表中

方式一 externalTrafficPolicy=Cluster

Service的spec.externalTrafficPolicy當爲Cluster的時候: 集羣當中的每臺主機均可以充當三層路由器,起到負載均衡及轉發的做用,可是因爲其對請求包進行了SNAT操做,如圖所示:

這樣致使IC的POD內獲取到的client-ip會是轉發包過來的那個worker節點的IP,因此在這個模式下咱們沒法獲取客戶端的真實IP,若是咱們不關心客戶端真實IP的狀況,可使用這種方式,而後將全部的worker節點IP加入到SLB的後端服務列表當中便可。

方式二 externalTrafficPolicy=Local

Service的spec.externalTrafficPolicy當爲Local的時候: 節點只會把請求轉給節點內的IC的POD,因爲不通過SNAT操做,IC能夠獲取到客戶端的真實IP,若是節點沒有POD,就會報錯。這樣咱們就須要手工維護IC POD,節點,與SLB後端服務之間的關係。那麼有沒有一種方式能夠自動管理維護這個關係呢?實際上是有的,阿里雲容器服務爲咱們作好了這一切,只要在type爲LoadBalancer的Service上增長以下幾個annotation,它就能夠爲咱們將啓動了POD的worker節點的端口及IP自動添加到SLB後端服務當中,擴容縮容自動更新,以下所示(注意咱們使用的是內網SLB,type爲intranet,你們根據實際狀況修改):

metadata:
 annotations:
    service.beta.kubernetes.io/alicloud-loadbalancer-address-type: intranet
    service.beta.kubernetes.io/alicloud-loadbalancer-force-override-listeners: "true"
    service.beta.kubernetes.io/alicloud-loadbalancer-id: lb-2zec8x×××××××××965vt
複製代碼
方式一(Cluster) vs 方式二(Local)
對比 Cluster Local
優勢 簡單,K8S的默認方式 減小網絡轉發,性能好,能獲取客戶端真實IP
缺點 SNAT地址假裝網絡上增長一跳,性能降低,沒法獲取客戶端真實IP 須要對節點的端口是否打開作檢查,須要自定義服務發現(阿里雲這塊已經作了與SLB的集成)

繞坑篇

nginx worker進程數的問題

咱們知道nginx的默認配置worker_processes爲auto的時候,會根據當前主機cpu信息自動計算,可是nginx並非一個cgroups aware的應用,因此其會盲目「自大」的認爲有「好多」cpu能夠用,這裏咱們就須要對其進行指定,能夠在configmap中設置參數:

apiVersion: v1
data:
 worker-processes: "8"
kind: ConfigMap
metadata:
 annotations:
 labels:
 app: ingress-nginx
 name: xwz-nginx-configuration
 namespace: kube-system

複製代碼

內核參數設置

這塊咱們暫時使用默認的deployment當中給定的參數,後續調優的時候會根據狀況調整

initContainers:
- command:
 - /bin/sh
 - -c
 - | sysctl -w net.core.somaxconn=65535 sysctl -w net.ipv4.ip_local_port_range="1024 65535" sysctl -w fs.file-max=1048576 sysctl -w fs.inotify.max_user_instances=16384 sysctl -w fs.inotify.max_user_watches=524288 sysctl -w fs.inotify.max_queued_events=16384 複製代碼

客戶端真實IP問題

小流量灰度期間業務同窗反饋說第三方的反做弊發現咱們調用他們的接口異常,一輪分析下來發現,原來是發給第三方請求中攜帶的客戶端IP被寫爲了咱們API網關的主機IP 還記得前面的架構圖嗎?

出現這個問題的緣由就是咱們的IC被放到了OpenResty後面,查看到IC的template中對X-REAL-IP設置的代碼部分以下:
這個the_real_ip又是哪來的呢?
能夠看到默認狀況下,是從remote_addr這個變量獲取,在咱們的場景下remote_addr就會是API網關的主機IP,因此咱們須要修改其爲API網關爲咱們設置好的名爲X_REAL_IP的header。咱們的作法是將template文件導出修改並以congfigmap的方式掛載進鏡像,覆蓋原來的配置,配置以下:

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  ...
spec:
 template:
 spec:
 containers:
 name: nginx-ingress-controller
        ...
 volumeMounts:
 - mountPath: /etc/nginx/template
 name: nginx-template-volume
 readOnly: true
      ....
 volumes:
 - name: nginx-template-volume
 configMap:
 name: xwz-nginx-template
 items:
 - key: nginx.tmpl
 path: nginx.tmpl
複製代碼

至此問題解決

監控篇 monitoring

在現有的CooHua API網關上,已經有比較詳細的各類指標監控,因爲ingress-controller也暴露了prometheus的metrics,因此也很簡單的直接部署了社區版的dashboard,並對其進行了簡單的修改,以下圖所示:

監控部署的參考文檔奉上: 請點擊我 自定義的dashboard上傳到我我的的github上了: 請點擊我

Troubleshooting篇

dump nginx.conf文件

請參考這篇文章 docs.nginx.com/nginx/admin…

Refs

yq.aliyun.com/articles/69… yq.aliyun.com/articles/64… bogdan-albei.blogspot.com/2017/09/ker… danielfm.me/posts/painl… www.asykim.com/blog/deep-d…

相關文章
相關標籤/搜索