容器 微服務 雲原生 雲端 PaaS Docker

評估支援雲原生基礎架構 正確選用平台部署應用系統

何種應用適合PaaS平台 容器/微服務架構釋疑

2021-11-23
雲原生特性在應用系統架構和基礎設施中獲得越來越廣泛地採用,開源軟體社群也不斷改進並抽象化對開發人員隱藏基礎設施的複雜性,讓管理人員評估應使用哪種運算平台來部署應用系統,對此本文將說明容器技術注意事項和微服務架構的導入風險。

 

近年因容器和無伺服器運算出現,讓應用系統能夠迅速部署到公有雲,進而發展出雲原生技術,而企業所建置的私有雲該如何使用,系統架構如何設計才符合雲原生定義,無須改寫程式就可在雲端運行,這些問題在個人服務過的國泰金和目前的北富銀,不斷有同事提出類似的問題和疑惑,故本文將協助IT人員判斷負責之應用系統如何決定運行平台,為應用系統提供最適當且節省資源的基礎架構。

容器化技術與虛擬化技術主要差異是,新的容器化技術使用更高層次的作業系統層虛擬化,採用比虛擬機更精細的顆粒度來配置資源,同時支撐使用量不確定或變化大的營運模式,如線上購物雙十一促銷或線上購票活動,這類做法比虛擬機VM更符合雲原生架構的實踐原則。

適合使用PaaS容器平台的應用系統

由於容器與虛擬機有相當大的差異,請參閱後面內容,而容器化的方式也可能因為程式包裝方式,導致無法正確發揮容器管理平台的特點,淪為如圖1所示的作業系統式容器,而非正確的應用系統式容器。

圖1  作業系統式容器化與應用系統容器化的差異。

每個容器應該只有單一系統或服務

通常對第一次使用容器的人來說很難理解,常常將容器視為虛擬機,這意味著將程式需要的所有不同系統都塞進去,把資料庫、訊息佇列MQ及程式執行環境都放進一個容器中,這是一個很大的錯誤,這樣無法從容器化獲得任何好處,例如將程式所需服務都拆成個別容器後,應用系統與所需服務可個別管理,當應用系統忙碌時,可以再增加另一個應用系統容器來紓解工作負載,畢竟資料庫不是很簡單就可以橫向擴展。另外,當應用系統升級或資料庫改版,兩者彼此脫鉤,配合上版策略可無須中斷服務。

使用容器,就無須煩惱所有服務是否使用相同的Linux作業系統,不同的應用系統可各自使用經測試的基礎映像檔(Base Image),如Web Server使用RH7 Base Image來建立容器,Application Server使用RH8 Base Image來包裝成容器,兩者都可在容器平台上執行,與作業系統版本抽離。恰好,航運貨櫃的快速交付基本原則是一個貨櫃只裝一位顧客的單一商品,這也符合軟體容器,一個容器只裝一個程序(One Container, One Process)的精神。

只部署可在Linux上執行的應用系統

從容器技術發展歷史,2001年就有Linux VServer技術,2005年OpenVZ到2008年Linux Kernel直接整合LXC(Linux Container)功能,提供作業系統層級的Process隔離程式庫,而2013年Docker因友善的操作指令和封裝LXC使之方便使用,躍升成為容器代名詞,因此IT大廠也與Docker和Linux基金會一同制定出容器技術標準(OCI),從這簡短的介紹就可知道容器技術與Linux是出自同源,而2016年微軟推出的Windows Container,雖然也可用Docker指令來控制,但底層卻受限於Windows作業系統核心的限制,Windows Container的容器映像檔都會超過1GB的容量大小,故只有Linux Container適合部署到PaaS容器平台。

建議使用特定Base Image基礎映像檔來建置容器映像檔

