go程序写成systemd服务需用绝对路径execstart、自行处理sigterm信号、避免os.exit()、输出日志到stdout/stderr;关键字段包括type=simple、restart=on-failure、limitnofile=65536。

Go 程序怎么写成 systemd 服务
Go 编译出来的二进制是静态链接的可执行文件,直接扔给 systemd 就能跑,不需要额外依赖或 shell 包装脚本。关键不是“能不能”,而是“怎么让 systemd 正确接管进程生命周期”。
- 必须用
ExecStart=指向 Go 编译出的绝对路径二进制,不能写相对路径或带./ - Go 程序要自己处理信号(比如
os.Interrupt、syscall.SIGTERM),不能依赖 shell 转发;systemd 发SIGTERM后默认等 10 秒,超时才发SIGKILL - 避免在程序里用
os.Exit()突然退出,否则 systemd 会记为failed;应该优雅关闭监听器、等待 goroutine 结束 - 日志直接输出到 stdout/stderr 即可,systemd 自动捕获,不用重定向到文件
systemd service 文件里哪些字段最关键
一个最小可用的 .service 文件,真正影响 Go 服务行为的就那几个字段,其余多数是锦上添花。
-
Type=simple:最常用,systemd 认为ExecStart启动的主进程就是服务进程(适合大多数 HTTP server 或 CLI daemon) -
Type=notify:需要 Go 程序调用sdnotify库发送READY=1,才能配合systemd-notify实现启动完成通知;不配错的话,After=network.target之类才真正生效 -
Restart=on-failure:Go 程序 panic 或 exit code 非 0 时自动拉起;但别设always,否则掩盖初始化失败问题 -
LimitNOFILE=65536:Go HTTP server 并发高时容易 hit 默认 1024 限制,必须显式调大 -
Environment="GODEBUG=madvdontneed=1":可选,缓解某些版本 Go 在容器/低内存环境下的 RSS 虚高问题
Go 程序里怎么正确响应 systemd 的 SIGTERM
systemd 不关心你的 Go 是不是用了 http.Server,它只看进程是否在收到 SIGTERM 后干净退出。没做这件事,服务 reload 或 stop 就会卡住或被强杀。
- 用
signal.Notify监听os.Interrupt和syscall.SIGTERM,别只监听前者 - HTTP server 要调
srv.Shutdown(),传入带超时的 context,不能只关 listener - 其他长期运行的 goroutine(如定时任务、消息消费)也要提供退出通道,
select中监听donechannel - main 函数末尾加
log.Println("shutting down")这类日志,方便查journalctl -u your.service确认是否真收到了信号
srv := &http.Server{Addr: ":8080", Handler: mux}
go func() { log.Fatal(srv.ListenAndServe()) }()
<p>quit := make(chan os.Signal, 1)
signal.Notify(quit, os.Interrupt, syscall.SIGTERM)
</p>
为什么 journalctl 看不到 Go 的 panic 日志
panic 默认打印到 stderr,按理该被 systemd 捕获,但常见原因是 Go 程序启动太快崩溃,systemd 还没来得及接管 stdout/stderr —— 尤其是没加 StandardOutput=journal 或用了重定向。
- 确保 service 文件里没写
StandardOutput=null或重定向到文件(除非你真要双写) - panic 前如果用了
log.SetOutput(ioutil.Discard)或类似操作,日志就彻底消失了 - 更隐蔽的是:Go 程序用
exec.Command启了子进程并把 stdout/stderr 重定向了,结果 panic 输出被子进程 buffer 吞掉 - 调试期可在 main 开头加
log.SetFlags(log.LstdFlags | log.Lshortfile),让 panic 位置更明确
最稳的做法:上线前用 systemctl start your.service && systemctl status your.service 看一眼 ExitCode 和 MainPID,再查 journal,别等出问题才翻日志。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











