容器的六大理解誤區


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

 

這是很多人布道容器的時候經常說的一句話,往往人們會啟動一個 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,虛擬機也能看里面的應用是否工作了,不工作也可以重啟應用。

 

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

 

這個觀點當然不正確,應用的高可用性和重啟的速度沒有直接關系。高可用性一定要通過多個副本來實現,在任何一個掛掉之后,不能通過這一個應用快速重啟來解決,而是應該靠掛掉的期間,其他的副本馬上把任務接過來進行解決。

 

虛擬機和容器都可以有多副本,在有多個副本的情況下,重啟是 1 秒還是 20 秒,就沒那么重要了,重要的是掛掉的這段時間內,程序做了什么。

 

如果程序做的是無關緊要的操作,那么掛了 20 秒,也沒啥關系;如果程序正在進行一個交易和支付,那掛掉 1 秒也不行,也必須能夠修復回來。

 

所以應用的高可用性要靠應用層的重試,冪等去解決,而不應該靠基礎設施層重啟的快不快來解決。

 

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

 

對於有狀態服務,容器的重啟不但不是推薦的,而且可能是災難的開始。

 

一個服務有狀態,例如數據庫,在高並發場景下,一旦掛了,哪怕只有 1 秒,我們必須要弄清楚這 1 秒都發生了什么,哪些數據保存了,哪些數據丟了,而不能盲目的重啟,否則很可能會造成數據的不一致性,后期修都沒法修。

 

例如高頻交易下的數據庫掛了,按說 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 的虛擬機遷移都是非常麻煩,甚至不可能的,因為公有雲不提供虛擬機鏡像的下載和上傳功能,而且虛擬機鏡像太大了,一傳傳一天。

 

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

 

 

容器的十大正確使用場景

 

 

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

  • 部署無狀態服務,同虛擬機互補使用,實現隔離性。
  • 如果要部署有狀態服務,需要對里面的應用十分的了解。
  • 作為持續集成的重要工具,可以順利在開發,測試,生產之間遷移。
  • 適合部署跨雲,跨 Region,跨數據中心,混合雲場景下的應用部署和彈性伸縮。
  • 以容器作為應用的交付物,保持環境一致性,樹立不可變更基礎設施的理念。
  • 運行進程基本的任務類型的程序。
  • 用於管理變更,變更頻繁的應用使用容器鏡像和版本號,輕量級方便的多。
  • 使用容器一定要管理好應用,進行 health check 和容錯的設計。


​So,想體驗容器進行實踐演練可以前往華為雲進行操作體驗!華為雲容器引擎,高性能、企業級、開放兼容、簡單易用!
​大有可為雲容器!https://www.huaweicloud.com/product/cce.html​


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM