容器環境下的持續集成最佳實踐:構建基於 Drone + GitFlow + K8s 的雲原生語義化 CI 工做流

雲原生 (Cloud Native) 是伴隨的容器技術發展出現的的一個詞,最先出自 Pivotal 公司(即開發了 Spring 的公司)的一本技術小冊子 Migrating to Cloud-Native Application Architectures, 其中定義了雲原生應用應當具有的一些特質,如無狀態、可持續交付、微服務化等。隨後雲原生概念被廣爲引用,並圍繞這一律念由數家大廠牽頭,成立了 CNCF 基金會來推動雲原生相關技術的發展,主要投資並孵化雲原生生態內的若干項目,包括瞭如 Kubernetes / etcd / CoreDNS 等耳熟能詳的重要項目。而這張大大的雲原生版圖仍然在不斷的擴展和完善。html

從我的理解來講,傳統的應用因爲年代久遠,更多會考慮單機部署、進程間通訊等典型的「單機問題」,雖然也能工做在容器下,但因爲歷史包袱等緣由,架構上已經很難作出大的調整。而「雲原生」的應用大都是從一開始就爲容器而準備,不多考慮在單機環境下使用,有些甚至沒法脫離容器環境工做;考慮的場景少,從而更輕量,迭代更快。好比 etcd 之於 zookeeper , traefik 之於 nginx 等,相信只要在容器環境下實現一次一樣的功能,就能強烈的體會到雲原生應用所特有的便捷之處。node

在 CNCF 的版圖下,持續集成與持續交付(Continuous Integration & Delivery)板塊一直缺乏一個欽定的主角,雖然也不乏 Travis CI、GitLab、Jenkins 這樣的知名項目,但最能給人云原生應用感受的,應該仍是 Drone 這個項目,本文將圍繞 Drone 結合 GitFlow 及 Kubernetes 介紹一些容器環境下持續集成、持續發佈 (CI/CD) 方面的實踐經驗android

主流 CI/CD 應用對比

以前我也介紹過基於 Travis CI 的一些持續集成實踐。後來通過一些比較和調研,最終選擇了 Drone 做爲主力 CI 工具。截止本文,團隊已經使用 Drone 有 2 年多的時間,從 v0.6 一路用到如今即將發佈的 v1.0,雖然也踩了很多坑,但總的來講 Drone 仍是能夠知足大部分需求,並以不錯的勢頭在完善和發展的。nginx

下面這張表總結了主流的幾個 CI/CD 應用的特色git

項目名稱 開發語言 配置語言 公有云服務 私有部署 備註
Travis CI Ruby YAML 不支持 公共項目免費,私有項目 $69/單進程, $129/2 進程
CircleCI Clojure YAML 不支持 單進程免費,$50/加 1 進程
Gitlab CI Ruby YAML 支持 綁定 Gitlab 代碼管理
Jenkins Java Groovy 支持
Drone Go YAML 支持 Cloud 版本不支持私有項目,自建版本無此限制

Travis CI 和 CircleCI 是目前佔有率最高的兩個公有云 CI,易用性上相差無幾,只是收費方式有差別。因爲不支持私有部署,若是並行的任務量一大,按進程收費其實並不划算;並且因爲服務器位置的緣由,若是推送鏡像到國內,速度很不理想。github

Gitlab CI 雖然好用,但和 Gitlab 是深度綁定的,咱們的代碼託管在 Github,總體遷移代碼庫的成本太大,放棄。sql

Jenkins 做爲老牌勁旅,也是目前市場佔有率最高的 CI,幾乎能夠覆蓋全部 CI 的使用場景,因爲使用 Java 編寫,配置文件使用 Groovy 語法,很是適合 Java 爲主語言的團隊。Jenkins 顯然是能夠知足咱們須要的,只是團隊並不是 Java 爲主,又已經習慣了使用 YAML 書寫 CI 配置,抱着嚐鮮的心態,將 Jenkins 做爲了保底的選擇。docker