由於容器映像檔建置過程是透過Dockerfile定義檔來設計,可以從無到有來建立,亦可使用特定Linux發行版的基礎映像檔(Base Image),已熟悉的套件管理工具來建立,雖然前面有談到Linux Container每個Linux發行版皆可執行,但共用相同的基礎映像檔,除了便於累積經驗來除錯,更可節省映像檔儲存空間,因採用相同Base Image會以Reference來儲存、載入和執行,如圖2所示,且不同的基礎映像檔所建立出來的容器映像檔,在效果跟大小上差異頗大。舉例來說,Ubuntu映像檔約140MB,但Alpine映像檔卻只要5MB,光檔案傳輸的效率就差了28倍,故若功能面上符合需求,盡可能採用較小的Base Image,見圖3。

圖2  容器映像檔的Layer階層結構。
圖3  各種容器Base Image的尺寸和Layer結構。

但是Base Image的程式庫或套件版本可能有安全漏洞,如何兼顧效能尺寸跟資安要求?目前Red Hat官方有推出Universal Base Images,UBI的Docker Hub位置,除了定期更新套件,還會優化整個映像檔Layer結構,在安全與效能取得平衡。而Google官方也有推出「Distroless」Docker Images,也是另一種選擇。

避免將單體式應用系統封裝成容器

