軟件開發領域在Docker和Kubernetes時代是如何變化的? 是否有可能使用這些技術搭建一勞永逸的架構? 當全部東西都被「打包」進容器中時,是否有可能統一開發及集成的流程? 這些決策的需求是什麼? 它們會帶來什麼限制? 它們會讓開發人員更輕鬆,或者相反,反而增長沒必要要的複雜性嗎?php
如今是時候以文本和原始插圖方式闡明這些以及其餘問題了!前端
這篇文章將帶您踏上從現實生活到開發流程再到架構最後回到現實生活的旅程,並一路爲您解答在這些停靠站點上遇到的最重要問題。 咱們將試圖肯定一些應該成爲架構一部分的組件和原則,並演示一些示例,但不會進入其實現領域。python
文章的結論可能會讓你心煩意亂,或者無比開心,這一切都取決於你的經驗、你對這三章故事的見解,甚至在閱讀本文時你的心情。 在下面能夠發表評論或提出問題,讓我知道您的想法!git
在大多數狀況下,我所見過的或者很榮幸搭建的全部開發流程都只是爲了一個簡單的目標——縮短從概念產生到交付生產環境之間的時間間隔,同時保持必定程度的代碼質量。redis
想法的好壞可有可無。 由於糟糕的想法來也匆匆,去也匆匆——你只要嘗試一下,就能夠把它們丟 進故紙堆裏。 這裏值得一提的是,從一個糟糕的想法回滾是能夠落在自動化設施的肩膀上的,這能夠自動化您的工做流程。算法
持續集成和交付看起來像是軟件開發領域的救命稻草。 究竟還有什麼比這更簡單呢? 若是你有一個想法,你有代碼,那麼就去作吧! 若是不是輕微的問題,這將是完美無瑕的——集成和交付過程相對而言難以獨立於公司特有的技術和業務流程以外。docker
然而,儘管任務看起來很複雜,但在生活中不時會出現一些優秀的想法,這些想法可讓咱們(固然,我本身是肯定的)更接近構建一個無瑕疵的而且幾乎能夠在任何場合使用的機制。 對我來講,離這樣的機制最近的步驟是Docker和Kubernetes,他們的抽象層次和思想方法使我認爲如今能夠用幾乎相同的方法解決80%的問題。數據庫
其他的20%的問題顯然還在原地踏步,但這正是如此你才能夠將你發自心裏的創意天賦聚焦在有趣的工做上,而不是處理重複的例行公事。 只要照料「架構框架」僅僅一次,就可讓您忘掉已經解決的80%問題。編程
這一切意味着什麼?以及Docker如何解決開發工做流程的問題的? 讓咱們看一個簡單的過程,這對於大多數工做環境來講也足夠了:json
經過適當的方法,您能夠自動化並整合上面序列圖中的全部內容,並在將來幾個月內將其拋之腦後。

一個項目應該包含一個docker-compose.yml文件,這可讓你省去考慮在本地機器上運行應用程序/服務須要作些什麼以及如何操做的問題。 一個簡單的命令docker-compose up應該啓動您的應用程序及其全部依賴項,使用fixtures填充數據庫,上傳容器內的本地代碼,啓用代碼跟蹤以便即時編譯,並最終在指望的端口開始響應請求。 即便在設置新服務時,您也沒必要擔憂如何啓動、在哪裏提交更改或使用哪一個框架。 全部這些都應該提早在標準說明中描述,並由針對不一樣設置的服務模板指定:前端、後端和worker。

全部你想知道的關於「黑匣子」(至於爲何我把容器稱之爲如此的更多信息,將在文章中的稍後部分闡明)的狀況是,它裏面的一切都無缺無損,是或否,1或0。您能夠在容器內部執行有限數量的命令,而docker-compose.yml
描述了它的全部依賴關係,您能夠輕鬆自動化和整合這些測試,而沒必要過度關注實現細節。
好比,像這樣!在這裏,測試不只意味着單元測試,還包括功能測試、集成測試、(代碼樣式)測試和副本、檢查過期的依賴關係以及已使用軟件包的許可證正常與否等等。 關鍵是全部這些都應該封裝在Docker鏡像中。

