不能直接在test阶段后加deploy,因gitlab ci默认对所有分支、mr、tag触发pipeline,若deploy无限制会导致每次push都发版;应通过rules:或only:限定deploy仅响应tags或手动触发。

直接用 .gitlab-ci.yml 定义多阶段流水线,核心是分清「构建什么」和「发布到哪」——Go 项目最终交付物通常是静态二进制或 Docker 镜像,不是源码;发布动作必须显式触发,不能靠 push 主分支自动完成。
为什么不能直接在 test 阶段后加 deploy?
GitLab CI 默认对所有分支、MR、tag 都触发 pipeline,如果 deploy 阶段没加限制,每次 push 都会往生产环境发包。真实场景中:
-
test和build应该跑在main、develop、MR 上,快速反馈质量 -
release或deploy只能响应tags(如v1.2.0)或手动触发(when: manual) - 用
only:或rules:控制触发条件,rules:更推荐(支持布尔逻辑、变量判断)
go build 命令怎么写才适合 CI 环境?
本地能跑的 go build 在 CI 容器里常失败,因为默认启用 CGO、没设目标平台、带调试符号。CI 构建必须明确指定:
-
CGO_ENABLED=0:禁用 C 依赖,确保纯静态链接(否则 Alpine 镜像运行报no such file or directory) -
GOOS=linux GOARCH=amd64:交叉编译到目标部署环境(K8s 节点通常是 Linux AMD64) -
-ldflags="-s -w":剥离符号表和调试信息,二进制体积减少 30%~50% - 输出路径用绝对路径或
./bin/,避免被缓存干扰;建议统一为./bin/app
示例命令:CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o ./bin/app ./cmd/main
怎么安全地把镜像推到私有仓库?
不能把账号密码硬编码在 .gitlab-ci.yml 里。正确做法是用 GitLab 的 CI_REGISTRY_USER 和 CI_REGISTRY_PASSWORD 预置变量(需先在项目 Settings → CI/CD → Variables 中开启 Mask variable 并设为 protected):
- 登录:
docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY - 打标签:
docker tag app:latest $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG(仅 tag 构建时) - 推送:
docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG - 注意:
$CI_REGISTRY_IMAGE是 GitLab 自动注入的完整仓库地址(如registry.example.com/group/project),别自己拼
缓存 go mod 为什么有时不生效?
常见原因是没在 before_script 或每个 job 开头执行 go mod download,导致缓存路径存在但 Go 工具链仍去拉取。必须同时满足:
- 在
cache:下声明.go/pkg/mod路径(注意前面的点) - 每个使用
go build或go test的 job,开头加go mod download - 确保
go.mod和go.sum已提交,否则go mod download无依据 - 不要在
script里用go get动态改依赖,这会绕过缓存且破坏可重现性
缓存失效最常发生在 go.mod 变更后第一次构建——这是正常行为,后续就快了。
真正容易被忽略的是:GitLab Runner 的执行器类型(Docker vs Shell)直接影响能否用 docker build。如果 Runner 是 shell 类型,job 直接跑在宿主机上,docker 命令可用;但如果是 docker 类型,job 运行在容器里,默认没挂载 /var/run/docker.sock,必须显式配置 docker:dind 或挂载 socket,否则 docker build 会报 Cannot connect to the Docker daemon。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