綜上,最終選擇 Drone 的結論也就不可貴出了,Drone 即開源,又能夠私有化部署,同時做爲雲原生應用,官方提供了針對 Docker、Docker Swarm、K8s 等多種容器場景下的部署方案,針對不一樣容器場景還有特別優化,好比在 Docker Swarm 下 Drone 是以 agent 方式運行 CI 任務的,而在 K8s 下則經過建立 K8s Job 來實現,顯然充分利用了容器的優點所在,這也是 Drone 優於其餘 CI 應用之處。我的還以爲 Drone 的語法是全部 CI 中最容易理解和掌握的,因爲 Drone 每個步驟都是運行一個 Docker 容器,本地模擬或調試也很是容易。數據庫

一句話概況 Drone,能夠將其看作是能夠支持私有化部署的開源版 CircleCI,而且目前仍然沒有看到有其餘主打這個定位的 CI 工具,所以我的認爲 Drone 是 CI/CD 方面雲原生應用頭把交椅的有力競爭者。npm

容器環境下一次規範的發佈應該包含哪些內容

技術選型完成後,我想首先演示一下最終的成果,但願能直觀的體現出 CI 對自動化效率起到的提高,不過這就涉及到一個問題:在容器環境下,一次發佈應該包含哪些內容,其中有哪些部分是能夠被 CI 自動化完成的。這個問題雖然每家公司各不相同,不過按經驗來講,容器環境下一次版本發佈一般包含這樣一個 Checklist:

  • [ ] 代碼的下載構建及編譯
  • [ ] 運行單元測試,生成單元測試報告及覆蓋率報告等
  • [ ] 在測試環境對當前版本進行測試
  • [ ] 爲待發布的代碼打上版本號
  • [ ] 編寫 ChangeLog 說明當前版本所涉及的修改
  • [ ] 構建 Docker 鏡像
  • [ ] 將 Docker 鏡像推送到鏡像倉庫
  • [ ] 在預發佈環境測試當前版本
  • [ ] 正式發佈到生產環境

看上去很繁瑣對嗎,若是每次發佈都須要人工去處理上述的全部內容,不只容易出錯,並且也沒法應對 DevOps 時代一天至少數次的發佈頻率,那麼下面就來使用 CI 來解決全部問題吧。

CI 流程演示

爲了對 CI 流程有最直觀的認識,我建立了一個精簡版的 Github 項目 AlloVince/drone-ci-demo 來演示完整的流程,同時項目對應的 CI 地址是 cloud.drone.io/AlloVince/drone-ci-demo ,項目自動構建的 Docker 鏡像會推送到 docker registry 的 allovince/drone-ci-demo,。爲了方便說明,假設這個項目的核心文件只有 index.html 一個靜態頁面。

單人開發模式

目前這個項目背後的 CI 都已經配置部署好,假設我是這個項目的惟一開發人員,如何開發一個新功能併發布新版本呢?

  1. Clone 項目到本地, 修改項目代碼, 如將 Hello World 改成 Hello World V2
  2. git add .,而後書寫符合約定的 Commit 並提交代碼, git commit -m "feature: hello world v2」
  3. 推送代碼到代碼庫git push,等待數分鐘後,開發人員會看到單元測試結果,Github 倉庫會產生一次新版本的 release,release 內容爲當前版本的 ChangeLog, 同時線上已經完成了新功能的發佈。

雖然在開發者看來,一次發佈簡單到只需 3 個指令,但背後通過了以下的若干次交互,這是一次發佈實際產生交互的時序圖,具體每一個環節如何工做將在後文中詳細說明。

image

多人開發模式

一個項目通常不止一個開發人員,好比我是新加入這個項目的成員,在這個 Demo 中應該如何上線新功能呢?一樣很是簡單:

  1. Clone 項目到本地,建立一個分支來完成新功能的開發, git checkout -b feature/hello-world-v3。在這個分支修改一些代碼,好比將Hello World V2修改成Hello World V3
  2. git add .,書寫符合規範的 Commit 並提交代碼, git commit -m "feature: hello world v3」
  3. 將代碼推送到代碼庫的對應分支, git push origin feature/hello-world
  4. 若是功能已經開發完畢,能夠向 Master 分支發起一個 Pull Request,並讓項目的負責人 Code Review
  5. Review 經過後,項目負責人將分支合併入主幹,Github 倉庫會產生一次新版本的 release,同時線上已經完成了新功能的發佈。

