前言
之前研究了使用 GitHub Action 自動構建和釋出 nuget 包:開發現代化的.NetCore控制檯程式:(4)使用GithubAction自動構建以及釋出nuget包
現在更進一步,使用 GitHub Action 在其提供的 runner 裡構建 docker 映象,之後提交到阿里雲映象私有倉庫,再在本地的 runner 將映象 pull 下來執行。
本文以 AIHub 專案為例。
AIHub 是一個整合多種模型的AI平臺,提供了大模型、計算機視覺、自然語言處理等多種功能,基於 AspNetCore + Blazor Server 技術開發。
準備 Dockerfile
首先準備好構建映象的 Dockerfile
一般來說使用 dotnet cli 建立專案的時候可以自動生成這個 Dockerfile
沒有的話可以參考我這個
FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443
FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
WORKDIR /src
COPY ["AIHub.Blazor/AIHub.Blazor.csproj", "AIHub.Blazor/"]
RUN dotnet restore "AIHub.Blazor/AIHub.Blazor.csproj"
COPY . .
WORKDIR "/src/AIHub.Blazor"
RUN dotnet build "AIHub.Blazor.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "AIHub.Blazor.csproj" -c Release -o /app/publish /p:UseAppHost=false
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "AIHub.Blazor.dll"]
可以先在本地試一下 build 有沒有問題
docker build .
一切正常可以進入下一步
建立私有映象倉庫
可以自行搭建,也可以使用雲服務產品,我這裡使用阿里雲的「容器映象服務 ACR」,個人版是免費的。
建立一個映象倉庫,程式碼源選擇「本地倉庫」,可以透過命令列推送映象到映象倉庫。
PS: 我看裡面還有 GitHub 倉庫作為程式碼源,不過我還沒測試。
建立之後有個操作指南,這不是很重要,我們只需要關注使用者名稱和密碼就行。
用於登入的使用者名稱為阿里雲賬號全名,密碼為開通服務時設定的密碼。
可以在訪問憑證頁面修改憑證密碼。
OK,這些資訊儲存好,接下來需要用到。
本文使用的倉庫地址是: registry.cn-hangzhou.aliyuncs.com/deali/aihub-test
配置 GitHub Action Secret
將阿里雲倉庫的地址、使用者名稱、密碼等資訊配置到 Action Secret 中
這裡我設定的名字是
ALIYUN_DOCKER_REPO
ALIYUN_USERNAME
ALIYUN_PWD
構建映象
編寫 GitHub workflow 配置,細節不再贅述,不清楚的同學可以參考這篇文章: 開發現代化的.NetCore控制檯程式:(4)使用GithubAction自動構建以及釋出nuget包
GitHub 提供的 workflow
這是 GitHub 提供的,我沒有使用這個方案,有興趣的同學可以自行嘗試一下。
# 此工作流使用未經 GitHub 認證的操作。
# 它們由第三方提供,並受
# 單獨的服務條款、隱私政策和支援
# 文件。
# GitHub 建議將操作固定到提交 SHA。
# 若要獲取較新版本,需要更新 SHA。
# 還可以引用標記或分支,但該操作可能會更改而不發出警告。
name: Publish Docker image
on:
release:
types: [published]
jobs:
push_to_registry:
name: Push Docker image to Docker Hub
runs-on: ubuntu-latest
steps:
- name: Check out the repo
uses: actions/checkout@v4
- name: Log in to Docker Hub
uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7
with:
images: my-docker-hub-namespace/my-docker-hub-repository
- name: Build and push Docker image
uses: docker/build-push-action@3b5e8027fcad23fda98b2e3ac259d8d67585f671
with:
context: .
file: ./Dockerfile
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
簡單版 workflow
這是我自己寫的 workflow 比 GitHub 提供的例子簡單一些
name: Docker Image CI
run-name: ${{ github.actor }} is building a image
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Build the Docker image
run: docker build . --file ./next-antd/AIHub.Blazor/Dockerfile --tag ${{ secrets.ALIYUN_DOCKER_REPO }}:${{ github.run_id }}
- name: Login to aliyun docker repository
run: docker login -u ${{ secrets.ALIYUN_USERNAME }} -p ${{ secrets.ALIYUN_PWD }} ${{ secrets.ALIYUN_DOCKER_REPO }}
- name: Push image to aliyun docker repository
run: docker push ${{ secrets.ALIYUN_DOCKER_REPO }}:${{ github.run_id }}
這個 build 步驟,在 GitHub 託管的 runner 上構建 docker 映象,並且推送到私有映象倉庫。
映象的 tag 版本,我使用了 run_id
這個環境變數,代表本次構建任務的唯一ID,例如 1658821493
也可以使用 ${{ github.sha }}
代表這次的 commit sha ,但我覺得太長了就沒有使用
GITHUB_SHA
: 觸發工作流的提交 SHA。 此提交 SHA 的值取決於觸發工作流程的事件。 有關詳細資訊,請參閱“觸發工作流的事件”。 例如,ffac537e6cbbf934b08745a378932722df287a53
。
PS:官方提供的例子使用的是 $(date +%s)
時間戳,但我還沒探索出在環境變數裡拼接字串的方法。
搭建本地 runner
映象構建完事了,還得在本地的伺服器上跑。
很簡單,就是把映象 pull 下來,然後 docker compose up
就行,現在我們把這一步也加入到 workflow 裡。
新增 runner
訪問這個頁面: https://github.com/star-plan/aihub/settings/actions/runners
點選 New self-hosted runner 按鈕
選擇對應的系統和架構,然後根據裡面的命令來就完事了。
本文選擇的是 Linux - x64 的方案
GitHub 提供的命令是這樣(這裡只是給出例子,實際要以自己的專案配置頁面為準)
# Create a folder
$ mkdir actions-runner && cd actions-runner# Download the latest runner package
$ curl -o actions-runner-linux-x64-2.311.0.tar.gz -L https://github.com/actions/runner/releases/download/v2.311.0/actions-runner-linux-x64-2.311.0.tar.gz# Optional: Validate the hash
其中下載這一步可能會遇到問題,因為某些莫名其妙的原因,GitHub 訪問不了,這時候需要使用 proxychains
工具,請自行搜尋 + 配置。
PS:好傢伙,這個 runner 居然有 180M
接著執行命令 runner
./config.sh --url [REPO_URL] --token [TOKEN]
- 提示輸入 runner group 名稱,預設即可
- 提示需要輸入 runner 名字,我輸入了個
aihub
- 接著還可以輸入 label 啥的,根據需求來就行
配置搞定之後就啟動 runner
./run.sh
安裝服務
用 ./run.sh
啟動的 runner 並不穩定,退出登入之後就無了,就算使用 &
或者 Ctrl+Z
切換到後臺執行,也不是那麼穩定。
GitHub runner 可以作為 Linux 服務執行,使用也很簡單。
先關掉正在執行的 runner
ps aux | grep run.sh
# 找到 pid 之後 kill 掉
kill -9 pid
安裝服務
sudo ./svc.sh install
啟動服務
sudo ./svc.sh start
更多操作請檢視第四條參考資料。
檢視 runner 列表
在 GitHub 頁面上檢視當前執行的 runner
地址: https://github.com/star-plan/aihub/settings/actions/runners
可以看到已經有一個叫 aihub 的 runner 了
runner 網路代理
這波我只能說 GitHub 太貼心了!他已經考慮到了不能上 GitHub 的情況,他真的,我哭死?
可以在環境變數裡設定代理,也可以在 .env
檔案裡配置,這裡我還是選 .env
吧。
https_proxy=http://proxy.local:8080
no_proxy=example.com,myserver.local:443
更多關於搭建本地 runner 的文件,可以檢視參考資料的第三條。
使用本地 runner 部署服務
老規矩,使用 docker-compose 來編排服務。
先建立個目錄,裡面存放 docker-compose.yml
和一些持久化的檔案。
本例子的路徑是: /home/apps/aihub
version: '3.6'
services:
web:
image: ${IMAGE}
container_name: aihub-test
restart: always
environment:
- ASPNETCORE_ENVIRONMENT=Production
- ASPNETCORE_URLS=http://+:80
volumes:
- ./aihub.app.db:/app/aihub.app.db
ports:
- "12002:80"
networks:
- default
- swag
networks:
swag:
name: swag
external: true
default:
name: aihub-test
映象部分我用了環境變數,因為每次構建的映象tag都不一樣,我要在 workflow 裡將 tag 寫入 .env
檔案。當然也可以寫固定 latest
。
.env
檔案是這樣
IMAGE=registry.cn-hangzhou.aliyuncs.com/deali/aihub-test:6861065827
OK,總算是到了最後一步,繼續編輯我們的 workflow ,增加一個 deployment
任務。
name: Docker Image CI
run-name: ${{ github.actor }} is building a image
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Build the Docker image
run: docker build . --file ./next-antd/AIHub.Blazor/Dockerfile --tag ${{ secrets.ALIYUN_DOCKER_REPO }}:${{ github.run_id }}
- name: Login to aliyun docker repository
run: docker login -u ${{ secrets.ALIYUN_USERNAME }} -p ${{ secrets.ALIYUN_PWD }} ${{ secrets.ALIYUN_DOCKER_REPO }}
- name: Push image to aliyun docker repository
run: docker push ${{ secrets.ALIYUN_DOCKER_REPO }}:${{ github.run_id }}
deployment:
runs-on: [self-hosted, linux, x64]
needs: [build]
steps:
- name: export environment variant
run: echo 'IMAGE=${{ secrets.ALIYUN_DOCKER_REPO }}:${{ github.run_id }}' > /home/apps/aihub/.env
- name: docker compose deploy
run: docker compose -f /home/apps/aihub/docker-compose.yml --project-directory /home/apps/aihub up -d
注意:
-
jobs 預設是並行執行的,所以需要在 deployment 任務里加上
needs
配置,等 build 完了再部署使用
jobs.<job_id>.needs
標識執行此作業之前必須成功完成的所有作業。 它可以是一個字串,也可以是字串陣列。 如果某個作業失敗或跳過,則所有需要它的作業都會被跳過,除非這些作業使用讓該作業繼續的條件表示式。 如果執行包含一系列相互需要的作業,則故障或跳過將從故障點或跳過點開始,應用於依賴項鍊中的所有作業。 如果希望某個作業在其依賴的作業未成功時也能執行,請在jobs.<job_id>.if
中使用always()
條件表示式。 -
deployment
任務是在本地執行的,所以runs-on
透過多個 label 選擇了本地 runner -
執行
docker compose
命令的時候工作目錄是在 GitHub Action 工具的_work
目錄裡,所以需要指定--project-directory
引數,或者透過--env-file
指定.env
檔案位置
檢視執行結果
在 GitHub Action 頁面可以檢視執行結果。
搞定!
參考資料
- https://docs.github.com/zh/actions/learn-github-actions/variables
- https://docs.github.com/zh/actions/publishing-packages/publishing-docker-images
- https://docs.github.com/zh/actions/hosting-your-own-runners/managing-self-hosted-runners/about-self-hosted-runners
- https://docs.github.com/zh/actions/hosting-your-own-runners/managing-self-hosted-runners/configuring-the-self-hosted-runner-application-as-a-service
- https://docs.github.com/zh/actions/hosting-your-own-runners/managing-self-hosted-runners/using-a-proxy-server-with-self-hosted-runners
- https://docs.github.com/zh/actions/using-jobs/using-jobs-in-a-workflow
- https://docs.docker.com/compose/environment-variables/set-environment-variables/