6
ch6 部署
持續集成, CI, Continuous Integration
CI 能夠保證新提交的代碼 和已有代碼進行集成,從而讓所有人保持同步。 CI server 會檢測到代碼 已提交並簽出,然後花些時間驗證代碼可以 編譯 & 通過測試。
如何在 微服務、CI 構建、源代碼 三者之間,建立起合適的映射呢?
最簡單的做法
先把所有東西放在一起,我們有一個巨大的代碼庫,裡面包含全部的代碼,並且只有一個構建。 這個代碼庫的任何一個 commit 都會觸發構建,在這個構建中,我們會運行所有的驗證、測試。運行所有跟微服務有關的驗證。 每個構建會產生 構建號相同的物件。
優點:需要關心的代碼庫較少、概念上的構建比較簡單、開發者的工作也被簡化,只要提交就好了
缺點:如果僅僅修改一行,所有的其他服務也要進行驗證和構建,但事實上或許並不需要這樣做,所以我們花費了不必要的時間,而這會影響 CI 的週期,也會影響從開發到上線的速度。 更糟糕的是,我不知道那些構建物應該被重新部署、那些不應該。 很慘的是,如果有一行構建失敗,在那個構建修復以前,其他的代碼都無法提交,倘若多個團隊都在同一個巨大的代碼庫,真的是超歡樂的。
上述的變體
保留一個代碼庫,但是存在多個CI,會分別映射到代碼庫的不同目錄。 如果目錄結構定義的合理,就會很容易把一個目錄映射到一個構建中。
雙面刃:可以簡化 檢出/檢入 的流程,也可以讓你覺得同時提交多個服務的修改很容易,從而做出將多個服務耦合在一起的修改。
每個微服務都有自己的CI
這樣就可以在微服務部署到生產環境之前,先做一個快速的驗證。 這裡的每個微服務都有自己的代碼庫,分別與對應的CI綁定。 當進行修改時,可以只運行相關的構建和測試,並且只會得到一個需要部署的構建,代碼庫和團隊的匹配程度也更高。
缺點:跨微服務的修改會更難,但相比單塊代碼庫 & 單塊建構流程 所帶來的問題,這應該比較好解決。(使用命令行腳本)
構建流水線和持續交付
把一個構建 分成很多個階段 是很有價值的。 將各個不同的類型分開,例如測試分成:單元測試、整合測試。 畢竟單元測試都沒過,整合測試也沒有測的必要。 將構建分解成多個階段,也就是所謂的 構建流水線。
構建流水線 可以很好的追蹤軟體的構建進度:每完成一個階段,就離終點更近一步。 構建物會在整個構建的第一個環節被生成,然後被用在整個流水線中。 隨著構建物通過不同的階段,我們越來越確認軟體的品質。
CD, Continuous Delivery, 持續交付
基於上述的概念,CD 能夠將每次提交是否有達到上線的要求,並將信息回饋給我們。它會把每次提交 當成是發布版本來對待。
流水線的範例
使用真正重視 CD 概念的工具,利用對整個軟體上線過程建立流程,使軟體品質的可視化得到極大的改善,這可以大幅減少發布之間的間隔。
在微服務的世界,我們希望服務之間彼此獨立,所以擁有各自獨立的 CI,會是比較好的做法。
不可避免的例外
每個微服務一個構建 基本上在大多數情況下都是合理的,那麼 .. 是否有例外呢?
當一個團隊剛開始啟動一個新項目,對於服務的邊界還沒有這麼明確時,可以先將初始服務都先放在一起。
在最開始的階段,經常會發生跨服務邊界的修改,時常會有某些內容 移入/移出 某個服務,這個階段 把服務都放在同一個構建,可以節省跨服務的成本。
平台特定的構建物
技術的構建物
Java JAR、WAR Ruby gem Python egg
從微服務的角度看,有些技術中,只有構建物本身是不夠的。 可能還需要 Apache or Nginx 中的進程管理器。 所以為了部署、啟動 這些構建物,需要安裝、配置一些環境,接著才能啟動構建物。 例如 Puppet、Chef 這樣的自動化配置管理工具,就可以很好的解決這個問題。
另一個問題是 不同技術的構建物 都不相同,如何利用自動化去對不同構建物的底層進行部署,讓事情從複雜變得簡單。
操作系統的構建物
為了避免多種技術帶來的問題,可以用 作業系統 支持的操作方式。 RedGat or CentOS --> RPM Ubuntu --> deb Windows --> MSI
使用OS特定構建物的好處是,不用考慮底層用什麼技術,就可以完成安裝。這些系統操作工具也可以進行 卸載&查詢,也可以自動幫我們做很多事情。
缺點:剛開始編寫構建腳本的過程可能會比較困難 Linux --> FPM Windows --> NuGet
如果在Linux 上工作,很建議用 基於OS的軟件包管理工具 來進行部署。
定制化鏡像
使用自動化配置管理工具的一個問題,就是花費大量時間去運行腳本、進行配置。
有時候不希望讓機器運行時間過長,所以會定時去關閉、開啟新的實體。
而且這些安裝的時間,也會使CI無法得到快速的反饋。
(藍/綠部署 可以讓我們老版本不下線的同時,去部署新版本)
為了減少啟動時間,有一種方式就是創建虛擬機鏡像。 可以先在該鏡像內處理好一定程度的環境、工具,之後要部署時,只需要根據該鏡像創建一個實體,然後在上面安裝最新的版本就好了。
只要核心依賴沒有改變,那麼就可以一直用這個鏡像。
缺點:構建鏡像會花費大量時間,而這個問題可以用 Docker 這種容器技術去避免。
Packer (http://www.packer.io/) 可以用來簡化這個創建過程。這個工具提供 VMWare、AWS、Rackspcace雲、Digital Ocean、Vagrant 這些支持,而且在Linux、Windows都可以使用。 而且可以用喜歡的工具 (Chef、Ansible、Puppet),去做同一配套中,不同平台的鏡像。 也可以開發和測試用 Vagrant,生產環境用 AWS
不可變服務器
將 config 也納入版本控制,我們可以自動化重建服務,甚至重建整個環境。 但如果在部署完成之後,有人登入到機器上,對配置做了一些修改呢? 這會導致機器上的實際配置 和 Source Code 的配置不一致,就個問題就叫做 配置飄移。
為了避免這個問題,可以禁止對任何運行中的 server 做手動修改。(可以在創建鏡像的過程中,將其設定為禁止SSH) 相反,不論多小的修改,都需要經過構建流水線的過程。
任何能簡化工作的措施都值得嘗試。
環境
當軟體在CD流水線的不同階段時,也會被部署到不同的環境中。 我們的微服務構建物從頭到尾都是一樣的,但環境不同。 至少他們的主機是隔離的、配置也不一樣。
但真實情況往往會更複雜得多 ... 舉個例子,我們的生產環境可能會包括兩個 資料中心 的多台主機,使用 負載平衡 來管理,而測試環境可能會把所有服務運行在同一台機器上。 而這樣環境之間的不同,可能會引起一些問題。
從筆記本到UAT,最終再到生產環境,我們希望前面的那些環境可以不斷靠近生產環境,這樣就可以更快的碰到環境差異導致的問題。 這是必須去做權衡的,雖然重建生產環境所消耗的時間和代價會讓人望而卻步。
生產環境 和 快速反饋 之間的平衡不是一成不變,應該按照那些產生的bug 和反饋時間去做調整。
服務配置
有一種方法是 只創建一個 構建物,並將配置單獨管理。 這針對的是每個環境 一個屬性文件,或者是傳入到安裝過程的一些參數。
還有一個應對大量微服務 比較流行的方法,就是使用專用系統來配置服務。(ch11)
服務與主機之間的映射
單主機多服務
優點:簡化開發人員的工作、
缺點:監控變困難、服務之間互相影響 資源分配、可能會演變成無法獨立部署、對於團隊的自治性不利。
每個主機一個服務
這種模型避免了前者的問題,並簡化監控和錯誤恢復。 這種方式也可以減少潛在的單點故障。 一台主機當機,只會影響一個服務(雖然在虛擬化平台上不一定真的是這樣) (ch11 會提到更多伸縮和故障方面的討論)
我們也很容易獨立於其他服務的對某一個服務進行擴展,安全性措施也可以更有目的的在更小範圍內進行。
更重要的是 我們可以採取不同的部署技術,比如前面提到的 基於鏡像的部署 和 不可變服務器模式
我們已經為 微服務架構 帶來了很多複雜性。 接下來我們要尋找更多複雜性的根源 ~
如果沒有一個適合的 PaaS 平台,這個模型已經很好的降低複雜性。
優點:降低系統複雜性、簡化問題排查、更容易擴展和部署
缺點:主機數量的增加,也會引入很多的隱式代價。盡管如此,作者仍然認為這是比較適合微服務架構的模型。 下面會提到如何降低管理大量主機所帶來的額外負擔。
PaaS , Platform-as-a-Service, 平台即服務
Heroku 就是一個黃金級的 PaaS,不僅能夠管理服務的運行,還能用簡單的方式提供資料庫的服務。
當 PaaS 正常工作時,他表現得很好。但當出現問題時,我們通常沒辦法通過操作底層系統去解決問題,而這就是我們必須做出的取捨。
因為平台通常都是為了滿足比較通用的需求,所以當我們的應用程式越不標準,就越難和 PaaS 進行工作。
自動化
如果都用手動管理所有的事情,當主機數量增加,就會產生更多的管理開銷,更何況單主機單服務會需要很多主機。
所以我們需要將主機控制、服務部署、服務監控、日誌收集等工作自動化~
從物理機到虛擬機
管理大量主機的關鍵之一,就是利用VMWare這樣的傳統虛擬化技術,或者AWS,去減少管理主機的開銷。
傳統的虛擬化技術
如果我們想把每個服務部署在獨立的主機上,為什麼不把物理設備切小塊呢?
但就像是抽屜的隔板,VMWare 就是那些隔板,也是需要成本、花費的,當一台機器切割了越多主機,VMWare就會吃越多資源,那服務本身能用的資源總和就越少。
Vagrant
Vagrant 是一個很有用的部署平台,通常在開發和測試的時候使用,而非生產環境。 Vagrant 可以在你的筆記本創建一個虛擬的雲,底層使用標準的虛擬化技術(通常是VirtualBox,但也可以使用其他平台) 可以使用文字檔來定義一系列的虛擬機,也可以在其中定義網路配置&鏡像等信息。 也可以把這個文字檔提交到代碼庫,與團隊其他成員共享。
優點: 這些工具可以幫助我們在本地機器上 輕鬆的創建出類似生產環境的模擬。 也可以同時創建多個VM,通過關掉幾台來測試故障模式,也可以把本地目錄映射到虛擬機,這樣改完就可以馬上看到效果。 對於使用AWS的團隊,使用Vagrant 帶來的快速反饋也有不少好處。
缺點: 開發機上有很多額外的資源消耗。 如果一個服務占用一台虛擬機,可能會很難在本地機器上搭建整個系統。 結果就是為了開發和測試,可能會把其中的一些依賴打掉,讓事情比較容易控制。
Linux 容器
相比起 hypervisor 隔離&控制 虛擬主機的方法,Linux 容器 可以直接創建一個隔離的 進程空間,進而在這個空間運行其他進程。
在Linux上,進程必須用戶來控制,根據權限有不同的能力。 進程可以創建其他進程。 Linux 內核 就是在維護這個進程樹。
Linux 容器 擴展了這個想法,每個容器都像是一個系統進程樹的子樹: 最常用的 LXC,其他還有 Solaris Zones、Open VZ。
因為必須共享內核,因為進程樹存在於內核,所以我們可以主機操作Ubuntu,容器中運行 CentOS,因為只要內核相同就可以了。
優點:啟動很快,而且可以更好對容器進行資源分配,也比較容易去調整底層。對資源的利用更有效率。
缺點:需要用某種方式 把外界的請求 對應到內部的容器中。 (利用 IPTable 來配置端口映射,從而直接將容器暴露給外界) 容器並不是完全隔離,有些文檔和已知方法都介紹了,某些容器中的進程,有可能會跳出該容器與其他容器的進程,或者和底層發生干擾。 (如果想要更高的隔離性,應該使用虛擬機)
Docker
Docker 是構建在極輕量級容器之上的平台。 它可以幫忙處理大多數跟容器管理有關的事情。 你可以在 Docker 中創建和部署應用,這些基於容器的應用 與 VM世界中的鏡像很類似。 Docker 也可以管理容器的配置,並處理一些網路問題,甚至還可以存儲應用程序的版本 (registry)
Docker 還可以緩解更多服務進行本地開發&測試的問題,我們可以將許多個Docker 實例 運行在 Vagrant 的 單個VM上,而非原來的 一個VM 對 一個服務。 當使用完後,就可以使用 Vagrant 去銷毀 Docker 平台本身。 並使用 Docker 來快速配置每個服務。
Docker 有很多相關的技術,例如 CoreOS 就是一個專門為 Docker 設計的操作系統。 它是一個被裁剪過的 Linux OS,僅提供有限的功能來保證 Docker 的運行,因此它也只需要較少的資源,從而把更多的資源留給容器。 它甚至沒有debs、RPM 這樣的工具,因為所有軟件都被裝在一個獨立的 Docker 應用程式內,並只在自己的容器內運行。
Docker 本身並不能解決所有問題,它只是一個很簡單的 PaaS 你需要搭配適合的工具,來幫助你跨多台機器去管理 Docker 上的服務。 最近 Google 的開源工具 Kubernetes 和 CoreOS 集群技術 能夠提供幫助。
另一個有趣的工具是 Deis (http://deis.io) ,它試圖在Docker 之上,提供一個類似 Heroku 那樣的 PaaS。
一個部署接口
不管部署的底層平台 or 構建物是什麼,用一個統一的接口來部署給定的服務,都是一個很關鍵的實踐。 從本地開發 ~ 生產環境,盡量讓它們一致,就可以讓問題及早發現。
參數化的命令行調用 例如: $ deploy artifact=catalog environment=local version=local
Fabric:Python Boto:AWS Capistrano:Ruby PowerShell:Windows
環境定義
可以把環境定義想像成 微服務 到 計算、網路、存儲資源 之間的映射。
改變環境配置的大小,去應對不同的測試,使資源充分利用
能夠在不同環境設置不同憑證(credential)。敏感環境的憑證會被存儲到不同的程式庫,這些代碼只有少數人可以訪問。
默認狀況下,如果一個服務有多台節點需要配置,會自動為其創建負載平衡。
構建一個類似的系統的工作量很大。 這些代價基本上都需要在前期付出, 但是做好以後,可以很好的管理部署的複雜性。 我希望將來你不需要自己做這些事情~ Terraform 是來自 Hashicorp 的一個很新的工具,它可以幫你做上述的事情。
小結
專注於保持 服務能夠獨立於其他服務 進行部署 的能力,無論採用什麼技術,都要確保能提供這樣的能力。 我傾向於一個服務一個代碼,對於 每個微服務一個CI 這件事情,不僅僅是傾向,而是非常堅持。 因為只有這樣,才能實現獨立部署。
將每個服務 放到單獨的 主機/容器 中。 看看類似 LXC or Docker 這樣的替代技術,如何簡化對多個服務的管理。 但記住,不論選用什麼樣的技術,都要確保可以 自動化。 如果採用的技術不能自動化,就去選一個可以新的可以自動化的技術吧 ~ (使用 AWS 這樣的平台,能夠在進行自動化時,提供許多便利。)
確保能理解 部署的技術 會對 開發人員 產生怎樣的影響,並確保他們也能感受到。 創建工具 對任何給定服務到不同環境的自動部署 提供服務 是非常重要的! 他對開發、測試、微運人員,都能提供很大的幫助。
Last updated