03 部署 Go 应用
Go 是部署最省心的语言:编译产物是一个静态二进制文件,不需要装运行时、不需要装依赖。配合 Docker 多阶段构建,最终镜像能小到 20MB 以内。本篇以一个 Gin 写的 API 服务为例。
1. 示例项目结构
go-api/
├── main.go
├── go.mod
├── go.sum
├── Dockerfile
├── .dockerignore
└── compose.yaml
main.go 里准备一个健康检查接口(后面 healthcheck 要用):
package main
import "github.com/gin-gonic/gin"
func main() {
r := gin.Default()
r.GET("/health", func(c *gin.Context) {
c.JSON(200, gin.H{"status": "ok"})
})
r.GET("/api/hello", func(c *gin.Context) {
c.JSON(200, gin.H{"message": "hello from go"})
})
r.Run(":8080")
}
2. 编写 Dockerfile(多阶段构建)
Dockerfile
# ---------- 阶段 1:编译 ----------
FROM golang:1.22-alpine AS builder
WORKDIR /app
# 国内环境配置 Go 模块代理,加速依赖下载
ENV GOPROXY=https://goproxy.cn,direct
# 先拷贝依赖清单并下载,利用缓存:go.mod 没变就不用重新下载
COPY go.mod go.sum ./
RUN go mod download
# 再拷贝源码编译
COPY . .
# CGO_ENABLED=0:编译纯静态二进制,不依赖任何系统库,才能放进极简镜像
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o server .
# ---------- 阶段 2:运行 ----------
FROM alpine:3.20
WORKDIR /app
# 时区数据 + CA 证书(调用 https 接口必需)
RUN apk add --no-cache tzdata ca-certificates && \
cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
# 只把二进制拷过来,源码、Go 工具链全部留在上一阶段
COPY --from=builder /app/server .
EXPOSE 8080
CMD ["./server"]
.dockerignore:
.dockerignore
.git
*.md
compose.yaml
对比一下:
golang:1.22完整镜像约 800MB,而这个最终镜像只有约 15MB——编译环境和运行环境分离就是多阶段构建的价值。追求极致还可以把第二阶段换成scratch(完全空白的镜像),但排查问题时连 shell 都没有,新手建议用 alpine。
3. 编写 compose.yaml
compose.yaml
services:
go-api:
build: .
container_name: go-api
restart: unless-stopped
ports:
- "127.0.0.1:8080:8080" # 只监听本机,公网流量走 Nginx 反代
environment:
- GIN_MODE=release # 生产模式,关掉调试日志
healthcheck:
test: ["CMD", "wget", "-q", "--spider", "http://localhost:8080/health"]
interval: 30s
timeout: 3s
retries: 3
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
启动与验证:
docker compose up -d --build
docker compose ps # STATUS 应显示 healthy
curl http://127.0.0.1:8080/api/hello # {"message":"hello from go"}
Nginx 反向代理的配置和 FastAPI 篇完全一样(proxy_pass http://127.0.0.1:8080),不再重复。
4. 不用 Docker 行不行?
行,而且 Go 是唯一「裸部署也很舒服」的语言——把二进制传上去用 systemd 管理即可:
/etc/systemd/system/go-api.service
[Unit]
Description=Go API Service
After=network.target
[Service]
ExecStart=/opt/go-api/server
Restart=always
RestartSec=3
Environment=GIN_MODE=release
[Install]
WantedBy=multi-user.target
systemctl daemon-reload
systemctl enable --now go-api # 开机自启 + 立即启动
systemctl status go-api # 查看运行状态
journalctl -u go-api -f # 看日志
两种方式怎么选:
| Docker 部署 | systemd 裸部署 | |
|---|---|---|
| 环境一致性 | 好,本地和服务器一个镜像 | 依赖交叉编译目标正确 |
| 和其他容器服务协作 | 同一网络互访方便 | 要走宿主机端口 |
| 资源开销 | 略高一点点 | 几乎为零 |
| 适合场景 | 项目里还有 MySQL/Redis 等容器 | 单个小工具、Agent 类程序 |
本教程的技术栈都容器化了,推荐统一用 Docker,管理方式一致。
小结
- Go 编译出静态二进制(
CGO_ENABLED=0),多阶段构建后镜像只有十几 MB go mod download单独一层,依赖不变就走缓存,构建飞快- alpine 阶段记得补
tzdata(时区)和ca-certificates(HTTPS 调用) - 端口只绑
127.0.0.1,加/health接口配 healthcheck,和其他后端服务一个套路 - systemd 裸部署是 Go 独有的轻量选项,单体小程序可以用
下一篇解决数据层:04-部署MySQL与Redis