無服務器 vs 容器,2022誰來稱霸?
2022年即將到來,如同DevOps浪潮中的大多數趨勢一樣,選無服務器還是選容器已經成為困擾無數從業者的應用程序部署難題。而且從目前的情況看,這場決定軟件開發思路的大討論恐怕不會很快結束。
然而,這場關于無服務器與容器的比較,核心究竟是什么?到底是在爭二者誰更流行,還是說有人覺得無服務器只是容器的某種替代品?更有甚者,似乎有些人認為無服務器只是適用于容器環境的一種常規技術。
在本文中,我們將對這兩項技術做出詳盡比較,希望真正較量出個高下。但如果沒有明確的勝負,我們至少也要弄清楚二者有哪些共同點與區別、各自適合哪些應用用例。閑言少敘,讓我們先從背景信息聊起。
我們為什么需要無服務器計算?
多年以來,我們一直習慣于把應用程序部署在大型服務器之上。而由此帶來的資源管理或供應責任自然全部由我們自己承擔。這種方式帶來了以下幾個問題:
- 即使完全沒有任何負載需求,服務器也在持續運行,因此會消耗大量不必要的資源。
- 需要負責完成服務器維護以及正常運行時間保障等日常工作。
- 需要負責對服務器進行適當的安全更新。
- 隨著使用量的增加,我們需要親自管理服務器擴展工作;與之對應,當工作負載回落,我們又得進行規模收縮。
面對這么多現實問題,中小型企業乃至個人顯然不愿意、甚至沒辦法投入相應的精力。另外,傳統服務器模式的上述特性還會影響產品的整體上市時間與交付成本,而這些正是決定定制化軟件開發命運的核心所在。
“無服務器計算”的概念于是應愿而生。借助無服務器計算,我們可以獲得一套執行模型,由云服務商(包括AWS、Azure或者Google Cloud)通過動態分配的資源執行一段段代碼。作為用戶,我們只需要承擔應用程序代碼運行所對應的資源用量費用。如果把這種計算成本與傳統服務器相比較,我們會發現支出將得到大幅削減。這樣,我們的整體計算體驗將達成“無服務器”狀態(服務器資源的管理成本更低)。所以再次強調,無服務器不是沒有服務器——基礎設施還在,只是不再困擾我們。
我們為什么需要容器化?
容器化的必要性,在于它解決了一個重要問題:確保軟件在某一計算環境遷移至另一計算環境時,仍能正確運行。容器化應用程序還幫助不同團隊得以獨立處理應用程序中的不同部分;只要各組件間的交互方式不出現重大變化,各團隊就能安心打理自己的環節。如此一來,整個軟件開發流程會變得更輕松、開發者也能更快測試一切潛在錯誤。
在敏捷化、DevOps的世界中,上述能力的重要意義不言自明。容器能幫助開發者建立起信心,讓他們堅信自己的軟件在任何環境下都能順利運行。也正是容器化趨勢催生出了當下同樣熱門的“微服務架構”。
下面來看容器中常見的幾種綁定要素:
- 應用程序本體
- 依賴項
- 庫
- 二進制文件
- 配置文件
但為了管理這些容器,我們還需要仰仗另一套專用軟件,例如Docker Swarm、Kubernetes等等。這些軟件可以幫助我們編排容器,將其正確推送至不同的目標設備并保證它們在那里順暢運行。
下圖所示,為容器化技術的基本工作原理:
既然無服務器和容器都很重要,那二者的區別在哪里?下面我們就將逐一比較它們在實際部署中的具體表現:
1.壽命限制
無服務器:大家應該了解,函數的壽命往往很“短”。這里的短,一般是在5分鐘以內。函數的這種臨時特性,意味著運行該函數的容器也會在一次執行之后即告清除。
但也正是這種較短的生命周期,讓函數獲得了極高的敏捷性,幫助開發人員得以自由靈活地將應用程序推向各類易于擴展的生產環境。
容器:容器的情況則有所不同。容器始終保持運行,而且在執行完畢后也不會被消除。這就讓容器得以充分利用緩存性能優勢,同時被迫放棄了瞬時擴展的能力。
2. 狀態持久性
無服務器:如前文所述,函數總是具有臨時性或者說“短壽命”特性,這也決定了它們的無狀態屬性。而函數越是保持這種無狀態性,就適合被用來組合并構建起強大的整體解決方案。
無狀態計算的強大之處,在于幫助開發人員編寫出眾多強大的、可重用的函數并靈活組合起來。但也正是由于這種無狀態性,導致函數無法緩存任何內容以供后續使用。沒有了緩存機制,其延遲水平也就更高。
容器:在容器一邊,我們倒是可以充分發揮緩存優勢。為了保證即使在容器終止后數據仍能正常存儲,我們需要一種存儲機制來容納容器之外的數據。說到這里,有些朋友可能要問,緩存有那么重要嗎?為什么我們在討論中總要提起緩存?
確實重要,因為如果容器將要在目標文件上生成的對象之前就曾經出現過,那么直接重用原有結果能夠節約下大量時間。而這些原有結果正是要由緩存來存放。所以在緩存的加持下,新容器能獲得極快的構建速度。
3. 無延遲與啟動時間
無服務器:函數的無狀態與不可緩存兩大特性,決定了其必然不具備在待機期間持續運行的函數副本,這就必然導致調用時間更長。所以函數只有兩種狀態:1)“保溫”狀態,即代碼根據命令執行的15分鐘以內;除此之外的任何其他時段皆屬于2)冷啟動狀態。
結果就是,對于存在眾多并發用戶的應用場景,無服務器計算必然存在延遲問題。為此,大家可以添加以下代碼使得函數始終“保溫”。
但這畢竟只是權宜之計,只適用于函數數量不大的場景。面對數量眾多的大規模系統,我們根本無法正確管理所有虛擬函數。所以以上方法只適用于函數數量較少,沒必要驚動整體容器的情況。
容器:容器誕生于前無服務器時代,所以它當然不像無服務器那樣“轉瞬即逝”。容器就在那里,隨時準備著接收我們的HTTPS請求、再以低延遲甚至即時方式做出響應。憑借著緩存優勢,容器的啟動速度很快、無需重復創建文件,單靠緩存數據引用就足夠定位并重用原有結構。
4. 可擴展性
無服務器:在無服務器架構中,應用程序后端會自動且固有地進行擴展以滿足負載需求。另外,無服務器計算更像是自來水供應系統:只要服務商把總閘打開,消費者那邊就永遠會有水可用,且只需要為自己家中龍頭里流出的水量付費。相比之下,容器技術則更像是挨家挨戶配送的桶裝飲用水,在可擴展性上顯然不及無服務器計算。
容器:在使用基于容器的架構時,開發者需要根據需求提前部署相應數量的容器,借此滿足應用程序的擴展申請。此外,隨著需求量增加,我們往往需要部署更多容器以應對負載波動。而在實際需求超過容器配置預期時,就必然要出現可擴展性瓶頸、且沒有很好的即時解決辦法。
5 可移植性與遷移
無服務器:假定大家已經在使用AWS的多種不同服務,這時候選擇Lambda函數肯定是明智之舉,因為其能夠與其他服務順暢集成且可支持快速訪問。
即使您并沒有使用AWS服務、而且擔心供應商鎖定問題,也可以通過域映射/DNS變更等方式保證代碼中使用的所有API端點和URL始終處于您的控制之下。
這樣我們就可以隨時切斷特定服務,并將其重新定向至您所選定的其他端點(例如其他FaaS服務商)。這種方式顯然比在不受控制或者您無法調整的端點中部署硬編碼代碼要安全得多。
但考慮到市面上FaaS服務商眾多,大家對供應商鎖定問題的擔憂也自有道理。以Lambda為例,如果它無法滿足您所在地區的特定要求,大家可以執行以下操作。一切Lambda處理程序的代碼都應處于隔離狀態,僅僅以“墊片”的形式在其他模塊/類中充當邏輯。
這種方式不僅提高了可重用性,而且能夠大大降低重構時Lambda遷移的便捷度與直接性。另外,這種方式還有利于支持單元測試。下面來看瘦Lambda處理程序實例:
說起遷移,目前人們對于如何將FaaS融入現有DevOps框架仍充當爭議。組織可能一口氣編寫了幾百個函數,但在一段時間之后再也沒人清楚哪些函數中包含著哪些其他函數、又有多少函數仍在正常使用。
容器:如果大家選擇了基于容器的微服務架構,就能享受到由此帶來的良好可移植性。我們可以輕松將程序代碼從開發者的筆記本電腦處轉移到本地數據中心或者不同云服務商的云計算平臺,整個過程既不費力也不費神。
隨著企業承擔的創新壓力越來越大、產品上市時間越來越短,微服務架構的加持能幫助大家快速為應用程序建立起全新版本。因此,如果大家是出于降低遷移難度、使用豐富的容器技術堆棧等目的而決定從單體式應用程序轉向容器,那么微服務架構應該是個理想的探索起點。
然而,在云平臺上運行容器時仍然涉及眾多依賴項。例如代碼升級需要協同規劃,具體涵蓋容器主機、容器鏡像、容器引擎以及容器編排等。
對于某些需要遷移至微服務形式的遺留應用程序,直接“容器化”所帶來的操作難度和成本往往要低于對整體應用程序進行重構。
6. 開發環境與語言支持
無服務器:主流FaaS服務商所能支持的語言種類非常有限,主要有Node.js、Python、Java、C#以及Go(以AWS Lambda為例)。
容器:容器能為大家提供良好的異構開發環境、供您使用一切您所熟悉的技術堆棧??紤]到如今的開發者往往同時精通多種開發語言,這種廣泛的支持性在人員招聘層面往往極具優勢。
如果大家正打算為新項目招聘開發人員,那容器能避免我們過多考慮微服務架構中的語言選擇。不同微服務可以獨立部署、獨立擴展,各服務之間擁有明確的模塊邊界,不同服務可以由任何語言編寫并由不同團隊負責管理。
7. 系統控制
無服務器:由于消除了基礎設施層面的復雜性,AWS Lambda等FaaS服務中的函數用起來可謂便捷順滑。這樣,大家可以更多專注于產品開發與業務成果實現。也就是說,無服務器計算能夠顯著縮短產品的上市時間,但容器卻不然。但以AWS Lambda為例,無服務器計算在使用時有著諸多注意事項、一旦違反很可能引發問題。
容器:容器方面的難題主要集中在集群配置上,這些嚴峻的挑戰要求我們具備扎實的容器技術背景。好在微服務層面的控制難度不算很高,而且Kubernetes等編排框架也能幫助我們提高架構的治理與控制效率。
基于容器的微服務架構讓我們掌握了對于容器系統的全面控制權,從而實現策略設置、資源分配與管理。此外,我們還能對安全和遷移服務進行精細化控制。
而憑借這種完全控制特性,我們可以隨時通過容器系統查看容器內外的基本情況,進而跨越多種環境和大量資源開展全面且有效的測試與調試。相比之下,我們無法驗證函數的本地實現與測試,所以很難提前判斷其運行性能。
8. 高強度資源處理
讓我們繼續以AWS Lambda為例,如果某一函數的處理時長超過5分名,系統就會要求我們將該任務拆分成多個更小的任務。當然,類似的限制要求還有很多。
大家最多可以為單一函數分配1.5 GB的內存,而部署包則不能超過50 MB。但在容器方面,我們則可根據應用程序的實際需求隨意分配計算資源。
9. 測試
無服務器:我們往往很難對基于無服務器的Web應用程序進行測試,因為開發者通常無法在本地環境中重現這種實際后端環境。
容器:由于各容器運行在部署時所處的同一平臺之上,我們可以相對簡單地在生產部署之前對基于容器的應用程序進行各類測試。
10. 維護
無服務器:無服務器類應用程序的維護難度比大多數人想象中低得多。由于無服務器服務商(例如AWS Lambda)一力承擔起服務器的管理及軟件更新等日常事務,因此整體維護負擔會維持在很低的水平。
容器:與幫助開發者告別維護煩惱的無服務器不同,容器要求開發者們繼續承擔從管理到更新的所有容器維護工作。
11. 成本比較
無服務器:之前已經提到,使用AWS Lambda等無服務器函數進行應用程序部署能幫助大家擺脫不必要的資源開銷,確保應用程序代碼只根據調用操作適時運行。這種形式完全不同于以往大家熟悉的無論用不用、都要持續付費的本地基礎設施系統。
容器:容器在成本方面表現得比較傳統,同樣是在沒人用時也始終保持運行,所以客戶需要根據服務器空間向云服務商付費。
12. 部署時間
無服務器: 由于無服務器函數在體量上小于容器微服務,而且其中不捆綁任何系統依賴項,所以每款應用程序的部署時長只需要幾毫秒。另外,無服務器應用程序能夠在代碼部署完成后立即上線。
容器:雖然容器的初始設置時間較長,但在全面配置設定完成之后,后續部署也能在幾秒內結束。
什么時候選無服務器?:無服務器用例解析
無服務器計算特別適合以下用例:
- 如果您的流量模式會自動變化,那么無服務器計算不僅能自動消解波動、還能在沒有流量時暫時關閉。
- 如果大家擔心服務器的維護成本以及應用程序消耗的資源,那就表明您適合選擇無服務器計算。
- 如果您不想花太多時間思考代碼在哪里運行、具體怎么運行,請選擇無服務器。
- 無服務器網站與應用程序的編寫與部署流程不涉及任何基礎設施設置環節。因此,我們可以在幾天之內通過無服務器計算構建起功能完備的應用程序或網站。
- 無服務器架構允許您為應用程序構建起各類性能強勁的圖像與視頻服務。您可以使用這些服務動態調整圖像大小,或者針對不同類型的目標設備執行視頻轉碼等。
什么時候選容器?:容器用例解析
以下幾種應用程序部署需求特別適合選擇容器技術:
- 如果您想要自主選擇操作系統,并充分控制其中安裝的編程語言和運行時版本。
- 如果您打算使用某些軟件的特定版本,那容器也是最好的選擇。
- 如果您之前一直在使用傳統大型服務器來處理Web API、機器學習計算以及其他各種需要長時間運行的業務流程,不妨試試容器——運行效果基本一樣,而成本卻較傳統服務器更低。
- 如果您打算開發新的容器原生應用程序。
- 如果您打算對某款體量龐大且極為復雜的單體式應用程序進行重構,那最好選擇容器——容器更適合復雜的應用結構。
- 有些組織也在使用容器將現有應用程序直接遷移至更為現代的環境當中。Kubernetes等容器編排工具包含一系列明確的已知最佳實踐,能夠輕松管理大規模容器設置。
- Docker等容器編排平臺能夠通過自動規模伸縮解決流量無法預測的問題(但請注意,容器的規模伸縮過程無法即時完成)。
小結
聊了這么多,大家到底會做何選擇?看起來無服務器和容器各有各的優勢。根據具體用例,兩者都有可能成為最佳或者最差的解決方案,所以咱們最好別抱任何心理預設。
如果您的現有應用程序體量很大而且運行在本地,那么不妨先把它遷移到容器當中,之后再把某些組件轉換為函數。而如果您已經擁有完全基于微服務架構的應用程序而且不介意供應商鎖定問題,那直接選擇無服務器也挺好。
無論如何,無服務器架構那獨一無二的成本效益值得每位朋友認真考量。
總而言之,我們不能說無服務器的出現就意味著容器技術的消亡,至少短期之內不能。而且容器與無服務器也在各自的道路上不斷發展,二者相互競爭、持續演變出新形態才是我們消費者最樂于看到的未來。