歡迎光臨
每天分享高質量文章

有關容器的六大誤區和八大正確場景


做容器的研究和容器化幾年了,從最初對於容器的初步認識,到積攢了大量的容器遷移經驗,並和客戶解釋了容器技術之後,發現原來對於容器的理解有大量的誤解,而且容器並非虛擬機的替代,而是有十分具體的應用場景的。

 

第一部分:容器的理解誤區

 

誤區一:容器啟動速度快,秒級啟動

 

這是很多人佈道容器的時候經常說的一句話,往往人們會啟動一個nginx之類的應用,的確很快就能夠啟動起來了。

 

容器為啥啟動快,一是沒有內核,二是鏡像比較小。

 

然而容器是有主行程的,也即Entrypoint,只有主行程完全啟動起來了,容器才算真正的啟動起來,一個比喻是容器更像人的衣服,人站起來了,衣服才站起來,人躺下了,衣服也躺下了。衣服有一定的隔離性,但是隔離性沒那麼好。衣服沒有根(內核),但是衣服可以隨著人到處走。

 

所以按照一個nginx來評判一個容器的啟動速度有意義麽?對於Java應用,裡面安裝的是tomcat,而tomcat的啟動,加載war,並且真正的應用啟動起來,如果你盯著tomcat的日誌看的話,還是需要一些時間的,根本不是秒級。如果應用啟動起來要一兩分鐘,僅僅談容器的秒級啟動是沒有意義的。

 

現在OpenStack中的VM的啟動速度也優化的越來越快了,啟動一個VM的時候,原來需要從Glance下載虛擬機鏡像,後來有了一個技術,是的Glance和系統盤共享Ceph儲存的情況下,虛擬機鏡像無需下載,啟動速度就快很多。

 

而且容器之所以啟動速度快,往往建議使用一個非常小的鏡像,例如alpine,裡面很多東西都裁剪掉了,啟動的速度就更快了。

 

OpenStack的虛擬機鏡像也可以經過大量的裁剪,實現快速的啟動

 

我們可以精細的衡量虛擬機啟動的每一個步驟,裁剪掉相應的模塊和啟動的過程,大大降低虛擬機的啟動時間。

 

例如在UnitedStack的一篇博客裡面:

https://www.ustack.com/blog/build-block-storage-service,我們可以看到這樣的實現和描述

 

 

“使用原生的OpenStack創建虛擬機需要1~3分鐘,而使用改造後的OpenStack僅需要不到10秒鐘時間。這是因為nova-compute不再需要通過HTTP下載整個鏡像,虛擬機可以通過直接讀取Ceph中的鏡像資料進行啟動。”

 

所以對於虛擬機的整體啟動時間,現在優化的不錯的情況下,一般能夠做到十幾秒到半分鐘以內。這個時間和Tomcat的啟動時間相比較,其實不算是負擔,和容器的啟動速度相比,沒有質的差別,可能有人會說啟動速度快一點也是快,尤其是對於在線環境的掛掉自修複來講,不是分秒必爭麽?關於自修複的問題,我們下麵另外說。

 

然而虛擬機有一個好處,就是隔離性好,如果容器是衣服,虛擬機就是房子,房子立在那裡,裡面的人無論站著還是躺著,房子總是站著的,房子也不會跟著人走。使用虛擬機就像人們住在公寓裡面一樣,每人一間,互補干擾,使用容器像大家穿著衣服擠在公交車裡面,看似隔離,誰把公交弄壞了,誰都走不了。

 

綜上所述,容器的啟動速度不足以構成對OpenStack虛擬機的明顯優勢,然而虛擬機的隔離性,則秒殺容器。

 

誤區二:容器輕量級,每個主機會運行成百上千個容器

 

很多人會做實驗,甚至會跟客戶說,容器平臺多麼多麼牛,你看我們一臺機器上可以運行成百上千個容器,虛擬機根本做不到這一點。

 

但是一個機器運行成百上千個容器,有這種真實的應用場景麽?對於容器來講,重要的是裡面的應用,應用的核心在於穩定性和高併發支撐,而不在於密度。

 

