这阵子还挺忙的,上周二飞天津参加第五空间线下赛,回来后周五又是助手那边的面试,周六又是省赛线下赛,这星期又是协会这边的面试。
忙里偷闲抽了点时间继续在看 Redis,目前已经看到原理篇了,了解到了不少有意思的算法与实现原理,整本书看完后会写点东西总结一下。近期助手的 GitHub 教师认证通过了,并开通了团队的一堆福利。考虑到 GitHub 这边的工单系统更加完善,因而逐渐开始从自建 Gitea 逐步转移到 GitHub。CI 也由原来的 Drone,转到 GitHub Actions。
因此今天就想聊聊 GitHub Actions。它是 GitHub 今年新推出的可用于创建项目自动化构建工作流,说白了就是 GitHub 上也可以做 CI/CD 了。Circle CI 和 Travis CI 瑟瑟发抖
GitHub Actions 对于个人的免费额度是一个月 3000 分钟。对于个人项目足足有余。那么说干就干,来试试用 GitHub Actions 编译 Golang 后构建 Docker 镜像并发布到阿里云镜像仓库。
YML 格式
点击项目 Repo 上方的 Actions 进入面板。在这里可以看到项目的构建情况。
Build & Deploy/.github/workflows
name: Build & Deploy
on: [push]
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
....
dockerfile:
....
nameBuild & Deployonpush
jobsjobssteps
这里使用 Drone CI 来进行对比说明。
.drone.ymlpipeline
Job
A defined task made up of steps. Each job is run in a fresh instance of the virtual environment. You can define the dependency rules for how jobs run in a workflow file. Jobs can run at the same time in parallel or be dependent on the status of a previous job and run sequentially. For example, a workflow can have two sequential jobs that build and test code, where the test job is dependent on the status of the build job. If the build job fails, the test job will not run.
而 Jobs 下面又有许多 Steps,这才是我们 Drone CI 中的一个个遵循从上到下进行执行的“步骤”。
使用预制的 Golang 构建
现在我们开始编写自动编译 Golang 代码的配置,这里直接使用 GitHub Actions 的预制构建即可。
name: Build & Deploy
on: [push]
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- name: Set up Go 1.12
uses: actions/setup-go@v1
with:
go-version: 1.12
id: go
- name: Check out code into the Go module directory
uses: actions/checkout@v1
- name: Get dependencies
run: |
go get -v -t -d ./...
if [ -f Gopkg.toml ]; then
curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
dep ensure
fi
- name: Build
run: |
go build -v .
pwd
- name: Archive production artifacts
uses: actions/upload-artifact@v1
with:
name: drone_test
path: /home/runner/work/drone_test/drone_test
这里个 Job 里共有四个 Steps,前面三个都是 GitHub Actions 预制的,第四个是我自己加的,这里聊一下第四个配置 —— Artifact。
Artifact
ArtifactArtifactArtifactactions/upload-artifactwith
打包 Docker 镜像
needs
dockerfile:
name: Build Image
runs-on: ubuntu-latest
needs: build
steps:
- name: Get artifacts
uses: actions/download-artifact@master
with:
name: drone_test
path: /home/runner/work/drone_test/drone_test
needs: builddockerfilebuildactions/download-artifact
二进制文件打包进镜像并发布
jerray/publish-docker-action
起初是想通过环境变量动态生成 Tag,但折腾了两三天后依然无果。便开始自己魔改这个 Action。
魔改 publish-docker-action
helper.goresolveSemanticVersionTag
func getFormatTag(tagFormat string) []string{
tags := make([]string, 0)
t := tagFormat
t = strings.Replace(t, "%TIMESTAMP%", strconv.Itoa(int(time.Now().Unix())), -1) // %TIMESTAMP% Timestamp
t = strings.Replace(t, "%YYYY%", strconv.Itoa(time.Now().Year()), -1) // %YYYY% Year
t = strings.Replace(t, "%MM%", strconv.Itoa(int(time.Now().Month())), -1) // %MM% Month
t = strings.Replace(t, "%DD%", strconv.Itoa(time.Now().Day()), -1) // %DD% Day
t = strings.Replace(t, "%H%", strconv.Itoa(time.Now().Hour()), -1) // %H% Hour
t = strings.Replace(t, "%m%", strconv.Itoa(time.Now().Minute()), -1) // %m% Minute
t = strings.Replace(t, "%s%", strconv.Itoa(time.Now().Second()), -1) // %s% Second
tags = append(tags, t)
return tags
}
这里将传入的字符串中的格式化符号进行替换,然后返回。
同时在上面的代码中:
tags := make([]string, 0)
// Add `latest` version
tags = append(tags, "latest")
tags = append(tags, getFormatTag(inputs.TagFormat)...)
inputs.Tags = tags
latestTagFormataction.yml
tag_format:
description: 'Set the tag format'
default: '%TIMESTAMP%'
action.yml
发布!!
最后一步,就是使用我魔改后的 Action 进行发布部署啦~
- name: Build & Publish to Registry
uses: wuhan005/publish-docker-action@master
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
registry: registry.cn-hongkong.aliyuncs.com
repository: registry.cn-hongkong.aliyuncs.com/eggplant/drone-test
tag_format: "%YYYY%_%MM%_%DD%_%H%%m%%s%"
auto_tag: true
secrets
最终配置
name: Build & Deploy
on: [push]
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- name: Set up Go 1.12
uses: actions/setup-go@v1
with:
go-version: 1.12
id: go
- name: Check out code into the Go module directory
uses: actions/checkout@v1
- name: Get dependencies
run: |
go get -v -t -d ./...
if [ -f Gopkg.toml ]; then
curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
dep ensure
fi
- name: Build
run: |
go build -v .
pwd
- name: Archive production artifacts
uses: actions/upload-artifact@v1
with:
name: drone_test
path: /home/runner/work/drone_test/drone_test
dockerfile:
name: Build Image
runs-on: ubuntu-latest
needs: build
steps:
- name: Get artifacts
uses: actions/download-artifact@master
with:
name: drone_test
path: /home/runner/work/drone_test/drone_test
- name: Build & Publish to Registry
uses: wuhan005/publish-docker-action@master
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
registry: registry.cn-hongkong.aliyuncs.com
repository: registry.cn-hongkong.aliyuncs.com/eggplant/drone-test
tag_format: "%YYYY%_%MM%_%DD%_%H%%m%%s%"
auto_tag: true
总结
每一次配 CI 都要踩好多坑啊……不过最后总算是搞定了!
不过还没结束,把镜像发布到阿里云镜像仓库后还要部署到服务器上,同时还要能实现版本回滚。这些我打算是使用 Swarm + Portainer 实现。
但这俩东西还只是听说过,自己还没实际用过。学校图书馆好像有 Swarm 的书,等把手头这本 Redis 的看完就去借!