Docker环境中repmgrd脱离supervisord控制的问题求助
1. 为什么会出现这种情况?
你碰到的这个问题,本质上是进程托管的信号传递逻辑和repmgrd的默认行为不匹配,分两种场景来看:
场景1:没加--daemonize=false时,repmgrd直接脱离supervisord
repmgrd默认会以守护进程模式运行——它会自动fork出一个子进程来执行核心逻辑,然后让父进程直接退出。而supervisord监控的是你配置的启动命令对应的进程(也就是repmgr_helper.sh或者repmgrd的父进程),当这个父进程退出后,supervisord就会认为这个服务已经终止了,但实际repmgrd的子进程会被PID为1的进程(你的entrypoint脚本)接管,彻底脱离supervisord的控制范围。
场景2:加了--daemonize=false后,stop操作只杀了脚本,repmgrd残留
你用bash -c "sleep 10 && /usr/local/bin/repmgr_helper.sh"作为启动命令,这时候supervisord实际监控的是bash进程,而不是repmgrd本身。当你执行supervisorctl stop jm:repmgrd时,supervisord只会给bash进程发终止信号,但bash的子进程repmgrd不会自动收到这个信号,会变成“孤儿进程”被PID 1接管。至于启动时提示PID文件存在,是因为之前的repmgrd没有被正确终止,PID文件残留下来了。
2. 怎么让repmgrd一直受supervisord控制?
针对上面的问题,我们可以从简化启动链、强化信号传递、清理残留状态这几个方向调整:
方法1:直接让supervisord托管repmgrd,去掉多余的脚本包装
把supervisord里的repmgrd配置改成直接启动repmgrd,同时强制它在前台运行:
[program:repmgrd] command = /usr/lib/postgresql/10/bin/repmgrd --verbose --daemonize=false user = postgres stdout_logfile = /var/log/supervisor/repmgr-stdout.log stderr_logfile = /var/log/supervisor/repmgr-stderr.log stopasgroup = true killasgroup = true
--daemonize=false:强制repmgrd在前台运行,不fork子进程,这样supervisord就能直接监控它的状态。stopasgroup=true和killasgroup=true:确保停止这个服务时,会向整个进程组发送终止信号,就算有意外的子进程也会被一起干掉,不会残留。
方法2:如果需要等PostgreSQL启动,用可靠的等待脚本代替sleep
sleep 10太不靠谱了——万一PostgreSQL启动慢了,repmgrd启动时连不上数据库就会出问题。可以用wait-for-it这类脚本(或者自己写个简单的循环检查PostgreSQL端口的脚本)来等PostgreSQL就绪后再启动repmgrd:
- 把
wait-for-it.sh放到/usr/local/bin/ - 修改supervisord的repmgrd配置:
[program:repmgrd] command = /usr/local/bin/wait-for-it.sh pg-dock-1:5432 -t 60 -- /usr/lib/postgresql/10/bin/repmgrd --verbose --daemonize=false user = postgres stdout_logfile = /var/log/supervisor/repmgr-stdout.log stderr_logfile = /var/log/supervisor/repmgr-stderr.log stopasgroup = true killasgroup = true
这样能保证repmgrd只有在PostgreSQL完全能连上的时候才启动,避免启动失败的情况。
方法3:清理残留的PID文件
在repmgr.conf里指定一个明确的PID文件路径:
pidfile='/var/run/repmgrd.pid'
然后在启动repmgrd前先清理这个文件,避免残留导致启动报错。可以把启动命令改成这样:
[program:repmgrd] command = bash -c "rm -f /var/run/repmgrd.pid && /usr/local/bin/wait-for-it.sh pg-dock-1:5432 -t 60 -- /usr/lib/postgresql/10/bin/repmgrd --verbose --daemonize=false --pid-file=/var/run/repmgrd.pid" user = postgres stdout_logfile = /var/log/supervisor/repmgr-stdout.log stderr_logfile = /var/log/supervisor/repmgr-stderr.log stopasgroup = true killasgroup = true
这里虽然用了bash包装,但因为加了stopasgroup=true和killasgroup=true,停止的时候会把bash和repmgrd一起杀死,不会出现残留。
验证步骤
改完配置后,启动容器,做这几步验证:
- 执行
pstree -p,看repmgrd的父进程是不是supervisord的PID。 - 执行
supervisorctl stop jm:repmgrd,再用ps aux | grep repmgrd确认没有残留的repmgrd进程。 - 执行
supervisorctl start jm:repmgrd,确认repmgrd正常启动,而且父进程还是supervisord。
内容的提问来源于stack exchange,提问作者Stephan

