當前位置:
首頁 > 最新 > iHealth基於Docker的DevOps CI/CD實踐

iHealth基於Docker的DevOps CI/CD實踐

前言

相信我,一切事情的發生都是趕鴨子上架,沒有例外。人類所有偉大的變革都是迫不得已,可又是那麼順其自然。比如容器(docker)技術的誕生,比如箭在弦上的創業,比如野心勃勃的kubernetes,比如如今已作為左膀右臂的rancher,比如這篇文章。

iHealth致力於用全新的移動互聯體驗整合傳統的個人健康管理方式,公司業務範圍包含移動醫療、慢病護理以及健康與醫療硬體研發。不同於鄭兄的CI/CD實踐《如何利用Docker構建基於DevOps的全自動CI》,我們結合自身狀況,構建了一套我們自己的DevOps CI/CD流程,更輕更小,更適合Startup。

合適的才是最好的

(Node.js & Docker)

如果世界只有FLAG、BAT,那就太無趣了。iHealth是一家初創型公司,我所在的部門有大概10名研發人員,擔負著三端研發工作的同時,所有圍繞服務的交付和運維工作也都是我們來做。

技術的選型上,服務端、Web端和移動端(Android、iOS)都要上,但人少。所以招人的時候並沒有以貌取人,部門對外的Title都是全棧。能一門語言通吃三端,群眾基礎廣泛,恐怕沒有比Javascript/Typescript(Node.js)更合適的了。

服務端有Express、Koa、Feather、Nest、Meteor等各有其長的框架,前端大而火的Reactjs、Vuejs和Angular,不管是Server Render還是前後端分離,都可以得心應手。因為公司的健康設備(血糖儀、血壓計、體溫計、血氧、體脂秤等等)會有專門的部門研發設計以及提供SDK,所以移動端的研發工作更多是在設計實現和性能優化上,React Native是一枚大殺器。雖然現在公司並沒有桌面端的需求,但不能否認的是Electron是一個很有趣的項目,也為「全棧」這個詞增加了更多背書。

另外,選擇使用Node/Js/Ts作為全棧的基礎會附帶有RPC的好處。無需集成傳統意義上的RPC框架(如gRPC),只需在編寫遠程(微)服務方法時,編寫相應的npm package,也可以達到相同的目的,且成本更小,更易理解。

運維環境的選型上,所有的業務都運行在雲端,省去了機房維護和伺服器運維的成本。其實在盤古開荒時,我們也是編寫了Node程序後,使用PM2部署在伺服器上,並沒有使用Docker。當然也存在沒有使用Docker所帶來的一切問題:三端不同步、環境無法隔離……而Docker帶給我最大的驚喜除了超強的可移植性,更在於研發人員可以非常容易對程序的頂級架構進行推理。

事實上,我們直接使用docker-compose做容器編排著實有一段時間,在一次大規模的伺服器遷移中,發現需要重新思考越來越多的container管理和更完善的編排方案。Rancher(Cattle)就是在這時被應用到技術棧中。

一切從Github開始

在運維環境一波三折的同時,DevOps的征程也是亦步亦趨,步步驚心。幸運的是,我們知道自己缺乏什麼,想要什麼,所以能比較容易的做到「哪裡不會點哪裡」。如同上一章節所述,合適的才是最好的。持續集成(CI)與持續交付(CD)的迭代過程,從最初的代碼拷貝,到結合docker-compose與rsync命令,到使用CI/CD工具,做到相對意義上的自動化……迄今為止,我們摸索出一套相對好用並且好玩的流程:

故事大致是這樣的,當一隻代碼猴提交代碼之後,他需要去接一杯咖啡。在貓屎氤氳的霧氣里45°角仰望天花板,手機微信提醒這次構建成功(或失敗,並附帶污言穢語)。這時他可以開始往工位走,坐下時,微信又會提醒本次部署到Rancher成功(或失敗)。

這一切開始的地方是github。當開發者寫完 BUG 功能之後,需要有地方保存這些寶貴的資料。之所以沒有使用Gitlab或Bitbucket搭建私有的Git伺服器,是因為我們認為代碼是最直接的價值體現。服務如骨架,終端如皮膚,UE如衣服,三者組成讓人賞心悅目的風景,代碼是這背後的基礎。我們認為在團隊精力無法更分散、人口規模尚小時,購買Github的商業版是穩妥且必要的,畢竟那幫人修復一次故障就像把網線拔下來再插上那樣簡單。

Drone CI

Drone這個單詞在翻譯中譯作雄蜂、無人機。我特意諮詢了一位精通一千零二十四國語言的英國朋友,說這個詞的意思是autonomous,works by itself。白話就是有活它自己干,而且是自主的。不過這個解釋對於Drone來說名副其實。這個在Github上擁有13,000+ Stars的開源項目,使用Golang編寫,相比Jenkins的大而全,Drone是為Docker而生的CI軟體。如果有使用過Gitlab CI的小夥伴,相信對Drone的使用方式不會感到陌生,他們都是使用Yaml風格文件來定義pipeline:

pipeline:

build:

image: node:latest

commands:

- npm install

- npm run lint

- npm run test

publish:

image: plugins/npm

when:

branch: master

Drone的安裝方式如同Rancher一樣簡單,一行docker命令即可。當然,大家也可以看Drone的官方文檔,在這裡,只講一下使用Rancher catalog安裝Drone的方式:

查看大圖大家可以看到Drone使用Rancher catalog安裝的方法(with github),在Github 的Settings中創建Drone的OAuth App時,Home Page Url務必要寫你能訪問Drone的IP地址或域名,例如:

而OAuth App的Authorization callback URL應該對應上面的寫法:http://drone.company.com/authorize

小功告成:

登錄進Drone之後,在Repositories中找到你想要開啟CI的Git Repo,用switch按鈕打開它:

這表示已經打開了Drone對於這個Repo的webhook,當有代碼提交時,Drone會檢測這個Repo的根目錄中是否包含.drone.yml文件,如存在,則根據yaml文件定義的pipeline執行CI流程。

在決定使用Drone之前,需要知道的是,Drone是一個高度依賴社區的項目。其文檔諸多不完善(完善過,版本迭代,文檔跟不上了),plugins質量良莠。但對於擅長Github issue、Google、Stackoverflow的朋友來說,這並不是特別困難的事情。Drone也有付費版本,無需自己提供伺服器,而是像Github那樣作為服務使用。

如果你決定開始使用Drone,截止到上面的步驟,我們打開了Drone對於Github Repo的監聽,再次提醒,需要在代碼repo的根目錄包含.drone.yml文件,才會真正觸發Drone的pipeline。

那麼,如果想重現上面故事中的場景,應該如何進行集成呢?

我司在構建CI/CD的過程中,現使用Harbor作為私有鏡像倉庫,從提交代碼到自動部署到Rancher,其實應當經歷如下步驟:

在這裡節選公司項目中的一段yaml代碼,描述了上述步驟:

# .drone.yaml

pipeline:

# 使用plugins/docker插件,構建鏡像,推送到harbor

build_step:

image:plugins/docker

username:harbor_username

password:harbor_password

registry: harbor.company.com

repo:harbor.company.com/registry/test

mirror: "https://registry.docker-cn.com"

tag:

- dev

dockerfile:Dockerfile

when:

branch:develop

event:push

# 使用rancher插件,自動更新實例

rancher:

image: peloton/drone-rancher

url: "http://rancher.company.com/v2-beta/projects/1a870"

access_key: rancher access key

secret_key: rancher secret key

service: rancher_stack/rancher_service

docker_image:"harbor.company.com/registry/test:dev"

batch_size:1

timeout:600

confirm:true

when:

branch: develop

event: push

# 使用clem109/drone-wechat插件,報告到企業微信

report-deploy:

image: clem109/drone-wechat

secrets:

- plugin_corp_secret

- plugin_corpid

- plugin_agent_id

title: "$"

description:

構建序列: $ 部署成功,幹得好$ !

更新內容: $

msg_url: "http://project.company.com"

btn_txt: 點擊前往

when:

branch: develop

status:

- success

對接企業微信之前,需要在企業微信中新建自定義應用,比如我們的應用名字叫Drone CI/CD。當然,您也可以給每一個項目創建一個企業微信App,這樣雖然麻煩,但是可以讓需要關注該項目的人關注到構建信息。

下面是企業微信測試的截圖:

企業微信與微信客戶端是連通的,可玩性還不錯:

代碼地址:https://github.com/lizheming/drone-wechat/blob/master/index.js

而在構建完成後,可以看到Drone控制面板里小夥伴們戰鬥過的痕迹:

ELK與Rancher的集成

ELK是ElasticSearch、Logstash與Kibana的集合,是一套非常強大的分散式日誌方案。ELK的使用更多在於其本身的優化以及Kibana面向業務時的使用,這本身是一個很大的話題,只ElasticSearch就有許多奇技淫巧。因為人力資源的原因,我們使用了兄弟部門搭建的ELK,等同於使用已有的ELK服務。所以在此也不再贅述ELK的搭建,網上有許多資源可供參考。

在這裡要做的事情,就是把rancher中的日誌歸集到已有的ELK中。

在Rancher的catalog中找到logspout,這是一個logstash的adapter,為docker而生:

在配置中設置LOGSPOUT=ignore,然後把ROUTE_URIS設置為已經搭建好的logstash地址,就可以將當前環境的日誌集成到ELK中:

Traefik與Rancher的集成

目前看來一切都很好,對嗎?的確是這樣。我們提交了代碼,drone自動構建鏡像到harbor,自動部署到Rancher,自動發送構建結果,Rancher又可以幫助自動重啟死掉的container,使用Rancher webhook也可以實現自動彈性計算,並且可以使用yaml文件定製構建流程,定製一些report信息,當構建或部署失敗時,讓企業微信自動侮辱我們的小夥伴……

可是據說微服務還講究服務註冊和服務發現,如果並不想動用Zookeeper這樣的核武器(就像我們不想用Kong一樣,一是有一定學習和維護成本,二是Logo越改越丑),那就需要找到一個輕量級,能滿足需求的替代品。況且目前並沒有遇到需要削峰的處理。

對於域名的解析,我們選擇使用Traefik作為LB,這個同樣使用Golang編寫,同樣擁有將近13,000 Stars,並且兼具簡單的服務註冊和服務發現功能。更值得一提的是,Rancher catalog里的Traefik非常友好的集成了Let"s Encrypt(ACME)的功能,可以做到自動申請SSL證書,過期自動續期。當然,不推薦在生產環境使用,SSL免費證書的數量非常容易達到閾值而使得域名無法訪問。

Traefik內部架構圖(Image from traefik.io):

如何安裝Traefik呢?我們以Rancher catalog中的Traefik為例(不使用ACME):

我們的目的是做域名解析,integration mode應該設置為metadata。Http Port設置為80,Https Port設置為443,Admin Port可以根據自己實際情況填寫,默認8000。

此時的Traefik已經準備就緒,但是打開traefik_host:8000查看控制面板時,發現Traefik並沒有做任何代理。原因是需要在代理的目標中,使用rancher labels標示出traefik的代理方式。

需要注意的是,traefik.alias有可能導致重複解析,同時traefik有自己的一套默認解析規範。更詳細的文檔請看GitHub 地址:

rawmind0/alpine-traefik

在設置Rancher labels後,可以看到Traefik的控制面板中,已經註冊了服務地址:

利用Traefik的這個特性和Rancher對於Container的彈性計算,可以做到簡單的服務註冊和服務發現。

最後需要在域名服務商那裡做A記錄解析,解析的IP地址應為Traefik的公網地址。 因為域名解析的默認埠是80和443,後面發生的事情就和Nginx的作用一毛一樣了。域名解析到Traefik伺服器的80埠(https則是443),Traefik發現這個域名已經註冊到服務中,於是代理到10.xx開頭的虛擬IP,轉發請求並發送response。與Nginx Conf如出一轍:

至此,我們已經完全實現從代碼提交,到自動部署以及域名解析的自動化。在生產環境的Traefik on Rancher中開啟Https,可以把ssl的整個信任鏈以文本的形式粘貼進去,同時修改Traefik的Https選項為true即可:

另外,Traefik並不是LB/Proxy的唯一選擇,甚至不是最酷的選擇,但確是目前與Rancher集成最好的。下面圖中的程序都值得做調研(可以小小的注意一下istio,天庭飽滿,骨骼輕奇,這還只是2017年7月底的數據……):

事實上對於Traefik我們是又愛又恨。它能非常方便的與Rancher集成,功能簡便強大,性能可觀。但在最開始著實踩了不少坑,一度打算放棄並回歸到傳統的Nginx做反向代理的方式,甚至寫了PR並被merge到master中。截止目前Rancher Catalog中最新的1.5版本,已經是一個真正穩定可用的版本了。

小技巧

Node.js的項目中書寫Dockerfile時,經常會用到yarn或者npm i來拉取依賴包。但npm的伺服器遠在世界的另一端,這時可以使用淘寶的鏡像進行加速。通常我們在本地開發時執行會記得加上npm鏡像,在伺服器上跑Dockerfile也是一樣的道理:

FROM node:alpine

WORKDIR /app

COPY package.json .

RUN npm i --registry https://registry.npm.taobao.org

COPY . .

CMD [ "node", "bin/www" ]

Drone在構建鏡像並推送到鏡像倉庫時,需要根據Dockerfile的基礎鏡像進行構建,而docker伺服器也遠在世界的另一端,同樣的可以使用mirror來指定鏡像倉庫,並盡量使用alpine鏡像縮小體積:

pipeline:

build_step:

image: plugins/docker

username: harbor_name

password: harbor_pwd

registry: harbor.company.com

repo: harbor.company.com/repo/test

mirror: "https://registry.docker-cn.com"

作大死命令,不要在伺服器上使用。但本地開發很好用。意思是停止所有container,刪除所有container,刪除所有image:

docker stop $(docker ps -aq)&&docker rm$(docker ps -aq)&&docker rmi$(docker images -aq)

結語,附帶工具鏈匯總

羅馬不是一天建成,萬丈高樓平地起。在企業發展之初,我們在打基礎的同時,也要保證項目高速迭代。短時間內無法做到Netflix的體量以及其對於微服務治理的精妙,在運作的細節中也有諸多需要完善的部分,例如BDD、TDD的實踐,傳統意義上的UAT與藍綠灰度發布,移動時代的全鏈路日誌,服務熔斷、隔離、限流以及降級的能力,亦或是星火燎原的Service Mesh……所以退一步講,必須先生存,才能生活。我們可以允許服務死掉,但是要保證無感知或極短感知的情況下,服務能迅速的活過來。

在持續交付的過程中,我們也嘗試使用sonar代碼質量管理,使用phabricator作為code review環節,因為配置的變更和微服務數量的逐漸增多,配置中心(主要考慮攜程的Apollo)的引入也迫在眉睫,調用鏈監控以及代碼重新埋點的成本(二節所述npm package rpc的優勢又可體現)是否能抵過其帶來的好處等等。但因目前尚未達到一個非常成熟的階段,所以本次不再分享,僅表述其名來啟發各位聰明的小夥伴。

除此之外,技術視野的成長也非朝夕。就像我國政府在大家買不起自行車時就開始修建高速公路,時至今日,還能說它是面子(KPI)工程嗎?與社區一同進步,開闊視野的同時,保持獨立思考的能力,是比上述所有更為重要的技能。

回到本文開頭所寫,一切都是趕鴨子上架。與其說筆者天資聰慧才貌過人風度翩翩儒雅風流,不如說這都是被逼的。同事抱怨流程繁瑣不直觀,若要做到代碼和咖啡那樣大繁若簡,就需要思考CI/CD的目的與本質。大智若愚,真正的天才,必須能夠讓事情變得簡單。

拓展資料:

Rancher:https://github.com/rancher/rancher

Drone:https://github.com/drone/drone

Harbor:vmware/harbor

Traefik:containous/traefik

Phabricator:phacility/phabricator

SonarQube:SonarSource/sonarqube

Logspout:gliderlabs/logspout

配置中心(攜程做的,代碼寫的還不錯):ctripcorp/apollo

SuperSet(BI):apache/incubator-superset

Q&A

Q:traefik api方式有沒有嘗試過?有沒有坑分享下。可以對rancher lb做轉發嗎?

A:沒有嘗試過traefik api。我嘗試對rancher lb做轉發,但是沒有成功。

Q:如果traefik所在的主機崩潰掉了怎麼辦?有高可用么?npm的那個我的做法是在物理機上做好安裝然後再copy容器裡面。

A:traefik的安裝依賴於traefik=true的所有主機,所以是課已做到部分高可用。另外,traefik也本身也是container的形式,也可以考慮針對container做HA。

Q:因為域名是解析到traefik的公網IP所以是不是可以用vip的形勢漂移,到另外一台具有traefik的主機實現ha?

A:我感覺是可以的,這是個很好的思路,回頭我要試試,哈哈。

Q:請問伺服器都是遠程的話下載和更新不慢么?

A:原生是慢的,所以文中有使用鏡像的方法。對於部署時,因為harbor和業務伺服器在一個內網,或者做對等網路,速度也是很快的。

Q:Reactjs、Vuejs和Angular貴公司選的哪個?

A:首選Reactjs,畢竟重度使用React Native。內部一些admin portal,可以自由選擇,但目前並沒有發現有隊友主動使用angular。

Q:你們生產現在使用Rancher做多少節點?網路模式使用的是默認Rancher代理嗎? Rancher多節點的持續部署與單節點有沒有什麼不同?

A:我們伺服器規模不大,生產環境只有三台伺服器。資料庫是單獨的另外三台。網路模式也不複雜,traefik代替了nginx的地位做反向代理。多節點持續部署這個我不太清楚,和其他非生產環境感覺上並沒有什麼不同。

Q:請問Rancher在管理大規模docker時底層的網路結構和存儲是怎麼處理的?

A:這個問題有些寬……網路結構並不複雜,traefik接到請求後轉發到不同的微服務,伺服器的首選DNS是我們自己搭建的,直接指向伺服器的內網地址。存儲使用docker volume,伺服器會單獨掛載一塊硬碟用於docker的存儲。

A:不知道您說的企業號是不是企業微信,二者不同,文中指的是企業微信。

可以使用一個drone,也可以使用多個。但我感覺使用1個就可以。因為drone與企業微信集成時,可以根據項目的不同指向不同的企業微信agent id,就可以做到不同repo分發到不同的企業微信應用中。

Q:如何跟git flow結合的?是每個release開一個分支,然後分支合併到develop或者master的時候開啟pipeline?

A:可以都開,也可以只在master開。這是我覺得drone和gitlab ci做的很好的地方。查看drone的yaml文件可以看到,可以指定

when

branch=*

的時候觸發不同的構建,並且支持通配符。這就可以做很多想像了。

Q:traefik負載可以與很多種服務發現結合,請問下你們選擇的是哪種?為什麼這麼選呢?

A:直接使用的traefik本身。因為人少,精力少,業務上的並發並不驚人。

Q:選擇Reactjs在蘋果上架的時候有沒有什麼審核的限制?最近使用h5做蘋果應用, 發現H5與原生代碼的比例過高, 蘋果上架審核不能通過, Reactjs有沒有這種問題?

A:據我所知並沒有這個問題。我們大部分被駁回的理由都是文案、截圖,或者點擊提交按鈕前沒有默念佛祖保佑。

Q:traefik的性能和nginx哪個更好,有做過壓測嗎?

A:traefik官方的說法是traefik比nginx更快,但我們的測試看出不有太多優勢。勝在配置和部署更方便吧。

Q:請問sonar服務在流水線哪個環節引入?應用哪些插件?給出哪些指標?

A:sonar並未引入,在我們todo里。

Q:請問CI/CD對小型團隊來說有必要嗎,幫助有多大?

A:我們就是小型團隊,如果花時間重複做幾乎相同的事情,我覺得這些東西都是有必要自動化的。

Q:logspout能再詳細一點嗎?

A:logspout的使用真的是簡單的過分。真的就是只需要配置那兩個變數!這一點我最初也表示非常震驚,感謝Rancher。


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 RancherLabs 的精彩文章:

2017再見:願Rancher的答卷,不負你所望
基於Spring Cloud的微服務實踐

TAG:RancherLabs |