支撐多樣化數據服務的K8s容器化部署實踐

需求背景

SDMK(Smart Data Market)是TalkingData的數據智能市場,該平臺提供了多樣化的數據服務,包含API服務、SaaS服務以及Lookalike、預測引擎等人工智能算法模型服務,目地在於下降數據應用場景的難度,幫助更多的企業發現數據的深層價值。java

SDMK數據平臺是採用微服務架構來設計實現的,主要APP模塊近二十幾個,再加上第三方數據服務的適配器以及其它APP模塊近七十多個,提供的REST API近成百上千個。微服務架構的應用使得每一個服務均可以有專門的開發團隊(或我的)來開發,開發者能夠自由選擇開發技術,而且每一個微服務均可以獨立開發、升級、拓展。所以系統具有很高的穩定性和快速迭代能力,但同時也會遇到一些問題,如負載均衡、故障重啓、彈性伸縮、資源隔離等等。node

由於一個微服務背後可能有多個副本實例在支撐,如何作到自動負載均衡?當其中一個副本實例宕機了,是否須要人工重啓?當流量增長的時候,如何方便或者自動的增長節點,以知足相應的SLA?面對以上等等的這些問題,Kubernetes(K8s)可以很好解決,從而也使其成爲企業微服務容器化的首選解決方案。nginx

本文將簡單介紹K8s核心概念,並重點介紹TalkingData SDMK採用K8s的容器化部署實踐。redis

K8s核心概念

首先,Kubernetes(K8s)是自動化容器操做的開源平臺,這些操做包括部署、調度和節點集羣間擴展。若是你曾經用過Docker容器技術部署容器,那麼能夠將Docker當作Kubernetes內部使用的低級別組件。Kubernetes不只僅支持Docker,還支持另外一種容器技術Rocket。算法

使用Kubernetes能夠:docker

  • 自動化容器的部署和複製
  • 隨時擴展或收縮容器規模
  • 將容器組織成組,而且提供容器間的負載均衡
  • 很容易地升級應用程序容器的新版本
  • 提供容器彈性,若是容器失效就替換它,等等……

clipboard.png

Master數據庫

集羣擁有一個K8s Master(上圖中紫色方框)。K8s Master提供集羣的獨特視角,而且擁有一系列組件,好比K8s API Server。API Server提供能夠用來和集羣交互的REST 接口。Master節點包括用來建立和複製Pod的Replication Controller。apache

  • K8s API Server,提供了HTTP
    Rest接口的關鍵服務進程,是集羣中全部資源增、刪、改操做的惟一入口,也是控制集羣的惟一入口。
  • K8s Controller Manager,是集羣中全部資源對象的運行指揮中心。
  • K8s Scheduler,是負責調度Pod資源的進程。
  • Etcd服務,提供集羣中全部資源對象配置的持久化。

Node後端

Node做爲集羣中的工做節點,運行真正的應用程序,在Node上K8s管理的最小運行單元是Pod。Node上運行着K8s的Kubelet、kube-proxy服務進程,這些服務進程負責Pod的建立、啓動、監控、重啓、銷燬、以及實現軟件模式的負載均衡。節點(上圖橘色方框)是物理或者虛擬機器,做爲K8s worker,一般稱爲Minion。每一個節點都運行以下K8s關鍵組件:api

  • Kubelet:是主節點代理。
  • Kube-proxy:Service使用其將連接路由到Pod。
  • Docker或Rocket:K8s使用的容器技術來建立容器。

Pod

Pod是K8s最基本的操做單元,包含一個或多個緊密相關的容器,一個Pod能夠被一個容器化的環境看做應用層的「邏輯宿主機」;一個Pod中的多個容器應用一般是緊密耦合的,Pod在Node上被建立、啓動或者銷燬;每一個Pod裏運行着一個特殊的被稱之爲Pause的容器,其餘容器則爲業務容器,這些業務容器共享Pause容器的網絡棧和Volume掛載卷,所以他們之間通訊和數據交換更爲高效,在設計時咱們能夠充分利用這一特性將一組密切相關的服務進程放入同一個Pod中。

Pod的生命週期經過Replication Controller來管理,經過模板進行定義,而後分配到一個Node上運行,在Pod所包含容器運行結束後,Pod結束。K8s爲Pod設計了一套獨特的網絡配置,包括:爲每一個Pod分配一個IP地址,使用Pod名做爲容器間通訊的主機名等。

