基於Docker持續交付平台建設的實踐

作者:劉曉明,互聯網公司運維技術負責人,擁有10年的互聯網開發和運維經驗。一直致力於運維工具的開發和運維專家服務的推進,賦能開發,提高效能。 廣告時間:最後給自己代個鹽~~歡迎大家有空時翻下我牌子(知乎號:佈道 ),看看之前的文章,再點個讚唄,順便關注下專欄「開發運維」。

本文已經在《程序員》雜誌2017年11月刊,CSDN網站、高效運維、DevOps時代、DockerOne、Python中文社區等進行發表和轉載。本文禁止轉載,獲取授權請聯繫作者!

作為創業公司和推行DevOps工程師們來說,都遇到過這樣的問題:

1. 硬體資源利用率的問題,造成部分成本的浪費

在網站功能中不同的業務場景有計算型的,有IO讀寫型的,有網路型,有內存型的,集中部署應用就會導致資源利用率不合理的問題。比如,一個機器上部署的服務都是內存密集型,那麼CPU資源就都很容易浪費了。

2. 單物理機多應用無法對無法進行有效的隔離,導致應用對資源的搶佔和相互影響

一個物理機器跑多個應用,無法進行所使用的CPU,內存,進程進行限制,如果一個應用出現對資源的搶佔問題,就會引起連鎖反應,最終導致網站部分功能不可用。

3. 環境、版本管理複雜,上線部署流程缺乏,增加問題排查的複雜度

由於內部開發流程的不規範,代碼在測試或者上線過程中,對一些配置項和系統參數進行隨意的調整,在發布時進行增量發布,一旦出現問題,就會導致測試的代碼和線上運行的代碼是不一致的,增加了服務上線的風險,也增加了線上服務故障排查的難度。

4. 環境不穩定,遷移成本高,增加上線風險

在開發過程中存在多個項目並行開發和服務的依賴問題,由於環境和版本的複雜性很高,不能快速搭建和遷移一個環境,導致無法在測試環境中無法模擬出線上的流程進行測試,很多同學在線上環境進行測試,這裡有很高的潛在風險,同時導致開發效率降低。

5. 傳統虛擬機和物理機佔用空間大,啟動慢,管理複雜等問題

傳統虛擬機和物理機在啟動過程進行載入內核,執行內核和init進行,導致在啟動過程佔用很長時間,而且在管理過程中會遇到各種各樣的管理問題。

基於Docker容器技術,運維技術團隊開發了五阿哥網站的容器雲平台。通過容器雲平台95%的應用服務已經實現容器化部署。這些應用支持業務按需拓展,秒級伸縮,提供與用戶友好的交互過程,規範了測試和生產的發布流程,讓開發和測試同學從基礎的環境配置和發布解放出來,使其更聚焦自己的項目開發和測試。結合五阿哥容器雲平台和docker容器技術的實踐,本文先介紹如何實現7*24小時「一站式」的持續交付,實現產品的上線。

容器雲平台架構圖

Docker鏡像標準化

眾所周知,Docker的鏡像是分層的。對鏡像分層進行約定:

第一層是操作系統層,由CentOS/Alpine等基礎鏡像構成,安裝一些通用的基礎組件;

第二層是中間件層,根據不同的應用程序,安裝它們運行時需要使用到的各種中間件和依賴軟體包,如,nginx、tomcat等;

第三層是應用層,這層僅包含已經打好包的各應用程序代碼。

Docker Image分層

經驗總結:如何讓自己的鏡像變的更小,PUSH的更快?

Docker Image優化前後對比

  • dockerfile構建應用鏡像,在中間件層遇到一些需要安裝的軟體包時,儘可能的使用包管理工具(如yum)或以git clone方式下載源碼包進行安裝,目的是將軟體包的copy和安裝控制在同一層,軟體部署成功後清除一些無用的rpm包或源碼包,讓基礎鏡像的尺寸更小。
  • Java應用鏡像中並沒有將jdk軟體包打入鏡像,將jdk部署在每台宿主上,在運行鏡像時,通過掛載目錄的方式將宿主機上的java家目錄掛載至容器指定目錄下。因為它會把基礎鏡像撐得非常大
  • 在構建應用鏡像時,docker會對這兩層進行緩存並直接使用,僅會重新創建代碼出現變動的應用層,這樣就提高了應用鏡像的構建速度和構建成功後向鏡像倉庫推送的速度,從整體流程上提升了應用的部署效率。

容器的編排管理

編排工具的選型:

Docker編排工具對比

Rancher圖形化管理界面,部署簡單、方便,

可以與AD、LDAP、GITHUB集成,基於用戶或用戶組進行訪問控制,快速將系統的編排工具升級至kubernetes或者swarm,同時有專業的技術團隊進行支持,降低容器技術入門的難度。

Rancher架構圖

