經過 GitLab Runner 實現 CI/CD 工做流(下)

前言

上篇文章《經過 GitLab Runner 實現 CI/CD 工做流(上)》咱們講解了 GitLab Runner 的部署和配置優化,此次咱們來進行一次實戰演練,對一個完整的先後端分離的項目進行講解。這個項目分爲前端和後端兩個項目,他們有獨自的代碼倉庫,對於不一樣倉庫的代碼提交,會觸發對應的項目的代碼編譯、鏡像構建、鏡像發佈與項目部署。html

架構

咱們先來設計下項目的總體架構:前端

項目架構

項目須要運行四個容器,分別是反向代理、前端項目、後端項目和數據庫,他們都在同一個 bridge 類型的 docker 網絡下,只有反向代理容器暴露端口提供對外訪問,其餘容器均爲 bridge 網絡內部通訊。node

反向代理 Nginx 根據請求的 URL 將請求分發到前端項目或後端項目,前端項目將最終編譯生成的靜態資源打包在 Nginx 容器內提供訪問,後端項目是使用 Go 編寫的 API 服務,將編譯好的二進制文件打包進 alpine 容器運行,MySQL 容器提供數據庫訪問。linux

CI / CD 流程

CI/CD流程

當咱們爲項目綁定好 Runner 以後,開發者提交代碼到 GitLab 項目倉庫,GitLab 根據項目中的 .gitlab-ci.yml 配置文件要求觸發 Runner 工做。咱們在 .gitlab-ci.yml 文件中這樣定義了整個流程:構建應用鏡像 -> 推送到鏡像倉庫 -> 觸發服務器的自部署腳本 -> 服務器拉取新鏡像、中止並移除舊容器、啓動新容器nginx

部署策略

能夠看到,咱們的部署方式爲中止舊版本應用並啓用新版本應用,這種部署策略叫作 重建(recreate),這種更新應用的實現方式比較簡單,但有一個顯著的問題是,若是是線上生產服務器的部署,可能會出現短暫的流量中斷的狀況。git

其餘的部署更新策略還有藍綠部署滾動更新灰度發佈(金絲雀發佈)等,若是使用 Kubernetes 進行集羣容器應用管理,它自帶了多種更新策略,會很是方便咱們進行 CD。docker

請閱讀參考這篇文章:《Kubernetes 部署策略詳解》shell

項目講解

前端項目代碼

前端項目是一個使用 Vue.js 腳手架的 SPA,另外咱們還須要在項目中添加三個配置文件:數據庫

  • .gitlab-ci.yml   CI 流程
  • Dockerfile   鏡像構建流程
  • default.conf   前端 Nginx 容器中的配置

三者的關係是:CI 過程當中會調用 Dockerfile 進行鏡像構建,鏡像構建過程又會調用 default.conf 將文件寫進鏡像。npm

.gitlab-ci.yml
# 兩個階段:構建併發布鏡像、部署(經過執行遠程腳本的方式)
stages:
  - build
  - deploy

build_job:
  stage: build
  image: docker:latest
  tags:
    - jczh100
  only:
    - dev
  script:
    # 構建鏡像(每一個 Job 默認都會將項目代碼 fetch 到 Job 容器中,這一步將用到 Dockerfile)
    - docker build -t ucfe:latest .
    # 登陸鏡像倉庫
    - docker login -u $DOCKER_REGISTRY_USERNAME -p $DOCKER_REGISTRY_PASSWORD $DOCKER_REGISTRY_ADDR
    # 給鏡像打標籤
    - docker tag ucfe:latest ${DOCKER_REGISTRY_ADDR}/xvrzhao/ucfe:latest
    # 發佈到鏡像倉庫
    - docker push ${DOCKER_REGISTRY_ADDR}/xvrzhao/ucfe:latest

deploy_job:
  stage: deploy
  image: alpine:latest
  variables:
    # 跳過 git 操做,加快流水線執行速度,本 job 不須要獲取倉庫代碼
    GIT_STRATEGY: none
  tags:
    - jczh100
  only:
    - dev
  before_script:
    # 更換 alpine apk 源爲阿里源
    - sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
    # 安裝 sshpass,sshpass 能夠將密碼傳入 ssh,免去交互式輸入密碼
    - apk add --update --no-cache openssh sshpass
  script:
    # sshpass 參數:
    #   -p 將密碼傳入 ssh
    #
    # ssh 參數:
    #   -T 不申請僞終端
    #   -o 傳入 ssh 配置項,這裏傳入了 StrictHostKeyChecking 參數,默認值爲 ask,設置爲 no 表明 ssh 自動將遠程主機的 hostkeys 加入到 ~/.ssh/known_hosts,而且容許遠程主機的 hostkeys 發生變化
    - sshpass -p $TEST_SERVER_XVR_PASSWORD ssh -T -o StrictHostKeyChecking=no xvrzhao@$TEST_SERVER_IP 'bash /home/xvrzhao/cd-scripts/ucenter-fe.sh'
