標籤:

基礎架構的持續集成和應用部署

本文轉載自我的blog: martinliu.cn

持續集成通常是針對應用而言的,可是基礎架構的持續集成應該怎麼做?基礎架構的持續集成應該屬於持續交付/部署的基礎。貫串本文的一個問題,或者在閱讀本文時,您應該不斷地問自己這個問題:我們的應用部署流程是怎樣的?

在回答這個問題之前,我們先來回顧一下,目前幾乎所有人正在使用的手工環境和資源交付流程。在源碼被編譯打包了以後,安裝包文件被上傳保存到了內部的某個文件伺服器上。Ops團隊的某個組/人被分配到工單,根據工單描述的需求,它在測試或者生產環境中開始工作:

  • 用圖形界面進行虛擬機模板的手工克隆工作,或者由於沒有相應的許可權或者自助服務,不得不給虛擬化管理員發任務單,然後等待回復。
  • 獲取用戶名和密碼手工登錄伺服器,有些企業還要是等待領導的審批,才能得到密碼信封和所需要的訪問密碼。
  • 根據工單(變更單)里的描述和自己的經驗對虛擬機的操作系統進行配置,在這個過程中,Ops往往可能還需和需求方進行不止一次的溝通,確認相關參數。
  • 手工的下載應用安裝包,然後分別手工上傳到目標的伺服器,憑經驗和工單信息部署應用,然後測試部署結果,可能是看下頁面有沒有正常顯示,或者服務起沒起.
  • 手工測試和確認這些虛擬機的服務和狀態,憑經驗覺得OK了以後,回復工單,關閉工單。

以上的工作場景,可能是Ops人員很常規的一天,或者是幾天內的工作,當然在這個過程中,他們還需要參與一些救火行動;他們在這個過程中也可能會有疑問,也可能會對此工作結果不確定;但是,日常的工作經驗告訴他,差不多了,關閉任務單要緊,還有好多項目催活呢!就這樣,配置並不精確的虛擬機環境就交給了下游的需求方。

以上工作過程的問題如下:

  1. 工作周期長,速度慢。實際上工作周期拖延的越久,工作結果的質量就越差,而並不是我們想想中的慢工出細活。
  2. 所有步驟都是純手工操作,不僅費事費力,而且出錯幾率高,也幾乎不可能無痛的回退。可能有人會說了,我們不需要那麼快,我們也不是互聯網公司;可是從精益思想的角度看,以上這些工作都屬於對業務價值的交付貢獻為零的工作;你可能是由於公司給你發著工資,才錯誤的感覺到,這項工作活動應該有它的價值。
  3. 上游傳遞來的信息可能不全面,不準確,因此Ops很有可能造成錯誤配置,因此會返工。
  4. 傳遞給下游的虛擬機很可能會在後續的部署過程中,由於應用需求的變化,而需要下游的人員對其重新配置,產生重複的勞動。

手工部署的時間和代價 = 應的數量 X 應用版本數量 X 環境數量

對以上工作系統進行優化的原則:如果某一項活動的重複頻率越高,那麼對它進行優化,所產生的回報也會越明顯;這裡還要參考限制理論,優化的順序要正確。

我們從這個角度出發,就可以來設定基礎架構持續集成和應用部署流程的改進目標了:

  • 減少總體人工工作時間和代價
  • 提高交付的速度、可靠性和頻率
  • 能進行應用部署,能進行資料庫Schema的更新
  • 能夠實現部署流程的自服務,讓任何需要部署應用的人能一鍵式部署任何版本

到了這裡我們就必須將上述手工勞動,變為自動化的過程。因此,基礎架構即代碼IaC (Infrastructure as code)和相關的配置管理工具就會用到。