RC(Replication Controller)

RC就是複製控制器,新一代的RC叫RS(Replication Set)其主要功能以下:

  • 確保Pod數量:RC用來管理正常運行Pod數量,一個RC能夠由一個或多個Pod組成,在RC被建立後,系統會根據定義好的副本數來建立Pod數量。在運行過程當中,若是Pod數量小於定義的,就會重啓中止的或從新分配Pod,反之則殺死多餘的。
  • 確保Pod健康:當Pod不健康,運行出錯或者沒法提供服務時,RC也會殺死不健康的Pod,從新建立新的。
  • 彈性伸縮
    :在業務高峯或者低峯期的時候,能夠經過RC動態的調整Pod的數量來提升資源的利用率。同時,配置相應的監控功能(Hroizontal Pod
    Autoscaler),會定時自動從監控平臺獲取RC關聯Pod的總體資源使用狀況,作到自動伸縮。
  • 滾動升級:滾動升級爲一種平滑的升級方式,經過逐步替換的策略,保證總體系統的穩定,在初始化升級的時候就能夠及時發現和解決問題,避免問題不斷擴大。

Labels

Labels以key/value的形式附加到各類對象上,如Pod、Service、RC、Node等,以識別這些對象,管理關聯關係等,如Service和Pod的關聯關係,有了這種關聯關係,就能夠經過選擇器(Selector)來進行篩選那個服務和那個Pod進行關聯。

Label selectors

經過label selectors,用戶能夠識別一些對象,它是K8s的核心Grouping Primitive。其選擇器主要有如下兩種:

  • equality-based
  • Set-based requirement

Deployment

部署表示用戶對K8s集羣的一次更新操做。部署是一個比RS應用模式更廣的API對象,能夠是建立一個新的服務,更新一個新的服務,也能夠是滾動升級一個服務。滾動升級一個服務,實際是建立一個新的RS,而後逐漸將新RS中副本數增長到理想狀態,將舊RS中的副本數減少到0的複合操做;這樣一個複合操做用一個RS是不太好描述的,因此用一個更通用的Deployment來描述。以K8s的發展方向,將來對全部長期伺服型的的業務的管理,都會經過Deployment來管理。

Service

RC、RS和Deployment只是保證了支撐服務的Pod的數量,可是沒有解決如何訪問這些服務的問題。一個Pod只是一個運行服務的實例,隨時可能在一個節點上中止,在另外一個節點以一個新的IP啓動一個新的Pod,所以不能以肯定的IP和端口號來提供服務。要穩定地提供服務須要服務發現和負載均衡能力。服務發現完成的工做,是針對客戶端訪問的服務,找到對應的後端服務實例。在K8s集羣中,客戶端須要訪問的服務就是Service對象。每一個Service會對應一個集羣內部有效的虛擬IP,集羣內部經過虛擬IP訪問一個服務。在K8s集羣中服務的負載均衡是由Kube-proxy實現的。Kube-proxy是K8s集羣內部的負載均衡器。它是一個分佈式代理服務器,在K8s的每一個節點上都有一個;這一設計體現了它的伸縮性優點,須要訪問服務的節點越多,提供負載均衡能力的Kube-proxy就越多,高可用節點也隨之增多。

SDMK容器化部署實踐

1、服務暴露實踐

上文概念中所說Service一般只是集羣內部有效,在K8s的世界裏,服務要對集羣外暴露,一般有這麼幾種方式:

1. Cluseter IP

集羣內的私有IP,這是默認值。

2. NodePort Service

其實質上是經過在集羣的每一個node上暴露一個端口,而後將這個端口映射到某個具體的service來實現的,雖然每一個node的端口不少,但因爲安全性和易用性(服務多了就亂了,易產生端口衝突),實際使用的並很少。

clipboard.png

3. LoadBalancer Service

是K8s深度結合雲平臺的一個組件,當使用LoadBalancer service暴露服務時,其實是經過底層平臺申請建立一個負載均衡器來向外暴露服務的。這種方式多數應用於雲平臺上來使用。

4. Ingress Service