我在很多演講的會議上遇到了很多知名的處理雙十一和618的講師,普遍反饋當前的Java應用基本上4核8G是標配,如果遇見容量不足的情況,少部分通過縱向擴容的方式進行,大部分採用橫向擴容的方式進行。

 

如果4核8G是標配,不到20個服務就可以占滿一臺物理服務器,一臺機器跑成百上千個nginx有意思麽? 這不是一個嚴肅的使用場景。

 

當然現在有一個很火的Serverless無服務架構,在無服務器架構中,所有自定義代碼作為孤立的、獨立的、常常細粒度的函式來編寫和執行,這些函式在例如AWS Lambda之類的無狀態計算服務中運行。這些計算服務可以是虛擬機,也可以是容器。對於無狀態的函式來講,需要快速的創建可刪除,而且很可能執行一個函式的時間本身就非常短,在這種情況下容器相比於虛擬機還是有一定優勢的。

 

 

目前無服務架構比較適用於運行一些任務型批量操作,利用行程級別的橫向彈性能力來抵消行程創建和銷毀帶來的較大的代價。

 

在spark和mesos的集成中,有一個Fine-Grained樣式,同通常大資料的執行的時候,任務的執行行程早就申請好了資源,等在那裡分配資源不同,這種樣式是當任務分配到的時候才分配資源,好處就是對於資源的彈性申請和釋放的能力,壞處是行程的創建和銷毀還是粒度太大,所以這種樣式下spark運行的性能會差一些。

 

 

spark的這種做法思想類似無服務架構,你會發現我們原來學操作系統的時候,說行程粒度太大,每次都創建和銷毀行程會速度太慢,為了高併發,後來有了執行緒,執行緒的創建和銷毀輕量級的多,當然還是覺得慢,於是有了執行緒池,事先創建在了那裡,用的時候不用現創建,不用的時候交回去就行,後來還是覺得慢,因為執行緒的創建也需要在內核中完成,所以後來有了協程,全部在用戶態進行執行緒切換,例如AKKA,Go都使用了協程,你會發現趨勢是為了高併發,粒度是越來越細的,現在很多情況又需要行程級別的,有種風水輪流轉的感覺。

 

誤區三:容器有鏡像,可以保持版本號,可以升級和回滾

 

容器有兩個特性,一個是封裝,一個是標準。有了容器鏡像,就可以將應用的各種配置,檔案路徑,權限封裝起來,然後像孫悟空說“定”,就定在了封裝好的那一刻。鏡像是標準的,無論在哪個容器運行環境,將同樣的鏡像運行起來,都能還原當時的那一刻。

 

容器的鏡像還有版本號,我們可以根據容器的版本號進行升級,一旦升級有錯,可以根據版本號進行回滾,回滾完畢則能夠保證容器內部還是原來的狀態。

 

但是OpenStack虛擬機也是有鏡像的,虛擬機鏡像也是可以打snapshot的,打snapshot的時候,也會儲存當時的那一刻所有的狀態,而且snapshot也可以有版本號,也可以升級和回滾。

 

似乎容器有的這些特性OpenStack虛擬機都有,二者有什麼不同呢?

 

虛擬機鏡像大,而容器鏡像小。虛擬機鏡像動不動就幾十個G甚至上百G,而容器鏡像多幾百M。

 

虛擬機鏡像不適合跨環境遷移。例如開發環境在本地,測試環境在一個OpenStack上,開發環境在另一個OpenStack上,虛擬機的鏡像的遷移非常困難,需要拷貝非常大的檔案。而容器就好的多,因為鏡像小,可以很快的從不同的環境之間遷移。

 

虛擬機鏡像不適合跨雲遷移。當前沒有一個公有雲平臺支持虛擬機鏡像的下載和上傳(安全的原因,盜版的原因),因而一個鏡像在不同的雲之間,或者同一個雲不同的region直接,無法進行遷移,只能重新做一個鏡像,這樣環境的一致性就得不到保障。而容器的鏡像中心是獨立於雲之外的,只要能夠連上鏡像中心,到哪個雲上都可以下載,並且因為鏡像小,下載速度快,並且鏡像是分層的,每次只需要下載差異的部分。

 

