在 Docker 和 Kubernetes 時(shí)代,軟件開發(fā)的世界發(fā)生了怎樣的變化?有可能使用這些技術(shù)一勞永逸地構(gòu)建一個(gè)放之四海而皆準(zhǔn)的架構(gòu)嗎?當(dāng)所有東西都“打包”在容器中時(shí),有可能統(tǒng)一開發(fā)和集成的過程嗎?這些決策有什么要求?它們會(huì)帶來什么限制?它們會(huì)讓開發(fā)人員的生活變得更輕松,還是會(huì)增加不必要的復(fù)雜性?
是時(shí)候討論和闡明這些(以及其它一些)問題了!(在本文和原創(chuàng)插圖中)
本文將帶你踏上從現(xiàn)實(shí)到開發(fā)過程到架構(gòu)再回到現(xiàn)實(shí)的旅程,在沿途的每一站回答最重要的問題。我們將嘗試確定一些應(yīng)該成為體系架構(gòu)一部分的組件和原則,并在不過多深入實(shí)現(xiàn)細(xì)節(jié)的情況下展示一些示例。
這篇文章的結(jié)論可能會(huì)讓你不高興或高興。這完全取決于你的個(gè)人經(jīng)驗(yàn),你對(duì)這個(gè)分為三章的故事的看法,甚至可能取決于你閱讀時(shí)的心情。請(qǐng)?jiān)谖哪┌l(fā)表評(píng)論或提出問題,讓我知道你的想法!
一、從現(xiàn)實(shí)到開發(fā)流程
在很大程度上,我所見過或有幸參與建立的所有開發(fā)過程都服務(wù)于一個(gè)簡(jiǎn)單的目標(biāo) —— 降低將一個(gè)想法變成現(xiàn)實(shí)、并交付生產(chǎn)的時(shí)間,同時(shí)保持一定程度的代碼質(zhì)量。
這個(gè)想法是好是壞并不重要。壞主意來來去去,你可能只是嘗試一下,然后拒絕它們,讓它們自生自滅。值得一提的是,從一個(gè)壞主意中 回退(rolling back)的責(zé)任落在將你的開發(fā)流程自動(dòng)化的機(jī)器人肩上。
持續(xù)集成和交付(CI/CD)似乎是軟件開發(fā)世界中的一根救命稻草。畢竟,還有什么比這更簡(jiǎn)單的呢?你有想法,你有代碼,所以去做吧!如果不是存在一個(gè)小問題的話,這將會(huì)是完美無缺的 —— 如果與貴公司特有的技術(shù)和業(yè)務(wù)流程相分離,集成和交付流程將很難正規(guī)化。
然而,盡管這個(gè)任務(wù)看起來很復(fù)雜,生活還是在不斷引入優(yōu)秀的想法,讓我們(當(dāng)然是我自己)更接近于建立一個(gè)幾乎在任何場(chǎng)合都有用的完美機(jī)制。對(duì)我來說,實(shí)現(xiàn)這種機(jī)制的最近一步是 Docker 和 Kubernetes,它們的抽象層次和意識(shí)形態(tài)方法讓我認(rèn)為現(xiàn)有問題中的 80% 都可以通過幾乎相同的方法來解決。
剩下的 20% 我們也無法忽視。但是這正是你可以把你內(nèi)在的創(chuàng)造性天才集中于其上的有趣的工作,而不是用于處理重復(fù)性的日常任務(wù)。專注于 “架構(gòu)框架”(architectural framework)可以讓你忘記那 80% 已經(jīng)解決掉的問題。
所有這些意味著什么,Docker 是如何解決開發(fā)流程中的問題的?讓我們來看一個(gè)簡(jiǎn)單的流程,這對(duì)于大多數(shù)工作環(huán)境來說也是足夠的:
通過適當(dāng)?shù)姆椒?,你可以自?dòng)化和統(tǒng)一以下序列中的所有內(nèi)容,并在未來幾個(gè)月內(nèi)忘掉它。
建立開發(fā)環(huán)境
一個(gè)項(xiàng)目應(yīng)該包含一個(gè) docker-compose.yml 文件,這樣你就不用考慮在本地機(jī)器上運(yùn)行應(yīng)用程序/服務(wù)需要做什么和怎么做了。一個(gè)簡(jiǎn)單的 docker-compose up 命令就能啟動(dòng)你的應(yīng)用程序及其所有依賴項(xiàng)、用預(yù)制數(shù)據(jù)填充數(shù)據(jù)庫(kù)、上傳本地代碼到容器內(nèi)、啟用代碼跟蹤以便動(dòng)態(tài)編譯,并最終在預(yù)期的端口開始接收和響應(yīng)請(qǐng)求。即使是在設(shè)置新服務(wù)時(shí),你也不必?fù)?dān)心如何啟動(dòng)、向哪里提交代碼更改或使用哪些框架。所有這些都應(yīng)該在標(biāo)準(zhǔn)說明中預(yù)先描述,并由不同配置的服務(wù)模板決定: frontend、backend 和 worker。
自動(dòng)化測(cè)試
關(guān)于“黑盒”你只需要知道,所有該有的東西都已經(jīng)打包到里面了。是或否,1 或 0。更多關(guān)于我為什么將容器稱為黑盒的信息,將在本文的后面介紹。有了數(shù)量有限的可以在容器內(nèi)執(zhí)行的命令,以及描述其所有依賴關(guān)系的 docker-compose.yml,你就可以輕松地自動(dòng)化和統(tǒng)一測(cè)試工作,而無需過多關(guān)注實(shí)現(xiàn)細(xì)節(jié)。
例如, 像這樣 !
在這里,測(cè)試不僅意味著單元測(cè)試,還意味著功能測(cè)試、集成測(cè)試、檢查代碼風(fēng)格和重復(fù)代碼、檢查過時(shí)的依賴關(guān)系、可能使用違反許可證的軟件包以及許多其他事情。關(guān)鍵是所有這些都應(yīng)該封裝在你的 Docker 鏡像中。
系統(tǒng)交付
你想在何時(shí)何地安裝項(xiàng)目并不重要。結(jié)果,就像安裝過程一樣,應(yīng)該總是相同的。至于你將安裝整個(gè)生態(tài)系統(tǒng)中的哪個(gè)部分,或者從哪個(gè) git 代碼庫(kù)中獲取代碼,也沒有任何區(qū)別。這里最重要的組成部分是 冪等性(idempotence)。唯一應(yīng)該指定的是控制安裝的變量。
以下是在我看來解決這個(gè)問題非常有效的算法:
從你所有的 Dockerfile 中收集鏡像(例如,像 這樣 )
使用一個(gè)元項(xiàng)目,通過 Kube API 接口將這些鏡像傳遞給 Kubernetes。啟動(dòng)交付過程通常需要如下幾個(gè)輸入?yún)?shù):
- Kube API 的 訪問入口(endpoint)
- 一個(gè) Secret 對(duì)象,因不同的上下文而異(local/showroom/staging/production)
- 要顯示的系統(tǒng)的名稱和這些系統(tǒng)的 Docker 鏡像的標(biāo)簽(在前面的步驟中獲得)
作為一個(gè)包含所有系統(tǒng)和服務(wù)的元項(xiàng)目的例子(換句話說,一個(gè)描述生態(tài)系統(tǒng)是如何安排的以及更新是如何傳遞給它的項(xiàng)目),我更喜歡使用 Ansible 劇本與 這個(gè)模塊 同 Kube API 進(jìn)行集成。然而,復(fù)雜的 自動(dòng)機(jī)(automators)可以引用其他選項(xiàng),我將在后面詳述我自己的選擇。但是,你必須考慮一種集中/統(tǒng)一的架構(gòu)管理方法。擁有一個(gè)這樣的方法可以讓你方便和統(tǒng)一地管理所有服務(wù)/系統(tǒng),中和即將到來的執(zhí)行類似功能的技術(shù)和系統(tǒng)叢林可能帶給你的任何復(fù)雜性。
通常,在以下情況下需要安裝環(huán)境:
- ShowRoom — 測(cè)試環(huán)境, 對(duì)系統(tǒng)進(jìn)行了一些手動(dòng)檢查或調(diào)試
- Staging — 準(zhǔn)生產(chǎn)環(huán)境,和外部系統(tǒng)的集成 (通常位于 DMZ,而不是 ShowRoom)
- Production — 生產(chǎn)環(huán)境,供最終用戶使用的實(shí)際環(huán)境
集成和交付的連續(xù)性
如果你有一個(gè)統(tǒng)一的測(cè)試方法對(duì) Docker 鏡像(或“黑盒”)進(jìn)行測(cè)試,你就可以假設(shè)這樣的測(cè)試結(jié)果將允許你無縫地(并且問心無愧地)將 特性分支(feature-branch)集成到 git 存儲(chǔ)庫(kù)的 上游(upstream)或 主分支(master)中。
或許,這里唯一的障礙是集成和交付的順序。當(dāng)沒有發(fā)布時(shí),你如何在一個(gè)擁有一組并行 功能分支(feature-branches)的系統(tǒng)上防止“競(jìng)爭(zhēng)條件”的發(fā)生?
因此,只有在沒有競(jìng)爭(zhēng)的情況下,這個(gè)過程才應(yīng)該開始,否則“競(jìng)爭(zhēng)條件”會(huì)一直縈繞在你的腦海中:
任何步驟中發(fā)生的任何失敗都應(yīng)該終止交付過程,并將任務(wù)返回給開發(fā)人員來修復(fù)錯(cuò)誤,不管是測(cè)試失敗還是代碼合并沖突。
你可以使用此流程來在多個(gè)代碼庫(kù)上工作。只需一次對(duì)所有代碼庫(kù)執(zhí)行每個(gè)步驟(在 A 庫(kù)和 B 庫(kù)上執(zhí)行步驟 1,在 A 庫(kù)和 B 庫(kù)上執(zhí)行步驟 2,如此類推),而不是在每個(gè)單獨(dú)的代碼庫(kù)上重復(fù)執(zhí)行整個(gè)過程(在 A 庫(kù)上執(zhí)行步驟 1-6,在 B 庫(kù)上執(zhí)行步驟 1-6,如此類推)。
此外,Kubernetes 還允許你進(jìn)行部分更新,以便執(zhí)行各種 A/B 測(cè)試和風(fēng)險(xiǎn)分析。Kubernetes 在內(nèi)部通過分離服務(wù)(訪問點(diǎn))和應(yīng)用程序來實(shí)現(xiàn)這一點(diǎn)。你總是可以按期望的比例平衡組件的新版本和舊版本,以便于問題分析并為潛在的回滾提供可能。
回滾系統(tǒng)
架構(gòu)框架的強(qiáng)制性要求之一是回滾任何部署過程的能力。這反過來又會(huì)帶來一些顯而易見和隱含的細(xì)微差別。以下是其中一些最重要的:
- 服務(wù)應(yīng)該能夠設(shè)置其環(huán)境以及回滾更改。例如,數(shù)據(jù)庫(kù)遷移、RabbitMQ 的 schema 等等。
- 如果無法回滾環(huán)境,那么服務(wù)應(yīng)該是 多態(tài)的(polymorphic),并且支持舊版本和新版本的代碼同時(shí)共存部署。例如:數(shù)據(jù)庫(kù)遷移不應(yīng)該導(dǎo)致舊版本服務(wù)的中斷(通常是之前的 2 到 3 個(gè)舊版本)。
- 任何服務(wù)更新的向后兼容性。通常,這是應(yīng)用編程接口 API 的兼容性、消息格式等等。
在 Kubernes 集群中的回滾狀態(tài)非常簡(jiǎn)單(運(yùn)行 kubectl rollout undo deployment/some-deployment,Kubernes 將恢復(fù)到以前的“快照”),但是為了有效地做到這一點(diǎn),你的元項(xiàng)目應(yīng)該包含關(guān)于該快照的信息。非常不鼓勵(lì)使用更復(fù)雜的交付回滾算法,盡管它們有時(shí)是必要的。
以下是觸發(fā)回滾機(jī)制的因素:
- 發(fā)布后應(yīng)用程序發(fā)生錯(cuò)誤的百分比很高
- 來自關(guān)鍵監(jiān)控點(diǎn)的信號(hào)
- 冒煙測(cè)試 (smoke tests)失敗
- 手工模式— 人為因素
確保信息安全和審計(jì)
沒有一個(gè)工作流可以神奇地“構(gòu)建”防彈車級(jí)別的安全并保護(hù)你的生態(tài)系統(tǒng)免受外部和內(nèi)部威脅,因此你需要確保你的架構(gòu)框架在執(zhí)行時(shí)著眼于公司在每個(gè)級(jí)別和所有子系統(tǒng)中的標(biāo)準(zhǔn)和安全策略。
稍后,我將在關(guān)于監(jiān)控和告警的章節(jié)中討論建議的所有三個(gè)級(jí)別解決方案,它們對(duì)系統(tǒng)完整性也至關(guān)重要。
Kubernetes 有一套良好的內(nèi)置 訪問控制機(jī)制 、 網(wǎng)絡(luò)策略 、 事件審計(jì) 和其它與信息安全相關(guān)的強(qiáng)大工具,可用于構(gòu)建出色的 周界保護(hù)(perimeter of protection)、抵御和防止攻擊以及數(shù)據(jù)泄漏。
二、從開發(fā)工作流到架構(gòu)
應(yīng)該認(rèn)真對(duì)待在開發(fā)流程和生態(tài)系統(tǒng)之間建立緊密集成的嘗試。將這種集成的需求添加到傳統(tǒng)架構(gòu)需求集(靈活性、可伸縮性、可用性、可靠性、防威脅等)中,可以極大地增加架構(gòu)框架的價(jià)值。這是非常關(guān)鍵的一個(gè)方面,它導(dǎo)致了DevOps(Development Operation)這個(gè)概念的出現(xiàn)。DevOps 是實(shí)現(xiàn)基礎(chǔ)設(shè)施完全自動(dòng)化和優(yōu)化的一個(gè)合乎邏輯的步驟。然而,一個(gè)設(shè)計(jì)良好的架構(gòu)架構(gòu)和可靠的子系統(tǒng),可以讓 DevOps 任務(wù)最小化。
微服務(wù)架構(gòu)
沒有必要贅述面向服務(wù)的架構(gòu)(SOA)的好處,包括為什么服務(wù)應(yīng)該是 “微”(micro)粒度的。我只想說,如果你已經(jīng)決定使用 Docker 和 Kubernetes,那么你很可能會(huì)理解(并接受)這樣的觀點(diǎn):維護(hù)一個(gè) 單體(monolithic)架構(gòu)是困難的,甚至在意識(shí)形態(tài)上是錯(cuò)誤的。Docker 被設(shè)計(jì)成運(yùn)行 單進(jìn)程(single process)應(yīng)用,并和持久層一起工作。它迫使我們?cè)?DDD( 領(lǐng)域驅(qū)動(dòng)開發(fā)(Domain-Driven Development))框架內(nèi)思考。在 Docker 中,被打包的代碼被視為一個(gè)暴露部分訪問端口的黑盒。
生態(tài)系統(tǒng)的關(guān)鍵組成部分和解決方案
根據(jù)我設(shè)計(jì)可用性和可靠性更高的系統(tǒng)的經(jīng)驗(yàn),有幾個(gè)組件對(duì)微服務(wù)的運(yùn)行至關(guān)重要。稍后我將列出并討論這些組件,即使以下我只是在 Kubernetes 環(huán)境中引用它們,你也可以將我的這個(gè)列表作為任何其他平臺(tái)的檢查表使用。
如果你(和我一樣)能得出這樣的結(jié)論,也即將這些組件作為一個(gè)常規(guī) Kubernetes 服務(wù)來管理是非常好的,那么我建議你在不同于 “生產(chǎn)集群”(production)的單獨(dú)集群中運(yùn)行它們。例如, “預(yù)生產(chǎn)/準(zhǔn)生產(chǎn)”(staging)集群可以在生產(chǎn)環(huán)境不穩(wěn)定時(shí)挽救你的生命,因?yàn)檫@種情況下你會(huì)迫切需要一個(gè)能提供鏡像、代碼或監(jiān)控工具的環(huán)境??梢哉f,這解決了雞和蛋的問題。
身份服務(wù)
和往常一樣,一切開始于對(duì)服務(wù)器、虛擬機(jī)、應(yīng)用程序、辦公室郵件的訪問。如果你現(xiàn)在就是或想要成為主要企業(yè)平臺(tái)之一(IBM、谷歌、微軟)的客戶,訪問問題將由服務(wù)提供商提供的服務(wù)來處理。然而,如果你想有自己的解決方案,那么這一切就只能由你自己搞定,并且不能超出你的預(yù)算?
這個(gè)關(guān)于單點(diǎn)登錄的 列表 將幫助你決定合適的解決方案,并估計(jì)安裝配置和維護(hù)該解決方案所需的工作量。當(dāng)然,你的選擇必須符合公司的安全政策并得到信息安全部門的批準(zhǔn)。
自動(dòng)化的服務(wù)開通
雖然 Kubernetes 只要求在物理機(jī)器/云虛擬機(jī)上的少量組件(docker、kubelet、kube proxy、etcd 集群),但你仍然需要能夠自動(dòng)化完成添加新機(jī)器和對(duì)集群進(jìn)行管理。以下是幾個(gè)簡(jiǎn)單的方法:
- KOPS — 這個(gè)工具允許你在兩個(gè)公有云 (AWS 或 GCE)上安裝集群
- Teraform — 這個(gè)工具讓你可以管理任何環(huán)境的基礎(chǔ)架構(gòu),并遵循 IAC( 基礎(chǔ)架構(gòu)即代碼(Infrastructure as Code))的思想
- Ansible — 用于構(gòu)建任何類型自動(dòng)化的通用工具
就我個(gè)人而言,我更喜歡第 3 個(gè)選項(xiàng)(加上一個(gè) Kubernetes 集成模塊 ),因?yàn)樗试S我同時(shí)使用服務(wù)器和 k8s 對(duì)象,并實(shí)現(xiàn)任何類型的自動(dòng)化。然而,沒有什么能阻止你使用 Teraform 及其 Kubernetes 模塊 。KOPS 不能很好地使用“裸機(jī)”,但是它仍然是一個(gè)很好的使用 AWS/GCE 的工具!
Git 代碼庫(kù)和任務(wù)跟蹤器
不用說,要為開發(fā)人員和其他相關(guān)角色提供全面的工作環(huán)境,你需要有一個(gè)團(tuán)隊(duì)協(xié)作和代碼存儲(chǔ)的地方。我很難確定哪種服務(wù)是最合適的,但我個(gè)人最喜歡的任務(wù)跟蹤工具是 redmine (免費(fèi))或 Jira (付費(fèi))。對(duì)代碼庫(kù)而言,有比較老牌的 gerrit (免費(fèi))或 bitbucket (付費(fèi))。
值得注意的是,企業(yè)環(huán)境中協(xié)作工作的兩個(gè)最一致的工具(盡管是商業(yè)版本的)是: Atlassian 和 Jetbrains 。你可以使用它們中的任何一個(gè)作為獨(dú)立的解決方案,或者將兩者的各種組件結(jié)合起來。
要充分利用任務(wù)跟蹤器和代碼庫(kù)的組合,請(qǐng)考慮它們的集成策略。例如,以下是一些確保代碼和相關(guān)任務(wù)關(guān)聯(lián)性的提示(當(dāng)然,你也可以選擇自己的方法):
- 只有當(dāng)試圖 推送(push)的分支存在相應(yīng)的任務(wù)號(hào)(如:TASK-1/feature-34)時(shí),才能提交到遠(yuǎn)程代碼庫(kù)中
- 任何分支只有在一定數(shù)量的合格代碼復(fù)查迭代之后才可以進(jìn)行合并
- 如果相應(yīng)的任務(wù)不是 “進(jìn)行中”(In Progress)或類似狀態(tài),則任何分支都應(yīng)被阻止和禁用,以備將來更新
- 任何旨在實(shí)現(xiàn)自動(dòng)化的步驟都不應(yīng)該直接提供給開發(fā)人員使用
- 只有經(jīng)過授權(quán)的開發(fā)人員才能直接修改master主分支—其它的一切都由自動(dòng)化機(jī)器人控制
- 如果相應(yīng)的任務(wù)處于 “交付”(For Delivery)或類似狀態(tài)之外的任何狀態(tài),則分支不可用于合并
Docker 注冊(cè)表
應(yīng)特別注意 Docker 鏡像管理系統(tǒng),因?yàn)樗鼘?duì)于存儲(chǔ)和交付服務(wù)至關(guān)重要。此外,該系統(tǒng)應(yīng)該支持用戶和用戶組的訪問,能夠刪除舊的和不必要的鏡像,提供圖形用戶界面和 RESTful 應(yīng)用編程接口。
你可以使用云解決方案(例如, hub.docker.com )或私有托管服務(wù),甚至可以安裝在你的 Kubernetes 集群中。作為 Docker 注冊(cè)表的企業(yè)解決方案, Vmware Harbor 就是一個(gè)很好的例子。最壞的情況是,如果你只想存儲(chǔ)鏡像,而不需要復(fù)雜的系統(tǒng),你就直接使用 Docker Registry 好了。
CI/CD 和服務(wù)交付系統(tǒng)
我們之前討論過的組件(git 存儲(chǔ)庫(kù)、任務(wù)跟蹤器、帶有 Ansible 劇本的元項(xiàng)目、外部依賴項(xiàng))都不能像懸浮在真空中一樣彼此分開運(yùn)行。將它們連接起來的是持續(xù)集成和交付服務(wù)。
CI — 持續(xù)集成 (Continuous Integration)
CD — 持續(xù)交付(Continuous Delivery)
服務(wù)應(yīng)該足夠簡(jiǎn)單,并且沒有任何與系統(tǒng)交付或配置相關(guān)的邏輯。CI/CD 服務(wù)應(yīng)該做的就是對(duì)外部世界的事件(git 存儲(chǔ)庫(kù)中的變化,任務(wù)跟蹤器中任務(wù)的移動(dòng))做出反應(yīng),并啟動(dòng)元項(xiàng)目中描述的操作。此外,CI/CD 服務(wù)是管理所有代碼存儲(chǔ)庫(kù)的控制點(diǎn)和管理它們的工具(代碼分支合并、來自上游/主分支的更新)。
我使用過一個(gè)來自 Jetbrains 的 工具 TeamCity ,這是一個(gè)相當(dāng)強(qiáng)大但非常簡(jiǎn)單的工具。但是你也可以決定嘗試其他東西,比如免費(fèi)的 Jenkins 。
在我們上面描述的方案中,集成服務(wù)主要負(fù)責(zé)啟動(dòng)四個(gè)主要流程和一個(gè)輔助流程,如下所示:
- 自動(dòng)化服務(wù)測(cè)試 — 通常情況下,對(duì)單一代碼庫(kù)而言,當(dāng)分支狀態(tài)改變或狀態(tài)改變?yōu)?ldquo;等待自動(dòng)測(cè)試”(或類似情況)時(shí)
- 服務(wù)交付 — 通常來自元項(xiàng)目和多個(gè)服務(wù)(分別來自于不同的代碼庫(kù)),當(dāng) QA 測(cè)試和生產(chǎn)環(huán)境部署的狀態(tài)分別更改為 “等待展示”(Awaiting Showroom)或 “等待交付”(Awaiting Delivery)時(shí)
- 回滾 — 通常來自元項(xiàng)目和單個(gè)服務(wù)或整個(gè)服務(wù)的特定部分,由外部事件或在交付不成功的情況下觸發(fā)
- 服務(wù)移除 — 這是從單個(gè) 測(cè)試環(huán)境(showroom)中完全移除整個(gè)生態(tài)系統(tǒng)所必需的,當(dāng) 測(cè)試中(In QA)狀態(tài)已過期或不再需要該環(huán)境時(shí)
- 鏡像構(gòu)建器(輔助過程) — 可以集成到服務(wù)交付過程中,或者獨(dú)立地用于編譯 Docker 鏡像并將其發(fā)送到 Docker 注冊(cè)表。這經(jīng)常需要處理常用的鏡像(數(shù)據(jù)庫(kù)、通用服務(wù)或不需要頻繁更改的服務(wù))
日志收集和分析系統(tǒng)
任何 Docker 容器使其日志可訪問的唯一方法是將它們寫入容器中運(yùn)行的根進(jìn)程的標(biāo)準(zhǔn)輸出或標(biāo)準(zhǔn)錯(cuò)誤設(shè)備(STDOUT 或 STDERR)。微服務(wù)的開發(fā)人員并不真正關(guān)心日志數(shù)據(jù)接下來會(huì)發(fā)生什么,只需要保證它們應(yīng)該在必要時(shí)可用,并且最好包含過去某個(gè)時(shí)刻的記錄。Kubernetes 和支持這個(gè)生態(tài)的工程師負(fù)責(zé)這一切的實(shí)現(xiàn)。
在 官方文檔 中,你可以找到處理日志的基本(也是一個(gè)好的)策略的描述,這將有助于你選擇用于聚合和存儲(chǔ)大量文本數(shù)據(jù)的服務(wù)。
在日志記錄系統(tǒng)的推薦服務(wù)中,相同的文檔提到了用于收集數(shù)據(jù)的 fluentd (作為代理在集群的每個(gè)節(jié)點(diǎn)上啟動(dòng))和存儲(chǔ)和索引數(shù)據(jù)的 Elasticsearch 。即使你可能不同意這種解決方案的效率,但它是可靠且易于使用的,所以我認(rèn)為這至少是一個(gè)好的開始。
Elasticsearch 是一個(gè)資源密集型解決方案,但是它可以很好地?cái)U(kuò)展,并且有現(xiàn)成的 Docker 鏡像來運(yùn)行單個(gè)節(jié)點(diǎn)或所需大小的集群。
追蹤系統(tǒng)
盡管你的代碼盡可能完美,但失敗確實(shí)會(huì)發(fā)生,然后你想在生產(chǎn)環(huán)境中仔細(xì)研究它們,并嘗試?yán)斫?ldquo;在我的本地機(jī)器上一切工作都是正常的呀,現(xiàn)在到底是哪里出了什么問題呢?”。數(shù)據(jù)庫(kù)查詢速度慢、緩存不當(dāng)、磁盤速度慢或與外部資源的連接速度慢、生態(tài)系統(tǒng)中的事務(wù)、瓶頸和規(guī)模不足的計(jì)算服務(wù),這些都是你必須跟蹤和估計(jì)在實(shí)際負(fù)載下執(zhí)行代碼所花費(fèi)的時(shí)間的原因。
Opentracing 和 Zipkin 可以支持在大多數(shù)現(xiàn)代編程語言中完成這一任務(wù),并且在 埋點(diǎn)(instrumenting)代碼之后不會(huì)增加任何額外的負(fù)擔(dān)。當(dāng)然,所有收集的數(shù)據(jù)都應(yīng)該存儲(chǔ)在一個(gè)合適的地方,被 組件 之一所使用。
在代碼中埋點(diǎn)并通過所有服務(wù)、消息隊(duì)列、數(shù)據(jù)庫(kù)等轉(zhuǎn)發(fā) “跟蹤標(biāo)識(shí)”(Trace Id)時(shí)帶來的復(fù)雜性由上述開發(fā)標(biāo)準(zhǔn)和服務(wù)模板解決。后者還負(fù)責(zé)保證方法的一致性。
監(jiān)控和報(bào)警
Prometheus 已經(jīng)成為現(xiàn)代系統(tǒng)中事實(shí)上的監(jiān)控和報(bào)警標(biāo)準(zhǔn),更重要的是,它在 Kubernetes 中幾乎 開箱即用 。你可以參考 官方 Kubernetes 文檔 ,了解更多有關(guān)監(jiān)控和報(bào)警的信息。
監(jiān)控是必須安裝在群集內(nèi)的少數(shù)輔助系統(tǒng)之一。集群是一個(gè)受監(jiān)控的實(shí)體。但是監(jiān)控系統(tǒng)的監(jiān)控(請(qǐng)?jiān)徶貜?fù))只能從外部執(zhí)行(例如,從相同的 “預(yù)生產(chǎn)”(Staging)環(huán)境)。在這種情況下,交叉檢查對(duì)于任何分布式環(huán)境來說都是一個(gè)方便的解決方案,不會(huì)使高度統(tǒng)一的生態(tài)系統(tǒng)的架構(gòu)復(fù)雜化。
整個(gè)監(jiān)控范圍分為三個(gè)完全邏輯隔離的級(jí)別。以下是我認(rèn)為在每個(gè)級(jí)別跟蹤點(diǎn)的最重要的例子:
- 物理資源級(jí)別:
- 網(wǎng)絡(luò)資源及其可用性
- 磁盤 (I/O,可用空間)
- 單個(gè)節(jié)點(diǎn)的基本資源(CPU、RAM、LA)
- 集群級(jí)別:
- 主集群系統(tǒng)中每個(gè)節(jié)點(diǎn)的可用性(kubelet、kubeAPI、DNS、etcd 等)
- 監(jiān)控服務(wù)允許的和實(shí)際的資源消耗
- Pod 的重新加載
- 服務(wù)級(jí)別:
- 任何類型的應(yīng)用程序監(jiān)控
- 從數(shù)據(jù)庫(kù)內(nèi)容到應(yīng)用編程接口調(diào)用的頻率
- API 網(wǎng)關(guān)上的 HTTP 調(diào)用錯(cuò)誤數(shù)
- 隊(duì)列的大小和 worker 節(jié)點(diǎn)的利用率
- 數(shù)據(jù)庫(kù)的多個(gè)指標(biāo)(復(fù)制延遲、事務(wù)的時(shí)間和數(shù)量、慢速請(qǐng)求等)
- 非 HTTP 協(xié)議進(jìn)程的錯(cuò)誤分析
- 監(jiān)控發(fā)送到日志系統(tǒng)的請(qǐng)求(你可以將任意請(qǐng)求轉(zhuǎn)換為度量值)
至于每個(gè)級(jí)別的報(bào)警通知,我建議你使用無數(shù)外部服務(wù)之一,這些服務(wù)可以通過電子郵件、短信發(fā)送通知或撥打手機(jī)號(hào)碼。我還要提到另一個(gè)系統(tǒng) — OpsGenie ,它可以 與 Prometheus 的報(bào)警管理器緊密集成 。
OpsGenie 是一種靈活的報(bào)警工具,有助于處理故障升級(jí)報(bào)告、全天候工作、通知渠道選擇等。在團(tuán)隊(duì)之間分發(fā)報(bào)警信息也很容易。例如,不同級(jí)別的監(jiān)控應(yīng)該向不同的團(tuán)隊(duì)/部門發(fā)送通知:物理資源 —> 基礎(chǔ)設(shè)施 + Devops,集群 —> Devops,應(yīng)用 — > 向相關(guān)團(tuán)隊(duì)發(fā)送通知。
API 網(wǎng)關(guān)和單點(diǎn)登錄
為了處理授權(quán)、身份驗(yàn)證、用戶注冊(cè)(外部用戶——公司的客戶)和其他類型的訪問控制等任務(wù),你需要一個(gè)高度可靠的服務(wù),它可以與您的 API 網(wǎng)關(guān)保持靈活的集成。使用與“身份服務(wù)”相同的解決方案沒有害處,但是您可能希望將這兩種資源分開,以實(shí)現(xiàn)不同級(jí)別的可用性和可靠性。
服務(wù)間集成不應(yīng)該很復(fù)雜,你的服務(wù)不應(yīng)該擔(dān)心用戶的授權(quán)和認(rèn)證。相反,架構(gòu)和生態(tài)系統(tǒng)應(yīng)該有一個(gè)代理服務(wù)來處理所有的通信和 HTTP 流量。
讓我們考慮與 API 網(wǎng)關(guān)集成的最合適的方式 — 令牌(token)。這種方法適用于所有三種訪問場(chǎng)景:從圖形用戶界面(UI),從服務(wù)到服務(wù),以及從外部系統(tǒng)。那么接收令牌的任務(wù)(基于登錄名和密碼)由用戶界面本身或服務(wù)開發(fā)人員承擔(dān)。區(qū)分用戶界面中使用的令牌的生命周期(較短的 TTL)和其他情況下使用的令牌的生命周期(較長(zhǎng)的和自定義的 TTL)也很有意義。
以下是 API 網(wǎng)關(guān)解決的一些問題:
- 從外部和內(nèi)部訪問生態(tài)系統(tǒng)中的服務(wù)(服務(wù)之間不直接交流)
- 與單點(diǎn)登錄服務(wù)集成:
- 令牌的轉(zhuǎn)換和附加 HTTPS 請(qǐng)求頭包含所請(qǐng)求服務(wù)的用戶標(biāo)識(shí)數(shù)據(jù)(ID、角色、其他詳細(xì)信息)
- 基于從單點(diǎn)登錄服務(wù)接收的角色來啟用/禁用對(duì)所請(qǐng)求服務(wù)的訪問控制
- 對(duì) HTTP 協(xié)議流量的單點(diǎn)監(jiān)控
- 組合來自不同服務(wù)的 API 接口文檔(例如,組合 Swagger 的 json/yml 文件 )
- 能夠根據(jù)域名和請(qǐng)求的 URI 來管理整個(gè)生態(tài)系統(tǒng)的路由
- 外部流量的單一接入點(diǎn),以及與接入提供者的集成
事件總線和企業(yè)集成/服務(wù)總線
如果你的生態(tài)系統(tǒng)包含在一個(gè)宏域中工作的數(shù)百個(gè)服務(wù),你將不得不處理數(shù)以千計(jì)的服務(wù)通信方式。為了簡(jiǎn)化數(shù)據(jù)流,你應(yīng)該考慮在某些事件發(fā)生時(shí)將消息分發(fā)到大量收件人的能力,而不管事件的上下文如何。換句話說,你需要一個(gè)事件總線來發(fā)布基于標(biāo)準(zhǔn)協(xié)議的事件并訂閱它們。
對(duì)于事件總線,你可以使用任何能夠操作所謂代理的系統(tǒng): RabbitMQ 、 Kafka 、 ActiveMQ 和其它類似的系統(tǒng)。一般來說,數(shù)據(jù)的高可用性和一致性對(duì)于微服務(wù)至關(guān)重要,但是由于 CAP 定理 ,你仍然需要犧牲一些東西來實(shí)現(xiàn)總線的正確分布和集群管理。
很自然,事件總線應(yīng)該能夠解決各種服務(wù)間通信的問題,但是隨著服務(wù)數(shù)量從成百上千增加到數(shù)萬,即使是最好的基于事件總線的架構(gòu)也可能會(huì)失敗,因此你需要尋找另一種解決方案。一個(gè)很好的例子是集成總線方法,它可以擴(kuò)展上述 “愚蠢的管道 —智能的消費(fèi)者”(Dumb pipe — Smart consumer)策略的能力。
有很多原因決定了需要使用“ 企業(yè)集成/服務(wù)總線 ”方法,該方法旨在降低面向服務(wù)架構(gòu)的復(fù)雜性。下面列出了部分原因:
- 多個(gè)消息的聚合
- 將一個(gè)事件拆分成幾個(gè)事件
- 系統(tǒng)對(duì)事件響應(yīng)的同步/事務(wù)分析
- 接口的適配,這對(duì)與外部系統(tǒng)的集成尤其重要
- 事件路由的高級(jí)邏輯
- 與相同服務(wù)的多重集成(從外部和內(nèi)部)
- 數(shù)據(jù)總線的不可擴(kuò)展集中化
作為企業(yè)集成總線的開源軟件,你可能需要考慮 Apache ServiceMix ,它包含了設(shè)計(jì)和開發(fā)這種 SOA 所必需的幾個(gè)組件。
數(shù)據(jù)庫(kù)和其他有狀態(tài)服務(wù)
和 Kubernetes 一樣,對(duì)于需要數(shù)據(jù)持久性和與磁盤緊密合作的服務(wù),Docker 徹底改變了游戲規(guī)則。有人說,服務(wù)應(yīng)該在物理服務(wù)器或虛擬機(jī)上以舊的方式“生存”。我尊重這一觀點(diǎn),我不會(huì)就其利弊展開爭(zhēng)論,但我相當(dāng)肯定,這種說法的存在只是因?yàn)樵? Docker 環(huán)境中暫時(shí)缺乏管理有狀態(tài)服務(wù)的知識(shí)、解決方案和經(jīng)驗(yàn)。
我還應(yīng)該提到,數(shù)據(jù)庫(kù)通常占據(jù)存儲(chǔ)世界的中心位置,因此你選擇的解決方案應(yīng)該為在 Kubernetes 環(huán)境中工作做好充分準(zhǔn)備。
根據(jù)我的經(jīng)驗(yàn)和市場(chǎng)情況,我可以區(qū)分以下幾組有狀態(tài)服務(wù),并為每一組服務(wù)提供最合適的面向 Docker 的解決方案示例:
- 數(shù)據(jù)庫(kù)管理系統(tǒng) — PostDock 是任何 Docker 環(huán)境中 PostgreSQL 的簡(jiǎn)單可靠的解決方案
- 隊(duì)列/消息 代理(broker) — RabbitMQ 是構(gòu)建消息隊(duì)列系統(tǒng)和路由消息的經(jīng)典軟件。RabbitMQ 配置中的 cluster_formation 參數(shù)對(duì)于集群設(shè)置是必不可少的
- 緩存服務(wù) — redis 被認(rèn)為是最可靠和靈活的數(shù)據(jù)緩存解決方案之一
- 全文搜索 — 我前面已經(jīng)提到過的 Elasticsearch 技術(shù)棧,最初用于文本搜索,但同樣擅長(zhǎng)存儲(chǔ)日志和處理大量文本數(shù)據(jù)的任何工作
- 文件存儲(chǔ)服務(wù) — 為任何類型的文件存儲(chǔ)和交付(ftp、sftp 等)提供的通用服務(wù)
鏡像依賴
如果你還沒有遇到你需要的包或依賴項(xiàng)已經(jīng)從公共服務(wù)器上刪除或暫時(shí)不可用的情況,請(qǐng)不要認(rèn)為這種情況永遠(yuǎn)不會(huì)發(fā)生。為了避免任何不必要的不可用性并為內(nèi)部系統(tǒng)提供安全性,請(qǐng)確保你的服務(wù)的構(gòu)建和交付都不需要互聯(lián)網(wǎng)連接。在內(nèi)部網(wǎng)絡(luò)中配置所有鏡像和拷貝:Docker 鏡像、rpm 包、源代碼庫(kù)、python/go/js/php 模塊。
這些和任何其它類型的依賴都有自己的解決方案。最常見的方法可以通過在搜索引擎查詢“ X 的私有依賴鏡像 ”找到。
三、從架構(gòu)回歸現(xiàn)實(shí)
不管你喜不喜歡,你的整個(gè)架構(gòu)遲早都會(huì)失敗。這樣的情況一直在發(fā)生:技術(shù)很快就過時(shí)了(1-5 年),方法論變化慢一點(diǎn)(5-10 年),設(shè)計(jì)原則和基礎(chǔ)理論偶爾變化(10-20 年),但不管怎么樣這個(gè)趨勢(shì)是不可阻擋的。
考慮到技術(shù)的過時(shí),請(qǐng)始終努力將你的生態(tài)系統(tǒng)保持在技術(shù)創(chuàng)新的巔峰,規(guī)劃和推出新的服務(wù)來滿足開發(fā)人員、業(yè)務(wù)部門和最終用戶的需求,向利益相關(guān)方推廣新的實(shí)用程序,提供知識(shí)來推動(dòng)你的團(tuán)隊(duì)和公司向前發(fā)展。
通過融入專業(yè)社區(qū)、閱讀相關(guān)文獻(xiàn)和與同事交流,可以讓你保持領(lǐng)先地位。意識(shí)到你的機(jī)會(huì),并在項(xiàng)目中正確使用新趨勢(shì)。試驗(yàn)并應(yīng)用科學(xué)的方法來分析你的研究結(jié)果,或者依靠你信任和尊重的其他人的結(jié)論。
現(xiàn)在,很難為根本性的變化做好準(zhǔn)備。但如果你是這個(gè)領(lǐng)域的專家,這也是可能的。我們所有人一生中只會(huì)見證幾個(gè)重大的技術(shù)變革,但不是頭腦中的知識(shí)數(shù)量讓我們成為專業(yè)人士,讓我們達(dá)到頂峰,而是我們對(duì)于新想法的開放性和接受蛻變的能力。
回到標(biāo)題中的問題,“有可能構(gòu)建一個(gè)完美的架構(gòu)嗎”?不,當(dāng)然不可能輕易“一勞永逸”,但一定要為之奮斗,在某個(gè)時(shí)刻,“在一個(gè)階段內(nèi)”,你一定會(huì)成功!
【責(zé)任編輯:武曉燕 TEL:(010)68476606】