一 本文目的
記錄如何通過 Github Action 實現 SpringBoot 專案的自動化釋出。其中 SpringBoot 專案通過 Docker 進行管理。
二 Github Action 需要的主要步驟
- 設定 Java 環境並通過 Maven 進行專案構建
- 構建 Docker 映象並推送到私有映象倉庫
- 登入遠端伺服器,拉取映象並重啟服務
三 設定 Java 環境並通過 Maven 進行專案構建
這裡使用 JDK 11 並設定 maven 快取加快構建時間。其中 -DskipTests=true
表示構建時跳過測試。如果需要測試可以刪掉這個引數。
- name: Set up JDK 11
uses: actions/setup-java@v3
with:
java-version: '11'
distribution: 'temurin'
cache: maven
- name: Build with Maven
run: mvn -B -DskipTests=true package --file pom.xml
四 構建 Docker 映象並推送到私有映象倉庫
這裡主要解決的問題是怎麼選擇 Docker 私有映象,選擇有下
名稱 | 優點 | 缺點 |
---|---|---|
Github Package | 免費,和 Github 高度整合,配置方便 | 我國大陸雲伺服器連線 Github 太 TM 慢 |
Docker 官方 | Docker 官方,值得信賴,大陸雲伺服器連線速度 OK | 只支援一個私有映象倉庫,多了要錢 |
Coding.net | 大陸雲伺服器連線速度快,免費,被騰訊收了,服務應該不會中斷 | 配置略微複雜但可以接受 |
我最終的選擇是 Coding.net,但如果雲伺服器允許,我會選擇 Github Package。
4.1 Coding.net 私有映象倉庫
4.1.1 通過 Coding.net 建立私有映象倉庫如下:
- 詳見專案,比如選擇專案名叫 docker-image
- 在專案中建立 “製品倉庫”,製品型別選擇 docker,然後起名,設定許可權(可以預設)就 OK
建立制品 | 設定製品資訊 |
---|---|
4.1.2 推送、拉取 Docker 映象:
一般通過兩種途徑:命令列或者 CICD 工具。不過哪種,都需要先登入到有專案操作許可權的賬號。
登入
點選操作指引:
獲取登入令牌:
docker login \
-u random-user-name \
-p random-passworkd \
coding-username-docker.pkg.coding.net
這裡注意,雖然生成的是隨機 username 和 password,但確實長期有效,所以可以放在 Gihub repo 的 secrets 中供 Github Action 使用。
推送/拉取映象
推送:
docker push \
coding-username-docker.pkg.coding.net/docker-image/test-service/<PACKAGE>:<VERSION>
推送前確保已經有了這個 coding-username-docker.pkg.coding.net/docker-image/test-service/<PACKAGE>:<VERSION>
這個 tag,沒有的話要打標籤。
docker tag \
<LOCAL_IMAGE_TAG> coding-username-docker.pkg.coding.net/docker-image/test-service/<PACKAGE>:<VERSION>
拉取:
docker pull \
coding-username-docker.pkg.coding.net/docker-image/test-service/<PACKAGE>:<VERSION>
4.1.3 在 Github Action 中設定 Coding.net 私有映象倉庫
# 登入
- name: Log in to the Coding docker registry
uses: docker/login-action@v1
with:
registry: ${{ env.REGISTRY }} # REGISTRY 為 GHA 環境變數
username: ${{ secrets.CODING_USER }} # coding.net 的 random-user-name,設定在 GHA 的 secrets 中
password: ${{ secrets.CODING_TOKEN }} # coding.net 的 random-password
# 設定 image 名稱
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v3
with:
images: ${{ env.IMAGE_NAME_TOTAL_NAME }}
# 推送
- name: Build and push Docker image
uses: docker/build-push-action@v2
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
Docker tags 設定規則可以參考 docker/metadata-action,最基本規則如下
Event | Ref | Docker tags |
---|---|---|
push | refs/heads/main | main |
push | refs/heads/releases/v1 | releases-v1 |
push tag | refs/tags/v1.2.3 | v1.2.3, latest |
這裡注意,如果是 Push Github tag
,那麼會預設生成兩個 Docker tag(Perfect!)。
如果我們想要 push tag 後觸發 GHA,需要在 workflow 中新增
on:
push:
tags: # tags 更新時觸發 workflow
- 'v*'
4.2 Github Package 私有映象倉庫
首先 Github Package 不一定是 Docker 映象,可以去官網瞭解更多,這裡只介紹利用 Github package 配合 Github 的 Container registry 建立私有 Docker 映象倉庫。
完整步驟可以參考 Container registry,流程和 Coding.net 類似:登入後然後推送、拉取。
4.2.1 登入 Container registry
推薦使用 GITHUB_TOKEN,在 Settings/Developer settings/Personal access tokens
中設定:
這裡要記得把 Token 儲存下來,不然後面就找不到了。
獲取 Token 後登入到 Contianer Registry
$ export CR_PAT=YOUR_TOKEN # 儲存 token 到變數 CR_PAT 中
$ echo $CR_PAT | docker login ghcr.io -u USERNAME --password-stdin
> Login Succeeded
4.2.2 Push/Pull 映象
# push
$ docker push ghcr.io/OWNER/IMAGE_NAME:TAG
# pull
docker pull ghcr.io/OWNER/IMAGE_NAME:TAG
4.2.3 GHA 中的設定
可以直接用 github
本身變數,比如 github.actor
作為 username。和 coding.net 重複部分不再贅述。
- name: Log in to the Container registry
uses: docker/login-action@v1
with:
registry: ${{ env.REGISTRY }} # 為 ghcr.io
username: ${{ github.actor }} # 可以直接讀取當前 Github 觸發事件的使用者
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v3
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} # IMAGE_NAME 為 ${{ github.repository }}
- name: Build and push Docker image
uses: docker/build-push-action@v2
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
五 登入遠端伺服器,拉取映象並重啟服務
這裡使用了 appleboy/ssh-action,它的有點事可以直接在 GHA workflow 中配置登入遠端伺服器後需要執行的指令碼。
- name: executing remote ssh commands using password
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.REMOTE_HOST }}
username: ${{ secrets.REMOTE_USER }}
# 通過 ssh key 登入
key: ${{ secrets.REMOTE_ACCESS_TOKEN }}
script: |
docker login -u ${{ secrets.CODING_USER }} -p ${{ secrets.CODING_TOKEN }} ${{ env.REGISTRY }}
docker pull ${{ env.IMAGE_TOTAL_NAME }}:${{ env.IMAGE_TAG }}
docker stop ${{ env.CONTAINER_NAME }}
docker rm ${{ env.CONTAINER_NAME }}
docker run -d \
--name ${{ env.CONTAINER_NAME }} \
-p ${{ secrets.EXPOSED_PORT }}:${{ secrets.EXPOSED_PORT }} \
--link ${{ secrets.DATABASE_CONTAINER_NAME }}:${{ secrets.DATABASE_CONTAINER_NAME }} \
${{ env.IMAGE_TOTAL_NAME }}:${{ env.IMAGE_TAG }}
這裡注意幾點:
- 使用 ssh key 登入遠端伺服器,主要是
key
不是password
; - 建議直接通過 命令列工具 把所有 key 檔案內容複製到剪貼簿,然後直接複製到 Github repo 的 secrets 中(如果手動複製,記得把註釋也放進去):
# mac 下操作
pbcopy < you_key_file
- 重啟服務前要把舊的 Docker container 刪掉。
六 完整 Github Actions YAML 檔案
name: GHA CI
# Controls when the workflow will run
on:
# Triggers the workflow on push or pull request events but only for the main branch
push:
# branches: [ main ]
tags: # tags 更新時觸發 workflow
- 'v*'
# pull_request:
# branches: [ main ]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
env:
IMAGE_TOTAL_NAME: ${{ secrets.CODING_REGISTRY }}/docker-image/${{ secrets.SERVICE_CONTAINER_NAME }}/${{ secrets.SERVICE_CONTAINER_NAME }}
IMAGE_TAG: latest
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
# maven build
- name: Set up JDK 11
uses: actions/setup-java@v3
with:
java-version: '11'
distribution: 'temurin'
cache: maven
- name: Build with Maven
run: mvn -B -DskipTests=true package --file pom.xml
# coding.net docker repo
- name: Log in to the Coding docker registry
uses: docker/login-action@v1
with:
registry: ${{ secrets.CODING_REGISTRY }}
username: ${{ secrets.CODING_USER }}
password: ${{ secrets.CODING_TOKEN }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v3
with:
images: ${{ env.IMAGE_TOTAL_NAME }}
- name: Build and push Docker image
uses: docker/build-push-action@v2
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
# pull new image and restart service
- name: executing remote ssh commands using password
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.REMOTE_HOST }}
username: ${{ secrets.REMOTE_USER }}
key: ${{ secrets.REMOTE_ACCESS_TOKEN }}
script: |
docker login -u ${{ secrets.CODING_USER }} -p ${{ secrets.CODING_TOKEN }} ${{ secrets.CODING_REGISTRY }}
docker pull ${{ env.IMAGE_TOTAL_NAME }}:${{ env.IMAGE_TAG }}
docker stop ${{ secrets.SERVICE_CONTAINER_NAME }}
docker rm ${{ secrets.SERVICE_CONTAINER_NAME }}
docker run -d \
--name ${{ secrets.SERVICE_CONTAINER_NAME }} \
-p ${{ secrets.EXPOSED_PORT }}:${{ secrets.EXPOSED_PORT }} \
--link ${{ secrets.DATABASE_CONTAINER_NAME }}:${{ secrets.DATABASE_CONTAINER_NAME }} \
${{ env.IMAGE_TOTAL_NAME }}:${{ env.IMAGE_TAG }}