.NetCore 配合 Gitlab CI&CD 實踐 - 單體專案
阿新 • • 發佈:2020-08-06
### 前言
上一篇博文 [.NetCore 配合 Gitlab CI&CD 實踐 - 開篇](https://gridea.run/post/netcore%E9%85%8Dgitlabci_cd%E5%AE%9E%E8%B7%B5-%E5%BC%80%E7%AF%87/),主要簡單的介紹了一下 `GitLab CI` 的持續整合以及持續部署,這篇將通過 `GitLab CI` 釋出一個 .net core 專案,來帶小夥伴們感受一下自動化的魅力,從此告別手動釋出。
### 準備工作
**建立一個空MVC專案來進行演示:**
```
mkdir hello-world
cd hello-world
dotnet new sln -n HelloWorld
mkdir src
cd src
dotnet new mvc -n GitLabCIDemo
cd ../
dotnet sln add .\src\GitLabCIDemo\GitLabCIDemo.csproj
```
完成以上建立後,用 vscode 開啟應該是下面這個樣子:
![image.png](https://i.loli.net/2020/08/05/X3eZDcpJKCywWdB.png)
**專案上傳至 GitLab**
在 gitlab 上新建一個 `hello-world` 的專案,將本地的專案上傳。這個按照如下提示操作即可:
![image.png](https://i.loli.net/2020/08/05/CzomNwcBLGZpbRO.png)
專案上傳成功後,切一個 dev 分支出來,我這裡的策略是,程式碼提交到 dev 分支是自動釋出到開發環境進行驗證的,生產環境是通過 master 分支打 tag 進行釋出的。
* 切換到 dev 分支!
* 切換到 dev 分支!
* 切換到 dev 分支!
**新增相關指令碼**
在 `hello-world` 資料夾內建立 `.build/docker` 資料夾,並新增如下指令碼以及`Dockerfile`:
* build-image.sh
```shell
docker build -f .build/docker/Dockerfile --build-arg PROJECT=$1 --build-arg ASPNETCORE_ENVIRONMENT=$2 -t $3 .
```
* build-project.sh
```shell
set -e
for arg in "$@"
do
target=$(pwd)/src/$arg
dotnet restore -v n $target
dotnet publish -c Release -o $target/publish $target
done
```
* push-image.sh
```shell
NEW_TAG="registry.cn-hangzhou.aliyuncs.com/xxx/$1"; // 這裡得用你自己名稱空間哦
docker tag $1 $NEW_TAG
docker push $NEW_TAG
docker rmi $NEW_TAG
```
* Dockerfile
```Dockerfile
FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-alpine
ARG PROJECT
ARG ASPNETCORE_ENVIRONMENT
ENV ASPNETCORE_ENVIRONMENT ${ASPNETCORE_ENVIRONMENT}
WORKDIR /app
COPY src/${PROJECT}/publish .
RUN echo "#!/bin/bash \n dotnet ${PROJECT}.dll" > start.sh && chmod +x ./start.sh
ENTRYPOINT ["./start.sh"]
```
這裡構建映象所使用 `3.1-alpine` 構建出來的映象體積只有其他映象版本構建出來體積的一半。推薦使用。到此目錄結構就成了現在這樣:
![image.png](https://i.loli.net/2020/08/06/S6GthqUaOZrN19T.png)
至此準備工作已經差不多了!我們先簡單過一下 CI 的流程:
提交程式碼 --> 編譯 --> 測試 --> 構建映象 --> 釋出
### 編譯 -- Build
在 `hello-world` 目錄下新增 `.gitlab-ci.yml` 檔案,新增 `build` 任務:
```yaml
stages:
- build
helloworld-build:
stage: build
tags:
- docker
image: mcr.microsoft.com/dotnet/core/sdk:3.1
script:
- .build/docker/build-project.sh GitLabCIDemo
artifacts:
paths:
- src/*/bin
- src/*/publish
```
這裡起一個容器來跑編譯任務,具體細節可以看 `build-project.sh` 指令碼,用 `artifacts` 將釋出出來的資源上傳,給後面的 構建任務使用,不需要重複 build,節約時間。這裡執行會出現如下錯誤:
```shell
/bin/bash: line 89: .build/docker/build-project.sh: Permission denied
```
這是因為指令碼沒有執行許可權,通過 `chmod` 命令可以解決:
```
chmod +x build-image.sh build-project.sh push-image.sh
```
重新提交,觸發 build 任務。開啟 GitLab CI 介面,執行成功,是不是很開心呢?
![image.png](https://i.loli.net/2020/08/06/mZbsMlArQRp8hFi.png)
### 測試 -- Test
現在專案裡面沒有測試程式碼,那就新建一個吧。在 `hello-world` 根目錄下:
```
mkdir test
cd test
dotnet new xunit -n GitLabCIDemo.UnitTests
```
更新 `.gitlab-ci.yml` 新增 Test 任務:
```yaml
stages:
- build
- test
helloworld-build:
stage: build
tags:
- docker
image: mcr.microsoft.com/dotnet/core/sdk:3.1
script:
- .build/docker/build-project.sh GitLabCIDemo
artifacts:
paths:
- src/*/bin
- src/*/publish
helloworld-test:
stage: test
tags:
- docker
image: mcr.microsoft.com/dotnet/core/sdk:3.1
script:
- dotnet test -c Release
dependencies:
- helloworld-build
```
等待一會,檢視 CI 執行。
![image.png](https://i.loli.net/2020/08/06/wRVcJ9I4FdEWUGO.png)
### 構建 Docker 映象 -- Pack
我這裡使用的是阿里雲的映象倉庫,需要現在阿里雲上建立對應的名稱空間以及映象名稱。我這裡給映象名取名為 `hello-world`,別忘了修改 `push-image.sh` 中的名稱空間哦!
![image.png](https://i.loli.net/2020/08/06/Qqvc6rGRMpViews.png)
更新 `.gitlab-ci.yml` 新增 Pack 任務:
```yaml
stages:
- build
- test
- pack
helloworld-build:
stage: build
tags:
- docker
image: mcr.microsoft.com/dotnet/core/sdk:3.1
script:
- .build/docker/build-project.sh GitLabCIDemo
artifacts:
paths:
- src/*/bin
- src/*/publish
helloworld-test:
stage: test
tags:
- docker
image: mcr.microsoft.com/dotnet/core/sdk:3.1
script:
- dotnet test -c Release
dependencies:
- helloworld-build
helloworld-pack-staging:
stage: pack
tags:
- shell
script:
- .build/docker/build-image.sh GitLabCIDemo Staging hello-world:beta
- .build/docker/push-image.sh hello-world:beta
dependencies:
- helloworld-build
only:
- dev
```
* `Staging`: 用來設定環境變數 `ASPNETCORE_ENVIRONMENT`,讓 `.net core` 讀取對應的配置檔案,可以設定`Development`,`Staging`,`Production` 三種,可以在對應的環境設定對應環境變數,使用不同配置。
* beta: 因為是開發環境,就一直使用同一個 tag 去覆蓋之前的映象,每次 pull 最新的映象就好了,也可以使用 `$CI_COMMIT_SHORT_SHA` 使用當前 git 提交的 hash 值作為版本號
CI 應該執行的差不多,再去瞧瞧唄! 又是全綠,真是開心!額,全綠,怪怪的...
![image.png](https://i.loli.net/2020/08/06/J5HT7rGWt24vw6M.png)
開啟阿里雲映象容器服務,檢視一下剛剛上傳的容器映象吧!
![image.png](https://i.loli.net/2020/08/06/vjzTqAB378NH2Jh.png)
### 部署 -- Deploy
> 這裡我圖方便就使用 `docker-compose` 來做演示了,通常部署環境都是使用叢集來部署的,當然單機部署也不是不無可能的,我內網用 k3s 搭建的一個叢集,為啥不用 k8s,因為要求資源配置比較高,k3s 剛好夠用。但是部署到 k3s 需要寫挺多的配置的檔案,可以單獨寫一篇博文介紹。
作為自動化最後一步,無論你是用 `k8s`,`k3s`,`docker swarm`......,都是拉取對應的映象,進行部署。思路是一樣的,只是部署方式略有不同。這裡通過 docker-compose 來發布 `hello-world` 應用咯。
在部署的 Linux 伺服器上建立資料夾 `/deploy` ,在目錄下新增 `docker-compose.yml` 檔案,新增如下內容:
```
version: "3.8"
services:
hello-world:
image: registry.cn-hangzhou.aliyuncs.com/jd-rd/hello-world:beta
container_name: hello-world
restart: always
ports:
- 5013:80
networks:
- basic_service
networks:
basic_service:
```
更新 `.gitlab-ci.yml`,新增部署任務:
```
stages:
- build
- test
- pack
- deploy
helloworld-build:
stage: build
tags:
- docker
image: mcr.microsoft.com/dotnet/core/sdk:3.1
script:
- .build/docker/build-project.sh GitLabCIDemo
artifacts:
paths:
- src/*/bin
- src/*/publish
helloworld-test:
stage: test
tags:
- docker
image: mcr.microsoft.com/dotnet/core/sdk:3.1
script:
- dotnet test -c Release
dependencies:
- helloworld-build
helloworld-pack-staging:
stage: pack
tags:
- shell
script:
- .build/docker/build-image.sh GitLabCIDemo Staging hello-world:beta
- .build/docker/push-image.sh hello-world:beta
dependencies:
- helloworld-build
only:
- dev
deploy-staging:
stage: deploy
tags:
- staging
script:
- cd /deploy && docker-compose pull && docker-compose up --force-recreate -d && docker ps
only:
- dev
```
等待整個CI跑完,檢視 CI 執行結果
![image.png](https://i.loli.net/2020/08/06/WOyHbhBGfqKiE7l.png)
通過伺服器IP + 5013 訪問應用了,就可以看到服務更新
![image.png](https://i.loli.net/2020/08/06/6PvYdEaZerSnMjb.png)
修改一下 `Index.cshtml` 將 `Welcome` 修改為 `Hello Devops !` ,重新提交一下,稍等片刻,重新訪問即可看到變化了。
![image.png](https://i.loli.net/2020/08/06/94dZqRzUbMAtPeG.png)
至此一個簡單的 CI 流程已經走完了,各位看官可以根據自己的需求,繼續探索。
### 題外話
如果將指令碼都放在專案裡面的話,將來涉及到指令碼變更,需要每個專案都給改過去,這是十分痛苦的事情。這裡推薦小夥伴可以通過 `git submodule` 子模組的方式進行引用,將公共的指令碼都給提取出來,專案通過子模組來載入指令碼專案,將來指令碼變更,每個專案只需要更新一下子模組就好。
### 總結
通過以上一個小案例已經帶小夥伴瞭解了一圈 `GitLab-CI` 如何來發佈一個 `.net core` 應用,感受了一下 `GitLab-ci` 的魅力。但是以上方案還是有瑕疵的,對於單體應用來說,沒有太大問題,但並不適合微服務專案。在微服務專案中,如果多個服務散落在多個倉庫中,需要多個專案程式碼改動其實是很不方便的,所以現在很多的微服務專案都採用 `Mono` 倉庫風格及將所有的服務都放在一個倉庫裡面,倉庫體積雖然大,但是改動起來更方便。本篇就先到這裡了,有關於 `GitLab-CI` 對於 `Mono` 倉庫風格專案 `CI&CD` 探索實踐,且聽下回分解。