歡迎光臨
每天分享高質量文章

即便一個小專案也有它的CI/CD流水線

現如今,使用市面上的一些工具配置一套簡單的CI/CD流水線並不是一件難事。給一個副專案弄一套這樣的流水線也是一個學習許多東西的好方法。Docker,Gitlab,Portainer這些優秀的組件可以用來搭建這個流水線。
示例專案

 

作為一名法國索菲亞科技園區(位於法國南部)的技術活動組織者,我經常被問到是否有辦法知道所有即將舉行的活動(會議,灌水,由當地協會組織的聚會等……)。由於此前並沒有一個單獨的地方列出所有的這些活動,我便開發了https://sophia.events ,這是一個非常簡單的網站頁面,它會嘗試維護一份最新的活動串列。此專案的代碼可以在GitLab[1]上找到。
宣告:這個專案超級簡單,但是專案本身的複雜度並不是本文的重點。這裡我們將詳細介紹到的CI/CD流水線的各個組件可以用幾乎相同的方式應用到更複雜的專案上。它們也非常適合微服務的場景。
快速過一下代碼

 

為了簡化起見,這裡有一份events.json檔案,每個新事件均會被添加到裡面。該檔案的部分內容見下麵的代碼段(抱歉裡面摻雜了一些法語):
  1. {
  2.  events”: [
  3.    {
  4.      title”: All Day DevOps 2018”,
  5.      desc”: Were back with 100, 30-minute practitioner-led sessions and live Q&A on Slack. Our 5 tracks include CI/CD, Cloud-Native Infrastructure, DevSecOps, Cultural Transformations, and Site Reliability Engineering. 24 hours. 112 speakers. Free online.”,
  6.      date”: 17 octobre 2018, online event”,
  7.      ts”: 20181017T000000”,
  8.      link”: https://www.alldaydevops.com/",
  9.      sponsors”: [{“name”: all-day-devops”}]
  10.    },
  11.    {
  12.      title”: Création dune Blockchain dentreprise (lab) & introduction aux smart contracts”,
  13.      desc”: Venez avec votre laptop ! Nous vous proposons de nous rejoindre pour réaliser la création dun premier prototype dune Blockchain dentreprise (Lab) et avoir une introduction aux smart contracts.”,
  14.    ts”: 20181004T181500”,
  15.    date”: 4 octobre à 18h15 au CEEI”,
  16.    link”: https://www.meetup.com/fr-FR/IBM-Cloud-Cote-d-Azur-Meetup/events/254472667/",
  17.    sponsors”: [{“name”: ibm”}]
  18.    },
  19.    
  20.  ]
  21. }
此檔案將會被一個mustache模板[2]渲染並生成最終的網站素材。
Docker多階段構建
一旦生成了最終的網站素材,它們將會被拷貝到一個Nginx鏡像里,該鏡像將會被部署到標的機器上。
得益於多階段構建(multi-stage build),本次構建分為兩部分:
  • 網站素材的生成

  • 包含網站素材的最終鏡像的創建

用來構建鏡像的Dockerfile如下:
  1. # 生成素材
  2. FROM node:8.12.0-alpine AS build
  3. COPY . /build
  4. WORKDIR /build
  5. RUN npm i
  6. RUN node clean.js
  7. RUN ./node_modules/mustache/bin/mustache events.json index.mustache > index.html
  8.  
  9. # 構建托管它們的最終鏡像
  10. FROM nginx:1.14.0
  11. COPY --from=build /build/*.html /usr/share/nginx/html/
  12. COPY events.json /usr/share/nginx/html/
  13. COPY css /usr/share/nginx/html/css
  14. COPY js /usr/share/nginx/html/js
  15. COPY img /usr/share/nginx/html/img
本地測試
為了測試生成站點,只需克隆該倉庫然後運行test.sh腳本即可。它將隨後創建出一個鏡像並運行一個容器:
  1. $ git clone [email protected].com:lucj/sophia.events.git
  2.  
  3. $ cd sophia.events
  4.  
  5. $ ./test.sh
  6. Sending build context to Docker daemon  2.588MB
  7. Step 1/12 : FROM node:8.12.0-alpine AS build
  8. ---> df48b68da02a
  9. Step 2/12 : COPY . /build
  10. ---> f4005274aadf
  11. Step 3/12 : WORKDIR /build
  12. ---> Running in 5222c3b6cf12
  13. Removing intermediate container 5222c3b6cf12
  14. ---> 81947306e4af
  15. Step 4/12 : RUN npm i
  16. ---> Running in de4e6182036b
  17. npm notice created a lockfile as package-lock.json. You should commit this file.
  18. npm WARN [email protected].0.0 No repository field.
  19. added 2 packages from 3 contributors and audited 2 packages in 1.675s
  20. found 0 vulnerabilities
  21. Removing intermediate container de4e6182036b
  22. ---> d0eb4627e01f
  23. Step 5/12 : RUN node clean.js
  24. ---> Running in f4d3c4745901
  25. Removing intermediate container f4d3c4745901
  26. ---> 602987ce7162
  27. Step 6/12 : RUN ./node_modules/mustache/bin/mustache events.json index.mustache > index.html
  28. ---> Running in 05b5ebd73b89
  29. Removing intermediate container 05b5ebd73b89
  30. ---> d982ff9cc61c
  31. Step 7/12 : FROM nginx:1.14.0
  32. ---> 86898218889a
  33. Step 8/12 : COPY --from=build /build/*.html /usr/share/nginx/html/
  34. ---> Using cache
  35. ---> e0c25127223f
  36. Step 9/12 : COPY events.json /usr/share/nginx/html/
  37. ---> Using cache
  38. ---> 64e8a1c5e79d
  39. Step 10/12 : COPY css /usr/share/nginx/html/css
  40. ---> Using cache
  41. ---> e524c31b64c2
  42. Step 11/12 : COPY js /usr/share/nginx/html/js
  43. ---> Using cache
  44. ---> 1ef9dece9bb4
  45. Step 12/12 : COPY img /usr/share/nginx/html/img
  46. ---> e50bf7836d2f
  47. Successfully built e50bf7836d2f
  48. Successfully tagged registry.gitlab.com/lucj/sophia.events:latest
  49. => web site available on http://localhost:32768
我們可以使用上述輸出的末尾提供的URL訪問網站頁面。 
標的環境

 

雲廠商創建的一臺虛擬機
或許你也註意到了,這個網站並不是那麼關鍵(每天只有幾十次訪問),也因此它只需要跑在一臺單個的虛擬機上即可。該虛擬機是由Exoscale[3],一個偉大的歐洲雲廠商,它上面的Docker Machine創建出來的。
順便一提,如果你想試試Exoscale的服務的話,知會我一聲,我可以提供20歐元的優惠券。
以Swarm樣式啟動的Docker守護行程
在上面這台虛擬機上運行的Docker守護行程被配置成以Swarm樣式運行,因此它支持使用Docker Swarm原生提供的stack,service,config以及secret等原語和它強大(且易於使用)的編排功能。
以docker stack形式運行的應用
下述檔案內容里定義了一個包含網站素材的nginx web服務器作為一個服務(service)運行。
  1. version: "3.7"
  2. services:
  3.  www:
  4.    image: registry.gitlab.com/lucj/sophia.events
  5.    networks:
  6.      - proxy
  7.    deploy:
  8.      mode: replicated
  9.      replicas: 2
  10.      update_config:
  11.        parallelism: 1
  12.        delay: 10s
  13.      restart_policy:
  14.        condition: on-failure
  15. networks:
  16.  proxy:
  17.    external: true
這裡有幾處需要解釋下:
  • 鏡像儲存在托管到gitlab.com的私有鏡像倉庫(這裡沒涉及到Docker Hub)。

  • 服務是以2個副本的形式運行在副本樣式下,這也就意味著同一時間該服務會有兩個正在運行中的任務/容器。Swarm的service會關聯一個VIP(虛擬IP地址),這樣一來標的是該服務的每個請求會在兩個副本之間實現負載均衡。

  • 每次完成服務更新時(部署一個新版本的網站),其中一個副本會被更新,然後在10秒後更新第二個副本。這可以確保在更新期間整個網站仍然可用。我們也可以使用回滾策略,但是在這裡沒有必要。

  • 服務會被系結到一個外部的代理網絡,這樣一來TLS termination(在Swarm里部署的,跑在另外一個服務里,但是超出本專案的範疇)可以發送請求給www服務。

要運行這個stack只需要執行如下命令:
  1. $ docker stack deploy -c sophia.yml sophia_events
統御一切的Portainer
Portainer[4]是一套很棒的Wbe UI工具,它可以很方便地管理Docker宿主機和Docker Swarm集群。下麵是Portainer操作界面的一張截圖,裡面列出了Swarm集群里當前可用的stack。 
當前設定下有3個stack:
  • Portainer自己

  • 包含了跑著我們網站的服務的sophia_events

  • tls,TLS termination服務

 

如果列出跑在sophia_events stack里的www服務的明細的話,我們將可以看到該服務的webhook已經處於激活狀態。Portainer 1.19.2(迄今為止最新的版本)已經加入了這一功能的支持,它允許定義一個HTTP Post端點,可以在被呼叫後觸發一次服務的更新。正如我們稍後將會看到的,GitLab runner會負責呼叫這個webhook。 
 
備註:從屏幕截圖中可以看到,筆者是通過localhost:8888這個地址訪問Portainer的用戶界面。由於筆者不想將Portainer實體對外暴露,因此是通過SSH隧道訪問,該隧道可以通過如下命令開啟:
  1. ssh -i ~/.docker/machine/machines/labs/id_rsa -NL 8888:localhost:9000 [email protected]$HOST
這樣一來,標的是本地機器上的8888端口的所有請求均會通過SSH轉發到虛擬機上的9000端口上。9000端口是Portainer在虛擬機上運行時監聽的端口,但是並未對外開放,因為它被Exoscale配置的一個安全組禁用了。
 
備註:在上述命令里,用來連接虛擬機的ssh key是在虛擬機創建時由Docker Machine生成的一個key。
GitLab runner
Gitlab的runner是一個負責執行定義在.gitlab-ci.yml檔案里的一組action的行程。就我們這個專案來說,我們定義了一個我們自己的runner,它在虛擬機上以一個容器的形式運行。
第一步就是帶上一堆引數來註冊該runner。
  1. CONFIG_FOLDER=/tmp/gitlab-runner-config
  2. docker run rm -t -i \
  3. -v $CONFIG_FOLDER:/etc/gitlab-runner \
  4. gitlab/gitlab-runner register \
  5.   --non-interactive \
  6.   --executor "docker" \
  7.   —-docker-image docker:stable \
  8.   --url "https://gitlab.com/" \
  9.   —-registration-token "$PROJECT_TOKEN" \
  10.   —-description "Exoscale Docker Runner" \
  11.   --tag-list "docker" \
  12.   --run-untagged \
  13.   —-locked="false" \
  14.   --docker-privileged
在上述引數中,PROJECT_TOKEN可以在GitLab.com的專案頁面上找到,並可以用來註冊外部的runner。 
用來註冊一個新的runner的註冊token。
一旦runner註冊上了,我們需要啟動它:
  1. CONFIG_FOLDER=/tmp/gitlab-runner-config
  2. docker run -d \
  3. --name gitlab-runner \
  4. —-restart always \
  5. -v $CONFIG_FOLDER:/etc/gitlab-runner \
  6. -v /var/run/docker.sock:/var/run/docker.sock \
  7. gitlab/gitlab-runner:latest
等到它註冊上了而且啟動起來了,該runner便會出現在GitLab.com上的專案頁面里。 
為此專案創建的runner。
每當有新的commit推送到倉庫,此runner隨後便會接收到一些要做的任務。它會按順序執行.gitlab-ci.yml檔案里定義好的測試、構建和部署幾個階段。
  1. variables:
  2.  CONTAINER_IMAGE: registry.gitlab.com/$CI_PROJECT_PATH
  3.  DOCKER_HOST: tcp://docker:2375
  4. stages:
  5.  - test
  6.  - build
  7.  - deploy
  8. test:
  9.  stage: test
  10.  image: node:8.12.0-alpine
  11.  script:
  12.    - npm i
  13.    - npm test
  14. build:
  15.  stage: build
  16.  image: docker:stable
  17.  services:
  18.    - docker:dind
  19.  script:
  20.    - docker image build -t $CONTAINER_IMAGE:$CI_BUILD_REF -t $CONTAINER_IMAGE:latest .
  21.    - docker login -u gitlab-ci-token -p $CI_JOB_TOKEN registry.gitlab.com
  22.    - docker image push $CONTAINER_IMAGE:latest
  23.    - docker image push $CONTAINER_IMAGE:$CI_BUILD_REF
  24.  only:
  25.    - master
  26. deploy:
  27.  stage: deploy
  28.  image: alpine
  29.  script:
  30.    - apk add --update curl
  31.    - curl -XPOST $WWW_WEBHOOK
  32.  only:
  33.    - master

 

  • 測試階段(test stage)將會運行一些預備檢查,確保events.json檔案格式正確,並且這裡沒有遺漏鏡像

  • 構建階段(build stage)會做鏡像的構建並將它推送到GitLab上的鏡像倉庫

  • 部署階段(deploy stage)將會通過發送給Portainer的一個webhook觸發一次服務的更新。WWW_WEBHOOK變數的定義可以在Gitlab.com上專案頁面的CI/CD設置里找到。

 

 
備註:
  • runner在Swarm上是以一個容器的形式運行。我們可以使用一個共享的runner,這是一些公用的runner,它們會在托管到GitLab的不同專案所需的任務之間分配時間。但是,由於runner需要訪問Portainer的端點(用來發送webhook),也因為筆者不希望Portainer能夠從外界訪問到,將runner跑在集群里會更安全一些。

  • 再者,由於runner跑在一個容器里,為了能夠通過Portainer暴露在宿主機上的9000端口連到Portainer,它會將webhook請求發送到Docker0橋接網絡上的IP地址。也因此,webhook將遵循如下格式:http://172.17.0.1:9000/api[…]a7-4af2-a95b-b748d92f1b3b。

部署流程

 

新版本的站點更新遵循如下流程: 
  1. 一個開發者推送了一些變更到GitLab。這些變更基本上囊括了events.json檔案里一個或多個新的事件加上一些額外贊助商的logo。

  2. Gitlab runner執行在.gitlab-ci.yml里定義好的一組action。

  3. Gitlab runner呼叫在Portainer中定義的webhook。

  4. 在接收到webhook後,Portainer將會部署新版本的www服務。它通過呼叫Docker Swarm的API實現這一點。Portainer可以通過在啟動時系結掛載的/var/run/docker.sock套接字來訪問該API。

    如果你想知道更多此unix套接字用法的相關信息,也許你會對之前這篇文章《Docker Tips : about /var/run/docker.sock[5]》感興趣。

  5. 隨後,用戶便能看到新版本的站點。

 
示例
讓我們一起來修改代碼里的一些內容隨後提交/推送這些變更。
  1. $ git commit -m 'Fix image'
  2.  
  3. $ git push origin master
如下截圖展示了GitLab.com上的專案頁面里的commit觸發的流水線作業。 
在Portainer一側,它將會收到一個webhook請求,隨後會執行一次服務的更新操作。這裡可能看不太清,但是一個副本已經完成了更新,通過第二個副本可以訪問站點。隨後,幾秒鐘之後,第二個副本也更新完畢。
 
小結
即便對於這樣一個小專案,為它建立一套CI/CD流水線也是一個很好的練習,尤其是可以更加熟悉GitLab(這一直在筆者要學習的串列裡面),它是一個非常出色而且專業的產品。這也是一次體驗大家期待已久的Portainer的最新版本(1.19.2)推出的webhook功能的機會。此外,對於像這樣的副專案,Docker Swarm的使用是無腦上手的,很酷而且易於使用……
相關鏈接:

  1. https://gitlab.com/lucj/sophia.events

  2. https://gitlab.com/lucj/sophia.events/blob/master/index.mustache

  3. http://exoscale.ch/

  4. https://portainer.io/

  5. https://medium.com/lucjuggery/about-var-run-docker-sock-3bfd276e12fd

原文鏈接:https://medium.com/lucjuggery/even-the-smallest-side-project-deserves-its-ci-cd-pipeline-281f80f39fdf

 

Kubernetes實戰培訓

 

Kubernetes實戰培訓將於2019年3月8日在深圳開課,3天時間帶你系統掌握Kubernetes,學習效果不好可以繼續學習本次培訓包括:雲原生介紹、微服務;Docker基礎、Docker工作原理、鏡像、網絡、儲存、資料捲、安全;Kubernetes架構、核心組件、常用物件、網絡、儲存、認證、服務發現、調度和服務質量保證、日誌、監控、告警、Helm、實踐案例等。
    閱讀原文

    赞(0)

    分享創造快樂