這個流程相比單人開發來多了 2 個環節,很適用於小團隊合做,不只強制加入了 Code Review 把控代碼質量,同時也避免新人的不規範行爲對發佈帶來影響。實際項目中,能夠在 Github 的設置界面對 master 分支設置寫入保護,這樣就從根本上杜絕了誤操做的可能。固然若是團隊中都是熟手,就無需如此謹慎,每一個人均可以負責 PR 的合併,從而進一步提高效率。

image

GitFlow 開發模式

在更大的項目中,參與的角色更多,通常會有開發、測試、運維幾種角色的劃分;還會劃分出開發環境、測試環境、預發佈環境、生產環境等用於代碼的驗證和測試;同時還會有多個功能會在同一時間並行開發。可想而知 CI 的流程也會進一步複雜。

能比較好應對這種複雜性的,首選 GitFlow 工做流, 即經過並行兩個長期分支的方式規範代碼的提交。而若是使用了 Github,因爲有很是好用的 Pull Request 功能,能夠將 GitFlow 進行必定程度的簡化,最終有這樣的工做流:

image

  • 以 dev 爲主開發分支,master 爲發佈分支
  • 開發人員始終從 dev 建立本身的分支,如 feature-a
  • feature-a 開發完畢後建立 PR 到 dev 分支,並進行 code review
  • review 後 feature-a 的新功能被合併入 dev,若有多個並行功能亦然
  • 待當前開發週期內全部功能都合併入 dev 後,從 dev 建立 PR 到 master
  • dev 合併入 master,並建立一個新的 release

上述是從 Git 分支角度看代碼倉庫發生的變化,實際在開發人員視角里,工做流程是怎樣的呢。假設我是項目的一名開發人員,今天開始一期新功能的開發:

  1. Clone 項目到本地,git checkout dev。從 dev 建立一個分支來完成新功能的開發, git checkout -b feature/feature-a。在這個分支修改一些代碼,好比將Hello World V3修改成Hello World Feature A
  2. git add .,書寫符合規範的 Commit 並提交代碼, git commit -m "feature: hello world feature A"
  3. 將代碼推送到代碼庫的對應分支, git push origin feature/feature-a:feature/feature-a
  4. 因爲分支是以feature/命名的,所以 CI 會運行單元測試,並自動構建一個當前分支的鏡像,發佈到測試環境,並自動配置一個當前分支的域名如 test-featue-a.avnpc.com
  5. 聯繫產品及測試同窗在測試環境驗證並完善新功能
  6. 功能經過驗收後發起 PR 到 dev 分支,由 Leader 進行 code review
  7. Code Review 經過後,Leader 合併當前 PR,此時 CI 會運行單元測試,構建鏡像,併發布到測試環境
  8. 此時 dev 分支有可能已經積累了若干個功能,能夠訪問測試環境對應 dev 分支的域名,如 test.avnpc.com,進行集成測試。
  9. 集成測試完成後,由運維同窗從 Dev 發起一個 PR 到 Master 分支,此時會 CI 會運行單元測試,構建鏡像,併發布到預發佈環境
  10. 測試人員在預發佈環境下再次驗證功能,團隊作上線前的其餘準備工做
  11. 運維同窗合併 PR,CI 將爲本次發佈的代碼及鏡像自動打上版本號並書寫 ChangeLog,同時發佈到生產環境。

由此就完成了上文中 Checklist 所需的全部工做。雖然描述起來看似冗長,但不難發現實際做爲開發人員,並無任何複雜的操做,流程化的部分所有由 CI 完成,開發人員只須要關注本身的核心任務:按照工做流規範,寫好代碼,寫好 Commit,提交代碼便可。

接下來將介紹這個以 CI 爲核心的工做流,是如何一步步搭建的。

