基於Gitlab-CI/CD Docker 持續整合 node 專案

? JIEYUFENG發表於2019-01-24

最近揮霍青春、沉淪於學習科學文化知識,摸了些旁門左道,故而在此想做一些分享,同時也是小弟我第一次編寫文章。以下都是一些僅代表個人的一些觀點和心得,本人儘量使用比較通俗易懂的話語來闡述,希望能給各位少俠一些啟發和幫助。若有解釋不當的地方,還請各位大鍋指點一二。


CI/CD介紹

對與Gitlab 提供的 CI/CD, 其稱之為持續整合服務、通俗點就是自動化(打包、測試、部署 ...)

CI (持續構建)  -->  程式碼提交後觸發自動化的單元測試,程式碼預編譯,構建映象,上傳映象等.

CD (持續釋出)  --> 將構建好的程式釋出到各種環境,如預釋出環境,正式環境.


為什麼要使用持續整合?

我們都知道,專案開發最基本的流程不過於 "開發" -> "打包" -> "測試" -> "部署",在一個完整專案的生命週期中,避免不了多次以上這樣的流程。傳統的模式可能會讓我們覺得厭煩,原因不外乎於下面幾點:

1、開發和打包都在同一臺機器上運作,可能導致打包的過程中,影響開發的進度

2、一天天的都是手動打包、測試、部署、日復一日的工作使我們厭倦

3、每個開發者都在自己的機器打包專案,不同的環境配置可能會有各種千奇百怪的問題

除以上問題,當然還有其他不盡人意的缺點,就不一 一描述了,上面的問題足以讓人腦殼深疼。在這個時候,我相信強大的持續整合方案一定能解決你對開發流程不滿的地方

1、提供後臺整合服務,開發、打包,互不影響

2、減少人工編譯部署過程中的低階錯誤

3、解決不同環境下構建專案產生不一致的問題

還有各種好處。。。(具體還是根據大家不同的場景需求定製自己的自動化方案吧)


特別說明

本人使用的系統環境都是 ubuntu、使用docker,專案語言是node.js

不熟悉docker基礎的仁兄們需要先看看docker的一些基礎知識

個人建議使用兩臺以上的伺服器, 一臺開發+測試, 一臺部署釋出專案 (筆者認為,如果只使用一臺伺服器,實際上發揮不出 CI/CD 的威力)

對與網上現有的各大分享gitlab ci/cd 的文章,使用 shell 來搭建ci/cd服務的太多了,在此我就不介紹這種方案了。但是基於docker 方案的文章,也看了不少,對與我個人而言並不是太友好,有些說的比較高深,難以理解,有些過於簡單,不夠詳細。所以決定寫下這篇文章希望能和大家互相探討。該文章主要是基於docker 的持續整合方案。


認識Gitlab-CI/CD流程

該小節適於初步認識gitlab持續整合服務的讀者閱讀、對於已經理解基本流程的可以直接跳過本小節(這一節已經不能教你什麼了)

GitLab8.0之後,GitLab CI/CD 就已經整合在GitLab裡了。給我幾分鐘,你會發現,其實流程很簡單。

基於Gitlab-CI/CD Docker 持續整合 node 專案

pipeline

每次程式碼提交就會觸發一次pipeline。如上去所示,每一行就是一個整合服務,我們稱之為流水線。一次pipeline可以看成一次構建任務。

stage

stage就是上述構建任務中的各個構建階段。一般會包含:安裝依賴,測試,編譯,部署服務等多個階段。

job

job表示構建工作,是每個stage構建階段裡具體執行的工作。

基於Gitlab-CI/CD Docker 持續整合 node 專案

我們可以點選進去看看,你會發現整個流程就像工廠車間裡面的任務一樣,一個任務完成後接著執行下一個(或多個)任務,不難看出來,以上的任務順序為 build ->test -> deploy。

(並且如果有其中一個任務失敗了,我們可以選擇後面的任務是否能繼續下去,這對我們很有用,而這些任務都是我們可以預先設定好的,並按照我們的規則來進行)

通過上面這些概念上的東西,你應該對 Gitlab-CI/CD 的工作 流程有了初步的瞭解,但是這些任務都是交給誰來託管呢? 沒錯,它就是 GitLab runner。下面就開始我們的主題吧。


啟動GitLab runner服務

1、首先要安裝docker、已經安裝的可跳過此步驟:

(附上docker 安裝的官方文件 Get Docker CE for Ubuntu | Docker Documentation)

sudo apt-get update
sudo apt-get install docker-ce
複製程式碼

2、建立gitlab-runner容器:

sudo docker pull gitlab/gitlab-runner:latest
sudo docker stop gitlab-runner && docker rm gitlab-runner
複製程式碼
sudo docker run -d --name gitlab-runner --restart always \
  -v /srv/gitlab-runner/config:/etc/gitlab-runner \
  -v /var/run/docker.sock:/var/run/docker.sock \
  gitlab/gitlab-runner:latest
複製程式碼

3、註冊runner(繫結gitlab專案):

首先我們需要開啟自己gitlab的專案,開啟一下紅色指示的地方

基於Gitlab-CI/CD Docker 持續整合 node 專案

基於Gitlab-CI/CD Docker 持續整合 node 專案

註冊runner:

sudo docker exec -it gitlab-runner gitlab-ci-multi-runner register -n \
  --url 這裡填寫上圖中的url \
  --registration-token 這裡填寫上圖中的token \
  --executor docker \
  --description "gitlab-runner in docker" \
  --tag-list "ci-cd" \
  --docker-privileged=false \
  --docker-pull-policy="if-not-present" \
  --docker-image "docker:latest" \
  --docker-volumes /var/run/docker.sock:/var/run/docker.sock
複製程式碼

成功後如下圖所示(該步驟有可能會出現網路異常的情況,可以嘗試多次重試)

基於Gitlab-CI/CD Docker 持續整合 node 專案

上面的命令將註冊一個新的 Runner 來使用 Docker 所提供的特殊docker:latest映象。這裡使用的是官方提供的 Use Docker socket binding 模式,是將/var/run/docker.sock繫結裝載到容器中,以便 docker 在該映象的上下文中可用。(請注意,它正在使用 宿主機 本身的 Docker 守護程式,docker 命令產生的任何容器都將是 一開始我們建立gitlab-runner容器的兄弟,而不是所執行程式的子程式)有興趣可自行閱讀官方文件,看看具體引數的用法。

這裡需要說明的是 docker-pull-policy,設定gitlab是否從遠端拉去image, 如果iamge是本地的,需要配置該屬性的值為 if-not-present,這樣可以避免docker 映象每次都pull

4、提升gitlab-runner使用者許可權:

由於runner執行過程中,是通過一個叫做 gitlab-runner 的使用者來進行操作的,因為不是root使用者,所以免不了會有許可權問題,這裡我們將其新增到docker組中,並驗證gitlab-runner是否可以訪問Docker

sudo usermod -aG docker gitlab-runner
sudo -u gitlab-runner -H docker info
複製程式碼

配置.gitlab-ci.yml

好了,通過上一節,我們已經成功註冊runner了,是時候定製我們的持續整合服務了,在專案根目錄中建立.gitlab-ci.yml檔案。(附上官方的文件說明 配置gitlab-ci.yml規則)

先附上完整版的yml,裡面均有註釋,後面再針對特殊的地方做些解釋

# 使用docker映象
image: docker:latest
# 設定變數
variables:
  # 映象倉庫地址
  REGISTRY: registry.cn-shenzhen.aliyuncs.com
  # 映象版本
  REGISTRY_IMAGE_TAG: registry.cn-shenzhen.aliyuncs.com/jieyufeng/gitlab-ci-cd:master
  # 映象啟動後的容器名
  CONTAINER_NAME: gitlab-ci-cd

stages:
  - build
  - test
  - deploy

# ----------------構建-----------------
build:
  stage: build
  script:
    # 停止並刪除正在使用當前映象的容器
    - if [ "$(docker ps -a | grep $CONTAINER_NAME)" ]; then
    -  docker stop $CONTAINER_NAME && docker rm $CONTAINER_NAME
    - fi
    # 刪除當前已存在的映象
    - if [ "$(docker images | grep $REGISTRY_IMAGE_TAG)" ]; then
    -  docker rmi $REGISTRY_IMAGE_TAG
    - fi
    # 登入映象倉庫
    - docker login -u $REGISTRY_USER -p $REGISTRY_PASSWORD $REGISTRY
    # 構建新的映象
    - docker build -t $REGISTRY_IMAGE_TAG .
    # 上傳映象
    - docker push $REGISTRY_IMAGE_TAG
  only:
    - master
  tags:
    - ci-cd

# ----------------測試-----------------
test:
  stage: test
  script:
    # 本地啟動容器進行測試
    - docker run -d --name $CONTAINER_NAME -p 3000:3000 $REGISTRY_IMAGE_TAG
  when: on_success
  only:
    - master
  tags:
    - ci-cd

# ----------------部署-----------------
deploy:
  # 切換ubuntu作為deploy任務的映象
  image: ubuntu:latest
  stage: deploy
  script:
    # 給runner配置私鑰
    - 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'
    - eval $(ssh-agent -s)
    - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - > /dev/null
    - mkdir -p ~/.ssh
    - chmod 700 ~/.ssh
    # 給runner配置ssh登入不驗證HostKey
    - '[[ -f /.dockerenv ]] && echo -e "Host *\\n\\tStrictHostKeyChecking no\\n\\n" > ~/.ssh/config'
    # 使用ssh遠端登入正式伺服器,並拉取之前build上傳好的映象進行部署
    - ssh root@$DEPLOY_HOST "
      docker images;
      docker login -u $REGISTRY_USER -p $REGISTRY_PASSWORD $REGISTRY;
      docker pull $REGISTRY_IMAGE_TAG;
      docker run -d --name $CONTAINER_NAME -p 3000:3000 $REGISTRY_IMAGE_TAG;"
  when: manual
  allow_failure: false
  only:
    - master
  tags:
    - ci-cd
複製程式碼

部分說明:

1、這裡會涉及一個概念,叫做docker in docker的概念,針對這個我個人也沒能很好的解釋是個什麼東西,以免誤導大家,因此提供官方的文件 docker socket binding 給大家參考

2、既然要使用自動化部署,那就免不了要使用ssh免密登入策略,此處就關於免密的就不做詳細介紹了,可以參考該文件 ssh免密登入

3、試想一下,有些敏感的引數我們是不希望明文暴露在.gitlab-ci.yml 中的,比如密碼、私鑰等,那怎麼辦才好呢?對此,官方提供了很好的方案 GitLab CI/CD Variables,設定後,我們可以直接使用引數來代替銘感資訊

基於Gitlab-CI/CD Docker 持續整合 node 專案

如上圖所示,我們需要在我們gitlab專案中設定對應的引數:

REGISTRY_USER:   登入你個人的docker映象倉庫使用者名稱

REGISTRY_PASSWORD:  登入你個人的docker映象倉庫密碼

DEPLOY_HOST:  你正式伺服器的地址

SSH_PRIVATE_KEY:  gitlab-runner所在的伺服器的ssh私鑰

如下圖所示,我們在.gitlab-ci.yml只需要使用對應的引數即可,很高大上的有木有

基於Gitlab-CI/CD Docker 持續整合 node 專案

對與不敏感的資訊,我們也可以.gitlab-ci.yml設定引數 (其實就跟我們平常寫程式碼,宣告一個全域性的引數是一個道理的)

基於Gitlab-CI/CD Docker 持續整合 node 專案

4、各流程簡單描述

build:  因為我們runner所在的伺服器就是測試伺服器,所以我們在構建映象之前,需要先將使用了該映象的容器給刪除,並刪除舊的映象。刪除後我們構建新的映象,並登陸個人映象倉庫進行上傳。(由於網路沒辦法翻牆,這裡推薦使用 阿里雲容器映象服務,也可以使用你們自己搭載的個人映象倉庫。 如果你們可以翻牆,也可以使用gitlab配套的容器映象倉庫,如下圖,沒有翻牆請慎用,可能會導致上傳映象失敗的情況)

基於Gitlab-CI/CD Docker 持續整合 node 專案

test:  正如剛才我們說的 runner所在的伺服器就是測試伺服器,所以test的任務比較簡單,就是使用docker啟動我們剛才編譯的映象即可

deploy:   因為docker:latest 映象中沒有ssh服務,我們可以換一種思路,切換ubuntu:latest 映象,大家都知道,使用ssh遠端登入肯定免不了登入驗證, 這裡一定要先打通伺服器之間的ssh免密登入,也就是上面的第2點說明,否則登入不成功,接著就是給runner配置私鑰了。針對這個,官方也有方案文件 ssh-keys-when-using-the-docker-executor。需要注意的地方是,一般我們不希望每一次構建一次流水線就部署一次,因此我們可以在deploy中設定when: manual,它讓我們可以通過手動來控制是否需要執行部署的操作

基於Gitlab-CI/CD Docker 持續整合 node 專案

如圖所示,我們可以根據前面的任務狀態來確定是否需要進行部署任務,只需點一下紅色標註的地方即可


總結

雞湯:可能對於像我這樣的新手一開始接觸這個,會有很多很多困惑,也會遇到各種各樣的問題。但是勇於嘗試,總能成功的,畢竟羅馬也不是一天建成的不是嗎?

最後,由於這個Gitlab-CI/CD太過強大,還有很多高大上的方案本人也還在學習中,文章中出現的文件連結都很有用,都是官方文件的,每個人可能都會遇到各種不同的問題,本文只是做一個總結分享,並不一定能解決大家所遇到的問題,大家可以多閱讀資料,互相探討一下

附上本人簡單的demo: gitlab-ci-cd

相關文章