針對目前熱門的微服務架構,本系列文章分三次來加以介紹,第一篇介紹了何謂微服務與物件導向設計觀念,第二篇則說明微服務架構的現況發展,以及軟體開發流程對架構和設計的影響,本文則是微服務架構之演進史系列的最後一部分。
微服務(Microservice)仍是近期的技術,個人認為其相關的發展才剛開始,在接下來的內容中,將探討未來可能的方向,預料這些趨勢將在架構典範的演進中發揮關鍵作用。微服務的最大優勢來自於其通用簡單的分散式方法:即使軟體內部的元件仍是自行管理之服務,亦可是鬆散耦合的系統,且具備之前討論的相關優點。然而,從另一個角度來看分散式,也凸顯出它最大的弱點:「撰寫分散式系統程式,先天就比單體式系統還難」,導致IT人員必須面對新問題,像是如何管理系統的變更,避免可能造成其他有連線通訊的服務發生預期之外的副作用影響?該如何防範利用網路通訊的惡意攻擊?
建構可靠性的系統
當使用微服務架構來撰寫應用程式時,必須留意諸多陷阱,尤其是要防止撰寫程式上的錯誤是很困難的,因此建構一套可靠的系統便非常具有挑戰性。
由於微服務是各自獨立治理的風格,因此可以自由地選擇最合適的程式語言或技術來開發每一個微服務,導入這種做法的一項缺點是,不同的技術堆疊(技術棧)通常會以不同的方法來定義服務組合使用的契約規格(例如Java的介面定義,或是Web服務中的WSDL檔案),有些技術甚至沒有特定的語言,或是微服務的相容性檢查器(例如Node.js基於JavaScript語言,便是一個很好的例子)。
那麼,現在處於怎樣的情況?不幸的是,目前的答案是非正式規格文件,大多數服務都附帶以自然語言來撰寫的非正式說明文件,描述客戶端應該如何使用其服務。由於自然語言本身潛在的含糊不清,使得撰寫客戶端的開發工作非常容易出錯。此外,沒有開發支援工具來檢查服務實作上是否正確地實現了它們的介面規格,好在有非官方的RAML和Swagger相關工具出現,產生網頁用來說明並測試服務API的介面規格,得以減輕這方面的問題和工作負擔。
而且為了解決這類問題,還有另一種方式,是採用有益於數據交換(含序列化),且具描述介面規格的訊息型態工具,其定義服務介面是可獨立運作,與特定技術或程式語言完全無關。然後,這些與技術無關的規格可以編譯成特定語言的介面,例如Java類型的介面或用於檢查訊息的正確型式,是基於介面,並且與傳輸通訊協定無關。提供這樣方法的工具包括Jolie(https://www.jolie-lang.org/)、Apache Thrift(https://thrift.apache.org/)和Google的Protocol Buffers(https://developers.google.com/protocol-buffers/)。
但是,這樣仍然不清楚該如何調整工具,以便實作並應用於微服務架構的訊息檢查機制(在編譯或執行時),像是REST,其介面被限制在一組固定的操作和動作,並以動態資源URL路徑來呈現。雖然Jolie嘗試提出了第一個基於操作和REST界接技術,且與介面無關,但是檢查兩者之間的綁定訊息規格之正確性仍須開發者的手動介入。另一種類似的方式,是嘗試將靜態類型檢查應用於動態語言,這些語言主要用於微服務的開發上,例如JavaScript(https://flow.org/)、Python(https://github.com/Microsoft/pyright、https://github.com/google/pytype)和Jolie。
就算是具備正規定義的API介面,仍不足以保證服務的相容性,正因如此,在執行期間,服務還必須在傳輸過程參與會話(Session),以精準的順序來進行訊息交換,但如果兩個服務進行會話並開始運行不相容的I/O傳輸,則可能導致各種問題,其中包括:客戶端對已關閉的線路或已停止的服務發送訊息、死鎖(當兩個服務期待對方發送消息,而相互等待不發送任何內容時),或是只有在成功執行了第三方分散式身分驗證授權後,客戶端才能存取伺服器所提供的API功能。
行為型別(Behavioural Types)是可以描述服務行為的種類,用於檢查兩個(或更多)服務之運作是否能夠相容,會話類型(Session Type)是行為型別的一種主要範例,用於定義撰寫結構化通訊程式的模型,目前會話類型已經成功應用於並行運算和分散式運算。
然而,在實作中仍未廣泛採用行為型別的理論,因行為型別在實際應用上仍未有突出的益處和便利,而行為介面在目前是一項熱門研究領域,很可能在未來的微服務上可以發揮作用,筆者猜想它在檢查服務通訊行為上的自動測試框架會蠻有用的,目前最著名的是Ballerina(https://ballerina.io/)。
編排是系統在通訊層上的高層次描述,與各別定義每個服務之行為的典型方法形成強烈對比,編排被用於針對行為介面的一些模式上,但它們實際是源於W3C在定義服務系統中全域行為所使用的描述語言。在過去的十年中,學術界持續在研究編排,產生出一種名為編排語言的新程式典範,如Chor(http://www.chor-lang.org/)。在編排語言中,開發人員使用編排來撰寫服務系統,然後使用編譯器自動產生成可相容的實作成果,這形成了一種正確的建構方法論,確保了一些重要的屬性,如避免死結和排除通訊上錯誤等。
編排可能在未來的微服務中扮演重要的角色,因為它縮短了需求和實作之間的差異,讓開發人員能夠在設計階段就驗證出服務在通訊上的問題,由於編譯器的正確性對從編排到分散式的實作是此方法論的重大關鍵,因此採用大量的正規模型來開發正確的編譯演算法,但仍缺少從單一通訊協定轉換到另一種協定的正式移植方式。此外,還不清楚如何將編排與彈性的部署模型相互結合,當中節點可能在執行時被複製或出錯。另外,編排語言在表達非確定性行為上仍舊有局限性,就像行為型別一樣。
信任與安全上的新挑戰
微服務架構典範帶來了許多信任和安全上的新挑戰,這些問題當然不是新產生的,因為它們也發生在服務導向架構(SOA)和一般的分散式運算之中,但是它們在微服務環境卻變得更困難更具挑戰性,在此主要討論當中一些關鍵的安全面問題。
在單體架構中,應用程式中的程序是透過內部資料結構或內部通訊(例如Socket或RMI)來進行溝通,攻擊面通常也限制在單一作業系統;相反地,微服務架構的特色在於應用程式被拆解並暴露於網路中,以API來相互作用的服務,而API是獨立的,與主機硬體架構無關,甚至是程式語言,但它們暴露出比傳統的子程式,或是大型應用系統之功能還要更多的潛在攻擊面,這還只是同一套應用系統與其他部分元件相互作用而已。此外,微服務的內部應用程式甚至還有開放從外部環境來存取,換句話說,這表示此微服務系統,基本上可以從「你家」外面發動攻擊至「屋中」的特定應用程式。
微服務架構是基於建立許多互動頻繁的輕量獨立應用程式,但這卻會導致複雜的網路架構和大量的傳輸,如此複雜的網路,明顯會增加微服務應用系統在執行整體安全的困難度,實際上,在現實世界的應用系統被分解之後,可能一下就創造出數百個微服務,正如線上租車預約App,Mytaxi(之前名為Hailo)的架構文章中便可證實(https://medium.com/@mattheath/a-long-journey-into-a-microservice-world-a714992d2841),這種先天存在的複雜性註定了整個應用系統在除錯、監控、審查和鑑識分析上不斷加劇的困難,惡意攻擊者可以利用這樣複雜性來發動對應用程式的襲擊。
對微服務架構而言,內部服務通常在開發的早期階段,一般皆預設為完全相互信任,考量到微服務的信任代表了「服務串接」中最重要的假設,當中服務可以與異質環境以開放的方式彼此溝通,但是單一微服務可能會受到惡意攻擊者的攻擊和控制,不僅會破壞單獨微服務,還會更徹底地破壞整套應用系統。舉個實際發生的著名案例,雲端或軟體服務的子網域(Subdomain)接管漏洞(DNS Hijacking)被揭露(https://medium.com/bugbountywriteup/how-i-started-a-chain-of-subdomain-takeovers-and-hacked-100s-of-companies-770d8f84885e),藉由此子網域,駭客可以在主要網域的上下文中提供任何內容。
此外,由於大多數皆允許從任一子網域存取所有用戶的Cookie,控制子網域的惡意駭客就能竄改經過身分驗證的使用者帳號及其資訊。未來微服務平台需要安全機制來監視微服務相互之間的連接與傳輸,確保對個別微服務的信任,進而限縮任一個微服務受到惡意入侵時所造成的潛在危害。
微服務架構之典範讓分散式系統具備非常優異的異質相容性,允許各種程式語言來開發,以API(DAO層)存取各式資料庫,實際上,基於微服務架構之系統的主要特色是:數量龐大的自治個體,甚至是來源不明的其他服務(再次面臨到信任問題);大量不同的網域需要管控安全問題,不同的服務供應商所造成的競爭情形;跨不同網域的大量相互存取(藉由API);沒有一體適用的安全基礎架構,不同於傳統的「可信賴的運算架構」;最後是沒有整體管控系統來執行政策面規則,只能透過Layer 7交換器或API Gateway來管制。目前學術與產業界還無法充分根本解決上述的安全問題,然而,建構安全且值得信賴的微服務架構系統仍是一項的開放問題和挑戰。
結語
微服務架構在過去幾年越來越受歡迎,無論是在學術界還是在產業界。特別是筆者所服務的網購業,每年的雙11購物節,系統架構都面臨極大的壓力和挑戰,對於關係到重要後端系統需要重構的許多公司而言,朝向微服務架構的轉變是一個敏感問題。儘管此一系列微服務相關文章提出了筆者主觀的看法,因個人能力未逮,故無法涵蓋所有微服務領域的知識,但可以確定的是,架構演變跟軟體開發流程是息息相關,如圖1所示,在單體時代所使用的瀑布式軟體開發方法,就不適用於N-tier和微服務架構。相對地,微服務架構也必須配合迭代速度快的DevOps開發方法,才能發揮出微服務的好處,輕鬆應付大量「微」服務的需求變動,若能再結合容器封裝方式,便可逐步朝向雲原生(Cloud Native)的現代化架構轉型,希望這三篇微服務系列文章,可以與正在轉型路上的資訊從業人員共勉之。
<本文作者:鄭淳尹,Docker/Moby.Taipei社群共同發起人,現為momo購物網資深工程師,曾任臺北榮民總醫院資訊工程師、玉山銀行資訊處專員、宏碁eDC維運工程師,系統維護及開發設計超過15年。開源技術愛好者,陸續在COSCUP開源人年會、Container Summit研討會、台灣微軟開發者大會、群益期貨和永豐金證券等分享資訊技術,並在多間大學資工系擔任Docker容器技術講師。現任微軟MVP,並翻譯審閱多本容器技術書籍。>