Step by Step 構建 CI 工做流

Step.0: 基於 K8s 部署 Drone v1.0.0

以 Github 爲例,截止本文完成時間(2019 年 3 月 28 日), Drone 剛剛發佈了第一個正式版本 v1.0.0。官方文檔已經提供了分別基於 Docker、K8s 的 Drone 部署說明,不過比較簡略,所以這裏給出一個相對完整的配置文件。

首先須要在 Github 建立一個 Auth App,用於 repo 的訪問受權。應用建立好以後,會獲得 Client IDClient Secret 。同時 Authorization callback URL 應填寫 Drone 服務對應域名下的 /login,如https://ci.avnpc.com/login

Drone 支持 SQLite、MySQL、Postgres、S3 等多種後端存儲,主要用於記錄 build logs 等文本信息,這些信息並非特別重要,且咱們的 CI 有可能作遷移,所以我的更推薦使用 SQLite。

而在 K8s 環境下,SQLite 更適合用掛載 NAS 的方式供節點使用,所以首先將存儲的部分獨立爲文件drone-pvc.yml,能夠根據實際狀況配置 nfs.pathnfs.server

kubectl apply -f drone-pvc.yaml

Drone 的配置主要涉及兩個鏡像:

  • drone/kubernetes-secrets 加密數據服務,用於讀取 K8s 的 secrets
  • drone/drone:1.0.0-rc.6 就是 Drone 的 server 端,因爲在 K8s 下 Drone 利用了 Job 機制,所以不須要部署 agent。

這部分配置較長,能夠直接參考示例 drone.yaml

主要涉及到的配置項包括:

  • drone/kubernetes-secrets 鏡像中

    • SECRET_KEY: 數據加密傳輸所用的 key,可使用 openssl rand -hex 16 生成一個
  • drone/drone鏡像中

    • DRONE_KUBERNETES_ENABLED: 開啓 K8s 模式
    • DRONE_KUBERNETES_NAMESPACE: Drone 所使用的 Namespace, 這裏使用 default
    • DRONE_GITHUB_SERVER: Github 服務器地址,通常爲 https://github.com
    • DRONE_GITHUB_CLIENT_ID: 上文建立 Github Auth App 獲得的 Client ID
    • DRONE_GITHUB_CLIENT_SECRET: 上文建立 Github Auth App 獲得的 Client Secret
    • DRONE_SERVER_HOST: Drone 服務所使用的域名
    • DRONE_SERVER_PROTO: http 或 https
    • DRONE_DATABASE_DRIVER: Drone 使用的數據庫類型,這裏爲 sqlite3
    • DRONE_DATABASE_DATASOURCE: 這裏爲 SQLite 數據庫的存放路徑
    • DRONE_SECRET_SECRET: 對應上文的 SECRET_KEY
    • DRONE_SECRET_ENDPOINT: 加密數據服務的地址,這裏經過 k8s service 暴露,無需修改

最後部署便可

kubectl apply -f drone.yaml

部署後首次登陸 Drone 就會跳轉到 Github Auth App 進行受權,受權完畢後能夠看到全部能讀寫的 Repo,選擇須要開啓 CI 的 Repo,點擊 ACTIVATE 便可。 若是開啓成功,在 Github Repo 的 Settings > Webhooks 下能夠看到 Drone 的回調地址。

Step.1: Hello World for Drone

在正式開始搭建工做流以前,首先能夠測試一下 Drone 是否可用。Drone 默認的配置文件是 .drone.yml, 在須要 CI 的 repo 根目錄下建立.drone.yml, 內容以下,提交併git push到代碼倉庫便可觸發 Drone 執行 CI。

kind: pipeline  
name: deploy  
  
steps:  
- name: hello-world
  image: docker  
  commands:  
    - echo "hello world"

Drone v1 的語法主要參考的 K8s 的語法,很是直觀,無需閱讀文檔也能夠知道,咱們首先定義了一個管道 (pipeline),管道由若干步驟 (step) 組成,Drone 的每一個步驟是都基於容器實現的,所以 Step 的語法就回到了咱們熟悉的 Docker,一個 Step 會拉取 image 定義的鏡像,而後運行該鏡像,並順序執行 commands 定義的指令。