上圖是一個典型的持續交付流水線模型,在此我們對它的關注點如下:

  • 代碼的變更被Jenkins自動化的構建(CI是基礎),打包後的安裝包被存儲在Artifactory里,Artifactory裡面還可以存儲應用包的其它相關元數據,如測試結果,能否可以用於下一步部署的標籤等等。
  • Jenkins自動化的搭建所需要的環境,調用虛擬化或者公有雲資源池的API,製備虛擬機資源,然後調用Chef完成對虛擬機的配置,完成應用包部署所需要的所有層次的配置。
  • 環境配置完成後,應用正常運行了,在相關的測試工具對部署後的環境做驗收測試,Chef具有支持測試驅動的相關工具。

基礎架構的持續集成

為了實現完整的基礎架構持續集成流程,以上持續交付流水線必須具備的能力和概念包括:分層的系統管理、基礎架構即代碼IaC、配置管理、Chef工具等。下面詳細對它們進行描述。

分層的系統管理

系統管理的層次涉及到OS相關的三個層次。下面自下而上地簡單描述一下。

  1. 製備管理:涉及到虛擬化層,這一層是資源表達層,目前所有主流的虛擬化都支持標準的Rest API,包括VMWare、EC2和Nuanix等。大多數主流配置管理工具都具備用於虛擬機生命周期管理(從生成、到開機、到刪除等)的API功能,能按需的獲得任何數量、規模、網路和操作系統類型的部署環境。
  2. 配置管理:在任何類型的操作系統里自動化的安裝和配置軟體包,將所有配置參數配置好以後,持續保持這些配置點的狀態。對於簡單應用,來說按配置參數啟動服務即任務完成。
  3. 應用編排管理:對於複雜的分散式系統,由於各個自服務之間存在著依賴關係,所有自服務之前需要互通一些配置參數才能實現,應用程序整體的正常運行,配置應用伺服器的odbc資料庫連接,配置web前端的ldap認證伺服器等等。目前微服務所涉及的服務發現和路由,是應用編排必備的配套設施。

不同的DevOps配置管理工具也都力求能覆蓋以上三個層次,但是他們所追求的方向,或者想解決的主要問題並不相同。因此各個工具之間功能上有重疊。

因此在運用這些工具的時候,不僅要追求其卓越的功能,還要能意識到,並有意的在不同層面上做取捨。

基礎架構即代碼

IaC這個概念最早是被Chef這類工具提出並實現,它的基本想法就是讓Ops人員象開發人員一樣的,工作在基礎架構的代碼上,而不是面對著數十個圖形和文字終端界面。使用類似於開發應用程序的方式,開發和管理基礎架構環境,因此基礎架構能通過API訪問和操控是基礎,目前所有主流的虛擬化/雲計算平台都具備很好的API介面;可惜的是在傳統的企業環境中,這些資源池的API功能幾乎沒有被用到。

像開發應用代碼一樣的管理IT基礎架構,基礎架構的開發和管理也需要遵循與應用開發類似的原則,這些原則包括:

  • 一切從源代碼開始:並對其進行嚴格的版本管理,要對基礎架構變更,就需要對相應的代碼進行變更和測試,最後發布這些代碼。從而力求做到伺服器的無人登錄運維。
  • 模塊化設計:不同應用底層所使用的基礎架構有著大量的相似之處,模塊化的設計不僅意味著標準化,也意味著更少的重複代碼。我所用過的Terraform、Chef和Puppet這三種工具,都具有高度的模塊化特性。
  • 抽象能力:能夠使用不同的模塊和參數對任何特徵的應用進行建模,用IaC代碼進行表達,基礎架構的代碼開發也就是藉助這種抽象能力,將所有管理對象(配置管理項)具體化地描述為應用服務模型。編寫出來的基礎架構代碼,不僅包含了所有對應用配置描述性的語義,而且還是能夠被執行的代碼,在IaC代碼執行之後,你就得到了所期望的虛擬機、應用配置和應用服務。
  • 可測試性:這是一個經常忽略的能力,而在了解之後,你會發現IaC也是編程語言,就是對基礎架構進行高級的編程,而且IaC代碼本身和它的運行結果都是可以測試的。在執行前對其語義語法測試,在運行以後對其運行結果測試。Chef在這方面表現的尤為突出。

配置管理

我可能是最早的一批進行ITIL配置管理實踐,CMDB建設的這批人;我以前和甲方客戶有著大量的關於配置管理和CMDB的對話,所經歷過的項目也非常煎熬。而在DevOps場景下,感覺以前的經歷也是很有意思的,只是我現在說到的CI,在沒有特指的情況下,是持續集成的概念,還不是配置項了。

Process for establishing and maintaining consistency of a product』s performance, functional and physical attributes with its requirements, design and operational information throughout its life。

以上是配置管理在維基百科裡的定義,它所表達的含義還是值得借鑒的;而如今很多人對DevOps的認識,還有人是建立在DevOps配置管理相關的工具上的。為了糾正這個錯誤觀點,我們經常說:「天文學並不只是關於望遠鏡的。」

配置管理工具中有很多是基於主機(OS)的管理工具,包括:CFEngine、Puppet、Chef、Salt和Ansible等。它們都具有基礎架構即代碼的相關原則和特徵。都能實現:定義伺服器的目標期望狀態的能力,在每一次執行周期里,它們都進行狀態檢查,彙報當前狀態和目標狀態的偏差,在必要的時候也可以自動的執行必要的狀態修復操作。

Chef這種配置管理工具,使用了Ruby風格的DSL語言,使用者只需要用Chef代碼表達」What「即可,而不需要明白」How「;」What「既是對目標配置狀態的描述,使用者只需要將需求轉換為Chef代碼,然後用Chef客戶端工具運行它即可。Chef的代碼清晰,描述能力強大。在編碼的時候遵循DSL規則,如果有必要的話也可以調用Ruby。

Chef是客戶端伺服器的架構,安裝了Chef-client程序的節點可以註冊到一個Chef管理伺服器里。

Chef的開發人員(IaC編碼者),在安裝了用於和Chef伺服器交互的名為knife的工具,稱之為工作站的系統上開發基礎架構代碼。Chef使用大量內置的DSL資源(例如:package,service,file,directory等操作系統資源分類)對目標節點的配置進行建模,代碼可以映射到內部的用來執行這些代碼的各種提供者上。

所能實現的示例代碼如下所示,下面是配置Linux操作系統中的Apache伺服器。

package httpd do action :installendservice httpd do action [ :enable, :start ]end

下面是在Linux操作系統里配置 /a/b/c 目錄

directory /a/b/c do owner admin group admin mode 0755 action :create recursive trueend

以下面就是 /a/b/c 目錄的配置結果狀態:

$ls ‐ld /a/b/cdrwxr--‐xr--‐x. 5 admin admin 4096 Feb 14 11:22 /a/b/c

Chef其它的重要術語:

  • recipe :包含了一個或者對個資源描述定義(Chef預定義了文件、用戶、軟體包、服務等等資源,可以擴展開發自定義資源類)
  • cookbook :包含了一個或多個recipe配方
  • data bag :包含了一個或多個配置數據點(data bag item),是JSON格式,一個cookbook食譜可以包含一個或者多個數據袋
  • run list :包含了一個或者多個cookbook食譜,可將其部署在被管理的node節點上
  • role :一組特定內容的run list運行清單構成了一個角色
  • environments:同我們現在對環境的定義,並可以一一對應起來

Chef是偏主機配置管理的非常的Iac語言,它具有很豐富的擴展能力和生態系統。它有很好的擴展能力,很強的邏輯性,能夠進行深度的表達和鍛造。它和Terraform和Ansible都有較大差異。

部署流程設計

將以上手工處理過程轉換為自動化執行的、一鍵式觸發或者自動觸發的流程需要關注很多個要點。

使用Chef部署自開發的應用程序,包括配置所依賴的操作系統配置和軟體,以及自身所需的應用配置。可以使用Liquibase進行資料庫的schema部署和更新。可以用Jenkins協調和組織所有工序的執行。使用Jenkins管理部署流程的感覺和用它執行CI是類似。

從簡單開始,盡量將一組彼此相關的、版本化的可部署物組織在一起發布,例如在一個發布集合中可以包含:UI、REST伺服器、消息服務和資料庫。盡量使用一條命令構建,使用一條命令部署。

Cookbook設計類型

Library Cookbook 庫食譜 :這種類型的食譜涵蓋了通用的、可重用的邏輯。例如所有配置基線,也可以是安全基線。例如:dns、ntp、主機登錄提示、用戶和組、禁用服務清單等等。開發擴展的自定義chef資源,用來部署自開發應用。

Application Cookbook 應用系統食譜 :在以上庫食譜的基礎上,為一個套應用系統開發一個Cookbook食譜,每個應用可是一個recipe配方,recipe配方使用自定義開發的Chef資源。這樣就形成了非常輕量的代碼庫。

Data Bag 數據袋:包含了各種應用配置,例如:服務埠、JAVA_OPTS等等。一個應用系統Cookbook食譜對應一個數據袋,袋子裡面包含了該應用在每一套環境里的相關所有配置點。

上線一個應用的新版本意味著新版本IaC代碼的更新和部署,大致的流程是:編輯Chef代碼、推送到Chef管理伺服器、在節點上運行Chef客戶端程序執行部署動作。Chef伺服器的版本始終和版本控制庫里的Master主幹保持一致,這同樣意味著環境配置和Master主幹代碼保持一致。

用Chef開發自定義應用資源的實例代碼如下,這段代碼表示了一個Java應用war包的部署。

基於類似於以上的自定義資源類型,在必要的情況下,還可以對其開發Action(chef資源的操作),可能的操作定義有:

  • 從Artifactory伺服器下載Java、Tomcat和WAR包。
  • 在標準的路徑安裝Java和Tomcat。
  • 創建和配置Tomcat容器
  • 在特定的容器里安裝WAR包
  • 在主機上開防火牆埠
  • 生成應用屬性文件
  • 啟動Tomcat容器

Data Bag數據袋的實例代碼結構如下:

"version":"1.4.9","runtime":{ "my--app--ui":{ "java_opts": "--‐Xmx2G --‐XX:MaxPermSize=1024m" }},"app_config":{ "db.url": "jdbc:postgresql://devdb:5432/myapp", "svc.foo.url": "http://devsvc:9000/foo"}

以上是data_bags/my_app/DEV.json的定義,還可以有其它環境的定義data_bags/my_app/TEST.json和data_bags/my_app/PROD.json等。

人員角色

基礎架構的持續集成需要Dev和Ops的相互協作,才能做通,才能全面覆蓋應用所需要的技術棧。

部署人員 更data_bag新數據袋和環境定義文件,觸發生產環境部署的動作,調度chef-client客戶端的運行,或者推送新版本的Chef代碼更新。

技術負責人 維護應用系統Cookbook食譜。

框架開發人員 維護庫Cookbook食譜,維護框架,持續改進流程。

以上這三種角色,從上到下是從Ops到Dev的過渡。對於傳統IT組織的架構,部署人員是Ops團隊的,框架開發人員是Dev團隊的。

這三種角色都湊齊了,才能起到全套應用系統的整體建模和編碼,而且每個角色都有負責的部分。技術負責人可能是來自Ops和Dev團隊的技術大拿。他們對整體的正確性和完整性負責。

目前也有Dev團隊在其內部招聘運維研發的角色。這三種角色是基礎架構即代碼的層次結構和人員團隊架構的對應,在實際工作中可以靈活應用;一方面覆蓋所有技術層次,另外一方面引入所有必要的人員,是團隊形成合力。