雖然前面提到「One Container, One Process」,但傳統單體式應用伺服器,如IBM WebSphere Application Server,仍不適合部署到PaaS平台,由於WAS是承載多個應用系統War/Ear而設計,雖說仍可只部署一個應用系統,但是其伺服器啟動需要多個CPU和大量記憶體,因本身就是為了共用目的所設計,所以不建議將WAS、JBoss、Weblogic、Tomcat和IIS等共用式應用伺服器容器化,IBM技術文章WebSphere on the Cloud: Evolving to Microservices(https://www.ibm.com/cloud/blog/websphere-on-the-cloud-evolving-to-microservices)中有詳細的說明和解決方案。

建議採用微服務或雲原生的應用系統封裝成容器

由於單體式應用伺服器在雲端執行需要透過VM來建置部署,因此這些應用伺服器都針對新的雲原生特性來重新設計,像是IBM WebSphere Liberty(https://www.ibm.com/tw-zh/cloud/websphere-liberty)、Embedded Tomcat等,目前廣泛使用且最成熟的微服務框架就屬Spring Boot和Spring Cloud整合使用,Spring Framework仍屬單體式應用系統開發框架。推薦新軟體專案使用Spring Boot框架來開發,可完全發揮OpenShift私有雲的優點,並可輕鬆部署到雲端容器服務,如AWS EKS、GCP GKE等,如果使用Spring Boot和Spring Cloud來設計微服務架構系統,可參閱這本Microservices with Spring Boot and Spring Cloud - Second Edition(https://www.packtpub.com/product/microservices-with-spring-boot-and-spring-cloud-second-edition/9781801072977),並且將資料存放到外部,如HTTP Session改用Redis或DB存放,而不是容器內記憶體,避免容器增加後,使用者登入到不同容器應用系統時,遺失登入資料,導致不斷要使用者重新登入,這便是容器建議採用無狀態的系統設計。

而目前具雲原生特性的開發框架,都有內建程式專案封裝成容器映像檔的工具或函式庫,如Spring Boot 2.3就支援Buildpacks(https://buildpacks.io/),Red Hat Quarkus支援Google Jib(https://github.com/GoogleContainerTools/jib)和S2I(https://github.com/openshift/source-to-image),採用此方式建置容器映像檔,可依循框架所挑選的最佳基礎映像檔,無須顧慮其相容或資安漏洞問題。

微服務架構簡介

而對於微服務的定義又是另一個常常提到的問題,微服務架構是一種架構風格(Architecture Style),泛指由許多鬆散耦合且可獨立部署的小型服務所組成,主要受到服務導向架構(Service-oriented Architecture,SOA)所啟發的架構風格,這幾年隨著容器興起而開始流行起來,改良了不必要且煩雜的前置定義部分(如SOAP、WSDL等),以RESTful API/GraphQL、gRPC或新一代通訊協議來取代,並專注且有效率地撰寫實作出單一功能的簡單服務,主要解決如圖4中網站服務日益複雜,龐大的開發團隊花費大量時間在協調工作,因此依照功能模組拆成在Two Pizza Team(由Amazon CEO貝佐斯所提倡,把會議或專案人數控制在兩塊比薩就可吃飽的人數)以下的跨功能團隊,團隊內各領域技術⼈員就能採⽤Agile開發流程來設計微服務,確保可整個實現過程,保持需求彈性和技術優勢,如圖5所示。

圖4  網站每個版面內容都是後端獨立的API微服務所提供。
圖5  微服務開發團隊以業務功能區分而非技術功能。

採用微服務最大的誘因,是希望導入雲端或雲原生系統的優勢(請參閱後面雲原生特性),減少停機次數,加速開發時程,並可順利部署到私有雲或公有雲的PaaS/fPaaS/SaaS服務。

微服務架構風格並不會偏好或禁止使用任何特定的撰寫程式範例,它只是提供了將分散式應用程式之元件劃分為各自獨立單元的指導原則,每一個單元只解決它所負責的問題,單體式與微服務的差異請見圖6,這意味著如果微服務藉由訊息傳遞提供其功能,便可如前文所提到的,在微服務內部可使用任何主流程式語言來實作其商業邏輯。但目前符合企業等級的成熟微服務開發框架只有Spring Boot和Spring Cloud,因微服務屬分散式系統,必須搭配微服務設計模式才能有效發揮並順利管理維運,如表1所示。目前採用微服務的企業,多屬大型企業,如中國電商和線上影音服務,如圖6所示,因業務量才有資源可以支撐龐大的團隊來開發分散式微服務系統。因此,當企業尚未導入微服務或不熟悉微服務開發框架時,建議先把複雜度降到最低,不要直接跳到微服務,而是先採用Miniservices,再逐步導入微服務設計模式,如圖7所示,待維運及開發技能都完善後,再進行拆分成顆粒更細的Microservices,因為一套完整系統若相關配套措施還未準備好時,就直接從維運一台單體伺服器,變成十幾個微服務容器,光軟體專案版本管理就變成十倍,更何況是後續維運工作。

圖6  單體式與微服務架構的差異比較。
圖7  單體式與微服務之間還有Macroservices和Miniservices的拆分架構設計。

迷你服務和微服務設計步驟

當確定顆粒度較粗的系統架構會限制軟體交付過程、敏捷性和擴展能力時,才要將應用系統分解為更複雜的分佈式微服務,若要成功設計服務和微服務,有下列步驟:

1. 確定起點—是重構舊應用系統、替換原本舊系統還是從頭開始建立新的應用系統?因為這都會影響到微服務建置的步驟。

2. 不該直接從微服務開始,先以單體式系統內功能模組化成迷你服務為目標,採用增量式方法限制修改範圍,增加正確決策的機會,減少因錯誤決策造成損失。

3. 在服務之間定義明確的業務邊界和功能邊界,從設計、開發、部署、平台以及維運的角度來看,如採用集中式配置(ConfigMap)、無狀態設計(資料外部存放)、Log集中管理、分散式追蹤APM方案;讓迷你服務或微服務不要處理過多底層共用平台的工作。

4. 使⽤API或Event解耦服務,API屬一對一同步式通訊方式,成本高;以MQ或Event-Driven方式,可以達到一對多或多對多的Pub/Sub非同步通訊方式,成本低且低耦合,適合在高併發業務情境或外部服務成為瓶頸的情況。

5. 使用分解策略來降低服務複雜性,包括(1)保留單體延伸新功能的雙軌並行、(2)擷取單體服務到獨立新服務的先建後拆、 (3)從業務邏輯(產品與客戶獨立)和技術功能(前後端分離)拆分的單體瘦身。而且可結合以業務流程分析的DDD方式,以及從APIM/APM監控查看系統瓶頸再模組化的兩種定義方式。服務拆分不會一次到位,會隨業務成長而審視重新分解。

6.業務流程使用Orchestration或Choreography編排服務,這部分也會跟API和Event解耦服務步驟有關,最近已逐步採用BPM(https://camunda.com/)和Orchestration Engine(https://temporal.io/)的整合方案來解決複雜的業務流程組裝,見圖8。更多說明可參考IBM – 微服務文章(https://www.ibm.com/tw-zh/cloud/learn/microservices)。

圖8  微服務業務流程編排的兩種方式。

適合在PaaS上運行的應用系統建議清單

從前面的「避免將單體式應用系統封裝成容器」到「微服務架構簡介」,得知Miniservices或Microservices的輕量型應用伺服器適合運行在PaaS,但共用型的單體式應用伺服器則在IaaS上執行。

至於輕量型應用伺服器包含哪些,其定義又如何?輕量型應用伺服器也必須符合一個容器只裝一個程序(One Container, One Process)的精神,目前符合條件的輕量型伺服器包括IBM Liberty或Open Liberty、JBoss WildFly、Payara Micro等,以及支援Embedded Tomcat、RESTEasy或Netty等Web函示庫的開發框架,如VMware Spring Boot、Red Hat Quarkus、Oracle Micronaut以及符合Jakarta EE及MicroProfile標準規範的開發框架(圖9)。

圖9  支援Java的輕量型伺服器及開發框架Logo。

至於其他語言所開發的Web/API應用程式,如Python、Node.js、Go等,以及Linux Daemon程序,只要符合One Container, One Process亦可部署到PaaS(見表2清單,若未列入,亦可討論增列)。

但是,因為K8s/OpenShift必須監控應用系統的狀態,因此須提供liveness、Readiness和Startup狀態探針的方法或路徑(https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/),避免容器中的Process仍然存活,但是卻已經無法回應,OCP就可自動重啟Pod。

容器運算資源評估方法

與Kubernetes/OpenShift有關的最常見問題之一,是如何制定出在單一個Pod等級設置所需的運算資源限制,這不是一個容易回答的問題,因為這是一個典型的雞生蛋、蛋生雞例子,最終目標是希望在營運環境可以正確定義這些資源限制,但在將容器化應用系統部署到營運環境之前,這些資源設定參數,是必須透過此節所描述的過程,使用基本統計原理並結合Prometheus監控工具收集來的指標,藉由實際數據經驗,避免運算資源浪費來正確地定義CPU和記憶體使用量,此評估方法是參考Red Hat - Metrics-Driven Pod Constraints (https://cloud.redhat.com/blog/metrics-driven-pod-constraints)技術文章。

容器化的核心功能之一是在同一主機內核中共享CPU和記憶體,IT單位通常在同一個以K8s建立的叢集上託管來自不同團隊的應用系統,通常是多租戶使用情境,這意味著每組都將獲得一塊專屬的資源,進而需要ResourceQuotas和Pod等級的資源限制來強制約束避免其他應用系統運行和整個叢集的公平性,通常解決此類問題最簡單方法之一是隨便地猜想CPU和記憶體的限制約束值,這通常會有效,因為開發人員經常預估很高:一個簡單的無狀態Java Spring Boot需要8Gb記憶體和4 core CPU,當然它運行順暢良好,但這樣問題就解決了嗎?

導入PaaS私有雲的好處之一是,它能夠為IT單位帶來顯著的運算成本節省,但是初期遷移到公共雲、私有雲或混合雲時的期望,在上面描述的簡單情境,這通常會是應用系統性能良好,但一旦收到帳單,卻是受到嚴重的失望衝擊,為了有效地利用私有雲,需要正確的評估解決方案,這便是可觀察性(Observability)三大支柱(Logs、Metrics以及Tracing),Metrics可以協助確定資源限制值的用途。

初期階段

應用系統團隊通常有兩種狀況:(1)沒有資源限制,當應用系統已部署到PaaS平台後,由於太多系統及多租戶叢集的過度使用,需要節省運算成本,導致對資源限制的需要;(2)因為有過去的Metrics監控指標,故可確定資源限制的經驗值,但這是理想的情況。

第一種是最常見的情況,尤其是應用系統尚未容器化過,然後需要在首次部署到PaaS環境之設置資源限制參數,這挑戰是,此時沒有Metrics指標可以對這些參數做出任何經驗決策,這通常是開發人員設置過高評估後,導致應用系統運行良好,但資源卻未充分利用造成巨大的資源浪費,在這種情況下,有兩種選擇:

第一種選擇是利用與正式環境配置相同的SIT(System Integration Testing)環境,使用JMeter(https://jmeter.apache.org/)或Gatling(https://gatling.io/)等效能測試工具產生指標來準確估計系統的上線營運使用情況,這樣測試模擬須盡可能地符合上線後的使用情境,這對評估值正確性是息息相關。

第二個選項是先採用多數應用系統所設定的資源限制設定值(可先從0.5 core和256Mib記憶體開始:limits: cpu: "500m" memory: "256Mi"),在測試或SIT環境運作正常後,然後在營運環境持續監控應用系統,系統在營運環境監控下,重複調整資源限制參數逐步形成經驗值。

以Metrics數值來評估資源限制值

使用指標來決定資源經驗值時,首先需要確認的問題是,決定此評估值所需要的數據時間範圍,這不僅是應用系統開發人員的問題,通常回答這問題需要結合業務分析來探索應用系統的使用模式和時間模式,例如信用卡活動登記,通常是剛開放的前幾分鐘是最高資源用量,接下來登記額滿後,就只剩下零星名額查詢;或是處理應付帳款批次轉帳的應用系統,每天可能只處理幾次請求,但在月底它會處理大量的整批付款,可能每秒超過數百次請求。

如果將時間範圍選擇到沒有包括信用卡活動登記日,或是批次轉帳的月底過帳日,那麼你的分析將是錯誤的。

這些類型的請求通常稱為爆量請求(Burst Requests),在圖表上會顯示一個突然大峰值,確保這些包括這重要的爆量和任何類型的分析都在範圍之內,這也是為什麼隨著時間收集更多的數據可以更精確地確定資源限制的經驗數值。

如圖10所示的Grafana圖表,顯示了從Python程式收集到的數值,這些請求所使用的時間從30毫秒到30秒不等,這要談到自動橫向擴展的Pod數量,如果回應時間過長,撇除應用程式錯誤的可能性,就表示資源不足導致回應時間過長,但90%的CPU使用量都是0.07 core(70m),只有特定時間或特殊情況才會有CPU超過1 core(1000m),故CPU資源限制值可設為0.1 core(100m),提供多一些CPU資源,可讓應用系統在Pod數量增加轉換時間能有餘力處理請求,至於記憶體設定,則建議設定成使用觀察值+20%,避免Out of Memory(OOM)發生,K8s/OpenShift檢查容器健康狀況卻沒有回應時,導致整個容器被刪除重新建立。

圖10  Grafana上顯示了24小時內CPU使用情況之圖表。

結語

隨著雲原生特性在應用系統架構和基礎設施中獲得更廣泛的採用,開源軟體社群不斷改進並抽象化對開發人員隱藏基礎設施的複雜性,應該使用哪種運算平台來部署應用系統?這應用系統可以容器化放到OpenShift PaaS上運行嗎?這是目前多數IT人員的疑惑,本文說明容器技術注意事項和微服務架構的導入風險,而容器和無伺服器運算是目前雲端的最新選擇和技術,它們透過將應用程式和服務消耗資源的抽象等級向上轉移到應用系統層(Technology Stack)上,進而使得運算功能變得更加高效且靈活,之後文章將說明如何在IaaS、PaaS和fPaaS平台做出正確的技術選擇,尤其是金管會將逐步開放金融業上雲政策後更顯重要。

<本文作者:鄭淳尹,Docker.Taipei社群共同發起人,台北富邦銀行雲端系統部架構師,曾任微軟MVP、國泰金控技術架構師、momo購物網架構師、臺北榮民總醫院資訊工程師、玉山銀行資訊處專員、宏碁eDC維運工程師。開源技術愛好者,曾在多間大學資工系擔任Docker容器技術講師,並翻譯審閱多本容器技術書籍。>

 


追蹤我們Featrue us

本站使用cookie及相關技術分析來改善使用者體驗。瞭解更多

我知道了!