通過 Github Action 釋出 SpringBoot Docker 專案到雲伺服器

L小庸發表於2022-04-11

一 本文目的

記錄如何通過 Github Action 實現 SpringBoot 專案的自動化釋出。其中 SpringBoot 專案通過 Docker 進行管理。

二 Github Action 需要的主要步驟

  1. 設定 Java 環境並通過 Maven 進行專案構建
  2. 構建 Docker 映象並推送到私有映象倉庫
  3. 登入遠端伺服器,拉取映象並重啟服務

三 設定 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 建立私有映象倉庫如下:

  1. 詳見專案,比如選擇專案名叫 docker-image
  2. 在專案中建立 “製品倉庫”,製品型別選擇 docker,然後起名,設定許可權(可以預設)就 OK
建立制品設定製品資訊
image.pngimage.png

4.1.2 推送、拉取 Docker 映象:

一般通過兩種途徑:命令列或者 CICD 工具。不過哪種,都需要先登入到有專案操作許可權的賬號。

登入

點選操作指引:
image.png

獲取登入令牌:
image.png

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,最基本規則如下

EventRefDocker tags
pushrefs/heads/mainmain
pushrefs/heads/releases/v1releases-v1
push tagrefs/tags/v1.2.3v1.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 中設定:

image.png

這裡要記得把 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 }}

這裡注意幾點:

  1. 使用 ssh key 登入遠端伺服器,主要是 key 不是 password;
  2. 建議直接通過 命令列工具 把所有 key 檔案內容複製到剪貼簿,然後直接複製到 Github repo 的 secrets 中(如果手動複製,記得把註釋也放進去):
# mac 下操作
pbcopy < you_key_file
  1. 重啟服務前要把舊的 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 }}

相關文章