經過Ingress用戶能夠實現使用nginx等開源的反向代理負載均衡器實現對外暴露服務。它包含有三個組件:反向代理負載均衡器、Ingress Controller、Ingress。

  • 反向負載均衡器,簡單來講就是nginx、apache等,部署方式也較爲自由,能夠RC、Deployments、DaemonSet。
  • Ingress Controller:能夠簡單理解爲是個監視器,Ingress Crontroller經過不斷地跟Kubernetes
    API打交道,實時感知後端service、pod的變化,好比增長或者減小pod,service的增長或者減小等,當這些信息發生變化時,Ingress
    Controller再結合上下文,生成配置,而後更新到反向代理負載均衡器,並從新reload配置,達到服務發現的做用。
  • Ingress:簡單的說就是個規則定義;好比哪一個域名對應哪一個service,即哪一個域名請求進來,轉發給哪一個service。

clipboard.png

SDMK部署就是採用的Ingress這種方式。在K8s集羣中,Ingress 是受權入站鏈接到達集羣服務的規則集合,爲其提供七層負載均衡能力,從而能夠經過 Ingress 配置提供外部可訪問的 URL、負載均衡、SSL、基於名稱的虛擬主機等。

SDMK在部署的時候主要考慮瞭如下兩種部署方式:

  • 共享Ingress
  • 獨佔Ingress

01共享Ingress

clipboard.png

如上述部署架構圖,全部的服務共享一個Ingress實例,該實例做爲集羣全部服務的流量入口,但問題就是,一旦流量入口出現問題,例如性能問題、可用性問題等,就會影響集羣內全部服務的可用性。

02獨佔Ingress

clipboard.png

如上述部署架構圖,由多個獨佔 Ingress 實例組成統一接入層承載集羣入口流量,每一個微服務模塊獨佔一個Ingress,同時也可依據後端業務流量水平擴縮容 Ingress 節點。固然若是前期的集羣規模並不大,也能夠採用將 Ingress 服務與業務應用混部的方式,但建議進行資源限制和隔離。

clipboard.png

做爲集羣流量入口,Ingress的高可靠性顯得尤其重要,出於這樣的考慮,SDMK採用獨佔Ingress的同時,並將nginx-ingress-controller採用多副本部署的方式以及將其與後端service進行資源隔離,這樣能夠避免業務應用與 Ingress 服務發生資源爭搶,以及單點的問題。

2、彈性伸縮實踐

clipboard.png

在K8s中,Pod是最基礎的調度單位,多個Pod 能夠組成一個集合,這個集合向外提供服務。這時候,有如下兩種情形須要關注:

  1. 集合中的Pod可能會因爲某種緣由fail,這時候須要某種機制可以建立新的Pod以確保有足夠數量的Pod在運行。
  2. Pod 的個數由訪問請求決定。即當前實例個數不足以知足訪問請求時,須要增長實例個數,反之,須要經過某種策略減小實例數。

若是人肉來實時監控實例的運行狀態,手動啓動新的Pod以替代fail的Pod,監控實例的負載狀況,手動建立或者刪除Pod,這個工做繁瑣且工做量大,好在K8s已經有相應的機制來應對這種變化,這個機制就是彈性伸縮。

彈性伸縮式一種根據資源使用狀況自動伸縮工做負載的方法。彈性伸縮在K8s中有兩個維度:Cluster Autoscaler 和 Horizontal Pod Autoscaler ,兩者一塊兒可用於動態調整計算能力以及並行性的水平擴展能力,從而知足系統的SLA。Cluster Autaoscaler 依賴於託管集羣的雲提供商的底層功能,HPA就能夠獨立於Iaas/Paas提供商進行操做了。

clipboard.png

簡單的來說,就是HPA實現了一個控制環,能夠週期性的經過資源指標API查詢特定應用的CPU、MEM等信息,而Metrics server 負責收集各個應用Pod的指標信息,再經過控制器的計算,從而實現彈性伸縮。當前主要的指標分爲如下三種:

  • Resource:Pod的資源指標,例如cpu、mem等信息
  • Custom: 自定義的指標信息,例如:ingress的QPS、應用在線活躍人數等
  • External :集羣指標的監控指標,一般由雲廠商提供

計算的算法:

desiredReplicas = ceil[currentReplicas * ( currentMetricValue / desiredMetricValue )]

例如:

currentMetricValue 爲 200m, desiredMetricValue 爲100m ,那麼副本的個數就會翻倍,前提是不超過你設置的最大值,若是超過,就把最大值做爲當前的目標副本數目。若是算出來的值爲0.5,就大於等於該數的最小整數。

在這裏要說明是注意你所採用的K8s版本:

在K8s v1.1中首次引入了HPA特性,其第一個版本基於觀察到的CPU利用率,後續版不斷髮展,也支持了基於內存使用。在K8s 1.6中引入了一個新的API自定義指標API,它容許HPA訪問任意指標。在K8s1.7引入了聚合層,容許第三方應用程序經過註冊爲API附加組件來擴展K8s API。自定義指標API以及聚合層使得像Prometheus這樣的監控系統能夠向HPA控制器公開特定於應用程序的指標。

實踐部分

clipboard.png

須要在Deployment 的yaml文件中定義每一個APP使用的資源狀況,至於資源的限制大小須要根據實際的業務需求而定,並添加上對應探針策略。

  • Resoureces:用於限制容器的資源使用狀況,主要是CPU和內存資源。
  • readnessProbe:來肯定容器及服務是否已經就緒,從而能夠接受流量。只有當Pod的狀態,被kubelet認定爲就緒狀態時,纔會接受流量。也即控制哪些pod應該做爲service的後端。若是Pod處於非就緒狀態,那麼它們將會被從service得load
    balancer中移除。
  • livenessProbe:來肯定什麼時候重啓容器。例如:當應用程序處於運行狀態但沒法作進一步操做,liveness
    探針將捕獲到deadlock,就會重啓該狀態下的容器,使應用程序存在bug的狀況下,依然可以繼續運行下去。
  • Env:該選項用於設置一些環境變量,這裏的JAVA_OPTS用於開啓CGroup資源感知,對於JDK8如下版本是須要單獨設置,不然應用程序是沒法感知到容器資源限制的,而在JAVASE8u131
    和JDK9已經支持了對容器資源限制的感知能力。能夠參考這篇博客:https://blogs.oracle.com/java...
1apiVersion: autoscaling/v2beta1
 2kind: HorizontalPodAutoscaler
 3metadata:
 4  name: dm-sdmk-kafka-service
 5  namespace: dm
 6spec:
 7  scaleTargetRef:
 8    apiVersion: extensions/v1beta1
 9    kind: Deployment
10    name: dm-sdmk-kafka-service
11  minReplicas: 3
12  maxReplicas: 5
13  metrics:
14  - type: Resource
15    resource:
16      name: cpu
17      targetAverageUtilization: 50
18  - type: Resource
19    resource:
20      name: memory
21      targetAverageValue: 1536Mi

HPA的實現須要在HorizontalPodAutoscaler 的Yaml文件的定義具體彈性伸縮條件以及閾值,即最少的Pod個數、最多的Pod個數,根據什麼條件來伸縮,例如CPU平均利用率達到50%,或者內存平均使用超過1536M等,當相應服務的監控條件達到對應的閾值時,就會伸縮Pod。具體的設置要根據相應業務模塊類型來進行設置。SDMK在部署的時候多數根據內存條件來進行伸縮的,由於多數的APP應用在流量上來的時候消耗內存比較多。

3、引流實踐

clipboard.png

當K8s環境下的SDMK已經部署完畢,並通過簡單測試,處於可用狀態,如今想將特定用戶的流量引入到K8s環境下的SDMK,從而使其可以承擔起必定的業務流量,達到物理機環境下的SDMK與K8s環境下的SDMK混合部署效果,如上圖所示。

01物理機部署

clipboard.png

如上圖所示,原理物理機上的部署,是經過物理機上nginx作負載均衡的,下面掛載多個物理機上Gateway(網關)節點,Gateway是SDMK的一個網關,用於接收並處理服務調用請求的。

01並行部署

clipboard.png

並行部署就如上圖所示,將K8s環境的入口域名以Location的方式也掛載在nginx下,在nginx處來將流量區分開來,哪些用戶的流量打到K8s環境,哪些用戶的流量打到物理機環境。SDMK是經過在nginx中添加lua腳原本實現的,其部署狀況以下圖所示:

clipboard.png

實現步驟:

  • 在Nginx上添加特定的lua腳本,經過lua腳本鏈接redis
  • Lua腳本獲取打在nginx上每一個請求中的userId
  • 將拿到的userId再和Redis中配置的用戶userId進行匹配,若是是,則進入K8s環境;若是不是,則進入到物理機環境,從而將流量部分引入到K8s環境
  • 並行運行一段時間後,若是K8s環境沒有問題,再逐漸增長K8s環境的流量。

