天天看點

滬江基于容器編排的Dev/Ops流程add timezone and default it to Shanghai

【編者的話】我們整個 DevOps 流程是建立在容器編排的基礎上的,目的是簡化流程和實作自動化 CI/CD 和自動化運維。當中會有很多沒有想到的地方,可能也不太适用于複雜場景。

本文講的是<b>滬江基于容器編排的Dev/Ops流程</b>随着 DevOps 和 SRE 概念的流行,越來越多的 developer 和 operater 們摒棄傳統的開發部署流程,轉向了如下圖所示的無線循環模式:

<a href="http://dockerone.com/uploads/article/20171001/049b8190c596f48cbbebfe6d984f234d.jpg" target="_blank"></a>

在我了解 DevOps 包含三個大塊:靈活開發(Agile)、持續內建與傳遞(CI/CD)、自動運維(ITSM)。 

在容器化的時代,我們是如何實作 DepOps 或者 SRE 的呢?下面我就來分享一下滬江學習産品線團隊基于容器編排的 DevOps 流程。

大道至簡,所有血的教訓告訴我們,不要把簡單的事情複雜化。換句話說,不要用複雜的方法處理簡單的事情。我對靈活的了解是「快」和「微」。快指疊代快,開發快,上線快,性能快。微指微服務、微鏡像。圍繞這兩點,在開發階段我們需要做以下幾件事:

這是個比較大的概念,不在這裡讨論,有興趣可以參考我的其他文章。但隻有應用小了,才有可能快起來。

為了讓 Docker 啟動和運作得快,首先就是要對 Docker 瘦身。由于所有的應用全部會統一為 Java 語言開發,是以我們以 Java 為例,選用了 jre-alpine 作為我們的基礎鏡像,下面是 Dockerfile 的例子:

RUN apk --update add --no-cache tzdata

ENV TZ=Asia/Shanghai

RUN mkdir -p /app/log

COPY  ./target/xxx.jar  /app/xxx.jar

EXPOSE 9999

VOLUME ["/app/log"]

WORKDIR /app/

ENTRYPOINT ["java","-Xms2048m", "-Xmx2048m", "-Xss512k", "-jar","xxx.jar"]

CMD [] 

使用上述 Dockerfile 生成的鏡像平均隻有 80 多 MB,啟動時間幾乎在 5 秒内。使用 Alpine 鏡像雖然減小了體積,但缺少一些工具指令,例如 curl 等,可以根據需要酌情安裝。另外遇到的一個坑是時區問題:由于 Docker 鏡像内的時區是 UTC 時間,和主控端的東 8 區不一緻,是以必須安裝 timezone 工具并設定 TZ,才能使容器内時間和主控端保持一緻,對資料庫的寫入和日志的輸出都是非常必要的一環。

早在虛拟機時代,我們已經做到了使用包含依賴的虛拟機鏡像來加速部署,那麼為什麼要止步于此呢?我們可以更進一步,把服務本身也包含在鏡像中,Docker 用了更輕量的方式已經實作了這一點。

這裡我們還要介紹一個概念,要讓制作的鏡像,能在所有安裝了 Docker 的伺服器上運作,而不在乎主控端的作業系統及環境。借用 Java 的一句話來說:一次制作,多平台運作。是以,我們還會把所有環境的配置檔案,以不同的檔案名全部放入鏡像中,通過參數來選擇 Docker 啟動時使用的環境配置檔案。

值得注意的是,如果開發的應用是基于 Spring 架構的話,這個功能很好實作。但如果是其他語言開發,會有一定的開發量。

本文以預設 Java 開發當所有的開發工作完成後,推薦程式目錄結構是這樣的:

自動化的持續內建和傳遞在整個 DevOps 流中起了重要的角色,他是銜接開發和運維的橋梁。如果這一環做的不好,無法支撐大量微服務的快速的疊代和高效運維。在這一環節,我們需要靈活的運用工具,盡量減少人參與,當然仍然需要圍繞「快」和「微」做文章。

如何減少人工參與到持續內建與持續傳遞呢?我們最希望的開發過程是:對着計算機說出我們的想要的功能,計算機按照套路,自動編碼,自動釋出到測試環境,自動運作測試腳本,自動上線。當然,目前時代要實作自動編碼的過程還需要發明那隻「貓」。

<a href="http://dockerone.com/uploads/article/20171001/83ceccd73a7e9df32478f766cdc2fd97.jpg" target="_blank"></a>

但隻要對測試有足夠信心,我們完全可以實作一種境界:在炎熱的下午,輕松地送出自己編寫的代碼,去休息室喝杯咖啡,回來後看見自己的代碼已經被應用在生産環境上了。在容器時代,我們可以很快速的實作這一夢想,其具體步驟如下圖:

<a href="http://dockerone.com/uploads/article/20171001/c1cdb686030580b98e295b94d61242fd.jpg" target="_blank"></a>

持續內建的第一步是送出代碼(Code Commit),VCS 也由 CVS,SVN 進化到如今的 Git,自然不得不說一下 Gitflow。談起無人不曉的 Gitflow,大家一定會大談其優點:支援多團隊,設定多國家的開發人員并行開發,減小代碼沖突或髒代碼的上線機率。它的大緻流程如下:

<a href="http://dockerone.com/uploads/article/20171001/284a31eb5f123a239c6f575cfb9c1ad7.jpg" target="_blank"></a>

Gitflow 給我們展示了複雜團隊在處理不通代碼版本的優雅解決方案,它需要feature、develop、release、hotfix、master 5 條分支來處理不同時段的并行開發。但這真的合适于一個不超過 20 人的本地合作團隊開發嗎?我們的開發團隊不足 6 人,每個人負責 3 個以上的微服務,幾乎不可能在同個項目上安排兩個以上的同學并行開發。

在初期我們準守規定并使用标準的 Gitflow 流程,開發人員立刻發現一個問題,他們需要在至少 3 條分支上來回的 merge 代碼,且不會有任何代碼沖突(因為就一個人開發),降低了開發的效率。這讓我意識到,Gitflow 模式也許并不适合于小團隊微服務的世界,一種反 Gitflow 模式的想法出現在腦海中。我決定對Gitflow 進行瘦身,化繁至簡。

<a href="http://dockerone.com/uploads/article/20171001/fb728b373346ec8dfd77cd8afa094623.jpg" target="_blank"></a>

我們把 5 條分支簡化為 3 條分支,其中 Master 分支的作用隻是維護了最新的線上版本的作用,Dev 分支為開發的主要分支,所有的鏡像是以此分支的代碼為源頭生成的。這時開發的過程變為:

開發人員從 Dev 分支中 checkout 新的 feature 分支,在 feature 分支上進行開發

當開發完成後 merge 回 Dev 分支中,根據 Dev 分支的代碼打成鏡像,部署 QA 環境交給 QA 人員測試

測試中如有 bug 便在新分支中修複問題循環步驟 2

測試完成 merge 回 Master 分支

如此一來,隻有從 Feature 把代碼 merge 到 Dev 分支的一次 merge 動作,大大提升可開發效率。

Jenkins 作為老牌 CI/CD 工具,能夠幫我們自動化完成代碼編譯、上傳靜态代碼分析、制作鏡像、部署測試環境、冒煙測試、部署上線等步驟。尤其是Jenkins 2.0 引入 Pipeline 概念後,以上步驟變的如此行雲流水。它讓我們從步驟 3 開始,完全可以無人值守完成整個內建和釋出過程。

工欲善其事必先利其器,首先我們必須要在 Jenkins 上安裝插件 :

Pipeline Plugin(如果使用Jenkins2.0預設安裝)

Git

Sonar Scaner

Docker Pipeline Plugin

Marathon

現在,我們開始編寫 Groove 代碼。基于容器編排的 Pipeline 分為如下幾個步驟:

1、檢出代碼

這個步驟使用 Git 插件,把開發好的代碼檢出。

2、Maven 建構 Java 代碼

由于我們使用的是 Spring Boot 架構,生成物應該是一個可執行的 jar 包。

3、靜态代碼分析

通過 Sonar Scaner 插件,通知 Sonar 對代碼庫進行靜态掃描。

4、制作 Docker 鏡像

此步驟會調用 Docker Pipeline 插件通過預先寫好的 Dockerfile,把 jar 包和配置檔案、三方依賴包一起打入 Docker 鏡像中,并上傳到私有 Docker 鏡像倉庫中。

5、部署測試環境

通過事先寫好的部署檔案,用 Marathon 插件通知 Marathon 叢集,在測試環境中部署生成好的鏡像。

6、自動化測試

運作事先測試人員寫好的自動化測試腳本來檢驗程式是否運作正常。

7、人工測試

如果對自動化測試不放心,此時可選擇結束 Pipeline,進行人工測試。為了說明整個流程,我們這裡選擇跳過人工測試環節。

8、部署生産環境

當所有測試通過後,Pipeline 自動釋出生産環境。

最後我們來看看整個 Pipeline 的過程:

<a href="http://dockerone.com/uploads/article/20171001/e38d6d4cd794ae067f1933cf938735c7.png" target="_blank"></a>

在介紹靈活開發時,曾介紹過根據不同環境的配置參數部署到不同的環境。如何告知部署程式用什麼樣的配置檔案啟動服務,每個環境又用多少 CPU,記憶體和 instance 呢?

下面我們就來介紹一下容器編排的配置檔案。由于我們使用 Mesos+Marathon的容器編排方式,部署的重任從以前的寫部署腳本變成了寫一個 Marathon 的配置,其内容如下:

我們把這個配置内容儲存為不同的 Json 檔案,每個對應的環境都有一套配置檔案。例如 Marathon-qa.json,Marathon-prod.json。當 Pipeline 部署時,可以通過Jenkins Marathon 插件,根據選擇不同的環境,調用部署配置,進而達到自動部署的目的。

開發部署如此的簡單快捷,是不是每個人都能友善的使用呢?答案是否定的,并不是因為技術上有難度,而是在于權限。在理想的情況下,通過這套流程的确可以做到在送出代碼後,喝杯咖啡的時間就能看見自己的代碼已經被千萬使用者使用了。

但風險過大,我們并不是每個人都能像 Rambo 一樣 bug 的存在,大多數的情況還需要使用規範和流程來限制。就像自動化測試取代不了人工黑盒測試一樣,部署測試後也不能直接上生産環境,在測試通過後還是需要有個人工确認和部署生産的過程。

是以我們需要把自動化流程和最後的部署上線工作分開來,分别變成兩個 Job,并給後者單獨配置設定權限,讓有權限的人來做最後的部署工作。這個人可以是 Team leader、開發經理,也可以是運維夥伴,取決于公司的組織結構。

那這個部署的 Job 具體幹什麼呢?在容器編排時代,結合鏡像既建構物的思想,部署 Job 不會從代碼編譯開始工作,而是把一個充分測試且通過的鏡像版本,通過 Marathon Plugin 部署到産線環境中去。這裡是 Deploy_only 的例子:

為什麼不把這個檔案跟随應用項目一起放到 scripts 下呢?因為把部署和應用分開後,可以由兩撥人進行維護,兼顧公司的組織架構。

在 DevOps 的最後階段是運維階段。在容器時代,如何對龐大的鏡像制品進行運維呢?我們的目标是盡量實作自動化運維,這裡主要講述兩點:

容器的監控大緻有兩種方式:實體機上安裝其他服務監控本機上的所有容器;通過 Mesos 或 Kubernates 自帶 API 監控容器狀态。兩種方式其實都需要在實體機上安裝相應的監控軟體或 Agent。

在我們團隊目前使用 cAdvisor + InfluxDB + Grafana 的組合套件實作對容器的監控。

首先需要在 Mesos 叢集中所有的 Agent 安裝 cAdvisor 。他負責把主控端上所有運作中的容器資料以資料點(data point)形式發送給時序資料庫(InfluxDB),下面是 cAdvisor 監控的一些資料點:

<a href="http://dockerone.com/uploads/article/20171001/0782a29fae99cc98b03d21ff2744949c.jpg" target="_blank"></a>

這些資料點經過 Grafana 整理,展示在界面上,這樣我們就能掌握具體容器的性能名額了。下面是一個 Grafana 的截圖:

<a href="http://dockerone.com/uploads/article/20171001/7d1cf7e872d230b6bdeeb4d83784a029.jpg" target="_blank"></a>

除了對容器本身的監控,主控端的監控也是必不可少的。由于監控的點有很多,這裡不一一例舉。

有了監控名額隻是實作了自動化運維的第一步,當業務請求發生大量增加或減少,通過人工監測是不能及時的進行相應的,況且還不一定有那麼多的人,7×24 小時的監控。一定需要有一套根據監控資料自行伸縮容的機制。在學習産品線,我們針對容器編排的 Mesos+Marathon 架構,開發了一套針對應用本身的自動擴容微服務。其原理如下:

<a href="http://dockerone.com/uploads/article/20171001/b4670d2bf9981a12f1cec53dc9ea2c45.png" target="_blank"></a>

通過 Restful 的接口通知 AutoScaler 程式需要監控的應用服務。

AutoScaler 程式開始讀取每台 Agent 上部署相關應用的 Metrics 資料,其中包括 CPU,記憶體的使用狀況。

當發現有應用過于繁忙(其表現形式大多是 CPU 占用過高或記憶體占用過大)時調用 Marathon API 将其擴容

Marathon 收到消息後,立刻通知 Mesos 叢集釋出新的應用,進而緩解目前的繁忙狀況。

DevOps 和 SRE 并不是一個渴望而不可及的概念,它們需要在不同的環境中落地。我們整個 DevOps 流程是建立在容器編排的基礎上的,目的是簡化流程和實作自動化 CI/CD 和自動化運維。當中會有很多沒有想到的地方,可能也不太适用于複雜場景。其次,本文中的例子也做了相應的隐私處理,可能無法直接使用。希望大家能通過我們在實踐中産生的成功和遇到的問題,提煉出适合自己的 DevOps 流程。

<b>原文釋出時間為:</b>2017-10-01

<b>本文作者:</b>黃凱

<b>本文來自雲栖社群合作夥伴Dockerone.io,了解相關資訊可以關注Dockerone.io。</b>

<b></b>

<b>原文标題:</b><b>滬江基于容器編排的Dev/Ops流程</b>