構建與定製:惟品會 PaaS 基於 Kubernetes 的實踐

數人云上海&深圳兩地「容器之Mesos/K8S/Swarm三國演義」的嘉賓精彩實錄第三更來啦。惟品會是數人云Meetup的老朋友,去年曾作過RPC服務框架和Mesos容器化的分享。本次分享中,嘉賓王成昌分享了惟品會在Kubernetes上兩年的PaaS實踐,乾貨滿滿誠意奉上~前端

王成昌,惟品會PaaS平臺高級開發工程師
主要工做內容包括:平臺DevOps方案流程優化,持續部署,平臺日誌收集,Docker以及Kubernetes研究。node

你們好,我是惟品會PaaS團隊的王成昌,與你們分享一下PaaS在Kubernetes的實踐。基於2014年末或2015年初PaaS沒有推廣的現狀,惟品會PaaS部門目前已經作了兩年的時間。數據庫

PaaS 主要工做將分爲三個部分進行介紹,首先,PaaS定義的標準構建流程,持續集成和持續部署的架構以及已有組建上的功能定製;第二部分,基於Kubernetes實現的網絡方案,以及根據網絡方案作的擴展定製;第三部分,PaaS如何作日誌收集和監控方案,最後列一下惟品會目前爲止所遇到的問題和總結。api

惟品會現狀

惟品會目前線上有一千多個域,每一個域之間相互的依賴比較複雜,每次的部署發佈困難。線下有多套的測試環境,每套測試環境都要去維護單獨的應用升級和管理。公司層面也沒有統一的持續集成和部署的流程,你們各自去維護一個Jenkins或者一個Jenkins slave,看工程師的我的追求是否可以寫一個完整的從源代碼、到打包、最後到部署的腳本。安全

惟品會線上所有用物理機在跑,以前Openstack方式沒有在線上,只是在測試環境跑,物理機的使用效率仍是比較低的。即便在7週年大促的高峯時段,60~80%的物理機利用率也均低於10%。網絡

惟品會PaaS構建流程

基於前面提到的現狀,惟品會的PaaS定義了一個構建流程,整個流程不是一蹴而就,這是目前爲止的定義,首先從源代碼的角度出發,即Git,全部的7個Phase所有包括在Jenkins Pipeline裏,因爲是基於Kubernetes,因此Jenkins Pipeline的執行是經過Jenkins k8s Plugin去調度後臺的k8s Cluster,由k8s產生的Pod去運行Pipeline。整個Pipeline的幾個階段,除了傳統的編譯單元測試和打包以外,加入了烘焙鏡像、部署以及集成公司的集成測試(即VTP),打包和鏡像完成後會正常上傳到公司統一的包管理系統Cider和平臺維護的Docker registry。架構

部署完成後會觸發集成測試,若是經過測試的話,會把這個包或者是鏡像標記爲可用的狀態,通常先從測試環境標記,而後經過到staging環境。目前PaaS 平臺主要是維護測試環境和staging環境,線上尚未,可是已經定義了一個審批的流程,若是標記了這個包爲可用的狀態,須要一個審批來決定它是否能夠上線。部署後經過k8s client,由另一套k8s的集羣來管理部署裏面全部的節點。app

這是惟品會的PaaS架構,主要包含持續集成和持續部署。首先由一個統一UI的入口Dashboard,使用Nginx和Tomcat做爲服務的網關。其背後有兩套系統——CPMS和API server,CPMS主要管理持續集成的各個流程,API server主要管理應用部署,在CPMS背後是使用多個Jenkins server統一連到一個Kubernetes集羣上產生Pod做爲Jenkins slave去運行,不一樣的構建有多種語言也有不一樣的模板,這裏會提供各類方案讓不一樣的Jenkins Pipeline運行在不一樣的Kubernetes node裏面。框架

在部署實現一個Cloud Framework,能夠接入各類cloud provider,目前使用的是k8s provider,背後的服務發現也是k8s推薦使用的Skydns。爲了兼容公司基於包發佈的這樣一套模式,鏡像管理這部分會把包管理系統Cider接入進入,平臺的Docker Registry,以及應公司安全方面的要求,經過Clair對鏡像的內容進行檢查。dom

