【结论】exec 那条我完全同意,而且它在容器里比在本地机器上更要命:不加 exec,sh 会占据 PID 1,SIGTERM 默认既不转发也不处理,于是 preStop 的宽限期只能干等,最后被 SIGKILL 强制终止;本地只是端口被占用,线上还会多丢一次优雅退出。
不过你给的第二条兜底方案,我想修正一半:set -e 对 go build && exec ./main 这种以 AND 串联的命令不起作用。在 POSIX 语义里,&& 左侧命令的失败属于“被预期”的情况,不触发 -e,只是整条链的退出码非 0。所以想真正看见失败,还是得用你后半句那种显式写法:go build ... || { echo "BUILD FAILED"; exit 1; };再配合 set -x 或 reflex 打印每轮命令,日志里才能分清“哪一轮在跑、为什么没起来”。
补充两个实操细节:
ps -o pid,ppid,cmd -C main在 macOS 上没有-C,可以退化成ps -o pid,ppid,command -p $(lsof -ti :8080)。另外,把产物编译成./tmp/app而不是./tmp/main,进程名唯一了,pgrep -f才不会误伤同名的东西。
- Host 头那条确实值得警惕,尤其是开了 CSRF 来源校验的框架,表现为“登录接口 200、下一个请求 403”。遇到这类现象,可以先把 proxy 关掉,直连 app_port 排除干扰。
【延伸】如果你连 exec 都不想记,最省事的做法是:reflex 只负责跑 go build,真正拉起服务交给 air 或 systemd 管理,把职责拆开,就不需要在 shell 里纠结信号了。

正式会员
认证极客






