缘起

希望通过自动构建把 Golang 项目发布至ECS自建的Docker中。

步骤

Github Action

Github 的Action 中有不少成型的 workflow 可以参考。 Github Action 利用这些模板可以快速让你了解 Action 语法,如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# workflow名称
name: build
# 触发条件,当main分支有推送时或者main分支有pull request 时
on:
  push:
    branches: [ main ] 
  pull_request:
    branches: [ main ]

jobs:
#具体执行内容
  build:
#基于ubuntu操作系统
    runs-on: ubuntu-latest
    steps:
#步骤 签出代码
# 类似这种uses,可以通过 https://github.com/actions/checkout 查看其详情
    - uses: actions/checkout@v2

    - name: Set up Go
      uses: actions/setup-go@v2
# action 中可以自定义一些参数 通过with形式传入
      with:
        go-version: 1.17

    - name: Build
      run: go build -v ./...

    - name: Test
      run: go test -v ./...

看上面的注释你应该有了大概的了解,更多入门操作可以看阮一峰的GitHub Actions 入门教程 或者官方的快速入门 我在这里走的弯路主要是【以为所有的操作都可以通过增加 steps 下的name 来完成】。 按照这个思路 将 steps 组织成了这样,checkout -> setup-go -> build go -> docker build -> acr login-> docker push -> ssh login ->docker run 以上都是伪代码,大致就是 把代码签出来安装go 依赖,然后build 成可执行文件。将可执行文件 build 至docker 镜像中,然后推送至镜像仓库,最后使用ssh登录到ECS 运行镜像。 结果在docker build 步骤一直报错。可能是我理解问题,以为所有的执行步骤都是在同一个文件夹下执行,所以试图在 go build 出 可执行文件时,直接复制至Docker 中然后打包成镜像,直接推送至阿里云镜像仓库。然而一直没能成功就转向了使用Dockerfile build。

构建镜像

最终的dockerfile如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
FROM golang:1.17 AS builder
# 为我们的镜像设置必要的环境变量
ENV GO111MODULE=on \
    CGO_ENABLED=0 \
    GOOS=linux \
    GOARCH=amd64 \
    GOPROXY=https://goproxy.cn,direct

WORKDIR /builder

# 将代码复制到容器中
COPY . .
# 安装依赖
RUN go mod download

# 将我们的代码编译成二进制可执行文件 app
RUN go build -o app .

###################
# 接下来创建一个小镜像
###################
FROM scratch

# 从builder镜像中把/dist/app 拷贝到当前目录
COPY --from=builder /builder/app /

# 需要运行的命令
ENTRYPOINT ["/app"]

因为没有能够在 github Action中实现build image,所以只能利用dockerfile 先在本地先build完再推向镜像仓库了。

推送镜像至ACR

因为阿里云有发布 acr-login,就直接使用了。其中的secrets.ACCESSKEY_ID和secrets.ACCESSKEY_SECRET 设置路径在 github 对应 repository 的 Settings -> Secrets -> Actions -> New repository secret。 Vaule 来自阿里云的头像下拉列表的AccessKey管理菜单。 aliyun/acr-login 还提供了private registry 的登录方式,一定不要选错了。

1
2
3
4
5
6
		- name: Login to ACR
        uses: aliyun/acr-login@v1
        with:
          region-id: cn-beijing
          access-key-id: "${{ secrets.ACCESSKEY_ID }}"
          access-key-secret: "${{ secrets.ACCESSKEY_SECRET }}"

这里在列举几个你可能用到的action

拉取镜像并运行

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
      - name: Server Start
        uses: appleboy/ssh-action@master
        with:
            host: ${{ secrets.HOST }}
            username: ${{ secrets.USERNAME }}
            password: ${{ secrets.PASSWORD }}
            port: ${{ secrets.PORT }}
            script: |
              docker pull registry.cn-beijing.aliyuncs.com/gudao2008/${{ env.IMAGE_Name }}:${{ env.IMAGE_TAG }}
              docker container stop ${{ env.IMAGE_Name}} || echo "停止容器[${{ env.IMAGE_Name}}]异常"
              docker container rm ${{ env.IMAGE_Name}} || echo "删除容器[${{ env.IMAGE_Name}}]异常"
              docker container run -d --name ${{ env.IMAGE_Name }} -it registry.cn-beijing.aliyuncs.com/gudao2008/${{ env.IMAGE_Name }}:${{ env.IMAGE_TAG }}

在这一步遇到的问题是在执行时一直报错,先是因为这里的script 参数不同与action run里的参数格式。action中直接使用$.变量就行了,在script中需要用全局的 ${{}} 包裹才行。 后就开始报20***/03/08 02:25:11 dial tcp ***:***: i/o timeout 超时错误,以为是拉取镜像操作超时呢,把镜像仓库都改成了公开,还是无果。后来尝试在服务器上直接运行看是否能成功,结果成功了。才把原因定位到ssh登录超时,改了ECS安全组策略后终于成功了。

最后

完整的cli.yml如下,配合上面的dockerfile是可以直接运行的。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
name: Go

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]
env:
  IMAGE_TAG: ${{ github.sha }}
  IMAGE_Name: go_template
jobs:

  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2

      #      - name: Set up Go
#        uses: actions/setup-go@v2
#        with:
#          go-version: 1.17
#
#      - name: Build
#        run: go build -o app .

      #登录
      - name: Login to ACR
        uses: aliyun/acr-login@v1
        with:
          region-id: cn-beijing
          access-key-id: "${{ secrets.GUDAO_ACCESSKEY_ID }}"
          access-key-secret: "${{ secrets.GUDAO_ACCESSKEY_SECRET }}"

      #打包
      - name: Build and push image
        run: |
          docker build -t registry.cn-beijing.aliyuncs.com/gudao2008/$IMAGE_Name:$IMAGE_TAG .
          docker push registry.cn-beijing.aliyuncs.com/gudao2008/$IMAGE_Name:$IMAGE_TAG

      # 发布 镜像仓库改为公开
      - name: Server Start
        uses: appleboy/ssh-action@master
        with:
            host: ${{ secrets.HOST }}
            username: ${{ secrets.USERNAME }}
            password: ${{ secrets.PASSWORD }}
            port: ${{ secrets.PORT }}
            script: |
              docker pull registry.cn-beijing.aliyuncs.com/gudao2008/${{ env.IMAGE_Name }}:${{ env.IMAGE_TAG }}
              docker container stop ${{ env.IMAGE_Name}} || echo "停止容器[${{ env.IMAGE_Name}}]异常"
              docker container rm ${{ env.IMAGE_Name}} || echo "删除容器[${{ env.IMAGE_Name}}]异常"
              docker container run -d --name ${{ env.IMAGE_Name }} -it registry.cn-beijing.aliyuncs.com/gudao2008/${{ env.IMAGE_Name }}:${{ env.IMAGE_TAG }}

参考