多分支測試環境自動化部署

背景

如今公司內使用的是K8S進行服務部署,內網測試環境也提供了K8S環境。團隊內多人同時開發多個需求,必然會出現多個需求同時測試的狀況。而部署到內網K8S測試環境,一次只能部署一個服務。若是將多個需求分支合併到一個分支上去,勢必會相互影響,並且使用公司的TDC服務每次更新都要人工處理,效率偏低。爲了解決這個問題,我在公司內網物理機上,針對靜態網站和直出網站分別搭建了多分支的測試環境,並配合Gitlab CI自行自動化部署,減小人工部署耗時。html

實踐路線

目前手上有兩個項目,一個是正常的靜態網站,一個是服務端直出的網站。它們在部署上是有不一樣的,靜態網站咱們開發完打包好靜態文件部署到服務器上配置好Nginx訪問指定目錄訪問就好了。而服務端直出的項目則須要啓動一個服務,並配置Nginx將請求轉發到對應的服務上。前端

所以針對上述狀況咱們須要事情以下:node

  1. 根據不一樣的分支輸出對應的靜態文件或者啓動不一樣的服務;
  2. 經過配置Nginx規則來實現經過訪問URL上的信息將請求轉發到對應的文件或服務;
  3. 配置Gitlab CI並編寫自動化腳本進行自動部署。

如何區分不一樣分支

靜態網站

針對靜態網站,咱們最終是將其部署在服務器上的某個目錄中,經過Nginx轉發進行訪問。若是咱們可以將不一樣分支打包到不一樣的目錄下,並經過·Nginx配置進行訪問就能實現訪問。git

一個項目中每一個人開發需求都會取一個惟一的分支名,而在Gitlab執行CI時會提供CI_COMMIT_REF_SLUG這個字段,這個字段的值是分支名稱的值全轉成小寫並將除0-9a-z的其餘字符轉換成-後得到的,能夠用在URL中。正則表達式

這樣咱們只需在執行自動化腳本時,將CI_COMMIT_REF_SLUG當作參數傳入打包腳本,在指定目錄下新建對應名稱的目錄,並將打包文件經過過去變算是完成第一階段的工做了。docker

服務端直出網站

服務端渲染的網站須要啓動服務去吐出頁面,所以咱們的Nginx配置也是指向對應的服務。問題是項目的端口號是固定的。若是咱們每一個分支都人工修改項目端口號,一則可能上線時會忘記更改回來;二則還要人工確認對應端口是否被佔用。顯然是不合理的。shell

恰好公司項目都上了K8S,都是用Docker進行部署。Docker的特性裏有一條即是將容器內的端口暴露給外部,利用這個特性,咱們能夠在不改變項目啓動端口的狀況下,對外暴露不一樣的訪問端口了:npm

docker run  --name $CI_COMMIT_REF_SLUG -P -d <image>:$CI_COMMIT_REF_SLUG
複製代碼

這裏咱們關注參數-P,這表明隨機指定一個系統端口給當前容器。bash

可是咱們在測試時是須要修改bug的,每次修改完提交從新部署,確定是但願可以維持已有端口而不是每次提交都變更。那咱們能夠每次重啓服務的時候,先去獲取已有服務的端口號:服務器

HOSTPORT=$(docker inspect --format='{{(index (index .NetworkSettings.Ports "3000/tcp") 0).HostPort}}' $CI_COMMIT_REF_SLUG)
複製代碼

這樣一來不一樣分支啓動服務暴露不一樣端口的問題也就解決了。

Nginx規則配置

靜態網站

平時咱們配置Nginxserver_name的時候,通常都是指定對應域名。但咱們是能夠在裏面使用正則去匹配子域名的:

server {
  listen       80;
  server_name  ~^(.+)?\.static\.test\.com$;
  set $www_root $1;
  root /data/vhosts/static.test.com/$www_root/;
}
複製代碼

以上是我靜態項目的Nginx配置示例。個人靜態網站在測試環境最終訪問的連接是:

http://$CI_COMMIT_REF_SLUG.static.test.com
複製代碼

根據上面的正則表達式,Nginx在接到請求時,拿到了咱們的分支別名後,將請求轉發到項目目錄下的指定分支目錄了。

服務端直出網站

服務端直出網站的配置和靜態網站的思路是同樣的,只是把訪問目錄改爲了訪問服務而已:

server {
  listen  80;
  server_name ~^(.+)?.server.test.com$;
  root html;
  ...
  set $port $1;
  location ~ / {
    proxy_pass  http://127.0.0.1:$port;
    ...
	}
}
複製代碼

最終的訪問地址是:

http://$port.server.test.com
複製代碼
whistle規則

由於實際生產中兩個項目都是在客戶端內部打開的,因此其實在測試時給出的兩個網址和客戶端配置的並不相同。這裏能夠用whistle配置規則進行轉換,並且還可以支持HTTPS,很是的方便:

https://static.test.com http://$CI_COMMIT_REF_SLUG.static.test.com
https://server.test.com http://$port.server.test.com
複製代碼

