为什么你的 Go 项目需要 CI/CD,就像狗需要遛弯一样
你写了一个很棒的 Go 微服务。它运行飞快,测试通过,代码漂亮。但每天早上你都要手动运行 go build,通过 SSH 把二进制文件上传到服务器,然后祈祷一切正常。熟悉吗?如果是的话——我要告诉你一个消息:你把自己 15% 的生命浪费在了机器人可以做的事情上。
CI/CD(持续集成 / 持续部署)不仅仅是 DevOps 职位描述里的时髦词汇。它是你的私人助理,可以:
- 每次推送到 Git 时自动运行测试;
- 即使你用的是 Mac,也能构建 Linux/amd64 的二进制文件;
- 如果构建失败,通过 Telegram 发送通知;
- 当你将 PR 合并到 main 分支时,自动部署到服务器。
在本文中,我们将从零开始配置 Go 的 CI/CD 流水线。没有废话,没有“在 20 台服务器上安装 Jenkins”。只有 GitHub Actions、Docker 和一点点魔法。开始吧!
我们需要什么
配置需要以下内容:
- 一个 GitHub 仓库(现在就可以创建一个测试仓库);
- 一个带有模块的 Go 项目(我们将编写一个简单的 HTTP 服务器);
- 一个 Docker Hub 账户(或其他任何 registry);
- 5 分钟的专注和自动化日常工作的意愿。
我们将使用的所有代码都是开源的。你可以复制并根据自己的项目进行调整。主要别忘了把令牌和仓库名称换成你自己的。
步骤 1:编写一个简单的 Go 应用
为了演示,我们使用一个最小的 HTTP 服务器。创建文件 main.go:
package main
import ( "fmt" "net/http")
func main() { http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello from Go CI/CD pipeline!") })
http.ListenAndServe(":8080", nil)}以及用于验证功能的文件 main_test.go:
package main
import ( "net/http" "net/http/httptest" "testing")
func TestHelloHandler(t *testing.T) { req := httptest.NewRequest("GET", "/", nil) w := httptest.NewRecorder()
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello from Go CI/CD pipeline!") })
http.DefaultServeMux.ServeHTTP(w, req)
if w.Code != http.StatusOK { t.Errorf("expected status 200, got %d", w.Code) }}初始化模块:
go mod init github.com/你的用户名/go-cicd-examplego mod tidy检查一切是否正常:
go test ./... -vgo run main.go如果你看到 ok 并且在 localhost:8080 上看到欢迎信息——你太棒了。我们继续。
步骤 2:为 Go 应用创建 Dockerfile
容器化是现代部署的必备步骤。我们将构建一个静态二进制文件(不使用 CGO),并将其打包到基于 scratch 或 alpine 的最小镜像中。这将使我们的镜像大小约为 10–15 MB,而不是 300+ MB。
在项目根目录创建文件 Dockerfile:
FROM golang:1.22-alpine AS builder
WORKDIR /appCOPY go.mod go.sum ./RUN go mod downloadCOPY . .RUN CGO_ENABLED=0 GOOS=linux go build -o main .
FROM alpine:latestRUN apk --no-cache add ca-certificatesWORKDIR /root/COPY --from=builder /app/main .EXPOSE 8080CMD ["./main"]注意:我们使用了多阶段构建。第一阶段在完整的 Go 环境中编译。第二阶段是一个最小的 Alpine 镜像,只复制二进制文件。这是 Go 应用的行业标准。
在本地构建镜像并验证:
docker build -t go-cicd-example .docker run -p 8080:8080 go-cicd-example如果一切正常——我们进入下一步