實踐部分

Lua腳本實現腳本:

1local _M = {}
 2_M._VERSION = ‘0.01’
 3local function tryExec(prod,gray)
 4local redis = require ‘redis’
 5local red = redis.connect(‘172.20.33.5’, 6379, 0.2)
 6token = ngx.req.get_headers()["userId"]
 7if token == nil then
 8    return prod
 9end
10
11local temp , userId = token:match("([^,]+)-([^,]+)")
12local res = red:get(userId)
13if not res then
14    return prod
15end
16
17if res == ngx.null then
18    return prod
19else
20    return gray
21end
22end
23
24
25function _M.execByToken(prod,gray)
26local ok , msg = pcall(tryExec,prod,gray)
27if ok then
28ngx.exec(msg)
29else
30ngx.exec(prod)
31end
32end
33return _M

Nginx中添加配置:

1location /data {
 2              set $target '';
 3              set $header_host $host;
 4              access_by_lua '
 5              package.path = "/usr/local/nginx/conf/lua/?.lua"
 6              local sdmk = require "sdmk"
 7              ngx.var.target=sdmk.tryExec("dmkdataProd","dmkdataK8s")
 8              if ngx.var.target == "dmkdataK8s" then
 9          ngx.var.header_host = "nginx-dm-talkingdata-datamarket-gateway-k8s.dm.svc.sc.tendcloud.com"
10              end
11          ';
12
13            proxy_http_version 1.1;
14            proxy_set_header   Connection          "";
15            proxy_set_header Host  $header_host;
16            proxy_set_header   X-Real-IP        $remote_addr;
17            proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
18            proxy_set_header   X-Forwarded-Server $host;
19            proxy_pass        http://$target;
20          }

這裏須要注意的是須要在header_host中將域名帶到K8s環境中。

4、注意事項

1.容器優雅關閉的問題

容器是否可以進行優雅的關閉,接收到SIGTERM信號,而非直接殺掉,從而使得應用程序中可以有效的處理數據、釋放資源等,不然就有可能出現數據丟失等現象。

2.數據庫訪問限制

生產環境的數據庫都有相應的訪問限制,當前SDMK的數據庫訪問限制,是經過受權IP段形式來實現的。

3.Nginx-ingress-Controller的性能問題

不管採用共享Ingress的方式,仍是採用獨佔Ingress方式,都須要在上生產環境以前進行壓測一番,看是否可以知足系統的要求。

4.資源限制問題

  • 若是設置Pod的資源限制,應用程序可否識別容器的資源限制,便是否開啓了CGroup資源感知,在JAVASE8u131
    和JDK9支持了對容器資源限制的感知能力。https://blogs.oracle.com/java...
  • 設置的資源限制是否知足應用程序的基本須要:若是不知足就會出現,容器起來,殺死,起來殺死的死循環。

5.HPA的設置條件,應當根據應用程序特定需求,來設置彈性伸縮條件

  • 計算密集型,那就以CPU使用量爲條件
  • 內存密集型,那就之內存使用量爲條件
  • 兩者兼之的,都設置
  • 自定義metric:例如消息隊列,處於等待狀態的消息數量數

6.特殊鏡像新需求

  • 可能運維同事,只提供標準的鏡像,若是有特殊需求,就須要本身在Dockerfile中進行必定的處理。例如:
  • Springboot2 須要Jetty9.4 ,JDK1.8以上鏡像
  • 生成文檔應用程序,須要安裝特殊的中文字體庫

7.建議容器探針必定要加

  • Liveness probe: 來肯定什麼時候重啓容器。例如:當應用程序處於運行狀態但沒法作進一步操做,liveness
    探針將捕獲到deadlock,就會重啓該狀態下的容器,使應用程序存在bug得狀況下,依然可以繼續運行下去。
  • Readiness probe:
    來肯定容器及服務是否已經就緒,從而能夠接受流量。只有當pod的狀態,被kubelet認定爲就緒狀態時,纔會接受流量。也即控制哪些pod應該做爲service的後端。若是pod處於非就緒狀態,那麼它們將會被從service得load
    balancer中移除。

8.Nginx引流訪問K8s環境域名

  • 須要在header_host中將域名帶過去
  • 而且添加DNS解析,由於K8s生成的域名,對應的IP是動態變化

本文做者:TalkingData 楊雙亮

相關文章
相關標籤/搜索