如今公司內使用的是K8S進行服務部署,內網測試環境也提供了K8S環境。團隊內多人同時開發多個需求,必然會出現多個需求同時測試的狀況。而部署到內網K8S測試環境,一次只能部署一個服務。若是將多個需求分支合併到一個分支上去,勢必會相互影響,並且使用公司的TDC服務每次更新都要人工處理,效率偏低。爲了解決這個問題,我在公司內網物理機上,針對靜態網站和直出網站分別搭建了多分支的測試環境,並配合Gitlab CI自行自動化部署,減小人工部署耗時。html
目前手上有兩個項目,一個是正常的靜態網站,一個是服務端直出的網站。它們在部署上是有不一樣的,靜態網站咱們開發完打包好靜態文件部署到服務器上配置好Nginx
訪問指定目錄訪問就好了。而服務端直出的項目則須要啓動一個服務,並配置Nginx
將請求轉發到對應的服務上。前端
所以針對上述狀況咱們須要事情以下:node
Nginx
規則來實現經過訪問URL上的信息將請求轉發到對應的文件或服務;Gitlab CI
並編寫自動化腳本進行自動部署。針對靜態網站,咱們最終是將其部署在服務器上的某個目錄中,經過Nginx
轉發進行訪問。若是咱們可以將不一樣分支打包到不一樣的目錄下,並經過·Nginx
配置進行訪問就能實現訪問。git
一個項目中每一個人開發需求都會取一個惟一的分支名,而在Gitlab執行CI時會提供CI_COMMIT_REF_SLUG
這個字段,這個字段的值是分支名稱的值全轉成小寫並將除0-9
和a-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
的server_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
配置規則進行轉換,並且還可以支持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項目頁中找到setting下面的CI/CD,按照指引安裝Gitlab Runner便可。這裏很少說了。
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
複製代碼
至此,自動化部署的多分支測試環境就配置完成了。
Gitlab runner
須要去它的工做目錄之外的地方建立目錄,這時候出現了權限問題。由於是測試機,我直接將**/etc/passwd**中的gitlab-runner對於應的第三個參數user id修改成了0(root帳號)即可以了:
gitlab-runner:x:0:0:GitLab Runner:/home/gitlab-runner:/bin/bash
複製代碼
gitlab runner
讀取的是/home/gitlab-runner/.docker/
下的帳號配置,咱們須要在ci
中從新登陸docker
。