tcpip路由技术卷一_减少与开发的撕战,结合容器化技术轻松重构运维平台
荔枝微服務化進程較早,目前已有上千個服務模塊,先前的運維平臺漸漸無法滿足微服務架構下運維管理的需求,于是決定從2018年開始重構運維平臺,結合容器化技術,讓開發人員盡可能無感知遷移的同時享受容器化帶來的諸多好處。
本次將主要為大家介紹我們項目發布系統重構過程中,技術選型的考慮以及實踐過程中遇到的一些問題和解決方案。
一、背景
荔枝后端微服務化進程較早,目前已有上千個服務模塊,絕大多數是Java。得益于良好的規范,舊的運維平臺實現了一套簡單的自動化部署流程:
對接Jenkins進行編譯打包和版本標記
把指定版本的jar包,配置文件,啟動腳本一起發布到指定的機器上裸機運行
通過對Java進程的管理來完成重啟,關閉應用等運維操作
但是隨著開發人員,項目數量,請求量的增加,舊的運維平臺逐漸暴露出以下一些問題:
Java實例部署所需資源沒有清晰的統計和系統層面的隔離,僅僅依賴于啟動腳本中的JVM參數來進行內存的約束,新增實例或新上項目時,往往需要運維人員靠“感覺”指定部署的機器,沒有有效地分配機器資源,項目之間資源爭用會導致性能問題。
雖然大多數應用依賴的環境只有JDK 7和JDK 8,但一些JDK的小版本差異,以及一些自研發Java agent的使用,使得簡單地指定JAVA_HOME目錄的方式很難有效地管理運行環境。
開發人員的服務器權限需要回收。一個服務器上可能運行多個不同部門的項目,相關開發人員誤操作可能會導致其他項目被影響。
上述問題雖然也可以通過一些技術和規范約束來解決,但天生就是為了解決環境依賴和資源管理的容器技術是當下最合適的方案。
二、技術選型
核心組件方面,Docker和Kubernetes是當下最成熟的開源容器技術。我們對強隔離沒有太多的需求,所以沒有使用KVM等虛擬機方案,直接在裸機上部署Kubernetes。
分布式存儲方面,容器化的項目大多是無狀態的云原生應用,沒有分布式存儲的需求。極少數項目需要分布式存儲的場合,我們會把已有的MFS集群掛載到宿主機,由宿主機掛載到容器里提供簡單的分布式存儲。
容器本地數據卷方面,使用Docker默認的OverlayFS 2。
我們服務器操作系統主要是CentOS,DeviceMapper在生產環境必須使用direct-lvm模式,該模式需要獨立數據設備,對已有的SA自動化管理有一些影響。而OverlayFS 2在Linux內核4.17以上已經比較穩定,也不需要太多復雜的配置,開箱即用。
日志收集方面,我們已有一套基于ELK的收集方案,對應用日志也有一定約束(必須將日志打印到指定目錄下)。
傳統的基于控制臺輸出的Docker日志方案需要修改應用的日志輸出配置,并且海量的控制臺日志輸出也會影響dockerd的性能,所以我們通過掛載日志數據盤的方式即可解決問題。
監控方面,原有的監控設施是Zabbix,但在Kubernetes監控設施上Zabbix的方案顯然沒有親兒子Prometheus成熟和開箱即用。所以在Kubernetes的監控方面,我們以Prometheus+Granfana為核心,使用kube-state-metrics采集Kubernetes運行數據。
相比于Heapster,kube-state-metrics是Kubernetes生態的一部分,從Kubernetes的資源角度去采集數據,維度更多,信息更全面。
最后是比較重要的Kubernetes網絡方面,我們使用了比較新的網絡方案kube-router。kube-router是基于三層Routing和BGP的路由方案,其優點如下:
比Flannel等在數據包上再封裝一層通信協議(常見是VXLAN)的網絡實現性能上更優秀。
比同樣是基于BGP和三層路由的Calico來說更輕量簡單,易于部署。
Macvlan技術會使宿主機網絡和Pod網絡隔離,不太符合我們的需求。
在開啟Service Proxy模式后可以取代默認組件kube-proxy,Service Proxy的實現是IPVS,在性能上和負載均衡策略上靈活度更高(在Kubernetes 1.8后kube-proxy也有IPVS的實現支持,但到現在還是實驗性質)。
當然kube-router也存在一些不足:
項目比較新,現在最新的還是v0.2.5,使用過程=踩坑。
節點間網絡必須二層可達,不像Calico提供了IPIP的解決方案。
依賴于iptables,網絡要求高的場景Netfilter本身會成為瓶頸。
對于Pod IP的分配,Pod之間網絡的ACL實現較為簡單,無法應付安全要求高的場景。
基于三層路由的CNI解決方案:
三、業務落地實踐
搭好Kubernetes只是一個開始,我們這次重構有個很重要的目標是盡可能讓業務開發方無感知無修改地把項目遷移到Kubernetes上,并且要保證實例部署和容器部署同時并行過度。
理想的項目應該有Dockerfile聲明自己的運行環境,有Jenkinsfile解決編譯打包,有對應的Deployment和Service來告訴Kubernetes如何部署,但現實很骨干,我們有上千個項目,對應上千個Jenkins編譯打包項目,逐一地修改顯然不太現實。
自動化運維的前提是標準化,好在項目規范比較嚴謹,符合了標準化這個充分條件。
重新設計后的部署流程如下圖所示:
構建方面,項目統一使用同一個Dockerfile模板,通過變更基礎鏡像來解決一些不同環境項目(比如需要使用JDK 7)的問題。
基于Kubernetes Job和dind技術,我們開發了一個構建worker來實現從Jenkins拉取編譯后的應用包并打包成鏡像的流程,這樣Jenkins打出來的應用可以同時用在實例部署和容器部署上。
在運維后臺上即可完成版本的構建:
部署方面,項目的部署配置分成兩方面。
資源配置一般不經常修改,所以僅僅只是在運維平臺上修改記錄。經常變更的版本變更和實例數變更則與部署操作綁定。
將Kubernetes復雜的對象封裝成擴展成原有項目對象的資源配置參數,執行部署時,根據項目資源配置,版本和實例數生成對應的Deployment和Service,調用Kubernetes API部署到指定的Kubernetes集群上。
如果項目有在運維平臺上使用靜態配置文件,則使用ConfigMap存儲并掛載到應用Pod里。
在運維平臺上提供Pod列表展示,預發環境debug應用、灰度發布、狀態監控和webshell,方便開發觀察應用運行情況,調試和日志查看,同時也避免開發SSH到生產環境服務器上,回收了服務器權限。
在應用從實例部署遷移到容器部署的過程中主要遇到以下幾個問題:
1、Kubernetes集群內的Pod和集群外業務的通信問題。
為了風險可控,實例部署和容器部署之間將會存在很長一段時間的并行階段,應用方主要使用Dubbo做微服務治理,Kubernetes集群內的Pod和集群外業務的通信就成為問題了。
kube-router是基于三層Routing實現,所以通過上層路由器指定Pod IP段的靜態路由,或對接BGP動態交換路由表來解決問題。
2、JVM堆內存配置問題導致OOMKill的問題。
因為JVM的內存不止有Xmx配置的堆內存,還有Metaspace或PermSize,以及某些如Netty等框架還有堆外內存,把Xmx的配置等同于容器內存配置幾乎是一定會出現OOMKiil,所以必須放寬容器內存限制。
以我們的經驗來說,容器內存比Xmx多20%左右一般可以解決問題,但也有部分例外,需要額外配置。
3、Pod啟動失敗難以排查的問題。
有一些Pod一啟動就失敗,輸出的日志難以分析問題。我們構建和部署的描述文件都是運維平臺動態生成的,很難使用傳統docker run目標鏡像的方式進行調試。
所以我們在運維平臺上提供了debug容器的功能,新建一個和原有deployment一樣的debug部署,僅去掉健康檢查相關的參數和修改command參數使pod運行起來,業務開發方即可通過webshell控制臺進入Pod調試應用。
四、未來
開發經常需要使用的一些調試工具比如Vim、Arthas之類的,現在我們是打包到基礎鏡像中提供,但這樣不僅增加了鏡像的體積,而且需要重新打包新的鏡像。目前看到比較好的解決方案是起一個調試用的容器并加到指定Pod的namespace中,但還需二次開發集成到webshell中。
跨機房Kubernetes集群調度。當現有資源無法滿足峰值需求時,借助公有云來擴展系統是比較好的選擇,我們希望借助Kubernetes多集群調度功能做到快速擴容到公有云上。
峰值流量的自動擴容和縮容,Kubernetes提供的HPA策略較為簡單,我們希望能從更多的維度來計算擴容和縮容的數量,做到精準的控制。
Q1:容器的Pod網絡和外部網絡全部打通嗎,如何實現的?
A1:因為kube-router是基于三層路由,所以只要在頂層交換上指定Pod IP的靜態路由即可,比如宿主機是192.168.0.1,該宿主機上的pod ip range是10.0.0.1/24,那只要在交換機或需要訪問Pod的外部主機上添加路由10.0.0.1/24 via 192.168.0.1 ...即可。
Q2:你們如何去保證io的隔離?
A2:目前網絡和硬盤的io沒有做隔離,暫時還沒有這方面的剛需。kube-router對網絡IO這方面控制比較弱。硬盤IO方面Docker支持IOPS控制,但Kubernetes我還不太清楚是否支持。
Q3:Job和dind如何配合去實現打包鏡像的呢?
A3:首先是dind技術,通過掛載宿主機的docker client和docker sock,可以實現在容器內調用宿主機的Docker來做一些事情,這里我們主要就用于build。Kubernetes的Job則是用于執行這個構建worker的方式,利用Kubernetes的Job來調度構建任務,充分利用測試集群的空閑資源。
Q4:你們Kubernetes里面 統一配置是用的ConfigMap還是集成了第三方工具,例如Disconf。你們在Kubernetes中,APM用的是什么呢?Pinpoint還是Sky還是Jeager?還是其他?
A4:過去的項目配置文件是放運維平臺上的,所以只要ConfigMap掛進去就可以了。后來新的項目開始采用攜程的Apollo,Kubernetes上就只要通過ENV把Apollo的一些相關敏感信息傳給Pod即可。APM方面因為我們是Java棧所以使用的skywalking,也是開篇提到的Java agent技術。
Q5:Macvlan和IPvlan性能非常好,幾乎沒有損耗,但默認都是容器和宿主機網絡隔離的,但是也有解決方案,你們這邊是沒有考慮還是使用了一些解決方案發現有問題又放棄的?如果是后者,有什么問題讓你們選擇放棄?
A5:Macvlan之類的方式需要交換機層面上做一些配置打通VLAN,并且性能上并不會比基于三層的解決方案要高非常多,權衡之下我們還是選擇比較易用的基于三層的方案,甚至為了易用而選擇了更為激進的kube-router。
Q6:容器的多行日志收集如何解決?或者是,很多業務日志需要上下文關系,但是ELK只能查詢到單條,這種情況怎么處理呢?
A6:容器多行日志的問題只存在于標準輸出里,我們應用的日志是輸出到指定目錄下的,Filebeat有一些通用的多行日志解決方案。因為日志是存放在ES里的,所以可以通過調ES接口拿到某個Pod一個時間段里的日志,在UI上把它展示出來即可。
Q7:請問用的存儲是什么?如何集成的?存儲架構是怎樣的?有考慮過Ceph嗎?
A7:只有極少部分項目需要接分布式存儲,并且對存儲的管理,IOPS限制等沒有硬性要求,所以我們把已有的MFS集群掛載到宿主機,再掛到容器里來實現。
Q8:Jenkins的Slave是用Pod Template創建的嗎?Slave是Job共享還是需要時自動創建?
A8:Jenkins還是傳統的master-slave單機部署模式,因為版本比較舊連Kubernetes Slave都不支持,所以我們只是調用了Jenkins的API來完成這個部署的過程。
Q9:請問鏡像大小是否有做優化?生產中有用alpine之類的base鏡像嗎?
A9:暫時沒有,我們鏡像的大小大約在100-300M之間。而且比起鏡像大小的優化,運行環境的穩定和調試的便利更為重要。鏡像有分層的策略,即使是很大的鏡像,只要每次版本部署時更新的是最底層的鏡像就不會導致每次都要拉取完整鏡像。
作者:陳偲軼,荔枝研發中心DevOps工程師
來源:Docker訂閱號(ID:dockerone)
dbaplus社群歡迎廣大技術人員投稿,投稿郵箱:editor@dbaplus.cn
想了解更多運維實操演練
靈活解決項目實施疑難?
不妨來DAMS學點獨家技能
↓↓掃碼可了解更多詳情及報名↓↓
2019 DAMS中國數據智能管理峰會-上海站
總結
以上是生活随笔為你收集整理的tcpip路由技术卷一_减少与开发的撕战,结合容器化技术轻松重构运维平台的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: sqoop数据倾斜_北京卓越讯通大数据岗
- 下一篇: zenmap nmap输出无显示_液晶显