利用 GitHub Action 自動釋出 Docker

crossoverJie 發表於 2021-03-29
Docker Github

前言

最近公司內部專案的釋出流程接入了 GitHub Actions,整個體驗過程還是比較美好的;本文主要目的是對於沒有還接觸過 GitHub Actions的新手,能夠利用它快速構建自動測試及打包推送 Docker 映象等自動化流程。

建立專案

本文主要以 Go 語言為例,當然其他語言也是類似的,與語言本身關係不大。

這裡我們首先在 GitHub 上建立一個專案,編寫了幾段簡單的程式碼 main.go

var version = "0.0.1"

func GetVersion() string {
	return version
}

func main() {
	fmt.Println(GetVersion())
}
複製程式碼

內容非常簡單,只是列印了了版本號;同時配套了一個單元測試 main_test.go

func TestGetVersion1(t *testing.T) {
	tests := []struct {
		name string
		want string
	}{
		{name: "test1", want: "0.0.1"},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := GetVersion(); got != tt.want {
				t.Errorf("GetVersion() = %v, want %v", got, tt.want)
			}
		})
	}
}
複製程式碼

我們可以執行 go test 執行該單元測試。

$ go test                          
PASS
ok      github.com/crossoverJie/go-docker       1.729s
複製程式碼

自動測試

當然以上流程完全可以利用 Actions 自動化搞定。

首選我們需要在專案根路徑建立一個 .github/workflows/*.yml 的配置檔案,新增如下內容:

name: go-docker
on: push
jobs:
  test:
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags')
    steps:
      - uses: actions/[email protected]
      - name: Run Unit Tests
        run: go test
複製程式碼

簡單解釋下:

  • name 不必多說,是為當前工作流建立一個名詞。
  • on 指在什麼事件下觸發,這裡指程式碼發生 push 時觸發,更多事件定義可以參考官方文件:

Events that trigger workflows

  • jobs 則是定義任務,這裡只有一個名為 test 的任務。

該任務是執行在 ubuntu-latest 的環境下,只有在 main 分支有推送或是有 tag 推送時執行。

執行時會使用 actions/[email protected] 這個由他人封裝好的 Action,當然這裡使用的是由官方提供的拉取程式碼 Action

  • 基於這個邏輯,我們可以靈活的分享和使用他人的 Action 來簡化流程,這點也是 GitHub Action擴充套件性非常強的地方。

最後的 run 則是執行自己命令,這裡自然就是觸發單元測試了。

  • 如果是 Java 便可改為 mvn test.

之後一旦我們在 main 分支上推送程式碼,或者有其他分支的程式碼合併過來時都會自動執行單元測試,非常方便。

利用 GitHub Action 自動釋出 Docker

利用 GitHub Action 自動釋出 Docker

與我們本地執行效果一致。

自動釋出

接下來考慮自動打包 Docker 映象,同時上傳到 Docker Hub;為此首先建立 Dockerfile

FROM golang:1.15 AS builder
ARG VERSION=0.0.10
WORKDIR /go/src/app
COPY main.go .
RUN go build -o main -ldflags="-X 'main.version=${VERSION}'" main.go

FROM debian:stable-slim
COPY --from=builder /go/src/app/main /go/bin/main
ENV PATH="/go/bin:${PATH}"
CMD ["main"]
複製程式碼

這裡利用 ldflags 可在編譯期間將一些引數傳遞進打包程式中,比如打包時間、go 版本、git 版本等。

這裡只是將 VERSION 傳入了 main.version 變數中,這樣在執行時就便能取到了。

docker build -t go-docker:last .
docker run --rm go-docker:0.0.10
0.0.10
複製程式碼

接著繼續編寫 docker.yml 新增自動打包 Docker 以及推送到 docker hub 中。

deploy:
    runs-on: ubuntu-latest
    needs: test
    if: startsWith(github.ref, 'refs/tags')
    steps:
      - name: Extract Version
        id: version_step
        run: |
          echo "##[set-output name=version;]VERSION=${GITHUB_REF#$"refs/tags/v"}"
          echo "##[set-output name=version_tag;]$GITHUB_REPOSITORY:${GITHUB_REF#$"refs/tags/v"}"
          echo "##[set-output name=latest_tag;]$GITHUB_REPOSITORY:latest"

      - name: Set up QEMU
        uses: docker/[email protected]

      - name: Set up Docker Buildx
        uses: docker/[email protected]

      - name: Login to DockerHub
        uses: docker/[email protected]
        with:
          username: ${{ secrets.DOCKER_USER_NAME }}
          password: ${{ secrets.DOCKER_ACCESS_TOKEN }}

      - name: PrepareReg Names
        id: read-docker-image-identifiers
        run: |
          echo VERSION_TAG=$(echo ${{ steps.version_step.outputs.version_tag }} | tr '[:upper:]' '[:lower:]') >> $GITHUB_ENV
          echo LASTEST_TAG=$(echo ${{ steps.version_step.outputs.latest_tag  }} | tr '[:upper:]' '[:lower:]') >> $GITHUB_ENV

      - name: Build and push Docker images
        id: docker_build
        uses: docker/[email protected]
        with:
          push: true
          tags: |
            ${{env.VERSION_TAG}}
            ${{env.LASTEST_TAG}}
          build-args: |
            ${{steps.version_step.outputs.version}}
複製程式碼

新增了一個 deploy 的 job。

    needs: test
    if: startsWith(github.ref, 'refs/tags')
複製程式碼

執行的條件是上一步的單測流程跑通,同時有新的 tag 生成時才會觸發後續的 steps

name: Login to DockerHub

在這一步中我們需要登入到 DockerHub,所以首先需要在 GitHub 專案中配置 hub 的 user_name 以及 access_token.

利用 GitHub Action 自動釋出 Docker

利用 GitHub Action 自動釋出 Docker

配置好後便能在 action 中使用該變數了。

利用 GitHub Action 自動釋出 Docker

這裡使用的是由 docker 官方提供的登入 action(docker/login-action)。

有一點要非常注意,我們需要將映象名稱改為小寫,不然會上傳失敗,比如我的名稱中 J 字母是大寫的,直接上傳時就會報錯。

利用 GitHub Action 自動釋出 Docker

所以在上傳之前先要執行該步驟轉換為小寫。

利用 GitHub Action 自動釋出 Docker

最後再用這兩個變數上傳到 Docker Hub。

利用 GitHub Action 自動釋出 Docker

利用 GitHub Action 自動釋出 Docker

今後只要我們打上 tag 時,Action 就會自動執行單測、構建、上傳的流程。

總結

GitHub Actions 非常靈活,你所需要的大部分功能都能在 marketplace 找到現成的直接使用,

比如可以利用 ssh 登入自己的伺服器,執行一些命令或指令碼,這樣想象空間就很大了。

使用起來就像是搭積木一樣,可以很靈活的完成自己的需求。

參考連結:

How to Build a CI/CD Pipeline with Go, GitHub Actions and Docker