生产环境禁用 nohup go run main.go &,必须用 systemd 托管 go build 编译的二进制;需配置 Restart、日志重定向、环境变量,并手动处理 SIGTERM 实现优雅关机。

别用 nohup go run main.go & 部署 Gin 项目——它连基本的进程守护都算不上,更别说生产可用。
为什么不能用 go run 配 nohup
看似能后台运行,实则埋了五个雷:
-
go run每次启动都重新编译,浪费 CPU、无调试符号、无法做安全加固(如go build -buildmode=pie -ldflags="-s -w") - 进程仍属原 shell 会话:
ps -o pid,ppid,sid,tty查出来tty是?,但sid和父 shell 一致,未真正脱离会话 -
nohup.out默认写入当前目录,无轮转、无权限控制,日志撑爆磁盘是常态 - 崩溃后零拉起能力,
nohup不提供重启策略、资源限制或依赖管理 - 无法与
journalctl对接,排查时得满硬盘翻日志文件
systemd 服务文件关键字段怎么填
必须手写 /etc/systemd/system/gin-api.service,以下字段一个都不能少且不能错配:
-
Type=simple:Gin 二进制是前台长期运行程序,不是 fork 出子进程再退出的 daemon,别填forking -
ExecStart=/opt/gin-api/gin-api:路径必须绝对,且确保该文件是go build -o gin-api main.go编译出的静态二进制 -
User=www-data(或专用低权用户):禁止用root运行 Web 服务;若用root,LimitNOFILE等限制可能不生效 -
Restart=always+RestartSec=5:覆盖段错误、panic、主动 exit 等所有退出场景,但加延迟防雪崩 -
StandardOutput=journal+StandardError=journal:让日志进journalctl,别重定向到文件 -
Environment="GIN_MODE=release":强制关闭调试模式,避免泄露路由、中间件等敏感信息
部署后必须验证的三件事
跑起来不等于管得住,以下检查缺一不可:
- 执行
sudo systemctl daemon-reload && sudo systemctl enable --now gin-api后,立刻跑sudo systemctl status gin-api,确认状态是active (running),且Loaded行显示配置来自/etc/systemd/system/gin-api.service - 查日志用
journalctl -u gin-api -f,不是tail -f /var/log/xxx;如果看到standard output is not a terminal类报错,说明StandardOutput没设对 - 手动 kill 主进程:
sudo pkill -f gin-api,等 5 秒后执行sudo systemctl status gin-api,确认已自动重启且 PID 变化
Gin 里要配合做的优雅关机
systemd 发 SIGTERM 时,Gin 默认直接退出,连接被粗暴中断。你得在代码里显式处理:
- 监听
os.Interrupt和syscall.SIGTERM,收到后调用srv.Shutdown() -
srv.Shutdown()会等待活跃请求完成,但需设置超时(比如 10 秒),超时后强制os.Exit(1) - 别漏掉
time.Sleep或阻塞操作——它们会让 shutdown 卡住,systemd 最终发SIGKILL强杀
最易被忽略的是:没加 shutdown 处理的 Gin 服务,在 systemd reload 或机器重启时,正在传输的大文件响应、长轮询连接、数据库事务都会被瞬间切断,客户端收到 Connection reset 或空响应。


