在日誌收集方面,使用fluentd+ELK的組合,採用Prometheus作監控。在PaaS架構裏,安全是經過接入公司的CAS作認證的動做,有一個Oauth組件作鑑權機制,經過Gnats作消息傳輸的系統。配額的問題在構建和部署中都會有所體現,包括用戶對於Pipeline的個數控制或者Pipeline觸發的個數,以及對應用上的物理配額或者邏輯資源配額等。

Docker Registry改造,主要在Middleware作了一些工做,作了一個接入公司的CAS和Oauth作的驗證和受權。也接入了當有新的鏡像Push進來的時候,會自動觸發應用的部署。Docker Registry自己對全部的repository不一樣的tag索引仍是比較慢的,因此會針對push進來全部的鏡像信息存入數據庫作一個索引,方便查找鏡像。用戶行爲記錄主要針對pull和push的動做,把它記錄下來。鏡像安全經過接入Clair作掃描,push鏡像layer完成以後在push鏡像的manifest時,會將鏡像layer信息發送到Clair作鏡像的安全掃描。

原來的Jenkins Kubernetes Plugin,默認把Jenkins slave調度在全部Kubernetes node上,咱們作了一個node selecter,針對不一樣的Pipeline類型,須要跑在不一樣的節點上。調度上加入了node selecter,此外在每一個Pipeline要去run的時候申請資源,加入了資源的request limit,防止單個的Pipeline在運行的時候佔用過多的資源,致使其餘的應用或者是構建任務受影響。在掛載方面,像傳統的maven項目在下載過一個包以後,若是是同一個主機上會把.m2文件會掛載在主機上,不一樣的Jenkins Pool在跑的時候,能夠共享已經下載過的資源文件。

最後,實現了Container slave pool的策略。當要run一個Pipeline的時候,每次告訴k8s要起一個Jenkins slave Pod,當須要執行一個job的時候,等待時間比較長,這裏會定一個池的策略,就是一個預先準備的過程,當有一個新的任務要run的時候,馬上就能夠拿到一個可用的containerslave。

這是PaaS的功能點,包含三個主要的部分,構建,部署和測試集。構建是關於用戶定義Pipeline以及對Pipeline觸發的record的管理,以及Pipeline各個phase的管理。部署主要對應用配置的管理,這個應用包括服務的配置如何、資源的申請如何,以及應用實例的一些管理。測試集對接公司的集成測試環境,和平臺的應用進行關聯。

空間管理和鏡像管理,空間主要提供不一樣的隔離空間,提供應用快速的複製,好比你有一個測試環境,我也有一個測試環境,爲了你們環境之間相互不干擾能夠提供應用的快速複製。鏡像管理主要分三種,即平臺提供的基礎鏡像,業務部門一些特殊的需求會基於基礎鏡像作一些定製,以及具體業務鏡像。

PaaS網絡方案和定製

PaaS採用的網絡方案,網絡方案最開始直接使用的k8s 1.0的版本加flannel的一套工做模式,後來因爲業務需求,用戶需求可以直接訪問到實例IP,而flannel當時是封閉的子網。目前採用Contiv這套網絡模式,由公司統一分配Pod的IP網段。這裏作了一個kube-HAProxy,替換了節點上kube-proxy這個組件,用kube-HAProxy來作Service IP到end point的一個轉發。

在kube2sky,完成域名和服務IP的註冊。傳統的模式下,域名是短域名,Service的名字做爲短域名,還有Service自己的IP會註冊到Skydns上。這裏作了一些定製,由於公司的應用好比兩個業務域A和B都有自己的域名——a.vip.com和b.vip.com,A若是要訪問B,不能讓這個訪問跑到線上或者其餘環境去,因而經過kube-sky去解析規則,把b.vip.com加入到裏面,再加一個subdomain做爲擴展的domain search,最終找到平臺內部部署的B域。

goroute 主要是平臺內部的應用,每一個應用都會提供一個平臺的域名,這個域名主要是有一個組件叫作state aggregator,會watch k8s apiserver發出來的Service和end point的變化,最終經過Service的名字和end point的地址,把它寫到gorouter的route註冊表信息中,當咱們訪問平臺域名時就能夠找到真正的end point地址。這裏也有定製,採用HAproxy和KeepAlived替換了kube-proxy,以前從Service IP到end point IP的轉化,經過每一個節點部署的kube-proxy,它會檢測到 Service和end point的變化,去寫IPtables的規則,來找到最終end point的地址的IP。

