【編者的話】原本生活網(benlai.com)是一家生鮮電商平臺,提供蔬菜、水果、海鮮等優質生鮮果蔬食材食品網購服務。當今容器技術被普遍關注,原本生活網在經歷了一年的技術革命後,基本完成了容器化所需的基礎設施建設。本文介紹原本生活網在 Kubernetes 落地過程當中的實踐和經驗。git
原本生活網是一家生鮮電商平臺,公司很早就中止了燒錢模式,開始追求盈利。既然要把利潤最大化,那就要開源節流,做爲技術能夠在省錢的方面想一想辦法。咱們的生產環境是由 IDC 機房的 100 多臺物理機所組成,佔用率高達 95%,閒置資源比較多,因而咱們考慮藉助 Kubernetes 來重構咱們的基礎設施,提升咱們資源的利用率。github
容器化項目團隊最初加上我就只有三我的,同時咱們還有各自的工做任務要作,留給容器化的時間較少,所以咱們要考慮如何快速的搭建容器平臺,避免走所有自研這條路,這對咱們來講是個巨大的挑戰。在經歷了一年的容器化之旅後,分享下咱們這一年所踩過的坑和得到的經驗。docker
在搭建 Kubernetes 集羣前,有不少問題擺在咱們面前:數據庫
做爲小團隊去構建一個容器平臺,自研的工做量太大了。前期咱們調研過不少可視化平臺,好比 Kubernetes Dashboard 和 Rancher 等等,可是要操做這些平臺得須要專業的 Kubernetes 運維知識,這對咱們的測試人員不太友好。後來咱們嘗試了 KubeSphere(kubesphere.io) 平臺,各方面都比較符合咱們的需求,因而決定使用該平臺做爲咱們容器平臺的基礎,在這之上構建咱們本身的發佈流程。服務器
咱們的項目有 Java 也有 .NET 的,.NET 項目佔了 80% 以上。要支持 .NET 意味着要支持 Windows。在咱們去年開始啓動項目的時候,Kubernetes 剛升級到 1.14 版本,是支持 Windows 節點的第一個版本,同時功能也比較弱。通過實驗,咱們成功對 .NET Framework 的程序進行了容器化,在不改代碼的前提下運行在了 Windows 服務器上,並經過 Kubernetes 進行管理。不過咱們也遇到了一些比較難處理的問題,使用下來的總結以下:網絡
咱們調研了一段時間後決定放棄使用 Linux 和 Windows 的混合集羣,由於這些問題會帶來巨大的運維成本,並且也沒法避免 Windows 的版權費。架構
咱們也考慮過把這些項目轉換成 Java,但其中包含大量的業務邏輯代碼,把這些重構爲 Java 會消耗巨大的研發和測試的人力成本,顯然這對咱們來講也是不現實的。那麼有沒有一種方案是改動不多的代碼,卻又能支持 Linux 的呢?答案很明顯了,就是把 .NET 轉 .NET Core。咱們採用了這種方案,而且大多數狀況能很順利的轉換一個項目而不須要修改一行業務邏輯代碼。負載均衡
固然這個方案也有它的侷限性,好比遇到以下狀況就須要修改代碼沒法直接轉換:運維
這些修改的成本是可控的,也是咱們能夠接受的。到目前爲止咱們已經成功轉換了許多 .NET 項目,而且已運行在 Kubernetes 生產環境之上。微服務
因爲咱們是基於物理機部署也就是裸金屬(Bare Metal)環境,因此不管基於什麼平臺搭建,最終仍是要考慮如何暴露 Kubernetes 集羣這個問題。
咱們分別試用了兩套方案 MetalLB(metallb.universe.tf)和 Porter(github.com/kubesphere/porter),這兩個都是以 LoadBalancer 方式暴露集羣的。咱們測試下來都能知足需求。Porter 是 KubeSphere 的子項目,和 KubeSphere 平臺兼容性更好,可是目前 Porter 沒有如何部署高可用的文檔,我在這裏簡單分享下:
前置條件
配置和部署
架構和邏輯
Porter有兩個插件:Porter-Manager 和 Porter-Agent。
Porter-Manager 是使用 Deployment 部署到 Master 節點上的,但默認只部署1個副本,它負責同步 BGP 路由到物理交換機(單向 BGP 路由,只需將 Kubernetes 私有路由發佈給交換機便可,無需學習交換機內物理網絡路由)。還有一個組件,Porter-Agent,它以 DaemonSet 的形式在全部節點都部署一個副本,功能是維護引流規則。
高可用架構
部署好後,你可能會有疑問:
通常路由器或交換機都會準備兩臺作 VSU(Virtual Switching Unit)實現高可用,這個是網絡運維擅長的,這裏不細講了。主要說下其餘幾點怎麼解決:
MetalLB 的高可用部署也是相似思路,雖然架構稍有不一樣,好比它和路由器進行 BGP 通訊的組件是 Speaker,對應 Porter 的 Manager;它與 Porter 的區別在於高可用目前作的比較好;可是 Local 引流規劃不如 Porter,EIP 的下一跳節點必須是 BGP 對等體(鄰居)。
Kubernetes 的 ConfigMap 和 Secret 在必定程度上解決了配置的問題,咱們能夠很輕鬆的使用它們進行更改配置而不須要從新生成鏡像,但咱們在使用的過程當中仍是會遇到一些痛點:
爲了解決這些痛點,咱們綜合考慮了不少方案,最終決定仍是使用一套開源的配置中心做爲配置的源,先經過 Jenkins 把配置的源轉換成 ConfigMap,以文件形式掛載到 Pod 中進行發佈,這樣以上的問題均可以迎刃而解。
咱們選擇了攜程的 Apollo(github.com/ctripcorp/apollo) 做爲配置中心,其在用戶體驗方面仍是比較出色的,能知足咱們平常維護配置的需求。
在遷移微服務到 Kubernetes 集羣的時候基本都會遇到一個問題,服務註冊的時候會註冊成 Pod IP,在集羣內的話沒有問題,在集羣外的服務可就訪問不到了。
咱們首先考慮了是否要將集羣外部與 Pod IP 打通,由於這樣不須要修改任何代碼就能很平滑的把服務遷移過來,但弊端是這個一旦放開,將來是很難收回來的,而且集羣內部的 IP 所有可訪問的話,等於破壞了 Kubernetes 的網絡設計,思考再三以爲這不是一個好的方法。
咱們最後選擇告終合集羣暴露的方式,把一個微服務對應的 Service 設置成 LoadBalancer,這樣獲得的一個 EIP 做爲服務註冊後的 IP,手動註冊的服務只須要加上這個 IP 便可,若是是自動註冊的話就須要修改相關的代碼。
這個方案有個小小的問題,由於一個微服務會有多個 Pod,而在遷移的灰度過程當中,外部集羣也有這個微服務的實例在跑,這時候服務調用的權重會不均衡,由於集羣內的微服務只有一個 IP,一般會被看做是一個實例。所以若是你的業務對負載均衡比較敏感,那你須要修改這裏的邏輯。
咱們一直使用的是點評的 CAT(github.com/dianping/cat) 做爲咱們的調用鏈監控,可是要把 CAT 部署到 Kubernetes 上比較困難,官方也無相關文檔支持。總結部署 CAT 的難點主要有如下幾個:
爲了把 CAT 部署成一個 StatefulSet 而且支持擴容,咱們參考了 Kafka 的 Helm 部署方式,作了如下的工做:
擴容很簡單,只須要在配置中內心添加一條實例信息,從新部署便可。
因爲 KubeSphere 平臺集成了 Jenkins ,所以咱們基於 Jenkins 作了不少 CI/CD 的工做。
起初咱們爲每一個應用都寫了一個 Jenkinsfile,裏面的邏輯有拉取代碼、編譯應用、上傳鏡像到倉庫和發佈到 Kubernetes 集羣等。接着我爲告終合現有的發佈流程,經過 Jenkins 的動態參數實現了徹底發佈、製做鏡像、發佈配置、上線應用、回滾應用這樣五種流程。
因爲前面提到了 ConfigMap 不支持版本控制,所以配置中心拉取配置生成 ConfigMap 的事情就由 Jenkins 來實現了。咱們會在 ConfigMap 名稱後加上當前應用的版本號,將該版本的 ConfigMap 關聯到 Deployment 中。這樣在執行回滾應用時 ConfigMap 也能夠一塊兒回滾。同時 ConfigMap 的清理工做也能夠在這裏完成。
隨着應用的增多,Jenkinsfile 也愈來愈多,若是要修改一個部署邏輯將會修改所有的 Jenkinsfile,這顯然是不可接受的,因而咱們開始優化 Jenkinsfile。
首先咱們爲不一樣類型的應用建立了不一樣的 yaml 模板,並用模板變量替換了裏面的參數。接着咱們使用了 Jenkins Shared Library 來編寫通用的 CI/CD 邏輯,而 Jenkinsfile 裏只須要填寫須要執行什麼邏輯和相應的參數便可。這樣當一個邏輯須要變動時,咱們直接修改通用庫裏的代碼就所有生效了。
隨着愈來愈多的應用接入到容器發佈中,不可避免的要對這些應用的發佈及部署上線的發佈效率、失敗率、發佈次數等指標進行分析;其次咱們當前的流程雖然實現了 CI/CD 的流程代碼複用,可是不少參數仍是要去改對應應用的 Jenkinsfile 進行調整,這也很不方便。因而咱們決定將全部應用的基本信息、發佈信息、版本信息、編譯信息等數據存放在指定的數據庫中,而後提供相關的 API,Jenkinsfile 能夠直接調用對應的發佈接口獲取應用的相關發佈信息等;這樣後期無論是要對這些發佈數據分析也好,仍是要查看或者改變應用的基本信息、發佈信息、編譯信息等均可以遊刃有餘;甚至咱們還能夠依據這些接口打造咱們本身的應用管理界面,實現從研發到構建到上線的一體化操做。
咱們在構建咱們的測試環境的時候,因爲服務器資源比較匱乏,咱們使用了線上過保的機器做爲咱們的測試環境節點。在很長一段時間裏,服務器不停的宕機,起初咱們覺得是硬件老化引發的,由於在主機告警屏幕看到了硬件出錯信息。直到後來咱們生產環境很新的服務器也出現了頻繁宕機的問題,咱們就開始重視了起來,而且嘗試去分析了緣由。
後來咱們把 CentOS 7 的內核版本升級到最新之後就再也沒發生過宕機了。因此說內核的版本與 Kubernetes 和 Docker 的穩定性是有很大的關係。一樣把 Kubernetes 和 Docker 升級到一個穩定的版本也是頗有必要的。
咱們目前對將來的規劃是這樣的:
Q:Kubernetes 在生產上部署,推薦二進制仍是 kubeadm 安裝?kubeadm 安裝除了提升運維難度,在生產上還有什麼弊端?
A:咱們使用了 kubeadm 部署 Kubernetes;建議選大家運維團隊較熟悉的那種方式部署。
Q:用 Prometheus Operator 監控 Kubernetes 的時候,有點不明白爲啥默認的閾值要那麼配置,好比說 API server,我怎麼去定義個人閾值,只要報警了,我就知道 API server 有問題了?
A:若要對 API server 的高可用進行監控,可先對 ready/desired 數量進行比對,其次能夠在集羣外部對 API server 進行訪問監控。
Q:咱們這用的是 Dubbo,Pod 更新的時候,好比說已經進來的流量,我如何去優雅處理,我這 Pod 號更新的時候,有依賴這個服務的應用就會報 Dubbo 超時了。
A:這個咱們也在優化中,目前的方案是在進程收到 SIGTERM 信號後,先禁止全部新的請求(可使 readinessProbe 失敗),而後等待全部請求處理完畢,根據業務特性設置等待時間,默承認覺得 30 秒,超時後自動強制中止。
Q:Jar 包啓動時加 JVM 限制嗎?仍是隻作 request 和 limit 限制?
A:個人理解是 request 和 limit 只是對整個容器的資源進行控制; 而 JVM 的相關參數是對容器內部的應用作限制,這二者並不衝突,能夠同時使用,也能夠單獨使用 request 和 limit;只不過 JVM 的限制上限會受到 limit 的制約。
Q:.NET Core應用部署在 Kubernetes 中相比 Java 操做複雜嗎?想了解下具體如何從 NET 轉到 .NET Core。
A:實際在 Kubernetes 內部署 .NET Core 和部署 Java 都同樣,選擇好對應的 .NET Core 基礎鏡像版本;而後以該版本的爲基礎製做應用的鏡像後部署到 Kubernetes 便可,只不過在選擇 .NET Core 的基礎鏡像時,我建議直接選擇 SDK 正常版本。咱們試過 runtime、sdk-alpine 等版本,雖然這些版本佔用空閒小,可是你不知道它裏面會少哪些基礎庫的東西,咱們在這個上面踩了不少坑。如今選擇的基礎鏡像是:mcr.microsoft.com/dotnet/core/sdk:3.1。轉換過程根據程序的複雜度決定,有些應用沒修改業務邏輯,而有些改的很厲害。
Q:鏡像 tag 和代碼版本是怎樣的對應關係?
A:咱們的鏡像 tag = 源碼的分支: [develop|master] + 日期 + Jenkins 的編譯任務序號,如:master-202004-27這樣。當這個 tag 的鏡像在線下都測試完畢時準備上線了,那麼就以這個鏡像的 tag 做爲應用代碼的 tag 編號,這樣就可以經過鏡像的 tag 追溯到應用代碼的 tag 版本。
Q:CentOS 7的系統版本和內核版本號,能說明下麼?
A:內核版本:3.10.0-1062.12.1.el7.x86_64,這個對應的是 CentOS 7.7,具體可參見 https://access.redhat.com/articles/3078。
Q:原本生活的日誌和監控方案是怎樣設計的?
A:因爲 KubeSphere 的日誌在老版本有延時,所以咱們線上是採集到 Kafka 而後經過現有的 Kibana 進行查詢,也就是 ELK,監控是基於 KubeSphere 自帶的 Prometheus,沒作太多修改。
Q:大家對容器監控是採用哪一種方式,另外應用的監控是如何作的,主要是性能這部分,好比阿里雲的 ARMS,目前在看這個。
A:監控的部分咱們還在優化中,主要思路是對 ready/desired 數量進行比對;應用性能方面的監控主要依靠 CAT 來體現。
Q:大家 yaml 模版複用是怎麼使用的,Helm 有用到嗎?
A:咱們把 yaml 文件內一些應用相關的數據抽取成變量,使之成爲應用 yaml 文件的基礎模板;而後在 Pipeline 構建時經過接口獲取到對應應用的相關參數,將這些參數結合 yaml 文件模板自動填充後生成對應應用的 yaml 文件;而後進行部署操做。Helm 沒有在應用部署中使用,但中間件有。
Q:Pod綁定了 SVC,使用 LoadBalancer 的 IP 自動註冊註冊中心,這塊如何實現的?
A:咱們的微服務是手動註冊 IP,若是自動的話須要與 Pipeline 結合,EIP 是能夠預先分配的。
文章轉載自dockerone社區:http://dockone.io/article/9983
分享人陳杰,原本生活網的架構負責人。