隨着容器技術愈來愈火熱,各類大會上標杆企業分享容器化收益,帶動其餘還未實施容器的企業也在考慮實施容器化。不過真要在本身企業實踐容器的時候,會認識到容器化不是一個簡單工程,甚至會有一種茫然不知從何入手的感受。html
本文總結了通用的企業容器化實施線路圖,主要針對企業有存量系統改造爲容器,或者部分新開發的系統使用容器技術的場景。不包含企業系統從0開始全新構建的場景,這種場景相對簡單。<!--more-->java
企業着手實踐容器的路線,建議從3個維度評估,而後根據評估結果落地實施。3個評估維度爲:商業目標,技術選型,團隊配合。linux
容器技術是以應用爲中心的輕量級虛擬化技術,而傳統的Xen與KVM是以資源爲中心的虛擬化技術,這是二者的本質差別。以應用爲中心是容器技術演進的指導原則,正是在這個原則指導下,容器技術相對於傳統虛擬化有幾個特色:打包既部署、鏡像分層、應用資源調度。git
基於上述容器技術特色,能夠推導出容器技術的3大使用場景:CI/CD、提高資源利用率、彈性伸縮。這3個使用場景天然推導出通用的商業層面收益:CI/CD提高研發效率、提高資源利用率下降成本、按需彈性伸縮在體驗與成本之間達成平衡。github
固然,除了商業目標以外,可能還有其餘一些考慮因素,如基於容器技術實現計算任務調度平臺、保持團隊技術先進性等。docker
CI/CD是DevOps的關鍵組成部分,DevOps是一套軟件工程的流程,用於持續提高軟件開發效率與軟件交付質量。DevOps流程來源於製造業的精益生產理念,在這個領域的領頭羊是豐田公司,《豐田套路》這本書總結豐田公司如何經過PDCA(Plan-Do-Check-Act)方法實施持續改進。PDCA一般也稱爲PDCA循環,PDCA實施過程簡要描述爲:肯定目標狀態、分析當前狀態、找出與目標狀態的差距、制定實施計劃、實施並總結、開始下一個PDCA過程。編程
DevOps基本也是這麼一個PDCA流程循環,很容易認知到PDCA過程當中效率是關鍵,同一時間段內,實施更多數量的PDCA過程,收益越高。在軟件開發領域的DevOps流程中,各類等待(等待編譯、等待打包、等待部署等)、各類中斷(部署失敗、機器故障)是影響DevOps流程效率的重要因素。segmentfault
容器技術出來以後,將容器技術應用到DevOps場景下,能夠從技術手段消除DevOps流程中的部分等待與中斷,從而大幅度提高DevOps流程中CI/CD的效率。
容器的OCI標準定義了容器鏡像規範,容器鏡像包與傳統的壓縮包(zip/tgz等)相比有兩個關鍵區別點:1)分層存儲;2)打包即部署。安全
分層存儲能夠極大減小鏡像更新時候拉取鏡像包的時間,一般應用程序更新升級都只是更新業務層(如Java程序的jar包),而鏡像中的操做系統Lib層、運行時(如Jre)層等文件不會頻繁更新。所以新版本鏡像實質有變化的只有很小的一部分,在更新升級時候也只會從鏡像倉庫拉取很小的文件,因此速度很快。性能優化
打包即部署是指在容器鏡像製做過程包含了傳統軟件包部署的過程(安裝依賴的操做系統庫或工具、建立用戶、建立運行目錄、解壓、設置文件權限等等),這麼作的好處是把應用及其依賴封裝到了一個相對封閉的環境,減小了應用對外部環境的依賴,加強了應用在各類不一樣環境下的行爲一致性,同時也減小了應用部署時間。
基於容器鏡像的這些優點,容器鏡像用到CI/CD場景下,能夠減小CI/CD過程當中的等待時間,減小因環境差別而致使的部署中斷,從而提高CI/CD的效率,提高總體研發效率。
開發人員本地開發調試完成後,提交代碼,執行構建與部署,等待部署完成後驗證功能。這個等待的過程儘量短,不然開發人員工做容易被打斷,形成後果就是效率下降。若是提交代碼後幾秒鐘就可以完成部署,那麼開發人員幾乎不用等待,工做也不會被打斷;若是須要好幾分鐘或十幾分鍾,那麼能夠想象,這十幾分鍾就是浪費了,這時候很容易作點別的事情,那麼思路又被打斷了。
因此構建CI/CD環境時候,快是第一個須要考慮的因素。要達到快,除了有足夠的機器資源免除排隊等待,引入並行編譯技術也是經常使用作法,如Maven3支持多核並行構建。
不一樣行業存在不一樣的行業規範、監管要求,各個企業有一套內部質量規範,這些要求都對軟件交付流程有定製需求,如要求使用商用的代碼掃描工具作安全掃描,如構建結果與企業內部通訊系統對接發送消息。
在團隊協同方面,不一樣的公司,對DevOps流程在不一樣團隊之間分工有差別,典型的有開發者負責代碼編寫構建出構建物(如jar包),而部署模板、配置由運維人員負責;有的企業開發人員負責構建並部署到測試環境;有的企業開發人員直接能夠部署到生產環境。這些不一樣的場景,對CI/CD的流程、權限管控都有定製需求。
OCI標準包含容器鏡像標準與容器運行時標準兩部分,容器運行時標準聚焦在定義如何將鏡像包從鏡像倉庫拉取到本地並更新、如何隔離運行時資源這些方面。得益於分層存儲與打包即部署的特性,容器鏡像從到鏡像倉庫拉取到本地運行速度很是快(一般小於30秒,依賴鏡像自己大小等因素),基於此能夠實現按需分配容器運行時資源(cpu與內存),並限定單個容器資源用量;而後根據容器進程資源使用率設定彈性伸縮規則,實現自動的彈性伸縮。
這種方式相對於傳統的按峯值配置資源方式,能夠提高資源利用率。
應用運行到容器,按需分配資源以後,理想狀況下,Kubernetes的池子裏沒有空閒的資源。這時候擴容應用實例數,新擴容的實例會因資源不足調度失敗。這時候須要資源池能自動擴容,加入新的虛擬機,調度新擴容的應用。
因爲應用對資源的配比與Flavor有要求,所以新加入的虛擬機,應當是與應用所須要的資源配比與Flavor一致的。縮容也是相似。
彈性伸縮還有一個訴求點是「平滑」,對業務作到不感知,也稱爲「優雅」擴容/縮容。
上面提到的彈性伸縮通常是有計劃或緩慢增壓的場景,存在另一種沒法預期的請求風暴場景,這種場景的特徵是沒法預測、忽然請求量增大數倍或數十倍、持續時間短。典型的例子如行情交易系統,當行情突變的時候,用戶訪問量徒增,持續幾十分鐘或一個小時。
這種場景的彈性訴求,要求短期內能將資源池擴大數倍,關鍵是速度要快(秒級),不然會來不及擴容,系統已經被沖垮(若是無限流的話)。
目前基於 Virtual Kubelet 與雲廠家的 Serverless 容器,理論上能夠提供應對請求風暴的方案。不過在具體實施時候,須要考慮傳統託管式Kubernetes容器管理平臺與Serverless容器之間互通的問題,須要基於具體廠家提供的能力來評估。
計算(大數據/AI訓練等)場景的特徵是短期內須要大量算力,算完即釋放。容器的環境一致性以及調度便利性適合這種場景。
容器技術是屬於基礎設施範圍,可是與傳統虛擬化技術(Xen/KVM)比較,容器技術是應用虛擬化,不是純粹的資源虛擬化,與傳統虛擬化存在差別。在容器技術選型時候,須要結合當前團隊在應用管理與資源管理的現狀,對照容器技術與虛擬化技術的差別,選擇最合適的容器技術棧。
(1)容器是一種輕量化的應用虛擬化技術。
在討論具體的容器技術棧的時候,先介紹目前幾種經常使用的應用虛擬化技術,當前有3種主流的應用虛擬化技術: LXC,MicroVM,UniKernel(LibOS)。
三種技術對比表:
開銷 | 體積 | 啓動速度 | 隔離/安全 | 生態 | |
---|---|---|---|---|---|
LXC | 低(幾乎爲0) | 小 | 快(等同進程啓動) | 差(內核共享) | 好 |
MicroVM | 高 | 大 | 慢(小於1s) | 好 | 中(Kata項目) |
UniKernel | 中 | 中 | 中 | 好 | 差 |
根據上述對比來看,LXC是應用虛擬化首選的技術,若是LXC沒法知足隔離性要,則能夠考慮MicroVM這種技術。當前社區已經在着手融合LXC與MicroVM這兩種技術,從應用打包/發佈調度/運行層面統一規範,Kubernetes集成Kata支持混合應用調度特性能夠了解一下。
UniKernel 在應用生態方面相對比較落後,目前在追趕中,目前經過 linuxkit 工具能夠在UniKernel應用鏡像中使用docker鏡像。這種方式筆者還未驗證過,另外docker鏡像運行起來以後,如何監控目前還未知。
從上述三種應用虛擬化技術對比,能夠得出結論: (2)容器技術與傳統虛擬化技術不斷融合中。
再從規範視角來看容器技術,能夠將容器技術定義爲: (3)容器=OCI+CRI+輔助工具。
OCI規範包含兩部分,鏡像規範與運行時規範。簡要的說,要實現一個OCI的規範,須要可以下載鏡像並解壓鏡像到文件系統上組成成一個文件目錄結構,運行時工具可以理解這個目錄結構並基於此目錄結構管理(建立/啓動/中止/刪除)進程。
容器(container)的技術構成就是實現OCI規範的技術集合。
對於不一樣的操做系統(Linux/Windows),OCI規範的實現技術不一樣,當前docker的實現,支持Windows與Linux與MacOS操做系統。當前使用最廣的是Linux系統,OCI的實現,在Linux上組成容器的主要技術:
chroot
設置容器進程的根文件系統爲堆疊出的rootfs。pid
, uts
, mount
, network
, user
namesapce 分別隔離容器進程的進程ID,時間,文件系統掛載,網絡,用戶資源。veth
, macvlan
, bridge
等技術鏈接主機網絡與容器虛擬網絡。overlay2
。廣義的容器還包含容器編排,即當下很火熱的Kubernetes。Kubernetes爲了把控容器調度的生態,發佈了CRI規範,經過CRI規範解耦Kubelet與容器,只要實現了CRI接口,均可以與Kubelet交互,從而被Kubernetes調度。OCI規範的容器實現與CRI標準接口對接的實現是CRI-O。
輔助工具用戶構建鏡像,驗證鏡像簽名,管理存儲卷等。
選擇了應用虛擬化技術以後,還須要應用調度編排,當前Kubernetes是容器領域內編排的事實標準,無論使用何種應用虛擬化技術,都已經歸入到了Kubernetes治理框架中。
Kubernetes 經過 CRI 接口規範,將應用編排與應用虛擬化實現解耦:無論使用何種應用虛擬化技術(LXC, MicroVM, LibOS),都可以經過Kubernetes統一編排。
當前使用最多的是docker,其次是cri-o。docker與crio結合kata-runtime都可以支持多種應用虛擬化技術混合編排的場景,如LXC與MicroVM混合編排。
docker(now): Moby 公司貢獻的 docker 相關部件,當前主流使用的模式。
cri-o: 由 RedHat/Intel/SUSE/IBM/Hyper 公司貢獻的實現了CRI接口的符合OCI規範的運行時,當前包括 runc
與 kata-runtime
,也就是說使用 cir-o
能夠同時運行LXC容器與MicroVM容器,具體在[Kata]()介紹中有詳細說明。
前面主要講到的是容器與編排,包括CRI接口的各類實現,咱們把容器領域的規範概括爲南向與北向兩部分,CRI屬於北向接口規範,對接編排系統,OCI就屬於南向接口規範,實現應用虛擬化。
簡單來說,能夠這麼定義容器:
容器(container) ~= 應用打包(build) + 應用分發(ship) + 應用運行/資源隔離(run)
。
build-ship-run 的內容都被定義到了OCI規範中,所以也能夠這麼定義容器:
容器(container) == OCI規範
OCI規範包含兩部分,鏡像規範與運行時規範。簡要的說,要實現一個OCI的規範,須要可以下載鏡像並解壓鏡像到文件系統上組成成一個文件目錄結構,運行時工具可以理解這個目錄結構並基於此目錄結構管理(建立/啓動/中止/刪除)進程。
容器(container)的技術構成就是實現OCI規範的技術集合。
對於不一樣的操做系統(Linux/Windows),OCI規範的實現技術不一樣,當前docker的實現,支持Windows與Linux與MacOS操做系統。當前使用最廣的是Linux系統,OCI的實現,在Linux上組成容器的主要技術:
chroot
設置容器進程的根文件系統爲堆疊出的rootfs。pid
, uts
, mount
, network
, user
namesapce 分別隔離容器進程的進程ID,時間,文件系統掛載,網絡,用戶資源。veth
, macvlan
, bridge
等技術鏈接主機網絡與容器虛擬網絡。overlay2
。廣義的容器還包含容器編排,即當下很火熱的Kubernetes。Kubernetes爲了把控容器調度的生態,發佈了CRI規範,經過CRI規範解耦Kubelet與容器,只要實現了CRI接口,均可以與Kubelet交互,從而被Kubernetes調度。OCI規範的容器實現與CRI標準接口對接的實現是CRI-O。
容器與虛擬機的差別能夠總結爲2點:應用打包與分發的差別,應用資源隔離的差別。固然,致使這兩點差別的根基是容器是以應用爲中心來設計的,而虛擬化是以資源爲中心來設計的,本文對比容器與虛擬機的差別,更多的是站在應用視角來對比。
從3個方面對比差別:資源隔離,應用打包與分發,延伸的日誌/監控/DFX差別。
容器 | 虛擬化 | |
---|---|---|
mem/cpu | cgroup, 使用時候設定 require 與 limit 值 |
QEMU, KVM |
network | Linux網絡虛擬化技術(veth,tap,bridge,macvlan,ipvlan), 跨虛擬機或出公網訪問:SNAT/DNAT, service轉發:iptables/ipvs, SR-IOV | Linux網絡虛擬化技術(veth,tap,bridge,macvlan,ipvlan), QEMU, SR-IOV |
storage | 本地存儲: 容器存儲驅動 | 本地存儲:virtio-blk |
cgroup
的內存隔離致使問題: 典型的是 JVM
虛擬機,在 JVM
啓動時候會根據系統內存自動設置 MaxHeapSize
值,一般是系統內存的1/4,可是 JVM
並未考慮 cgroup
場景,讀系統內存時候任然讀取主機的內存來設置 MaxHeapSize
,這樣會致使內存超過 cgroup
限制從而致使進程被 kill
。問題詳細闡述與解決建議參考Java inside docker: What you must know to not FAIL。典型的網絡調優參數有: 轉發表大小 ```/proc/sys/net/netfilter/nf_conntrack_max``` 使用iptables 做爲service轉發實現的時候,在轉發規則較多的時候,iptables更新因爲須要全量更新致使很是耗時,建議使用ipvs。詳細參考[華爲雲在 K8S 大規模場景下的 Service 性能優化實踐](https://zhuanlan.zhihu.com/p/37230013)。
overlay2
驅動,這種模式應用寫本地文件系統文件或修改已有文件,使用Copy-On-Write方式,也就是會先拷貝源文件到可寫層而後修改,若是這種操做很是頻繁,建議使用 volume
方式。容器 | 虛擬化 | |
---|---|---|
打包 | 打包既部署 | 通常不會把應用程序與虛擬機打包在一塊兒,經過部署系統部署應用 |
分發 | 使用鏡像倉庫存儲與分發 | 使用文件存儲 |
調度運行 | 使用K8S親和/反親和調度策略 | 使用部署系統的調度能力 |
容器 | 虛擬化 | |
---|---|---|
監控 | cpu/mem的資源上限是cgroup定義的;containerd/shim/docker-daemon等進程的監控 | 傳統進程監控 |
日誌採集 | stdout/stderr日誌採集方式變化;日誌持久化須要掛載到volume;進程會被隨機調度到其餘節點致使日誌須要實時採集不然分散很難定位 | 傳統日誌採集 |
問題定位 | 進程down以後自動拉起會致使問題定位現場丟失;沒法中止進程來定位問題由於中止即刪除實例 | 傳統問題定位手段 |
Pod
的restartPolicy
設置爲never
,進程退出後進程文件都還保留着(/var/lib/docker/containers)。可是這麼作的話須要進程沒有及時恢復,會影響業務,須要本身實現進程重拉起。與周邊的開發團隊、架構團隊、測試團隊、運維團隊評審並交流方案,與周邊團隊達成一致。
根據當前已經存在的基礎實施狀況,選擇容器化落地策略。一般使用逐步演進的方式,因爲容器化引入了獨立的網絡namespace致使容器與傳統虛擬機進程網絡隔離,逐步演進過程當中如何打通隔離的網絡是最大的挑戰。
分兩種場景討論:
不一樣服務集羣之間使用微服務點對點模式互通(SpringCloud/ServiceComb/Dubbo都是這一類): 這種模式相對複雜,在逐步容器化過程當中,要求容器網絡與傳統虛擬機網絡可以互通(難點是在虛擬機進程內可以直接訪問到容器網絡的IP地址),當前解決這個問題有幾種方法。
選擇物理機運行容器仍是虛擬機運行容器,須要結合基礎設施與業務隔離性要求綜合考慮。分兩種場景:自建IDC、租用公有云。
選擇集羣時候,是多個應用共用一個大集羣,仍是按應用分組分紅多個小集羣呢?咱們把節點規模數量>=1000的定義爲大集羣,節點數<1000的定義爲小集羣。
大集羣的優勢是資源池共享容器,方便資源調度(削峯填谷);缺點是隨着節點數量與負載數量的增多,會引入管理性能問題(須要量化):
小集羣的優勢是不會有管理性能問題,缺點是會致使資源碎片化,不容易共享。共享分兩種狀況:
選擇集羣規模的時候,能夠參考上述分析,結合實際狀況選擇適合的集羣劃分。
Helm是爲了解決K8S管理對象散碎的問題,在K8S中並無"應用"的概念,只有一個個散的對象(Deployment, ConfigMap, Service, etc),而一個"應用"是多個對象組合起來的,且這些對象之間還可能存在必定的版本配套關係。
Helm 經過將K8S多個對象打包爲一個包並標註版本號造成一個"應用",經過 Helm 管理進程部署/升級這個"應用"。這種方式解決了一些問題(應用分發更方便)同時也引入了一些問題(引入Helm增長應用發佈/管理複雜度、在K8S修改了對象後如何同步到Helm)。對因而否須要使用Helm,建議以下:
DOCKER vs LXC vs VIRTUAL MACHINES
Introducing Container Runtime Interface (CRI) in Kubernetes
Java inside docker: What you must know to not FAIL
[KVM 介紹(4):I/O 設備直接分配和 SR-IOV [KVM PCI/PCIe Pass-Through SR-IOV]](https://www.cnblogs.com/sammy...
The Rise and Fall of the Operating System
The Design and Implementation of the Anykernel and Rump Kernels
更多雲最佳實踐 https://best.practices.cloud