如今統一使用的HAproxy加上KeepAlived,有一個kube2HAproxy組件,功能和kube-proxy前面一部分類似,都要watch kube-apiserver的Service和 end point的event來動態的生成一個HAproxy最新的配置。KeepAlived主要爲了高可用。有一個值得注意的細節,kube2HAproxy所在機器的IP,要和Service IP的網段在同一個網段裏,用戶在訪問真正的應用的時候直接使用Service IP是公共可見的,而不是隨便定義的Service IP。

對外應用訪問是由平臺提供的域名,後綴均爲*.PaaS.vip.com,解析到以後會有公司的DNS統一轉發到gorouter這臺機器上,由於gorouter會監聽到Service和end point的變化,route表裏面會存儲每一個域名對應的end point的地址,經過round robin的方式找到最終的Pod來完成http訪問。

最後一個定製關於Pod的IP固定,爲何要作PodIP固定?由於以前的測試環境不少應用都是部署在VM甚至在物理機上,IP都是固定的,有一些應用是須要白名單訪問的,應用在這個部署機上,須要將IP提供給相應的調用方或者是公司的某個部門來告訴他加入白名單。Docker 默認狀況下,每次銷燬和重建的過程當中,IP都會隨機申請和釋放,因此IP有可能變化。IP固定主要在k8s apiserver作,加了兩個對象,即Pod IP Allocator和IP recycler,Pod IP Allocator是一個大的Pod的網段,能夠認爲它是Pod的IP池, IP recycler主要記錄一些臨時回收的IP,或者叫臨時暫存區,IP不是一直存在,不然是一種IP浪費,有一個TTL時效性的。

當應用從新部署的時候,本來的Pod會被刪掉,刪掉的過程當中會先放在IP recycler中,當一個新的Pod啓動的時候,會經過namespace+RC name的規則去找是否有可用的IP,若是找到優先用這樣的IP記錄在Pod裏,這個Pod對象最終會交由kubelet去啓動,kubelet 啓動的時候會讀取這個Pod IP,而後告訴Docker 啓動的IP是什麼。最終有新的Pod啓動以後,它的IP是以前已經被銷燬的Pod IP,達到的效果就是Pod IP固定。在kubelet由於修改了Pod對象的結構,增長了Pod IP記錄使用IP的狀況,根據Pod的IP告訴Docker run的時候執行剛剛的IP來啓動。kubelet 在刪除Pod的時候會告訴k8s去release這個IP。

日誌收集和監控

日誌收集主要分三種類型:首先是平臺自身的服務組件的收集,好比像jenkins、Docker 或者Kubernetes 相關組件的日誌收集,另外一個是全部部署在平臺裏面應用的收集,最後還有一些域,由於公司一些已有系統(dragonfly)也是作日誌收集和監控的,有一些特定的規則對接公司。

平臺自身日誌收集的規則,包含系統組件還有平臺應用兩種設計。系統組件比較簡單,無外乎經過systemd或者是指定日誌文件的路徑作日誌的收集,應用收集主要在k8snode上,k8s會把每個Pod日誌link在一個特定的文件路徑下,由於Docker會記錄每個容器的日誌,能夠從這個地方讀取應用的日誌,可是隻拿到namespace和Pod name這樣的結構,咱們會經過fluentd裏的filter反向去k8s拿Pod所對應的meta data,最終發送到kafka,經過logstash達到elastic search。

Kibana的展示作了一些定製,由於平臺的展示主要基於namespace和應用名稱的概念查看日誌的,定製可以展示特定的namespace下的特定應用的日誌,同時把自定義的告警加在了這裏,由於告警是經過elastalert來作的,在Kibana上作一個自定義告警的UI入口,由用戶來指定想要監聽什麼樣的日誌內容的告警,去配置監聽的間隔或者出現的次數,以及最終的郵件接收人。

有一個組件是當用戶建立了自定義告警的規則時會發送到後面的elastalert ruler,ruler解析前臺UI的信息,生成elastalert可以識別的configure文件,動態地讀取configure的加入。
對接公司的業務系統比較簡單,特定的收集規則都是特定的,好比具體的目錄規則,通常來說都是經過掛載容器的目錄到主機目錄上,在主機上統一部署一個agent去run,主要體如今k8s node上,因此仍然使用Daemonset的方式去跑agent。