基於以上優點我們選擇Rancher作為我們容器雲平台的編排工具,在對應用的容器實例進行統一的編排調度時,配合Docker-Compose組件,可以在同一時間對多台宿主機執行調度操作。同時,在服務訪問出現峰值和低谷時,利用特有的rancher-compose.yml文件調用「SCALE」特性,對應用集群執行動態擴容和縮容,讓應用按需求處理不同的請求。https:/zhuanlan.zhihu.com/p/29093407

容器網路模型選型:

Docker 網路對比

由於後端開發基於阿里的HSF框架,生產者和消費者之間需要網路可達,對網路要求比較高,需要以真實IP地址進行註冊和拉取服務。所以在選擇容器網路時,我們使用了Host模式,在容器啟動過程中會執行腳本檢查宿主機並分配給容器一個獨立的埠,來避免衝突的問題。

持續集成與持續部署

· 持續集成

監測代碼提交狀態,對代碼進行持續集成,在集成過程中執行單元測試,代碼Sonar和安全工具進行靜態掃描,將結果通知給開發同學同時部署集成環境,部署成功後觸發自動化測試(自動化測試部分後續會更新)。

持續集成

靜態掃描結果:

靜態掃描結果

· 持續部署,是一種能力,這種能力非常重要,把一個包快速部署在你想要的地方。平台採用分散式構建、部署,master管理多個slave節點,每個slave節點分屬不同的環境。在master上安裝並更新插件、創建job、管理各開發團隊許可權。slave用於執行job。

持續部署

基於上述架構,我們定義了持續部署規範的流程:

(1)開發同學向gitlab提交代碼;

(2)拉取項目代碼和配置項文件,執行編譯任務;

(3)拉取基礎鏡像,將編譯好的應用包打入生成最新的應用鏡像,推送到鏡像倉庫;

(4)根據當前應用及所屬環境定製化生成docker-compose.yml文件,基於這個文件執行rancher-compose命令,將應用鏡像部署到預發環境(發布生產前的測試環境,相關配置、服務依賴關係和生產環境一致)。

(5)預發環境測試通過後將應用鏡像部署至線上環境,測試結果通知後端測試同學。

容器的運行管理

應用容器現在已經部署到線上環境,那麼在整個容器的生命周期中,還需要解決下面兩個問題:

(1) 如何保存應用程序產生的運行日誌和其它業務日誌;

(2) 如何在後端服務出現變化後nginx能夠自動發現並完成配置更新。

日誌管理

容器在運行時會在只讀層之上創建讀寫層,所有對應用程序的寫操作都在這層進行。當容器重啟後,讀寫層中的數據(包含日誌)也會一併被清除。雖然可以通過將容器中日誌目錄掛載到宿主機解決此類問題,但當容器在多個宿主機間頻繁漂移時,每個宿主機上都會有留存應用名的部分日誌,增加了開發同學查看、排查問題的難度。

綜上所屬,日誌服務平台作為五阿哥網站日誌倉庫,將應用運行過程中產生的日誌統一存儲,並且支持多種方式的查詢操作。

日誌管理

通過在日誌服務的管理界面配置日誌採集路徑,在容器中部署agent把應用日誌統一投遞到logstore中,再在logstore中配置全文索引和分詞符,以便開發同學能夠通過關鍵字搜索、查詢想要的日誌內容。

經驗總結:如何避免日誌的重複採集問題?

日誌服務的agent需要在配置文件「ilogtail_config.json」中增加配置參數「check_point_filename」,指定checkpoint文件生成的絕對路徑,並且將此路徑掛載至宿主機目錄下,確保容器在重啟時不會丟失checkpoint文件,不會出現重複採集問題。

服務的註冊

etcd是一個具備高可用性和強一致性的鍵值存儲倉庫,它使用類似於文件系統的樹形結構,數據全部以「/」開頭。etcd的數據分為兩種類型:key和directories,其中key下存儲單獨的字元串值,directories下則存放key的集合或者其他子目錄。

應用註冊

在五阿哥環境中,每個向etcd註冊的應用服務,它們的根目錄都以」/${APP_NAME}_${ENVIRONMENT}」命名。根目錄下存儲每個應用實例的Key信息,它們都以「${IP}-${PORT}」的方式命名。

下圖是使用上述約定,存儲在etcd上某應用實例的數據結構:

etcd數據存儲結構

可以看到我是使用get方法向etcd發送請求的,請求的是部署在預發環境(PRE)的搜索服務(search);在它的根目錄「/search_PRE」下,僅存儲了一個應用實例的信息,這個實例的key是「172.18.100.31-86」;對應的value是「172.18.100.31:86『』,整個註冊過程是這樣的:

① 通過代碼為容器應用程序生成隨機埠,和宿主機正在使用的埠進行比對,確保埠沒有衝突後寫入程序配置文件;

② 把通過python和etcd模塊編寫的服務註冊工具集成在腳本中,將IP地址和上一步獲取的隨機埠以參數的方式傳遞給服務註冊工具;