OpenStack對於鏡像方面的優化,基本上還是在一個雲裡面起作用,一旦跨多個環境,鏡像方便的多。

 

誤區四:容器可以使用容器平臺管理自動重啟實現自修複

 

容器的自修複功能是經常被吹噓的。因為容器是衣服,人躺下了,衣服也躺下了,容器平臺能夠馬上發現人躺下了,於是可以迅速將人重新喚醒工作。而虛擬機是房子,人躺下了,房子還站著,因而虛擬機管理平臺不知道裡面的人能不能工作,所以容器掛了會被自動重啟,而虛擬機裡面的應用掛了,只要虛擬機不掛,很可能沒人知道。

 

這些說法都沒錯,但是人們慢慢發現了另外的場景,就是容器裡面的應用沒有掛,所以容器看起來還啟動著,但是應用以及不工作沒有反應了。當啟動容器的時候,雖然容器的狀態起來了,但是裡面的應用還需要一段時間才能提供服務。所以針對這種場景,容器平臺會提供對於容器裡面應用的health check,不光看容器在不在,還要看裡面的應用能不能用,如果不能,可自動重啟。

 

一旦引入了health check,和虛擬機的差別也不大了,因為有了health check,虛擬機也能看裡面的應用是否工作了,不工作也可以重啟應用。

 

還要就是容器的啟動速度快,秒級啟動,如果能夠自動重啟修複,那就是秒級修複,所以應用更加高可用。

 

這個觀點當然不正確,應用的高可用性和重啟的速度沒有直接關係。高可用性一定要通過多個副本來實現,在任何一個掛掉之後,不能通過這一個應用快速重啟來解決,而是應該靠掛掉的期間,其他的副本馬上把任務接過來進行解決。虛擬機和容器都可以有多副本,在有多個副本的情況下,重啟是一秒還是20秒,就沒那麼重要了,重要的是掛掉的這段時間內,程式做了什麼,如果程式做的是無關緊要的操作,那麼掛了20秒,也沒啥關係,如果程式正在進行一個交易和支付,那掛掉一秒也不行,也必須能夠修複回來。所以應用的高可用性要靠應用層的重試,冪等去解決,而不應該靠基礎設施層重啟的快不快來解決。

 

對於無狀態服務,在做好重試的機制的情況下,通過自動重啟修複是沒有問題的,因為無狀態的服務不會儲存非常重要的操作。

 

對於有狀態服務,容器的重啟不但不是推薦的,而且可能是災難的開始。一個服務有狀態,例如資料庫,在高併發場景下,一旦掛了,哪怕只有一秒,我們必須要弄清楚這一秒都發生了什麼,哪些資料儲存了,哪些資料丟了,而不能盲目的重啟,否則會很可能造成資料的不一致性,後期修都沒法修。例如高頻交易下的資料庫掛了,按說DBA應該嚴格審核丟了哪些資料,而不是在DBA不知情的情況下,盲目的重啟了,DBA還覺得沒什麼事情發生,最終很久才能發現問題。

 

所以容器比較適合部署無狀態服務的,隨便重啟都可以。

 

而容器部署有狀態容器不是不能,而是要非常小心,甚至都是不推薦的。雖然很多的容器平臺都支持有狀態容器,然而平臺往往解決不了資料問題,除非你對容器裡面的應用非常非常非常熟悉,當容器掛了,你能夠準確的知道丟了哪些,哪些要緊,哪些不要緊,而且要寫代碼處理這些情況,然後才能支持重啟。網易這面的資料庫主備同步的情況下,是通過修改mysql原始碼,保證主備之間資料完全同步,才敢在主掛了的情況下,備自動切換主。

 

而宣傳有狀態容器的自動重啟,對於服務客戶來講是很不經濟的行為,因為客戶往往沒有那麼清楚應用的邏輯,甚至應用都是買的,如果使用有狀態容器,任憑自動重啟,最終客戶發現資料丟失的時候,還是會怪到你的頭上。

 

所以有狀態的服務自動重啟不是不可用,需要足夠專業才行。

 