監控有兩種,一種是對單個Pod的實例監控,在頁面上是能夠直接看這樣的實例的,單個Pod是一個應用實例了,而後經過node agent去包裝了cAdviser,前端去統一訪問,獲取對應的CPU和Memory使用信息。cAdviser對Network收集到的數據是不正確的,經過node agent 讀取Linux file獲取Network的信息。Websocket Server 是爲了提供網頁上直接對容器進行網頁控制檯的登陸。

另外一個是看整個的應用,由於應用是有多個實例的,經過Graphana去定製,去展示namespace的應用,有多個實例,就把多個實例的監控都展示出來。此處有一個promethus plugin的定製,以前有一些Swarm的節點加入,持續部署提到過它是一個多個cloud framework均可以接入的。惟品會接入了一些Swarm的信息,針對Swarm建立的容器的話,也要可以監控到它的容器監控信息的數據。在Promethus plugin經過Docker info獲取不一樣的Swarm node的信息,在每一個Swarm node上部署cAdviser,獲取由Swarm建立的容器的監控信息。

問題總結

遇到的問題很是多,到如今爲止將近兩年的時間,有不少都是能夠在GitHub找到的問題,以及經過升級能夠解決的問題。最開始採用的是Docker 1.6以及Kubernetes 1.0,中間經歷兩次的升級,如今主要使用Docker 1.10和Kubernetes 1.2。

Devicemapper loopback 性能問題

production使用direct-lvm作Devicemapper存儲。

Pod實例處於 pending狀態沒法刪除

目前發現一些Pod一直處於pending的狀態,使用kubectl或者Kubernetes API沒有辦法直接對Pod作任何操做,目前只能經過手動Docker的方式刪除容器。

k8s 僵死容器太多,佔用太多空間

k8s僵死容器是之前碰到的問題,如今最新版的Kubelet是支持這樣的參數,容許每個節點上最大僵死容器個數,交由Kubelet本身作清理的工做。

Kubernetes1.1.4上ResourceQuota更新比較慢

由於之前發現用戶來告訴配額不足的時候,調整配額並不會立馬的生效,升級之後就沒有這種問題了。

k8s batch job 正常退出會不斷重啓

這個是應用的問題,不是傳統理解的跑一次性任務,k8s batch job要求必定要exit 0。也有一些Job的類型,目前是直接對應的背後k8s的Pod方式。

Skydns ping get wrong IP

這是最新遇到的問題,上下文的話,稍微有一點複雜,有兩個應用部署在惟品會的平臺上,每一個應用都有本身的legacy域名,好比有一個域名叫user.vip.com,另外一個叫info.user.vip.com,這時候ping user.vip.com,有可能會拿到info.user.vip.com的IP,因爲kube-sky自己在寫Skydns record時候有問題,因此會添加一個group作一個惟一性的識別,這樣在ping子域名(user.vip.com)的時候就不會讀到info.user.vip.com。Skydns自己目錄結構的問題,加上group就不會再去讀到下面的子路徑。

Overlayfs issue: can’t doing mv operation

惟品會一直使用Devicemapper,中間嘗試一些節點用Overlayfs的存儲,可是在目錄操做時會file not found,官方也有一個issue說當Overlayfs 在不一樣的layer的時候,牽扯到刪除的操做,會出現這個問題。能夠升級解決,可是系統是固定的,升級很麻煩,因此沒有作升級而是切回了Devicemapper。

Devicemapper空間滿,須要改造cAdvisor監控容器空間使用

因爲容器佔用的磁盤空間過多,致使了整個k8s node的磁盤空間被佔滿的問題,惟品會是在Cadviser作一些改造,能夠監控到每個容器所佔用的磁盤空間,對磁盤空間作一些限制。

Kubernetes namespaces stuck in terminating state

在刪除一個namespace的時候,namespace是能夠動態建立和刪除的,在刪除的時候會看到namespace一直處於terminating的狀態,這個在GitHub上有一些解決方法,由於它自己是因爲namespace的finalizer會進入一個死循環,有一個work around,能夠手動的置空finalizer,把這個namespace update回去,就能夠正常刪除了。

以上就是我要分享的主要內容,謝謝你們。

相關文章
相關標籤/搜索