不管在什麼時候何地想安裝您的項目都無所謂。 結果就像安裝進程同樣,應該始終如一。 至於您要安裝的是整個生態系統的哪一個部分或者您將從哪一個git倉庫得到代碼也沒有區別。 這裏最重要的組件是冪等性。 惟一應該指定的是控制安裝過程的變量。
如下是我在解決這個問題時至關有效的算法:
1. 從全部Dockerfiles收集鏡像(例如像這樣)
2. 使用元項目,經過Kube API將這些鏡像交付給Kubernetes。 啓動交付一般須要幾個輸入參數:
● Kube API端點
● 一個「機密」對象,因不一樣的環境而異(本地/測試/預發佈/生產)
● 要展現的系統名稱以及針對這些系統的Docker鏡像的標籤(在上一步中獲取)
做爲一個涵蓋全部系統和服務的元項目的例子(換句話說,是一個描述生態系統如何編排以及如何交付更新的項目),我更願意使用Ansibleplaybooks,經過這個模塊來與Kube API集成。 然而,複雜的自動化能夠參考其餘選項,我稍後將詳細討論本身的選擇。 可是,您必須考慮中心化/統一的管理架構的方式。 這樣一個方式可讓您方便、統一地管理全部服務/系統,並消除即將到來的執行相似功能的技術和系統叢林可能帶來的任何複雜狀況。
一般,須要以下的安裝環境:
● 「測試」 - 用於對系統進行一些手動檢查或調試
● 「預發佈」 - 用於近乎實時的環境以及與外部系統的集成(一般位於DMZ而不是測試環境)
● 「生產」 - 最終用戶的實際環境
若是你有一個統一的方式來測試Docker鏡像——或者「黑盒子」——你能夠假設這些測試結果可讓你無縫地(而且心安理得)將功能分支集成到你的git倉庫的的上游或主分支中。也許,這裏惟一的交易斷路器是集成和交付的順序。若是沒有發行版,那麼如何經過一組並行的功能分支阻止一個系統上的「競爭條件」?所以,只有在沒有競爭的狀況下才能開始這個過程,不然「競爭條件」會縈繞腦海:
1. 嘗試將功能分支更新到上游(git rebase/ merge)
2. 從Dockerfiles構建鏡像
3. 測試全部構建的鏡像
4. 開始並等待,直到系統交付了構建自步驟2的鏡像
5. 若是上一步失敗,則將生態系統回滾到以前的狀態
6. 在上游合併功能分支並將其發送到存儲庫
在任何步驟中的任何失敗都應終止交付過程,並將任務返回給開發人員以解決錯誤,不管是失敗的測試仍是合併衝突。
您可使用此過程來操做多個存儲庫。只需一次爲全部存儲庫執行每一個步驟(步驟1用於代碼庫A和B,步驟2用於代碼庫A和B等),而不是對每一個單獨的存儲庫重複執行整個流程(步驟1-6用於代碼庫A ,步驟1-6用於代碼庫B,等等)。
此外,Kubernetes容許您分批次地推出更新以進行各類AB測試和風險分析。 Kubernetes是經過分離服務(接入點)和應用程序在內部實現的。您能夠始終以所需的比例平衡組件的新舊版本,以促進問題的分析併爲潛在的回滾提供途徑。
系統回滾
架構框架的強制性要求之一是可以回滾任何部署。反過來,這又須要一些顯式和隱式的細微差異。如下是其中最重要的一些事項:
● 服務應該可以設置其環境以及回滾更改。例如,數據庫遷移、RabbitMQ schema等等。
● 若是沒法回滾環境,則該服務應該是多態的,並支持舊版本和新版本的代碼。例如:數據庫遷移不該該中斷舊版本的服務(一般是2或3個之前的版本)
● 向後兼容任何服務更新。一般,這是API兼容性,消息格式等。
在Kubernetes集羣中回滾狀態至關簡單(運行kubectl rollout undo deployment/some-deployment,Kubernetes將恢復先前的「快照」),可是爲了讓此功能生效,您的元項目應包含有關此快照的信息。可是更爲複雜的交付回滾算法讓人望而生畏,儘管它們有時是必需的。
如下是能夠觸發回滾機制的內容:
● 發佈後應用程序錯誤的高比例
● 來自關鍵監控點的信號
● 失敗的[冒煙](https://en.wikipedia.org/wiki/Smoke_testing_%28software%29)
● 手動模式——人爲因素
確保信息安全和審計
沒有一個工做流程能夠奇蹟般地「搭建」刀槍不入的安全性並保護您的生態系統免受外部和內部威脅,所以您須要確保您的架構框架是在每一個級別和全部子系統裏按照公司的標準和安全策略執行的。
我將在後面的關於監控和告警的章節討論有關解決方案的全部三個級別,它們自己也是系統完整性的關鍵。
Kubernetes擁有一套良好的針對訪問控制、網絡策略、事件審計以及其餘與信息安全相關的強大工具的內置機制,可用於構建一個良好的防禦邊界,以抵禦和阻止攻擊及數據泄露。
從開發流程到架構
應該認真考慮將開發流程與生態系統緊密集成的想法。將這種集成的需求添加到架構的傳統需求集(彈性、可伸縮性、可用性、可靠性、抵禦威脅等)中,能夠大大提升架構框架的價值。 這是相當重要的一個方面,由此致使出現了一個名爲「DevOps」(開發運維)的概念,這是實現基礎設施全面自動化並優化的合理步驟。 可是,若是有一個設計良好的架構和可靠的子系統,DevOps任務能夠被最小化。
微服務架構
沒有必要詳細討論面向服務的架構——SOA的好處,包括爲何服務應該是「微」的。 我只會說,若是你決定使用Docker和Kubernetes,那麼你極可能理解(並接受)單體應用架構是很困難甚至根子上就是錯誤的。 Docker旨在運行一個進程並持久化,Docker讓咱們聚焦於DDD框架(領域驅動開發)內進行思考。 在Docker中,打包後的代碼被視爲具備一些公開端口的黑盒子。
生態系統的關鍵組件和解決方案
根據我在設計具備更高可用性和可靠性的系統方面的經驗,有幾個組件對於微服務的運維是相當重要的,稍後我會列出並討論這些組件,我將在Kubernetes環境中引用它們,也能夠參考個人清單做爲其它任何平臺的檢查單。
若是你(像我同樣)會得出這樣的結論,即將這些組件做爲常規的Kubernetes服務來管理,那麼我建議你在除「生產環境」以外的單獨集羣中運行它們。 好比「預發佈」集羣,由於它能夠在生產環境不穩定而且你迫切須要其鏡像、代碼或監控工具的來源時節省你的時間。 能夠說,這解決了雞和雞蛋的問題。
像往常同樣,它始於訪問——服務器、虛擬機、應用程序、辦公室郵件等。 若是您是或想成爲主要的企業平臺(IBM、Google、Microsoft)之一的客戶,則訪問問題將由供應商的某個服務處理。 可是,若是您想擁有本身的解決方案,難道只能由您並在您的預算以內進行管理?
此列表可幫助您肯定適當的解決方案並估算設置和維護所需的工做量。 固然,您的選擇必須符合公司的安全政策並經信息安所有門批准。
儘管Kubernetes在物理機器/雲虛擬機(docker、kubelet、kube proxy、etcd集羣)上只須要少許組件,但對於新機器的添加和集羣管理仍然須要自動化。 如下是一些簡單的方法:
● KOPS——此工具容許您在兩個雲供應商商(AWS或GCE)之一上安裝集羣
● Teraform——這可讓您管理任何環境的基礎設施,並遵循IAC(基礎架設施即代碼)的思想,
● Ansible——用於任何類型的通用自動化工具
就我的而言,我更喜歡第三個選項(帶有一個Kubernetes的集成模塊),由於它容許我使用服務器和k8s對象並執行任何類型的自動化。 可是,沒有什麼能阻止您使用Teraform及其Kubernetes模塊。 KOPS在「裸機」方面效果不佳,但它仍然是與AWS/GCE一塊兒使用的絕佳工具!
對於任何Docker容器,使其日誌可訪問的惟一方法是將它們寫入正在容器中運行的根進程的STDOUT或STDERR,服務開發人員並不關心日誌數據接下來的變化,而主要是它們應該在必要時可用,而且最好包含過去某個點的記錄。知足這些期許的全部責任在於Kubernetes以及支持生態系統的工程師。
在官方文檔中,您能夠找到關於處理日誌的基本(和好的)策略的說明,這將有助於您選擇用於聚合和存儲大量文本數據的服務。
在針對日誌系統的推薦服務中,同一文檔提到fluentd用於收集數據(在集羣的每一個節點上做爲代理啓動時)以及用於存儲和索引數據的Elasticsearch。即便你可能不贊同這個解決方案的效率,但鑑於它的可靠性和易用性,我認爲這至少是一個好的開始。
Elasticsearch是一個資源密集型的解決方案,但它能夠很好地擴展並有現成的Docker鏡像,能夠運行在單個節點以及所需大小的集羣上。
即便代碼很是完美,然而仍是會確實發生故障,接着你想在生產環境中很是仔細地研究它們,並試圖瞭解「若是在個人本地機器上一切工做正常,那麼在生產環境上究竟發生了什麼錯誤?」。好比緩慢的數據庫查詢、不正確的緩存、較慢的磁盤或與外部資源的鏈接、生態系統中的交易,瓶頸以及規模不足的計算服務都是您不得不跟蹤和估算在實際負載下代碼執行時間的一些緣由。
Opentracing和Zipkin足以應付大多數現代編程語言的這一任務,而且在封裝代碼以後不會增長額外的負擔。固然,收集到的全部數據應該存儲在適當的地方,並做爲一個組件使用。
經過上述的開發標準和服務模板能夠解決在封裝代碼以及經過服務、消息隊列、數據庫等轉發「Trace ID」時出現的複雜狀況。後者也考慮到了方法的一致性。
Prometheus已經成爲現代監控系統中事實上的標準,更重要的是,它在Kubernetes上得到了開箱即用的的支持。您能夠參考官方Kubernetes文檔來了解更多關於監控和警報的信息。
監控是必須安裝在集羣內的少數幾個輔助系統之一,集羣是一個受監控的實體。可是對於監控系統的監控(抱歉有些囉嗦)只能從外部進行(例如,從相同的「預發佈」環境)。在這種狀況下,交叉檢查可做爲一個針對任何分佈式環境的便捷解決方案,這不會使高度統一的生態系統架構複雜化。
整個監控範圍能夠分爲三個徹底邏輯隔離的層級。如下是我認爲的在每一個層級最重要的跟蹤點例子:
● 物理層:——網絡資源及其可用性——磁盤(I/O,可用空間)——單個節點(CPU、RAM、LA)的基本資源
● 集羣層:——每一個節點上主集羣系統的可用性(kubelet、kubeAPI、DNS、etcd等)——可用資源數量及其均勻分佈——容許的可用資源相對於服務消耗的實際資源的監控——pod的從新加載
● 服務層:——任何類型的應用程序監控——從數據庫內容到API調用頻率——API網關上的HTTP錯誤數量——隊列大小和worker的利用率——數據庫的多個度量標準(複製延遲、事務的時間和數量、緩慢的請求等)——對非HTTP進程的錯誤分析——發送到日誌系統請求的監控(能夠將任何請求轉換爲度量標準)
至於在每一個層級的告警通知,我想推薦使用了無數次的其中一個外部服務,能夠發送通知電子郵件,短信或打電話給手機號碼。我還會提到另外一個系統——OpsGenie——它與Prometheus的alertmanaer是緊密集成的。
OpsGenie是一種彈性的告警工具,可幫助處理升級、全天候工做、通知渠道選擇等等。在團隊之間分發告警也很容易。例如,不一樣級別的監控應向不一樣的團隊/部門發送通知:物理——Infra + Devops,集羣——Devops,應用程序——每個相關的團隊。
要處理諸如受權、認證、用戶註冊(外部用戶——公司客戶)和其餘類型的訪問控制等任務,您須要高度可靠的服務,以保持與API Gateway的彈性集成。使用與「身份服務」相同的解決方案沒有什麼壞處,可是您可能須要分離這兩種資源以實現不一樣級別的可用性和可靠性。
內部服務的集成不該該很複雜,您的服務不該該擔憂用戶和對方的受權和身份驗證。相反,架構和生態系統應該有一個處理全部通訊和HTTP流量的代理服務。
讓咱們考慮一下最適合與API Gateway集成的方式,即整個生態系統——令牌。此方法適用於全部三種訪問方案:從UI、從服務到服務以及從外部系統。接着,接收令牌(基於登陸名和密碼)的任務由用戶界面自己或服務開發人員完成。區分UI中使用的令牌的生命週期(較短的TTL)和其餘狀況(較長的和自定義的TTL)也是有意義的。
如下是API Gateway解決的一些問題:
● 從外部和內部訪問生態系統服務(服務不直接相互通訊)
● 與單點登陸服務集成:——令牌轉換和附加HTTPS請求,頭部包含所請求服務的用戶標識數據(ID、角色和其餘詳細信息)——根據從單點登陸服務接收到的角色啓用/禁用對所請求服務的訪問控制
● 針對HTTP流量的單點監控
● 複合不一樣服務的API文檔(例如,複合Swagger的json/yml文件)
● 可以根據域和請求的URI管理整個生態系統的路由
● 用於外部流量的單一接入點,以及與接入供應商的集成
若是您的生態系統包含數百個可在一個宏域中工做的服務,則您將不得不處理服務通訊的數千種可能方式。爲了簡化數據流,您應該具有在發生特定事件時將信息分發到大量收件人的能力,而無論事件的上下文如何。換句話說,您須要一個事件總線來發布基於標準協議的事件並訂閱它們。
做爲事件總線,您可使用任何能夠操做所謂Broker的系統:RabbitMQ、Kafka、ActiveMQ等。通常來講,數據的高可用性和一致性對於微服務是相當重要的,可是因爲CAP定理,您仍然不得不犧牲某些東西來實現總線的正確分佈和集羣化。
天然,事件總線應該可以解決各類服務間通訊問題,但隨着服務數量從幾百個增長到幾千個甚至幾萬個,即便是最好的基於事件總線的架構也會望而卻步,您將須要尋找另外一種解決方案。一個很好的例子就是集成總線方法,它能夠擴展上述「Dumb管——智能消費」策略的功能。
有幾十個使用「企業集成/服務總線」方法的理由,其目的是減小面向服務架構的複雜性。如下是其中幾個理由:
● 聚合多個消息
● 將一個事件拆分爲幾個事件
● 對於事件的系統響應的同步/事務分析
● 接口的協調,這對於與外部系統的集成特別重要
● 事件路由的高級邏輯
● 與相同服務的多重集成(從外部和內部)
● 數據總線的不可擴展中心化
做爲企業集成總線的一款開源軟件,您可能須要考慮Apache ServiceMix,其中包含幾個對於此類SOA的設計和開發相當重要的組件。
和Kubernetes同樣,Docker一次又一次地改變了全部用於須要數據持久化以及與磁盤緊密相關的服務的遊戲規則。有人說服務應該在物理服務器或虛擬機上以舊的方式「生存」。我尊重這一觀點,而且不會談論它的優勢和缺點,但我至關確定這種說法的存在僅僅是由於在Docker環境中暫時缺少管理有狀態服務的知識、解決方案和經驗。
我還應該提到數據庫常常佔據存儲世界的中心位置,所以您選擇的解決方案應該徹底準備好在Kubernetes環境中工做。
根據個人經驗以及市場狀況,我能夠區分如下幾組有狀態的服務以及每一個服務最適合的Docker解決方案的示例:
● 數據庫管理系統——PostDock是在任何Docker環境中PostgreSQL簡單可靠的解決方案
● 隊列/消息代理——RabbitMQ是構建消息隊列系統和路由消息的經典軟件。 RabbitMQ配置中的cluster_formation參數對於集羣設置是必不可少的
● 高速緩存服務——redis被認爲是最可靠和彈性的數據高速緩存解決方案之一
● 全文搜索——我上面已經提到過的Elasticsearch技術棧,最初用於全文搜索,但一樣擅長存儲日誌和任何具備大量文本數據的工做
● 文件存儲服務——用於任何類型的文件存儲和交付(ftp,sftp等)的通常化服務組
依賴鏡像
若是您還沒有遇到您須要的軟件包或依賴項已被刪除或暫時不可用的狀況,請不要認爲這種狀況永遠不會發生。 爲避免沒必要要的不可用性併爲內部系統提供安全保護,請確保構建和交付服務都不須要Internet鏈接。 配置鏡像和複製全部的依賴項到內部網絡:Docker鏡像、rpm包、源代碼庫、python/go /js/php模塊。
這些以及其餘任何類型的依賴關係都有本身的解決方案。 最多見的能夠經過查詢「private dependency mirror for ...」來Google搜索。
無論你喜不喜歡,你的整個架構命中註定早晚會難覺得繼。它老是會發生:技術過期很快(1 - 5年),方法和方法論——有點慢(5 - 10年),設計原則和基礎——偶爾(10 - 20年),但終歸是不可避免的。
考慮到技術的過期,須要老是試圖讓本身的生態系統處於技術創新的高峯期,計劃並推出新的服務以知足開發人員、業務和最終用戶的需求,向您的利益相關者推廣新的實用程序,交付知識來推進您的團隊和公司前進。
經過融入專業社區、閱讀相關文獻並與同事交流,保持在整個生態的頂端。注意項目中的新機會以及正確使用新趨勢。試驗並應用科學方法來分析研究結果,或依靠您信任和尊重的其餘人的結論。
除非你是本領域的專家,不然很難爲根本性的變化作好準備。咱們全部人只會在咱們的整個生涯中見證一些重大的技術變化,但並非咱們頭腦中的知識數量使得咱們成爲專業人士並讓咱們攀登到頂峯的,而是咱們思惟的開放性以及接受蛻變的能力。
回到標題的問題,「是否有可能搭建一個更好的架構?」不,不是「一勞永逸」,但必定要爭取它,並在某個時刻,「很短的時間」,你必定會成功!
推薦一個交流學習羣:685167672 裏面會分享一些資深架構師錄製的視頻錄像:有Spring,MyBatis,Netty源碼分析,高併發、高性能、分佈式、微服務架構的原理,JVM性能優化這些成爲架構師必備的知識體系。還能領取免費的學習資源,目前受益良多: