iHealth基於Docker的DevOps CI/CD實踐

【做者簡介】前端

郭拓,北京愛和健康科技有限公司(iHealth)。負責公司基礎服務構建與研發流程定製,曾供職於樂視、21vianet,高齡攻城獅活躍在一線研發工做中,樂此不疲。node

前言

相信我,一切事情的發生都是趕鴨子上架,沒有例外。人類全部偉大的變革都是無可奈何,可又是那麼順其天然。好比容器(docker)技術的誕生,好比箭在弦上的創業,好比野心勃勃的kubernetes,好比現在已做爲左膀右臂的rancher,好比這篇文章。git

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

合適的纔是最好的(Node.js & Docker)

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

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

服務端有Express、Koa、Feather、Nest、Meteor等各有其長的框架,前端大而火的Reactjs、Vuejs和Angular,無論是Server Render仍是先後端分離,均可以駕輕就熟。由於公司的健康設備(血糖儀、血壓計、體溫計、血氧、體脂秤等等)會有專門的部門研發設計以及提供SDK,因此移動端的研發工做更可能是在設計實現和性能優化上,React Native是一枚大殺器。雖然如今公司並無桌面端的需求,但不可否認的是Electron是一個頗有趣的項目,也爲「全棧」這個詞增長了更多背書。apache

輸入圖片說明

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

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

事實上,咱們直接使用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地址或域名,例如: http://drone.company.com

而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與Rancher、Harbor、企業微信的集成

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

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

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

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

  • 提交代碼,觸發Github Webhook

  • Drone使用docker插件,根據Dockerfile構建鏡像,並推送到Harbor中

  • Drone使用rancher插件,根據stack/service,部署上面構建好的image

  • Drone使用企業微信插件,報告部署結果

在這裏節選公司項目中的一段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: '${DRONE_REPO_NAME}'

    description: |    

       構建序列: ${DRONE_BUILD_NUMBER}  部署成功,幹得好${DRONE_COMMIT_AUTHOR} !     

       更新內容: ${DRONE_COMMIT_MESSAGE}  

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

    btn_txt: 點擊前往

    when:

      branch: develop

      status:

        - success

對接企業微信以前,須要在企業微信中新建自定義應用,好比咱們的應用名字叫Drone CI/CD。固然,您也能夠給每個項目建立一個企業微信App,這樣雖然麻煩,可是可讓須要關注該項目的人關注到構建信息。

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

輸入圖片說明

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

輸入圖片說明

在這裏我認爲有必要提醒一下,使用Drone的企業微信插件時,不要使用Drone Plugins列表裏的企業微信。翻閱其源碼能夠發現,其中一個函數會將企業的敏感信息發送至私人服務器。無論做者自己是出於BaaS的好意,仍是其它想法,我認爲都是不妥的

輸入圖片說明

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

在此Drone Plugins裏的企業微信插件出現好久以前,個人好友Clément 克雷蒙同窗寫了一個企業微信插件,至今仍在使用。歡迎檢查源代碼,提issue提bug,爲了避免讓克雷蒙同窗驕傲,我並不打算號召你們給他star:clem109/drone-wechat

而在構建完成後,能夠看到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的代理方式。

好比剛纔安裝的Drone,若是咱們想代理到drone.company.com這個域名,則須要在drone server的container中設置lables:

輸入圖片說明

  • traefik.enable=true 表示啓用traefik代理

  • traefik.domain=company.com 表示traefik代理的根域名

  • traefik.port=8000 表示這個container對外暴露的端口

  • traefik.alias=drone 表示想將drone server這個container解析爲drone.company.com

須要注意的是,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

Drone企業微信API插件: clem109/drone-wechat

Harbor: vmware/harbor

Traefik: containous/traefik

Phabricator: phacility/phabricator

SonarQube: SonarSource/sonarqube

Logspout: gliderlabs/logspout

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

SuperSet(BI): apache/incubator-superset

相關文章
相關標籤/搜索