如果不是本著將全套應用系統做全量的部署,以上任何角色做自己職責範圍內的IaC自動化實踐,其實效果是事倍功半的,或者機會只有技術學習和探索的價值。

構建Cookbook

在開發了各種Cookbook之後,我們就需要對其進行持續測試,因此就需要使用Cookbook的持續構建流程。這個步驟就如同我們對應用程序的代碼做CI一樣。

開發人員(程序員不是業務應用開發者的專業名詞,這裡指IaC開發者,可能來自任何團隊)在Workstations工作站上開發Chef的Cookbook代碼,將代碼提交到GitHub上的Chef代碼倉庫。

Jenkins的Master伺服器會觸發CI Job,調用Ruby Slave對Cookbook代碼進行集成和測試,然後觸發EC2臨時實例的創建,將Cookbook在EC2實例中進行測試,使用Artifactory中存儲的應用軟體包部署應用。如果測試都通過了,就觸發Release Job,它將Cookbook代碼上傳到Chef伺服器,供所各種環境中的被管理節點使用。

上圖用EC2作為Chef代碼的CI環境,可以替代的方案有Vagrant+虛擬化(Virtual Box或者kvm),或者使用其它虛擬機資源池,如Ovirt KVM、Xen、Nutanix。我實際測試過使用Terraform對接Nutanix資源池,虛擬機創建超級快,幾乎是秒得的速度。現有的虛擬化資源池就是最方便的對接對象,需要了解一下API和對接工具即可。

對於Jenkins構建伺服器而言,每一套應用系統對應的Cookbook組/集合的測試和發布都會在同一個構建伺服器上發生,一般情況下這個伺服器也是這些應用的CI伺服器;這個Jenkins伺服器也是相關Cookbook的CI作業和發布作業的運行地點。這個伺服器上會安裝所需要的Ruby gem包,應該能訪問到與Chef伺服器鏈接所需要的秘鑰;應該可以使用到創建EC2測試節點虛擬機的秘鑰,或者說訪問其他類型虛擬機資源池的用戶名和密碼。

Cookbook CI Job

Cookbook CI作業的觸發條件是:當有新Chef代碼被合併的時候。它會進靜態代碼掃碼和測試工作,包括如下內容:

  • 使用json和gem的相關工具分析JSON的句法
  • 使用Tailor做Ruby的句法和風格掃描
  • 使用Knife做Chef代碼的句法分析
  • 使用Foodctritic做Che代碼的句法分析和正確性分析

Chef代碼在測試虛擬機里的集成測試是本文的重點,集成測試工具使用Test Kitchen,這個工具有一系列和虛擬化/雲環境對接的插件,如 kitchen-ec2插件等。能按需臨時的創建用於集成測試的虛擬機,在測試完畢,得出了測試結果之後,就刪除本作業所創建的臨時虛擬機。

在集成測試的生命周期過程中也可能創建多個測試虛擬機/EC2實例,這個過程使對應用系統里的所有組件進行模擬的、實際的安裝包和服務部署,進行單節點或者多節點的全量應用系統部署。在每個節點上都執行Chef代碼,在Chef對應用系統的配置和部署完成之後,在對運行中的應用進行驗證測試,測試包括測試相關的服務埠是否能訪問,返回結果是否正常等等,Chef是可以進行測試驅動開發的,因此可以寫出較細緻的測試代碼,從而分析本Cookbook集成測試通過與否。在測試結束了以後(最好是10分鐘左右或更短),刪除所有測試的虛擬機資源。

應該儘可能的優化這個集成測試,盡量縮短它的執行時間。可以創建專用的EC2-AMI/Nutanix/Kvm/VMWare操作系統鏡像,預裝所需要的Ruby環境和Chef工具。