誤區五:容器可以使用容器平臺進行服務發現

 

容器平臺swarm, kubernetes,mesos都是支持服務發現的,當一個服務訪問另一個服務,都會有服務名轉化為VIP,然後訪問具體的容器。

 

然而人們會發現,基於Java寫的應用,服務之間的呼叫多不會用容器平臺的服務發現,而是用Dubbo或者spring cloud的服務發現。因為容器平臺層的服務發現,還是做的比較基礎,基本是一個域名映射的過程,對於熔斷,限流,降級都沒有很好的支持,然而既然使用服務發現,還是希望服務發現中間件能夠做到這一點,因而服務之間的服務發現之間使用容器平臺的少,越是需要高併發的應用,越是如此。

 

那容器平臺的服務發現沒有用了麽?不是,慢慢你會發現,內部的服務發現是一方面,這些Dubbo和spring cloud能夠搞定,而外部的服務發現就不同了,比如訪問資料庫,快取等,到底是應該配置一個資料庫服務的名稱,還是IP地址呢?如果使用IP地址,會造成配置十分複雜,因為很多應用配置之所以複雜,就是依賴了太多的外部應用,也是最難管理的一方面。如果有了外部的服務發現,配置就會簡單很多,也只需要配置外部服務的名稱就可以了,如果外部服務地址變了,可以很靈活的改變外部的服務發現。

 

誤區六:容器可以基於鏡像進行彈性伸縮

 

在容器平臺上,容器有副本數的,只要將副本數從5改到10,容器就基於鏡像進行了彈性伸縮。其實這一點虛擬機也能做到,AWS的Autoscaling就是基於虛擬機鏡像的,如果在同一個雲裡面,就沒有區別。

 

當然如果跨雲無狀態容器的彈性伸縮,容器方便很多,可以實現混合雲樣式,當高併發場景下,將無狀態容器擴容到公有雲,這一點虛擬機是做不到的。

 

 

容器理解誤區總結

 

 

如圖,左面是經常掛在嘴邊的所謂容器的優勢,但是虛擬機都能一一懟回去。

 

如果部署的是一個傳統的應用,這個應用啟動速度慢,行程數量少,基本不更新,那麼虛擬機完全能夠滿足需求。

 

  • 應用啟動慢:應用啟動15分鐘,容器本身秒級,虛擬機很多平臺能優化到十幾秒,兩者幾乎看不出差別

  • 記憶體占用大:動不動32G,64G記憶體,一臺機器跑不了幾個。

  • 基本不更新:半年更新一次,虛擬機鏡像照樣能夠升級和回滾

  • 應用有狀態:停機會丟資料,如果不知道丟了啥,就算秒級啟動有啥用,照樣恢復不了,而且還有可能因為丟資料,在沒有修複的情況下,盲目重啟帶來資料混亂。

  • 行程數量少:兩三個行程相互配置一下,不用服務發現,配置不麻煩

 

如果是一個傳統應用,根本沒有必要花費精去容器化,因為白花了力氣,享受不到好處。

 

第二部分:容器化,微服務,DevOps三位一體

 

什麼情況下,才應該考慮做一些改變呢?

 

傳統業務突然被互聯網業務衝擊了,應用老是變,三天兩頭要更新,而且流量增大了,原來支付系統是取錢刷卡的,現在要互聯網支付了,流量擴大了N倍。

 

沒辦法,一個字:拆

 

拆開了,每個子模塊獨自變化,少相互影響。

拆開了,原來一個行程扛流量,現在多個行程一起扛。

 

所以稱為微服務。

 

 

微服務場景下,行程多,更新快,於是出現100個行程,每天一個鏡像。

 

容器樂了,每個容器鏡像小,沒啥問題,虛擬機哭了,因為虛擬機每個鏡像太大了。

 

所以微服務場景下,可以開始考慮用容器了。

 

 

 

虛擬機怒了,老子不用容器了,微服務拆分之後,用Ansible自動部署是一樣的。

 

這樣說從技術角度來講沒有任何問題。

 

然而問題是從組織角度出現的。

 

