2核2G小服务器上用 Docker Compose 跑 Go 后端:镜像瘦身与重启策略
2核2G小服务器上用 Docker Compose 跑 Go 后端:镜像瘦身与重启策略
点餐小程序的后端是 Go 单二进制,本来用 systemd 直接跑已经够稳。最近我把它迁到了 Docker Compose,不是为了追时髦,而是想解决两个实际问题:部署时只传一个镜像就能回滚,开发机和服务器的环境差异不再靠人肉对齐。2核2G 的小机器跑 Docker 完全没问题,前提是镜像要瘦、资源要设上限、重启策略要写对。
多阶段构建是第一步
Go 程序最大的好处是能静态编译,镜像里其实不需要 Go 工具链。一个典型的多阶段 Dockerfile 长这样:
FROM golang:1.23 AS builder
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o server ./cmd/server
FROM scratch
COPY --from=builder /src/server /server
COPY --from=builder /src/config.example.yaml /config.yaml
EXPOSE 8080
ENTRYPOINT ["/server", "-config", "/config.yaml"]
第一阶段负责编译,第二阶段只留二进制和一份示例配置,最终镜像往往只有十几 MB。用 scratch 有个代价:没有 shell,排错不能 docker exec -it sh,也没有 CA 证书,程序如果要调 HTTPS 上游,得从第一阶段把 ca-certificates.crt 一起拷过来。我的后端只对内提供接口,暂时不需要这一步,就先保持最瘦。
.dockerignore 也别漏:.git、本地的 server 二进制、测试数据、.env 都要排除,否则每次构建上下文都很大,传到服务器也慢。
Compose 文件只写必需项
services:
order-backend:
image: order-backend:local
container_name: order-backend
restart: unless-stopped
ports:
- "8080:8080"
environment:
- GIN_MODE=release
volumes:
- ./config.yaml:/config.yaml:ro
- ./data:/data
deploy:
resources:
limits:
memory: 512M
cpus: "1.0"
healthcheck:
test: ["CMD", "/server", "-health"]
interval: 30s
timeout: 3s
retries: 3
几个点逐个说。restart: unless-stopped 是小机器最实用的策略:机器重启、容器异常退出都会自动拉起,但我手动 docker compose down 后不会被悄悄拉起来,和 systemd 的 on-failure 思路接近,语义更直白。mem_limit 一定要设,Go 程序内存泄漏时先被容器限制挡住,不会把整台 2G 机器拖垮。配置文件用只读挂载,容器里改不了配置,改配置的唯一入口是宿主机上的文件,出了问题先看宿主机,排查路径很固定。
健康检查要和程序配合:我的后端加了一个很轻的 -health 参数,只检查监听端口和关键依赖是否就绪,不做重查询。健康检查失败不等于立刻重启容器,Compose 默认只标记 unhealthy,真正要自动重启还得配合外部编排,这一点别误解。
更新与回滚流程
更新时我只做三步:本机构建并打新 tag,传到服务器,docker compose up -d。Compose 会发现镜像变了,停旧容器、起新容器,端口映射不变,前端小程序感知不到。回滚更简单:把 tag 指回上一版,再 up -d 一次。比直接覆盖二进制多了一层隔离,万一新版启动就崩,旧镜像还在本地,不用重新编译。
日志还是走 docker compose logs -f order-backend,再加 --since 过滤时间。不要在容器里写文件日志轮转,宿主机的 journald 或 Docker 自带的 json-file 驱动配好 max-size 和 max-file 就够了,否则小磁盘很容易被日志吃满。
什么时候不该用 Docker
如果服务只有一个、且永远不换机器,systemd 直接跑二进制更省资源、排错也更直接。Docker 的价值在“环境一致”和“回滚简单”这两点上,机器越小,越要把镜像做瘦、把资源上限写死,而不是把一堆中间件都塞进 Compose。我的原则是:先保证一个服务跑稳,再考虑要不要加第二个容器,不要一开始就搭一整套编排。
- 点赞
- 收藏
- 关注作者
评论(0)