你應該在什麼情況下使用微服務?

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"italic"},{"type":"strong"}],"text":"本文最初發佈於tailscale博客,經原作者授權由InfoQ中文站翻譯並分享。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最近,有人問我在什麼情況下使用微服務是個好主意。在“"},{"type":"link","attrs":{"href":"https:\/\/apenwarr.ca\/log\/20201227","title":"","type":null},"content":[{"type":"text","text":"系統設計詮釋世界"}]},{"type":"text","text":"”一文中,我談到了像第二系統效應、創新者困境等宏觀問題。系統設計能回答微服務的問題嗎?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"是的,但你可能不喜歡這個答案。首先,我們需要了解一些歷史資料。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"什麼是微服務?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你可以在網上找到各種各樣的定義。我的觀點是:微服務是對單體的最極端的抵制。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當你將整個應用程序所需要的全部東西鏈接成一個很大的程序,並將其作爲一個大型二進制包部署時,就會發生這樣的情況。單體有很長的歷史,可以追溯到像CGI、Django、Rails和PHP這樣的框架。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"現在,我們要放棄這樣的假設:單體和微服務羣是僅有的兩個選項。在“一個無所不能的巨型服務”和“無數個幾乎什麼都不做的微型服務”之間,存在着一個廣泛而微妙的連續體。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果你趕時髦,你至少有那麼一次已經構建了一個單體(不管是有意的,還是因爲傳統框架鼓勵你這麼做),後來發現了單體存在的一些問題,然後,你聽說微服務就是自己需要的答案,於是開始把一切都重新設計成微服務。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"但是,不要趕時髦。在這兩個極端之間,還有許多點。其中一個可能很適合你。更好的方法是從你想要將接口放在哪裏開始。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"方框和箭頭"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接口是模塊之間的連接。模塊是相關代碼的集合。在系統設計中,我們討論“方框和箭頭”設計:模塊是方框,接口是箭頭。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"更深層次的問題是:每個方框要多大?裏面放多少東西?我們如何決定什麼時候把一個大方框分成兩個小方框?連接這些方框最好的方法是什麼?有很多方法可以做到這一點。沒人知道什麼是最好的。這是軟件架構中最困難的問題之一。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在過去幾十年裏,我們經歷了許多種“方框”。"},{"type":"link","attrs":{"href":"https:\/\/homepages.cwi.nl\/~storm\/teaching\/reader\/Dijkstra68.pdf","title":"","type":null},"content":[{"type":"text","text":"Goto語句被“認爲是有害的”"}]},{"type":"text","text":",主要是因爲它們完全忽視了任何層次結構。然後我們添加了函數或過程;這些都是非常簡單的方框,它們之間有接口(參數和返回碼)。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"根據你選擇的編程路線,你會了解到遞歸函數、選擇符(combinator)、靜態函數原型、庫(靜態鏈接或運行時鏈接)、對象(OOP)、協程、受保護的虛擬內存、進程、線程、JIT、命名空間、沙盒、chroot、Jails、容器、虛擬機、supervisor、hypervisor、微內核和"},{"type":"link","attrs":{"href":"https:\/\/en.wikipedia.org\/wiki\/Unikernel","title":"","type":null},"content":[{"type":"text","text":"unikernel"}]},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這還只是方框!一旦有了彼此隔離的方框,就需要用箭頭連接它們。爲此,我們有ABI、API、系統調用、套接字、RPC、文件系統、數據庫、消息傳遞系統和“虛擬化硬件”。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果你想爲現代Unix系統畫一個完整的方框和箭頭圖(我不會這樣做),那太瘋狂了:函數在線程內,線程在進程內,進程在容器內,容器在用戶空間內,下層是內核,內核在VM裏,運行在雲提供商數據中心裏機架上的一臺硬件上,而這些硬件是通過一個編排系統連接在一起的,等等。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"每個抽象層上的每個方框都以某種方式隔離開來,然後連接到同層或其他層上的其他方框。有些方框在其他方框內部。如果你想在二維空間中畫出這幅畫的真實版本,那麼其中的線條肯定縱橫交錯。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這一切都是經過幾十年的演變發展而來的。時髦的人稱之爲“路徑依賴”。我稱之爲一個爛攤子。我們要清楚:其中的大部分已經沒有多少價值。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"與其把注意力集中在那些醜陋的進化結果上,我們不妨來談談,人們在發明這些東西的時候想要達成什麼目標。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"對模塊化的探索"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"模塊系統的首要目標是下面這幾個:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":1,"normalizeStart":1},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"將一部分代碼與其他部分隔離"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"僅在明確指定的位置(通過定義良好的接口)重新連接這些部分"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"保證你修改的部分仍然與其他部分兼容"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":4,"align":null,"origin":null},"content":[{"type":"text","text":"升級、降級以及擴展某些部分,其他部分都不需要同步升級"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"計算機行業花費了大量時間,試圖找出所有這些模塊化問題的完美平衡,同時保證開發過程儘可能的輕鬆愉快。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一句話,我們沒有成功。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"到目前爲止,我們做得最糟糕的一個方面是隔離。如果我們能真正有效地將一部分代碼同其他部分隔離開來,那麼其他的目標也就基本可以實現了。但我們根本不知道如何做到這一點。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"隔離是一個超級困難的問題。天曉得,人們已經盡力了。但瀏覽器沙箱逃逸仍然經常發生,未被發現的特權升級攻擊在每一個操作系統上都存在,iOS越獄仍然會週期性地發生,DRM從來就無效(無論好壞),虛擬機和容器會經常被發現有漏洞,而"},{"type":"link","attrs":{"href":"https:\/\/blog.alcide.io\/insecure-by-default-kubernetes-networking","title":"","type":null},"content":[{"type":"text","text":"在像k8這樣的系統中,其容器的配置默認就是不安全的"}]},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"人們甚至已經知道,"},{"type":"link","attrs":{"href":"https:\/\/blog.cryptographyengineering.com\/2013\/02\/04\/attack-of-week-tls-timing-oracles\/","title":"","type":null},"content":[{"type":"text","text":"通過在因特網上適時地向遠程服務器發送數據包,就可以找到加密密鑰"}]},{"type":"text","text":"。與此同時,近年來,最驚人的隔離事故是"},{"type":"link","attrs":{"href":"https:\/\/meltdownattack.com\/","title":"","type":null},"content":[{"type":"text","text":"Meltdown和Spectre攻擊"}]},{"type":"text","text":",它允許計算機上的任何程序,甚至是Web瀏覽器中的JavaScript應用程序,讀取同一臺計算機上其他程序的內存,甚至是跨沙盒或虛擬機。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"每一種新的隔離技術都會經歷一個從樂觀到絕望的週期:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"新想法:這次我們終於做對了,一勞永逸地!"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最初的試驗似乎有效。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(用戶抱怨它比我們上次嘗試的方法更慢、更費事。)"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"早期的致命缺陷被發現並修復。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"廣泛部署。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"越來越微妙的缺陷被陸續發現並修復。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最終,我們會發現我們根本不知道如何修補的缺陷。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"不再對這種方法能實現有效的隔離抱有希望。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"但我們永遠不能退役這種隔離方法,因爲現在有太多的人依賴它。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"重複上述過程。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"舉例來說,在這一點上,安全人員根本不相信以下任何一項(每一項都是當時最好的技術)是絕對安全的:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Unix系統上的進程隔離和內存保護;"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當允許遠程執行代碼(即安全人員所說的RCE)時,OS進程之間的權限隔離;"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通過過濾系統調用來隔離進程;"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"互不信任的進程共享一個CPU超線程;"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"位於一個CPU內核上的虛擬機之間的內存隔離。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"據我所知,目前最先進的隔離技術,是類似於Chrome沙盒或"},{"type":"link","attrs":{"href":"https:\/\/github.com\/google\/gvisor","title":"","type":null},"content":[{"type":"text","text":"gVisor"}]},{"type":"text","text":"這樣的東西。大型瀏覽器供應商和雲計算提供商都使用這樣的工具。這些工具也不完美,但是供應商確實在儘可能快地追蹤每一個新出現的漏洞,而且新漏洞出現的速度相當緩慢。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"隔離比以往任何時候都要好……如果你把所有的隔離都放在虛擬機(VM)級別,那麼雲提供商就可以幫你完成,因爲其他人不知道怎麼做,或者無法足夠頻繁地更新。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果你信任雲提供商的VM隔離,那麼就可以希望所有已知的問題都得到了緩解;但我們有充分的理由認爲,更多的問題將被發現。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"從各方面考慮,這其實很不錯。至少我們還有有效的東西。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"很好!都用虛擬機!"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"等一等。爲每個小模塊創建一個孤立的VM是一件很痛苦的事情。一個模塊該多大?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"很久以前,當Java第一次出現時,人們的夢想是每個對象中的每個函數的每一行都有嚴格的權限限制,甚至是在相同應用程序二進制文件中的對象之間,這樣就不需要CPU強制施加的內存保護。沒人再相信他們能在這方面取得成功了。除了類似“雲函數”這樣的市場宣傳,沒有人真的認爲你應該嘗試一下。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"目前,已知的隔離方法中沒有一種是完美的,但每一種都能達到某種近似的效果。越來越有經驗的攻擊者,或者越來越有價值的目標,需要更好同時也更惱人的隔離。現在,我們所知道的最好的隔離是由一級雲提供商提供的inter-VM沙箱。在最壞的情況下,它的隔離效果會降到零。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"假如驗證被跳過,由於大多數系統的耦合是如此之緊密,一個相當有經驗的攻擊者可以在模塊之間橫向突破。因此,舉例來說,如果有人可以將惡意庫鏈接到你的Go或C++程序中,那麼他們可能會控制整個程序。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"類似地,如果程序具有數據庫的寫入權限,那麼攻擊者可能會讓它寫入數據庫中的任何地方。如果它可以連接到網絡,那麼他們就可能連接到網絡中的任何地方。如果它可以執行任意的Unix命令或系統調用,那麼他們就可能獲得Unix根訪問權限。如果它在一個容器裏,那麼他們可能會從容器中掙脫出來,進入其他容器。如果惡意數據可以使"},{"type":"link","attrs":{"href":"https:\/\/imagetragick.com\/","title":"","type":null},"content":[{"type":"text","text":"png解碼器"}]},{"type":"text","text":"崩潰,那麼他們就可能讓它做解碼器程序可以做的任何事情,等等。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一種特別強大的攻擊形式是獲得提交代碼的能力,因爲這些代碼最終將在開發人員的機器上運行,而某些開發人員或某處的生產機器可能有權執行你想要執行的操作。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"以上說法可能有點過於悲觀,但是做出這些假設,可以幫助我們避免在不提高實際安全性的情況下使系統變得過於複雜。在”"},{"type":"link","attrs":{"href":"http:\/\/cr.yp.to\/qmail\/qmailsec-20071101.pdf","title":"","type":null},"content":[{"type":"text","text":"qmail 1.0十年之際關於安全的一些思考"}]},{"type":"text","text":"“一文中,Daniel J. Bernstein指出,在qmail中添加的許多防禦措施,特別是使用chroot和不同的Unix uid隔離各個不同的組件,並不值得,而且從未得到回報。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"無論如何,我們可以理所當然地認爲,具有代碼執行能力的攻擊者“通常”可以在耦合模塊之間橫向跳轉,這幾乎適用於任何一種模塊隔離技術。這意味着只有兩種模塊邊界:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可信的:兩個模塊彼此信任對方不存在惡意,並因此可以使用弱隔離邊界;"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"不可信的:模塊彼此之間相互不信任,因此必須使用強隔離邊界。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我在這裏並沒有說什麼非常深刻的東西。現代流行的平臺已經是圍繞這一區別構建出來的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"例如,Chrome在強隔離的沙箱虛擬機中運行任意的Web JavaScript,因爲網頁是不可信的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"大多數操作系統僅僅以進程(沒有沙箱)的形式運行原生應用,共享文件系統、網絡名稱空間等,因爲我們曾經認爲,它們是相對可信的。(病毒就是這樣產生的。)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"專家們不再信任多用戶Unix系統,因爲結果證明進程隔離很脆弱。雲虛擬機默認可以無密碼sudo,因爲root與非root隔離都被證明很脆弱,所以爲什麼還要麻煩呢。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(我們仍然要求用戶在刪除所有文件或其他東西時輸入sudo,以減少人爲錯誤的影響。)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"來自多個供應商的共享庫和DLL被鏈接到來自其他供應商的應用程序中,因爲所有代碼都被假定是可信的。(這爲通過開源庫供應商進行供應鏈攻擊打開了方便之門。我仍然感到驚訝的是,這類攻擊並不經常發生。我有時懷疑,也許它們確實存在,只是很少被發現而已。)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"手機操作系統之所以會被破解,是因爲應用商店的限制應該會讓應用沙盒足夠可信,但這種隔離總是被證明太脆弱。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Kubernetes和Docker在一臺機器或VM中運行多個不完全隔離的容器,因爲這些容器都被默認爲可信的。強烈建議你不要嘗試運行“多租戶”Kubernetes集羣(不可信的應用程序代表獨立的、相互不信任的用戶),因爲事實證明,容器的隔離效果很弱。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"還有,即使你對每個服務使用像gVisor'd VM那樣的強隔離,如果代碼本身不是使用強隔離的工具鏈構建的,也不會有什麼幫助。如果一組人可以更新一個庫,然後鏈接到一組應用程序,那麼這些應用程序並不算是真正的相互隔離,無論它們以什麼方式運行。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"模塊邊界 vs 服務邊界"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果這麼多隔離層都很脆弱,那麼我們爲什麼還要費心使用它們呢?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"主要是歷史沿革;如果我們拋棄這些層中的大部分,安全性不會受到太大影響,而簡潔性將得到提升。我預計,隨着時間的推移,這會發生。我們已經看到了這種趨勢。多用戶Unix系統已幾乎絕跡;“無服務器”服務器放棄了除最強隔離類型之外的所有隔離類型,並試圖將你鎖定在你所在的雲提供商那裏。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"但是,讓我們把歷史放在一邊。我必須介紹所有這些隔離概念,這樣我才能說得簡單點:你幾乎從不出於安全原因定義模塊邊界。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"相反,模塊邊界通常遵循"},{"type":"link","attrs":{"href":"https:\/\/en.wikipedia.org\/wiki\/Conway%27s_law","title":"","type":null},"content":[{"type":"text","text":"康威定律"}]},{"type":"text","text":"。人們分解模塊的根據是他們希望如何細分團隊中的開發工作,而模塊之間的通信則取決於團隊和團隊成員之間的溝通方式。(康威定律很吸引人,也很真實,但你可以在很多其他地方讀到它。)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"模塊邊界並不能定義部署單元的大小。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"以操作系統爲例:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"ChromeOS有成千上萬的開發人員,但用戶收到的一次更新中包含一個經過全面測試的組合,其中有Linux內核、設備驅動程序、窗口管理器、Web瀏覽器等。這些模塊之間的接口可以在任何版本中更改,因爲它們不需要向後兼容(當然,硬件和Web除外)。macOS、iOS和Android採用了類似的模式。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Debian Linux有數千名開發人員,但用戶下載和安裝的是單個的軟件包。你可以將來自古老的Debian穩定版本的程序包,和來自如今的Debian不穩定版本的程序包,一起運行,而且很可能行得通。可能沒有人測試過你這樣的特定組合,但它可能可以工作,因爲程序包之間有定義得非常好的接口。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(人們在開“桌面Linux”不可靠的玩笑時,談論的總是第二種小衆而難以測試的類型,而不是第一種主流的、容易測試的類型。我不認爲人們感知到的質量差異實際上是由企業資金與開源差異所導致的。不同之處在於部署模型。)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"兩個系統都包含許多程序包(模塊),這些包是由不同團隊的許多開發人員開發的。它們的模塊之間都有接口。如果你爲每個系統畫一個方框和箭頭圖,可能看起來會非常類似:內核、驅動程序、窗口系統、沙箱、Web瀏覽器,等等。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"然而,如果是後端雲服務而不是操作系統,我們將分別稱這兩個模型爲單體和微服務,因爲它們的部署模型。一個只有一個要部署的“服務”,而另一個有許多,每個都要單獨部署。相同的模塊架構!這是怎麼回事呢?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"模塊邊界和服務邊界是兩個不同的東西。"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"服務的邊界應該在哪裏?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"讓我們回顧一下最初的模塊化目標:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":1,"normalizeStart":1},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"隔離:如果出於安全考慮,確實需要強隔離,如果你需要單獨的服務,那麼唯一的方法就是分別提供虛擬機。(不過請注意:這更多的是隔離系統的限制,而非架構目標。“基礎設施即代碼”和"},{"type":"link","attrs":{"href":"https:\/\/martinfowler.com\/bliki\/BlueGreenDeployment.html","title":"","type":null},"content":[{"type":"text","text":"藍\/綠部署"}]},{"type":"text","text":"會設法讓這些服務再次同步,所以你可以有一個單體式的部署模型。)"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"連接:遵循康威定律。模塊邊界傾向於遵循團隊的個性化溝通模式。但與直覺相反,康威定律並不需要定義服務邊界。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"兼容性保證:迫使你轉向單體。如果你的單體是用一種類型安全的語言編寫的,比如Go、TypeScript、Rust,甚至是C++,則尤其如此。(例如,Chrome就是一個巨大的二進制文件。)"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":4,"align":null,"origin":null},"content":[{"type":"text","text":"升級、降級和可擴展性:這些是決定服務邊界的主要因素。關於這一點,讓我們再進一步探討下。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"以下是選擇服務邊界時需要考慮的一些事項:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你的單體需要很長時間才能啓動嗎?這讓升級變得很痛苦,所以你可能想把慢的部分分離出來,讓其他東西可以更快的升級。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你需要正確的數據存儲模式版本嗎?有時,這需要對後端所有實例進行同步升級\/降級,以便它們處於相同的模式版本上。同步升級是有風險的,而且往往會妨礙回滾;有時,你希望使依賴於模式的部分儘可能小。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"持續集成測試經常失敗嗎?如果是這樣,那真是個壞消息。那些失敗的測試說明代碼有問題。這是一個功能!拆分服務並單獨推出可能會欺騙測試,使其通過,但在生產環境中,你將會遇到兼容性和版本不對稱問題。那沒提供什麼幫助。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"有些部分的擴展方式不同於其他部分嗎?例如,一些操作佔用大量內存,而另一些操作佔用大量CPU。這並不像你想的那麼重要。如果所有實例的負載都得到了適當的平衡,那麼負載就會以一種非常有效的方式自然地分散在各處。如果負載平衡成爲問題,你可以測量它,並在稍後修復特定粒度問題。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"成本很高的請求是否需要以較低的並行度運行?一種常見的微服務架構是將請求轉儲到消息隊列中,並讓工作者實例按順序處理請求。但這比你想象的更容易出錯,而且有"},{"type":"link","attrs":{"href":"http:\/\/cpitman.github.io\/microservices\/2018\/03\/25\/microservice-antipattern-queue-explosion.html","title":"","type":null},"content":[{"type":"text","text":"更好的設計"}]},{"type":"text","text":"可以避免“隊列爆炸”問題。你可以在單體上實現同樣的設計。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你的服務是否具有不同的質量\/可靠性目標?這可能是拆分服務的一個好理由。例如,在Tailscale,我們只有幾個服務具有非常嚴格的正常運行時間目標:"},{"type":"link","attrs":{"href":"https:\/\/tailscale.com\/blog\/how-tailscale-works\/","title":"","type":null},"content":[{"type":"text","text":"協調服務"}]},{"type":"text","text":"和"},{"type":"link","attrs":{"href":"https:\/\/tailscale.com\/blog\/the-log-blog\/","title":"","type":null},"content":[{"type":"text","text":"日誌捕捉服務"}]},{"type":"text","text":"。爲了安全隔離,這兩個已經被分開了,因爲日誌非常敏感。最重要的是,我們的“實時”日誌\/指標處理管道可以容忍更長的停機時間,因此可以進行更多的試驗,因此我們把它從高可靠性服務中分離出來,用了不同的部署過程。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"事實上,在服務之間創建邊界時,上面的大部分理由都不是非常令人信服。它們是劃分模塊或團隊邊界的好理由!但你可以在將模塊重新組合成一個或幾個單體後推出它們。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"記住,ChromeOS是個單體,iOS也是個單體。你的團隊可能比這兩個團隊都小得多。你根本不需要爲了得到你想要的東西而在大量的微服務上做文章。用簡單的方法設計系統,直到你不得不用困難的方法。這就是我們的工作。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"英文原文:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"https:\/\/tailscale.com\/blog\/modules-monoliths-and-microservices\/"}]}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章