一般的公司,開發會比運維多的多,開發寫完代碼就不用管了,環境的部署完全是運維負責,運維為了自動化,寫Ansible腳本來解決問題。

 

然而這麼多行程,又拆又合併的,更新這麼快,配置總是變,Ansible腳本也要常改,每天都上線,不得累死運維。

 

所以這如此大的工作量情況下,運維很容易出錯,哪怕通過自動化腳本。

這個時候,容器就可以作為一個非常好的工具運用起來。

 

除了容器從技術角度,能夠使得大部分的內部配置可以放在鏡像裡面之外,更重要的是從流程角度,將環境配置這件事情,往前推了,推到了開發這裡,要求開發完畢之後,就需要考慮環境部署的問題,而不能當甩手掌柜。

 

這樣做的好處就是,雖然行程多,配置變化多,更新頻繁,但是對於某個模塊的開發團隊來講,這個量是很小的,因為5-10個人專門維護這個模塊的配置和更新,不容易出錯。

 

如果這些工作量全交給少數的運維團隊,不但信息傳遞會使得環境配置不一致,部署量會大非常多。

 

容器是一個非常好的工具,就是讓每個開發僅僅多做5%的工作,就能夠節約運維200%的工作,並且不容易出錯。

 

然而本來原來運維該做的事情開發做了,開發的老大願意麽?開發的老大會投訴運維的老大麽?

 

這就不是技術問題了,其實這就是DevOps,DevOps不是不區分開發和運維,而是公司從組織到流程,能夠打通,看如何合作,邊界如何劃分,對系統的穩定性更有好處。

 

 

 

所以微服務,DevOps,容器是相輔相成,不可分割的。

 

不是微服務,根本不需要容器,虛擬機就能搞定,不需要DevOps,一年部署一次,開發和運維溝通再慢都能搞定。

 

所以,容器的本質是基於鏡像的跨環境遷移。

 

鏡像是容器的根本性發明,是封裝和運行的標準,其他什麼namespace,cgroup,早就有了。這是技術方面。

 

在流程方面,鏡像是DevOps的良好工具。

 

容器是為了跨環境遷移的,第一種遷移的場景是開發,測試,生產環境之間的遷移。如果不需要遷移,或者遷移不頻繁,虛擬機鏡像也行,但是總是要遷移,帶著幾百G的虛擬機鏡像,太大了。

 

第二種遷移的場景是跨雲遷移,跨公有雲,跨Region,跨兩個OpenStack的虛擬機遷移都是非常麻煩,甚至不可能的,因為公有雲不提供虛擬機鏡像的下載和上傳功能,而且虛擬機鏡像太大了,一傳傳一天。

 

所以跨雲場景下,混合雲場景下,容器也是很好的使用場景。這也同時解決了僅僅私有雲資源不足,扛不住流量的問題。

 

第三部分:容器的正確使用場景

 

根據以上的分析,我們發現容器推薦使用在下麵的場景下。

 

1. 部署無狀態服務,同虛擬機互補使用,實現隔離性

 

2. 如果要部署有狀態服務,需要對裡面的應用十分的瞭解

 

3. 作為持續集成的重要工具,可以順利在開發,測試,生產之間遷移

 

4. 適合部署跨雲,跨Region,跨資料中心,混合雲場景下的應用部署和彈性伸縮

 

5. 以容器作為應用的交付物,保持環境一致性,樹立不可變更基礎設施的理念

 

6. 運行行程基本的任務型別的程式

 

7. 用於管理變更,變更頻繁的應用使用容器鏡像和版本號,輕量級方便的多

 

8. 使用容器一定要管理好應用,進行Health Check和容錯的設計

原文鏈接: 容器的六大誤區和八大正確場景

推薦閱讀:

>SUSE CaaS Platform 3新特性(附白皮書)

關於容器和Docker詳細分析,請通過“原文鏈接”參閱作者總結的<容器技術架構、網絡和生態詳解>電子書。


溫馨提示:

請搜索“ICT_Architect”“掃一掃”二維碼關註公眾號,點擊原文鏈接獲閱讀原文瞭解更多

求知若渴 虛心若愚

赞(0)

分享創造快樂