注意:爲了不一些敏感數據(好比密碼等)暴露在代碼庫中,能夠在 GitLab 項目倉庫的 CI 設置中添加一些變量,只有項目維護者能夠管理和查看這些變量。在 .gitlab-ci.yml 能夠經過 $ 符直接使用這些變量值。
Dockerfile
# node 做爲基礎鏡像來執行前端項目的構建
FROM node:alpine AS builder
WORKDIR /app
# 根據鏡像分層原理,先拷貝依賴文件,若依賴文件沒有改動,則下一步安裝依賴會直接利用緩存
COPY ./package.json ./
RUN npm install --registry=https://registry.npm.taobao.org
COPY ./ ./
RUN npm run build

# 將前端項目 dist 打包進 nginx 鏡像
FROM nginx:alpine
COPY --from=builder /app/dist/ /dist/
# 修改前端 Nginx 配置(這一步使用到 default.conf)
COPY ./default.conf /etc/nginx/conf.d/
default.conf
# 前端項目 dist 打包到了 nginx 鏡像,該文件爲 nginx server 配置
server {
    listen 80;
    location ^~ /ucenter {
        alias /dist;
    }
}

關於 Nginx 提供靜態資源訪問的優化,請參考這篇文章:《Nginx 優化靜態文件訪問》

部署腳本

經過 .gitlab-ci.yml 能夠看到,在 deploy_job 中咱們經過 SSH 遠程執行了所要部署的服務器上的腳本。腳本會拉取在 build_job 中推送的最新鏡像,而後中止並刪除目前服務器上運行的容器,啓動最新鏡像。

ucenter-fe.sh 腳本內容:

#!/bin/bash

# 服務器 CD 腳本
# Author:   Xavier Zhao <xvrzhao@gmail.com>
# Date:     2020/05/06

echo -e -n "\n開始執行部署 ...\n\n(1/4) 拉取最新版本鏡像 ucfe:latest ...\t"
res=$(docker pull registry.cn-shanghai.aliyuncs.com/xvrzhao/ucfe:latest 2>&1)
if [ $? -eq 0 ]; then
    echo "完成!"
else
    echo -e "錯誤!\n\n報錯信息:$res"
    exit 1
fi

echo -e -n "(2/4) 中止舊版本容器 ucenter-fe ...\t"
res=$(docker stop ucenter-fe 2>&1)
if [ $? -eq 0 ]; then
    echo "完成!"
else
    echo -e "錯誤!\n\n報錯信息:$res"
    exit 1
fi

echo -e -n "(3/4) 移除舊版本容器 ucenter-fe ...\t"
res=$(docker rm ucenter-fe 2>&1)
if [ $? -eq 0 ]; then
    echo "完成!"
else
    echo -e "錯誤!\n\n報錯信息:$res"
    exit 1
fi

echo -e -n "(4/4) 啓動新版本容器 ucenter-fe ...\t"
res=$(docker run -d --name ucenter-fe --network ucenter registry.cn-shanghai.aliyuncs.com/xvrzhao/ucfe:latest 2>&1)
if [ $? -eq 0 ]; then
    echo "完成!"
else
    echo -e "錯誤!\n\n報錯信息:$res"
    exit 1
fi

echo -e "\n部署完成!\n"

執行效果:

$ ./ucenter-fe.sh

開始執行部署 ...

(1/4) 拉取最新版本鏡像 ucfe:latest ...  完成!
(2/4) 中止舊版本容器 ucenter-fe ...    完成!
(3/4) 移除舊版本容器 ucenter-fe ...    完成!
(4/4) 啓動新版本容器 ucenter-fe ...    完成!

部署完成!

$ ./ucenter-fe.sh

開始執行部署 ...

(1/4) 拉取最新版本鏡像 ucfe:latest ...    完成!
(2/4) 中止舊版本容器 ucenter-fe ...    錯誤!

報錯信息:Error response from daemon: No such container: ucenter-fe

後端項目代碼

// TODO
相關文章
相關標籤/搜索