在 2核2G 小服务器上用 systemd 部署 Go 单二进制服务的实践
背景
手头有一台 2 核 CPU、2GB 内存的小规格云服务器,想在上面长期跑一个自己写的 Go 小服务,例如一个简单的 API 或者定时任务。机器配置不大,没必要上 Docker 和 Kubernetes:Go 本身就能编译成单个静态二进制,再配合 Linux 自带的 systemd,就是一套非常轻量、稳定的部署方案。这篇文章记录我实际部署时的做法和几个容易踩的坑。
一、交叉编译:本地构建,服务器只负责运行
在本地开发机上完成交叉编译,服务器上不需要安装 Go 环境:
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o myapp ./cmd/server
几个要点:
- CGO_ENABLED=0 保证纯静态链接,换任何发行版都能直接运行,不会遇到 glibc 版本不一致的问题;
- -ldflags=“-s -w” 去掉符号表和调试信息,能减小三成左右的体积,对小内存机器更友好;
- 构建产物用 scp 传到服务器的 /opt/myapp 目录即可,整个发布过程本质上就是替换一个文件。
二、用 systemd 托管进程
把服务交给 systemd 管理,可以直接获得开机自启、崩溃自动拉起、日志集中管理这些能力。新建 /etc/systemd/system/myapp.service:
[Unit]
Description=My Go Service
After=network.target
[Service]
Type=simple
User=myapp
WorkingDirectory=/opt/myapp
ExecStart=/opt/myapp/myapp
Restart=on-failure
RestartSec=3
[Install]
WantedBy=multi-user.target
部署并查看状态:
systemctl daemon-reload
systemctl enable --now myapp
systemctl status myapp
两个小经验:一是务必用单独的低权限用户 myapp 运行服务,不要图省事直接用 root;二是 Restart=on-failure 只在异常退出时才重启,手动停止服务不会反复拉起,排查问题时更可控。
三、日志交给 journald,别把磁盘写满
小服务器的系统盘通常只有几十 GB,日志失控是“磁盘突然满了”最常见的事故源。我的做法是让程序把日志直接输出到标准输出,交给 journald 接管:
journalctl -u myapp -f
journalctl -u myapp --since "1 hour ago"
再编辑 /etc/systemd/journald.conf,把 SystemMaxUse 设为 200M,重启 systemd-journald 生效。这样日志既方便查,又永远不会把磁盘撑爆。
四、端口安全与反向代理
- 服务本身只监听 127.0.0.1 的业务端口,不直接暴露到公网;
- 用 Nginx 或 Caddy 做反向代理对外提供 80/443,顺带处理 HTTPS 证书;
- 云平台安全组只放行 22、80、443,SSH 改用密钥登录并关闭密码登录;
- 数据库等组件如果也在同一台机器上,同样只监听本地回环地址。
五、内存不大,提前留好后路
2GB 内存跑 Go 服务其实很宽裕,因为 Go 运行时开销小。真正抢内存的是日志、缓存和突发流量。建议预留一个 1 到 2GB 的 swap 文件兜底,并可以在 unit 里加上 MemoryMax=512M 之类的资源限制,防止单个进程异常时把整台机器拖死。
总结
小服务器部署的核心就是两个字:简单。单二进制交付、systemd 托管、journald 管日志、安全组只开必要端口。这套方案跑下来,发布就是一条 scp 加一次 restart,稳定又省心,比在小机器上堆容器编排实在得多。
- 点赞
- 收藏
- 关注作者
评论(0)