一个标准的Go自动化流水线通常包含有:依赖下载、代码检查(Lint)、单元测试、编译构建、以及工件(Artifacts)保存。
一、核心流程设计
典型的Go流水线分为三个主要阶段:
1. Test:运行静态检查、代码格式化检查和单元测试。
2. Build:编译生成容器镜像,并把镜像push等到镜像服务器。
3. Deploy/Release:从镜像服务器拉取镜像,部署到目标服务器上。
二、编写.gitlab-ci.yml配置文件
在项目根目录下创建 .gitlab-ci.yml文件。以下是我们经常使用的示例:
# GitLab CI/CD 配置文件
# 定义整个CI/CD流水线的配置,包含安全扫描、构建、测试、部署和发布等完整流程
# 缓存配置
cache:
# 指定需要缓存的路径列表
paths:
# 缓存Go模块的下载包,加速后续构建
- .go/pkg/mod
# 缓存Go构建缓存
- .go/cache
# 定义流水线的阶段顺序
stages:
- gosec # 静态代码安全扫描阶段
- test # 测试阶段
- build # 构建阶段
- post-build # 构建后阶段(主要用于镜像扫描)
- deploy-test # 部署测试阶段(主要用于动态应用安全测试)
- deploy # 部署阶段
# - coverage # 代码覆盖率阶段(已注释掉,暂不启用)
- release # 发布阶段
# 静态应用程序安全测试任务
sast_gosec:
# 使用最新的gosec安全扫描镜像
image: securego/gosec:latest
# 指定任务所属的阶段
stage: gosec
# 任务执行的脚本命令
script:
# 使用gosec对当前目录下所有Go代码进行高严重级别的安全扫描
- gosec -severity high ./...
# 指定运行此任务的GitLab Runner标签
tags:
- runner-from-186
# 指定触发条件:只有在合并请求时运行
only:
- merge_requests
# 测试任务定义
test:
# 使用指定的Golang Docker镜像
image: "172.16.2.100:5000/golang:v1.21.0"
# 指定任务所属的阶段
stage: test
# 任务执行的脚本命令
script:
# 设置Go模块模式为开启
- go env -w GO111MODULE=on
# 设置Go代理,加速依赖下载
- go env -w GOPROXY=http://172.16.2.100:8080,direct
# 安装swag工具,用于生成API文档
- go install github.com/swaggo/swag/cmd/swag@v1.8.10
# 格式化所有Go代码
- go fmt $(go list ./... )
# 生成API文档
- swag init
# 检查代码中的常见错误
- go vet $(go list ./... )
# 进入tests目录并运行简短测试
- cd tests && go test -short `go list ./..`
- go test -race
# 指定运行此任务的GitLab Runner标签
tags:
- runner-from-186
# 指定触发条件:只有在合并请求时运行
only:
- merge_requests
# 构建任务定义
build:
# 使用Docker构建镜像
image: "172.16.2.100:5000/docker:20.10.16"
# 指定任务所属的阶段
stage: build
# 任务执行的脚本命令
script:
# 登录到私有Docker仓库
- docker login 172.16.2.100:7000 -u gitlab+deploy-token-10 -p Yyk8uTN9ijNvzMWVzFjy
# 构建Docker镜像,传递构建参数BUILDTAG为"swag",限制CPU使用权重
- docker build --build-arg BUILDTAG="swag" -t 172.16.2.100:7000/codee_jun/api-server --cpu-shares 2 .
# 推送镜像到仓库
- docker push 172.16.2.100:7000/codee_jun/api-server
# 退出Docker登录
- docker logout 172.16.2.100:7000
# 指定运行此任务的GitLab Runner标签
tags:
- runner-from-186
# 指定触发条件:只有在dev分支变更时运行
only:
- dev
# 容器镜像安全扫描任务
container_scan:
# 指定任务所属的阶段
stage: post-build
# 使用trivy镜像扫描工具的最新版本
image:
name: aquasec/trivy:latest
entrypoint: [""] # 清空镜像的默认入口点
# 任务执行的脚本命令
script:
# 使用trivy扫描镜像,只检查CRITICAL级别的漏洞,发现漏洞则退出码为1
- trivy image --severity CRITICAL --exit-code 1 $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
# 指定运行此任务的GitLab Runner标签
tags:
- runner-from-186
# 指定触发条件:只有在dev分支变更时运行
only:
- dev
# 动态应用程序安全测试任务
dast_scan:
# 指定任务所属的阶段
stage: deploy-test
# 使用OWASP ZAP动态应用安全测试工具镜像
image: owasp/zap2docker-stable
# 任务执行的脚本命令
script:
# 创建工作目录
- mkdir /zap/wrk
# 使用ZAP API扫描工具,基于OpenAPI规范扫描API
# -t 指定扫描目标(swagger.json文件)
# -f 指定文件格式为openapi
# -r 生成HTML格式的报告
- zap-api-scan.py -t ./docs/swagger.json -f openapi -r report.html
# 定义任务产物
artifacts:
# 产物存储路径
paths:
- report.html # 保存安全扫描报告
# 指定运行此任务的GitLab Runner标签
tags:
- runner-from-186
# 指定触发条件:只有在dev分支变更时运行
only:
- dev
# 部署任务定义
deploy:
# 使用SSH工具镜像
image: 172.16.2.100:5000/ssh_image:v1
# 指定任务所属的阶段
stage: deploy
# 定义任务变量
variables:
# SSH连接命令,通过端口1022连接到publisher用户
SSH_COMMAND: ssh -p 1022 publisher@172.16.2.100
# 任务执行的脚本命令
script:
# 通过SSH连接到远程服务器执行部署命令
- $SSH_COMMAND 'docker login 172.16.2.100:7000 -u gitlab+deploy-token-17 -p Yyk8uTN9ijNvzMWVzFjy
&& docker pull 172.16.2.100:7000/codee_jun/api-server
&& docker rm -f api-server || true
&& docker run -d -p 8081:80 -v /data/golang-project/api-server/runtime/logs/:/runtime/logs -v /data/golang-projects/api-server/assets:/assets --name api-server --restart=always 172.16.2.100:7000/codee_jun/api-server
&& docker logout 172.16.2.100:7000'
# 指定运行此任务的GitLab Runner标签
tags:
- runner-from-186
# 指定触发条件:只有在dev分支变更时运行
only:
- dev
# 发布任务定义
release:
# 指定任务所属的阶段
stage: release
# 使用GoReleaser镜像
image:
# 镜像地址
name: 172.16.2.100:5000/goreleaser:v1
# 清空镜像的默认入口点
entrypoint: ['']
# 定义任务变量
variables:
# 设置Git克隆深度,0表示克隆完整历史
GIT_DEPTH: 0
# 定义任务产物
artifacts:
# 产物存储路径
paths:
- dist/ # 发布产物目录
# 产物过期时间
expire_in: "1 week"
# 任务执行的脚本命令
script:
# 准备SSH密钥
- mkdir -p /root/.ssh/ && cp -r ./ssh/* /root/.ssh/
- chmod -R 0600 /root/.ssh
# 克隆公共库
- rm -rf ../go-common
- git clone ssh://git@172.16.2.101:1022/codee_jun/go-common.git ../go-common
# 更新公共库到最新
- cd ../go-common && git fetch --all && git reset --hard origin/main && git pull --force
# 返回项目目录
- cd ../api-server
# 使用GoReleaser发布(快照模式),发布后清理
- goreleaser release --snapshot --clean
# 执行发布脚本,传递Git标签作为参数
- sh release.sh $CI_COMMIT_TAG
# 指定运行此任务的GitLab Runner标签
tags:
- runner-from-186
# 指定触发条件:只有在打标签时运行
only:
- tags
三、关键优化点说明
缓存:Go 项目构建最耗时的是下载依赖包(go mod download)。通过设置 GOMODCACHE 并将其包含在 GitLab 的 cache 路径中,流水线在后续运行时可以直接读取已下载的包,减少 50% 以上的构建时间。
产物:在 build 阶段使用 artifacts 关键字。这样编译出的二进制文件会上传到 GitLab 服务器,你可以直接从 GitLab UI 下载,或者在后续的部署阶段(如推送到生产服务器)直接使用这些文件。
四、常见问题:Gitlab Runner配置
Gitlab配置Runner的位置在项目->设置->CI/CD->Runner.
要运行上述流水线,需要配置一个正确的Gitlab Runner:
Executor:建议使用docker模式,因为它可以隔离环境并轻松指定不同的Go版本。
Privileged:如果你的流水线最后需要构建Docker镜像(Docker-in-Docker),Runner需要开启Privileged=true模式。
在多个项目里,尽量给每个项目配置不同的Runner,流水线相当耗CPU,如果同时有多个流水线并行跑,会频繁切换流水线上下文,让整体编译特别慢,最好是一个项目一个流水线,一个流水线一台单独的机器。
人生版 GitLab CI/CD
人生就像一条 CI/CD 流水线:每一次选择都是一次提交,习惯是 lint,挫折是 test,坚持是 build,而真正上线的,永远是那些经得起自动校验的人。
*源码地址*
1、公众号“Codee君”回复“每日一Go”获取源码
如果您喜欢这篇文章,请您(点赞、分享、亮爱心),万分感谢!