③ 待應用程序完全啟動後,由服務註冊工具以約定好的數據結構將應用實例的寫入etcd集群,完成服務註冊工作;

④ 容器定時向etcd發送心跳,報告存活並刷新ttl時間;

⑤ 容器腳本捕獲rancher發送至應用實例的singnal terminal信號,在接收到信號後向etcd發送delete請求刪除實例的數據。

註:在ttl基礎上增加主動清除功能,在服務正常釋放時,可以立刻清除etcd上註冊信息,不必等待ttl時間。

經驗總結:容器在重啟或者意外銷毀時,讓我們一起看一下這個過程中容器和註冊中心都做了什麼事情?

應用在註冊是攜帶key

和value時攜帶了ttl超時屬性,就是考慮到當服務集群中的實例宕機後,它在etcd中註冊的信息也隨之失效,若不予清除,失效的信息將會成為垃圾數據被一直保存,而且配置管理工具還會把它當做正常數據讀取出來,寫入web server的配置文件中。要保證存儲在etcd中的數據始終有效,就需要讓etcd主動釋放無效的實例信息,來看一下註冊中心刷新的機制,代碼直接奉上:

#!/usr/bin/envnpythonnimport etcdnimport sysnarg_l=sys.argv[1:]netcd_clt=etcd.Client(host=172.18.0.7)ndefnset_key(key,value,ttl=10):ntry:nreturnnetcd_clt.write(key,value,ttl)nexcept TypeError:nprint key or vlaue is nullndefnrefresh_key(key,ttl=10):ntry:nreturnnetcd_clt.refresh(key,ttl)nexcept TypeError:n print key is nullndefndel_key(key):ntry:nreturn etcd_clt.delete(key)nexcept TypeError:nprint key is nullnif arg_l:nif len(arg_l) == 3:nkey,value,ttl=arg_lnset_key(key,value,ttl)nelif len(arg_l) == 2:nkey,ttl=arg_lnrefresh_key(key,ttl)nelif len(arg_l) == 1:nkey=arg_l[0]ndel_key(key)nelse:nraise TypeError,Only threenparameters are needed herenelse:nraise Exception(args is null)n

服務的發現

confd是一個輕量級的配置管理工具,支持etcd作為後端數據源,通過讀取數據源數據,保證本地配置文件為最新;不僅如此 ,它還可以在配置文件更新後,檢查配置文件語法有效性,以重新載入應用程序使配置生效。這裡需要說明的是,confd雖然支持rancher作為數據源,但考慮易用性和擴展性等原因,最終我們還是選擇了etcd。

和大多數部署方式一樣,我們把confd部署在web server所在的ECS上,便於confd在監測到數據變化後及時更新配置文件和重啟程序。confd的相關配置文件和模板文件部署在默認路徑/etc/confd下,目錄結構如下:

/etc/confd/n├── conf.dn├── confd.tomln└── templatesn

confd.toml是confd的主配置文件,使用TOML格式編寫,因為我們etcd是集群部署,有多個節點,而我又不想把confd的指令搞的又臭又長,所以將interval、nodes等選項寫到了這個配置文件里。

cond.d目錄存放web server的模板配置源文件,也使用TOML格式編寫。該文件用於指定應用模板配置文件路徑(src)、應用配置文件路徑(dest)、數據源的key信息(keys)等。

templates目錄存放web server下每個應用的模板配置文件。它使用Go支持的text/template語言格式進行編寫。在confd從etcd中讀取到最新應用註冊信息後,通過下面的語句寫入模板配置文件中:

{{range getvs "/${APP_NAME}/*"}}nserver {{.}};n{{end}}n

服務發現

通過supervisor管理confd進程。confd在運行後會每隔5秒對etcd進行輪詢,當某個應用服務的K/V更新後,confd會讀取該應用存儲在etcd中的數據,寫入到模板配置文件中,生成這個應用配置文件,最後由confd將配置文件寫入到目標路徑下,重新載入nginx程序使配置生效。

總結

本文是五阿哥運維技術團隊針對Docker容器技術在如何在持續交付過程中探索和實踐,目前已經將發布部署許可權開放給應用開發的owner,實現7*24小時「一站式」的持續交付,整體提高了公司的研發過程的交付能力。

接下來會不斷優化持續交付過程中遇到的各種場景,逐漸完善容器雲平台,同時會將容器雲平台各種功能,總結的經驗和教訓不斷分享給大家,給大家在工作中一些參考,避免走重複的「彎路」。


更多精彩請點擊:

用 Python 分析胡歌的《獵場》到底值不值得看?

XSS常見攻擊與防禦

利用500W條微博語料對評論進行情感分析

還在手調網路許可權?資深IT工程師都這樣玩企業組網

微服務在互聯網公司演進過程

推薦閱讀:

2017 Web 開發者學習路線圖
怎麼把SQL server放到docker里運行?

TAG:Docker | DevOps | 运维 |