在上例中,Drone 首先 clone git repo 代碼到本地,而後根據 .drone.yml 所定義的,拉取 Docker 的官方鏡像,而後運行該進行並掛載 git repo 的代碼到 /drone/src 目錄。

在 Drone 的界面中,也能夠清楚的看到這一過程。

image

本階段對應

Step.2: 單人工做流,自動化單元測試與 Docker 鏡像構建

有了 Hello World 的基礎,接下來咱們嘗試將這個工做流進行擴充。

爲了方便說明,這裏假設項目語言爲 js,項目內新增了test/index.js文件用於模擬單元測試,通常在 CI 中,只要程序的返回值爲 0,即表明運行成功。這個文件中咱們僅僅輸出一行 Log Unit test passed用於模擬單元測試經過。

咱們但願將代碼打包成 Docker 鏡像,根目錄下增長了 Dockerfile 文件,這裏直接使用 Nginx 的官方鏡像,構建過程只有 1 行COPY index.html /usr/share/nginx/html/, 這樣鏡像運行後能夠經過 http 請求看到index.html的內容。

至此咱們能夠將工做流改進爲:

  • 當 master 分支接收到 push 後,運行單元測試
  • 當 github 發佈一次 release, 構建 Docker 鏡像,並推送到鏡像倉庫

對應的 Drone 配置文件以下

kind: pipeline  
name: deploy  
  
steps:  
  - name: unit-test  
    image: node:10  
    commands:  
      - node test/index.js  
    when:  
      branch: master  
      event: push  

  - name: build-image  
    image: plugins/docker  
    settings:  
      repo: allovince/drone-ci-demo  
      username: allovince  
      password:  
        from_secret: DOCKER_PASSWORD  
      auto_tag: true  
    when:  
      event: tag

雖然比 Hello World 複雜了一些,可是可讀性仍然很好,配置文件中出現了幾個新概念:

Step 運行條件, 即 when 部分,上例中展現了當代碼分支爲 master,且收到一個 push;以及當代碼被標記 tag 這兩種狀況。Drone 還支持 repo、運行結果等不少其餘條件,能夠參考 Drone Conditions 文檔

Plugin 插件,上例中用於構建和推送鏡像的是 plugins/docker 這個 Plugin, 一個 Plugin 本質上仍然是一個 Docker 鏡像,只是按照 Drone 的規範接受特定的輸入,並完成特定的操做。因此徹底能夠將 Plugin 看作一個沒法更改 command 的 Docker 鏡像。

Docker 這個 Plugin 由 Drone 官方提供,用於 Docker 鏡像的構建和推送,具體的用法能夠查看Docker 插件的文檔 。例子中演示的是將鏡像推送到私有倉庫,若是不作特殊配置,鏡像將被推送到 Docker 的官方倉庫

此外 Docker 插件還有一個很方便的功能,若是設置 auto_tag: true,將根據代碼的版本號自動規劃 Docker 鏡像的標籤,如代碼版本爲1.0.0,將爲 Docker 鏡像打三個標籤 1, 1.0, 1.0.0。若是代碼版本號不能被解析,則鏡像標籤爲 latest

目前 Drone 的插件已經有不少,能夠覆蓋主流的雲服務商和常見的工做流,而且本身製做插件的成本也不高。

Secret 加密數據,鏡像倉庫的用戶名和密碼都屬於敏感信息,所以可使用 from_secret 獲取加密數據。一條加密數據就是一個 key / value 對,如上例中的 DOCKER_PASSWORD 就是咱們本身定義的加密數據 key。即使加密數據在 log 中被打印,UI 也只能看到 ***。加密數據的 value 須要提早保存好,保存的方式有 3 種:

  • 經過 Drone UI 界面中, repo -> Settings -> Secrets 添加,所添加的加密數據將保存在 Drone 的數據庫中,僅能在當前 repo 中使用。
  • 經過Drone cli 加密後保存在 .drone.yml文件中, 使用範圍僅限 yaml 文件內
  • 經過 K8s 保存爲K8s Secret,稱爲 External Secrets,全部的 repo 均可以共享。若是是團隊使用的話,這種保存方式顯然是最方便的,但也要注意安全問題,所以 External Secrets 還支持 repo 級別的權限管理, 能夠只讓有當前 repo 寫入權限的人才能使用對應 secret。