使用Chef Solo(不依賴chef伺服器)執行Chef代碼的測試,以免將臨時節點也添加到了Chef伺服器,同時也消除了Chef的客戶端和伺服器架構之間相互通訊的消耗,這個場景里其實沒有使用Chef伺服器的必要。使用一個名為CHEFDEV的偽環境來測試代碼,而JSON文件里定義的真實環境則被保留用於正式生產環境。在創建EC2虛擬機的時候,給它們打上特定的標籤,從而保持一定的可追蹤性和環境的可維護性。

Cookbook Release Job

這個作業的運行內容和CI Job基本一致的,而它是靠人為手工觸發的,從Chef角度看,可以說:本文上述的所有描述,屬於Chef風格的基礎架構即代碼程序的持續交付。本作業將測試成功的代碼在GitHub/GitLab里打上標籤,並且上傳Cookbook的新版本到正式的Chef生產伺服器上。

可以想像經過多個Cookbook的build job之後,Cookbook的某個版本被發布到了生產環境中,用於環境的配置和應用的部署。本Job作業交付了IaC的開發結果到生產環境中的Chef管理伺服器。

其它IaC的基礎架構持續及集成與本文描述的也應該是類似流程。

應用部署流程

Cookbook的開發和集成完畢了以後,它的結果產物是一些列新版本的Cookbook代碼,它們最終上傳到Chef伺服器。支持生產環境應用部署的Chef伺服器與各種環境保持連接,包括測試、預發布和生產等等。

在發布過程中所使用到的製品是從Artifactory中拉取的。下面簡單說下這個架構中的關鍵點。

Jenkins部署伺服器 這是專門用於各種部署工作的Jenkins Master伺服器,它和上一個步驟里Cookbook的Build伺服器是不同的。它的Slave應該滿足這些需求:安裝了所需要Ruby環境和gem包。安裝了Chef工具,並且具有能更新Chef伺服器的秘鑰。具有能訪問各種虛擬化環境中節點的SSH秘鑰。

部署作業的類型 可以對於每一個應用組(一套應用系統)設置兩個部署作業:在開發環境中的,用於開發人員使用的DEV部署作業;另外是運維人員所使用的Non-Dev部署作業。在實踐過程中也能發展出其他類型。

部署人員的工作流程:

  1. 變更Chef相關代碼和配置,包括:編輯應用的data bag配置數據點,有必要的話編輯環境文件,合併代碼。
  2. 然後在Jenkins部署伺服器上執行作業。

通過以上的流程和工具,開發、測試和運維的相關人員,如果需要部署應用了,就可以用一鍵式的、自助式的部署模式,將任何應用應用系統通過一鍵式的方式自動化的部署到各種應用環境中。

這樣我們將大量各種角色人員都從事的、沒有附加值的應用部署工作,徹底的消滅掉了,節省的時間可以用來做更多有意義的工作,Dev人員有更多時間編碼、測試人員不需要等待時間、運維人員也降低的工作壓力。

總結

關於基礎架構的持續集成,還有下列值得參考的原則:

  • 儘可能的標準化:包括技術、設計和流程等方面;要能夠支持環境的規模化擴展,例外是可以的,但是要盡量避免。
  • 所有工具最好有API:避免在某個工具,在工具鏈上的任何環節的脫節。
  • 使用多種形式的溝通路徑:這個實踐需要用各種方式進行宣傳和推廣,包括:全員大會的主題分享、每個團隊的啟動會議、與開發人員的隨時溝通,使用文檔進行知識傳播和溝通。
  • 保持樂觀,盡量發現和找到那些志同道合的早期響應者,讓他們和你站在一條戰線上。

引用

參考視頻

youtube.com/watch?

參考書籍


推薦閱讀:

如何跨出敏捷Scrum第一步?唯品會是這麼做的
說服上司落地實踐DevOps,這裡有幾組數據
DevOps發展的九個趨勢
基於Docker持續交付平台建設的實踐

TAG:DevOps |