甚至使用了whistle以後服務端直出網站均可以不用配置Nginx,直接經過IP訪問對應的服務就能夠了:

https://server.test.com http://ip:$port
複製代碼

部署腳本

靜態網站

靜態網站部署腳本相對簡單,只需對代碼進行打包,而後同步到對應的文件夾便可:

#!/bin/sh
CI_COMMIT_REF_SLUG="$1"
BUILD_COMMAND="yarn install --registry https://registry.npm.taobao.org; yarn build;"
TARGET_DIR="/data/vhosts/static.test.com/$CI_COMMIT_REF_SLUG/"
# 啓動docker打包文件
docker run -v $(pwd)/:/app -w  /app node:10 /bin/sh -c "$BUILD_COMMAND" || exit 1
# 建立指定目錄,-p表示:遞歸建立目錄,若是目錄已存在也不會報錯
mkdir -p $TARGET_DIR
# 同步打包後文件到對應目錄
rsync -av --delete-after ./dist/ $TARGET_DIR
複製代碼
服務端直出網站

服務端直出網站部署腳本相對複雜,主要體如今更新時對容器的處理上:

#!/bin/sh
BUILD_COMMAND="yarn install --registry https://registry.npm.taobao.org; yarn build;"
# 執行腳本時後面傳入的第一個參數
CI_COMMIT_REF_SLUG="$1"
 # 編譯打包項目
docker run -v $(pwd)/:/app -w  /app node:10 /bin/sh -c "$BUILD_COMMAND" || exit 1
# 將項目打包到docker鏡像,鏡像名稱帶上CI_COMMIT_REF_SLUG
# 運行此命令須要項目根路徑有Dockerfile文件,文件比較簡單,這裏就不列出了
docker build -t game-frontend:$CI_COMMIT_REF_SLUG .
# 判斷是否有當前分支對應的容器在運行
if [ ! "$(docker ps -q -f name=$CI_COMMIT_REF_SLUG)" ]; 
then
 # 若是有容器但已退出
    if [ "$(docker ps -aq -f status=exited -f name=$CI_COMMIT_REF_SLUG)" ]; 
    then
        # 刪除容器
        docker rm $CI_COMMIT_REF_SLUG
    fi
    # 利用剛剛構建的鏡像啓動一個名爲CI_COMMIT_REF_SLUG的容器
    docker run --name $CI_COMMIT_REF_SLUG -P -d game-frontend:$CI_COMMIT_REF_SLUG
    # 獲取容器端口號,輸出出來,方便查看
    HOSTPORT=$(docker inspect --format='{{(index (index .NetworkSettings.Ports "3000/tcp") 0).HostPort}}' $CI_COMMIT_REF_SLUG)
    echo "端口號:$HOSTPORT"
else
 # 若是已有容器,則獲取端口號
  HOSTPORT=$(docker inspect --format='{{(index (index .NetworkSettings.Ports "3000/tcp") 0).HostPort}}' $CI_COMMIT_REF_SLUG)
  echo "端口號:$HOSTPORT"
 # 中止容器,並刪除
  docker stop $CI_COMMIT_REF_SLUG
  docker rm $CI_COMMIT_REF_SLUG
 # 根據以前端口號啓動新的容器
  docker run --name $CI_COMMIT_REF_SLUG -p $HOSTPORT:3000 -d game-frontend:$CI_COMMIT_REF_SLUG
fi
複製代碼

配置自動化部署

完成了上面的準備工做以後,就能夠配置Gitlab CI了。

註冊Gitlab Runner

咱們在Gitlab項目頁中找到setting下面的CI/CD,按照指引安裝Gitlab Runner便可。這裏很少說了。

編寫.gitlab-ci.yml
cache:
 untracked: true
stages:
 - dev
before_script:
 - git lfs pull
dev:
 stage: dev
 script:
  # 這裏就是上面編寫的腳本
 - chmod +x ./scripts/test_dev.sh
 - ls -lsa ./scripts/test_dev.sh
 - ./scripts/test_dev.sh $CI_COMMIT_REF_SLUG
 tags:
  # 指定要使用的Gitlab runner
 - test-dev
複製代碼

至此,自動化部署的多分支測試環境就配置完成了。

遇到的問題

  1. 在執行部署腳本時Gitlab runner須要去它的工做目錄之外的地方建立目錄,這時候出現了權限問題。

由於是測試機,我直接將**/etc/passwd**中的gitlab-runner對於應的第三個參數user id修改成了0(root帳號)即可以了:

gitlab-runner:x:0:0:GitLab Runner:/home/gitlab-runner:/bin/bash
複製代碼
  1. 執行腳本拉取公司docker倉庫鏡像時報權限問題,可是我機器上明明已經登陸過了。這裏是由於gitlab runner讀取的是/home/gitlab-runner/.docker/下的帳號配置,咱們須要在ci中從新登陸docker
相關文章
相關標籤/搜索