這個階段對應

Step.3: GitFlow 多分支團隊工做流

上面的工做流已經基本能夠應付單人的開發了,而在團隊開發時,這個工做流還須要一些擴展。不須要引入 Drone 的新功能,只須要在上文基礎上根據分支作一點調整便可。

首先保證單元測試位於 steps 的第一位,而且限定團隊工做的分支,在 push 和 pull_request 時,都能觸發單元測試。

- name: unit-test  
  image: node:10  
  commands:  
    - node test/index.js  
  when:  
    branch:  
      include:  
        - feature/*  
        - master  
        - dev  
    event:  
      include:  
        - push  
        - pull_request

而後根據 Gitflow 的流程對於不一樣的分支構建 Docker 鏡像並打上特定標籤,以 feature 分支爲例,下面的配置約定了當分支名知足 feature/*,並收到 push 時,會構建 Docker 鏡像並打標籤,標籤名稱爲當前分支名去掉 feature/。如分支 feature/readme, 對應 docker 鏡像爲 allovince/drone-ci-demo:readme,考慮到 feature 分支通常都出於開發階段,所以新的鏡像會覆蓋舊的。配置以下

- name: build-branch-image  
  image: plugins/docker  
  settings:  
    repo: allovince/drone-ci-demo  
    username: allovince  
    password:  
      from_secret: DOCKER_PASSWORD  
    tag:  
      - ${DRONE_BRANCH##feature/}  
  when:  
    branch: feature/*  
    event: push

鏡像的 Tag 處再也不使用自動方式,其中DRONE_BRANCH是 Drone 的內置環境變量 (Environment),對應當前的分支名。##feature/是執行了一個字符串的替換操做 (Substitution)。更多的環境變量和字符串操做均可以在文檔中找到。

以此類推,能夠查看這個階段的完整 .drone.yml ,此時咱們的工做流示例以下:

可能細心的同窗會發現 dev -> master 的 pull request 時,構建鏡像失敗了,這是因爲 Drone 出於安全考慮限制了在 pull request 時默認沒法讀取加密數據,所以沒法獲得 Docker Registry 密碼。若是是私有部署的話,能夠在 Repo Settings 中勾選Allow Pull Requests,此處就能夠構建成功。

Step.4: 語義化發佈

上面基本完成了一個支持團隊協做的半自動 CI 工做流,若是不是特別苛刻的話,徹底能夠用上面的工做流開始幹活了。

不過基於這個工做流工做一段時間,會發現仍然存在痛點,那就是每次發佈都要想一個版本號,寫 ChangeLog,而且人工去 release。

標記版本號涉及到上線後的回滾,追溯等一系列問題,應該是一項嚴肅的工做,其實如何標記早已有比較好的方案,即語義化版本。在這個方案中,版本號一共有 3 位,形如 1.0.0,分別表明:

  1. 主版本號:當你作了不兼容的 API 修改,
  2. 次版本號:當你作了向下兼容的功能性新增,
  3. 修訂號:當你作了向下兼容的問題修正。

雖然有了這個指導意見,但並無很方便的解決實際問題,每次發佈要搞清楚代碼的修改究竟是不是向下兼容的,有哪些新的功能等,仍然要花費不少時間。

語義化發佈 (Semantic Release) 就能很好的解決這些問題。

語義化發佈的原理很簡單,就是讓每一次 Commit 所附帶的 Message 格式遵照必定規範,保證每次提交格式一致且都是能夠被解析的,那麼進行 Release 時,只要統計一下距離上次 Release 全部的提交,就分析出本次提交作了何種程度的改動,並能夠自動生成版本號、自動生成 ChangeLog 等。

語義化發佈中,Commit 所遵照的規範稱爲約定式提交 (Conventional Commits)。好比 node.js、 Angular、Electron 等知名項目都在使用這套規範。

語義化發佈首先將 Commit 進行分類,經常使用的分類 (Type) 有:

  • feat: 新功能
  • fix: BUG 修復
  • docs: 文檔變動
  • style: 文字格式修改
  • refactor: 代碼重構
  • perf: 性能改進
  • test: 測試代碼
  • chore: 工具自動生成

每一個 Commit 能夠對應一個做用域(Scope),在一個項目中做用域通常能夠指不一樣的模塊。

當 Commit 內容較多時,能夠追加正文和腳註,若是正文起始爲BREAKING CHANGE,表明這是一個破壞性變動。

如下都是符合規範的 Commit:

feat: 增長重置密碼功能
fix(郵件模塊): 修復郵件發送延遲BUG
feat(API): API重構

BREAKING CHANGE: API v3上線,API v1中止支持

有了這些規範的 Commit,版本號如何變化就很容易肯定了,目前語義化發佈默認的規則以下

Commit 版本號變動
BREAKING CHANGE 主版本號
feat 次版本號
fix / perf 修訂號

所以在 CI 部署 semantic-release 以後,做爲開發人員只須要按照規範書寫 Commit 便可,其餘的都由 CI 完成。

具體如何將語義化發佈加入 CI 流程中呢, semantic-release 是 js 實現的,若是是 js 的項目,能夠直接在package.json中增長配置項,而對於任意語言的項目,推薦像 Demo 中同樣,在根目錄下增長 配置文件release.config.js。這個配置目的是爲了禁用默認開啓的 npm 發佈機制,能夠直接套用。

semantic-release 要執行 Github release,所以咱們須要在 CI 中配置本身的 Personal access tokens 讓 CI 有 Github repo 的讀寫權限, 能夠經過 Github 點擊本身頭像 -> Settings -> Developer settings -> Personal access tokens -> Generate new token 生成一個 Token。 而後在 Drone 的 repo 設置界面新增一個 Secret, key 爲 GITHUB_TOKEN, value 填入剛生成的 Token。

最後在 .drone.yml 中增長這樣一段就能夠了。

- name: semantic-release  
  image: gtramontina/semantic-release:15.13.3  
  environment:  
    GITHUB_TOKEN:  
      from_secret: GITHUB_TOKEN  
  entrypoint:  
    - semantic-release  
  when:  
    branch: master  
    event: push

來再次模擬一下流程,feature 分支部分與上文相同

最終咱們能獲得這樣一個賞心悅目的 release

image

Step.5: Kubernetes 自動發佈

Docker 鏡像推送到倉庫後,咱們還剩最後一步就能夠完成全自動發佈的閉環,即通知 Kubernetes 將鏡像發佈到生產環境。這一步實現比較靈活,由於不少雲服務商在容器服務都會提供 Trigger 機制,通常是提供一個 URL,只要請求這個 URL 就能夠觸發容器服務的發佈。Demo 中咱們使用更爲通用的方法,就是將 kubectl 打包爲容器,以客戶端調用 K8s 集羣 Master 節點 API ( kube-apiserver ) 的形式完成發佈。

假設咱們在生產環境下 drone-ci-demo 項目的 K8s 發佈文件以下

---  
apiVersion: extensions/v1beta1  
kind: Deployment  
metadata:  
  name: ci-demo-deployment  
  namespace: default  
spec:  
  replicas: 1  
  template:  
    spec:  
      containers:  
        - image: allovince/drone-ci-demo  
          name: ci-demo  
      restartPolicy: Always

對應 .drone.yml 中增長 step 以下。這裏使用的插件是honestbee/drone-kubernetes, 插件中kubectl 鏈接 API 使用的是證書+ Token 的方式鑑權,所以須要先得到證書及 Token, 已經受權的 Token 保存於 k8s secret,能夠經過kubectl get secret [ your default secret name ] -o yaml | egrep 'ca.crt:|token:'得到並配置到 drone 中,注意插件要求 token 是明文的,須要 base64 解碼一下:echo [ your token ] | base64 -d && echo ''

- name: k8s-deploy  
  image: quay.io/honestbee/drone-kubernetes  
  settings:  
    kubernetes_server:  
      from_secret: KUBERNETES_SERVER  
    kubernetes_cert:  
      from_secret: KUBERNETES_CERT  
    kubernetes_token:  
      from_secret: KUBERNETES_TOKEN  
    namespace: default  
    deployment: ci-demo-deployment  
    repo: allovince/drone-ci-demo  
    container: ci-demo  
    tag:  
      - ${DRONE_TAG}  
  when:  
    event: tag

示例)中,能夠看到在語義化發佈以後 CI 會將新版本的 Docker 鏡像自動發佈到 K8s),這裏爲了演示僅打印了指令並未實際運行。至關於運行了以下的指令:

kubectl -n default set image deployment/ci-demo-deployment ci-demo=allovince/drone-ci-demo:v1.0.2

因爲自動發佈的環節勢必要接觸到生產服務器,須要格外注意安全問題,首推的方式固然是將 CI 和 K8s 集羣放於同一內網中,同時可使用 K8s 的 RBAC 權限控制,爲自動發佈單首創建一個用戶),並刪除沒必要要的權限。

後話

總結一下,本文展現了從 Hello World單人單分支手動發佈團隊多分支 GitFlow 工做流團隊多分支 semantic-release 語義化發佈通知 K8s 全自動發佈,如何從零開始一步一步搭建 CI 將團隊開發、測試、發佈的流程所有自動化的過程,最終能讓開發人員只須要認真提交代碼就能夠完成平常的全部 DevOps 工做。

最終 Step 的完成品能夠適配以前的全部 Step,若是不太在乎實現細節的話,能夠在此基礎上稍做修改,直接使用。

然而寫好每個 Commit 這個看似簡單的要求,其實對於大多數團隊來講並不容易作到,在實施過程當中,常常會遇到團隊成員不理解爲何要重視 Commit 規範,每一個 Commit 都要深思熟慮是否過於吹毛求疵等等疑問。

以 Commit 做爲 CI 的核心,我的認爲主要會帶來如下幾方面的影響:

  1. 一個好的 Commit,表明着開發人員對當前改動之於整個系統的影響,有很是清楚的認識,代碼的修改到底算 feat 仍是 fix ,何時用 BREAKING CHANGE 等都是要仔細斟酌的,每一個 Commit 都會在 ChangeLog 裏「留底」,從而約束團隊不隨意提交未經思考的代碼,提升代碼質量
  2. 一個好的 Commit 也表明開發人員有能力對所實現功能進行精細的劃分,一個分支作的事情不宜過多,一個提交也應該專一於只解決一個問題,每次提交(至少是每次 push )都應該保持系統可構建、可運行、可測試,若是能堅持作到這些,對於合併代碼時的衝突解決,以及集成測試都有很大幫助。
  3. 因爲每次發佈能清楚的看到全部關聯的 Commit 以及 Commit 的重要程度,那麼線上事故的回滾也會很是輕鬆,回滾到哪一個版本,回滾後哪些功能會受到影響,只要看 CI 自動生成的 Release 記錄就一目瞭然。若是沒有這些,回滾誤傷到預期外的功能從而引起連鎖反應的慘痛教訓,可能不少運維都有過相似經歷吧。

所以 CI 自動化實際上是錦上添花而非雪中送炭,若是團隊本來就無視規範,Commit 全是空白或者沒有任何意義的單詞,分支管理混亂,發佈困難,奢望引入一套自動化 CI 來能解決全部這些問題,無疑是不現實的。而只有本來就重視代碼質量,有必定規範意識,再經過自動化 CI 來監督約束,團隊在 CI 的幫助下代碼質量提升,從而有機會進一步改進 CI 的效率,才能造成良性循環。

願天下再也不有難發佈的版本。

References:

相關討論能夠在個人 Telegram Group 給我留言。

相關文章
相關標籤/搜索