Systemd管理Docker容器服务执行stop后进入failed状态求助
我有一个用于启停自研代理服务器的Systemd服务文件,内容如下:
[Unit] Description=Golang proxy server in Docker After=network.target docker.service Requires=docker.service [Service] Type=simple WorkingDirectory=/home/ooolledj/socks5-proxy ExecStart=/usr/bin/docker container rm goproxy --force ; /usr/bin/docker run --name goproxy -p 12345:12345 goproxy:local ExecStop=/usr/bin/docker container stop -t 1 goproxy Restart=on-failure TimeoutStartSec=10 [Install] WantedBy=multi-user.target
服务启动运行正常,但执行systemctl stop goproxy命令时,容器虽已成功停止,服务却进入"failed"状态。重启服务可恢复正常运行,推测原因是容器进程PID终止后,systemd将其判定为异常退出,报错信息为goproxy.service: Main process exited, code=exited, status=2/INVALIDARGUMENT,对应的状态信息如下:
root@my-test-host:/home/ooolledj/socks5-proxy# systemctl status goproxy ● goproxy.service - Golang proxy server in Docker Loaded: loaded (/etc/systemd/system/goproxy.service; disabled; vendor preset: enabled) Active: failed (Result: exit-code) since Thu 2023-08-17 18:05:09 MSK; 881ms ago Process: 49618 ExecStartPre=/usr/bin/docker container rm goproxy --force (code=exited, status=0/SUCCESS) Process: 49624 ExecStart=/usr/bin/docker run --name goproxy -p 12345:12345 goproxy:local (code=exited, status=2) Process: 49717 ExecStop=/usr/bin/docker container stop -t 3 goproxy (code=exited, status=0/SUCCESS) Main PID: 49624 (code=exited, status=2) CPU: 135ms Aug 17 18:04:58 my-test-host systemd[1]: Starting Golang proxy server in Docker: https://github.com/OOOlledj/socks5-proxy... Aug 17 18:04:58 my-test-host docker[49618]: goproxy Aug 17 18:04:58 my-test-host systemd[1]: Started Golang proxy server in Docker: https://github.com/OOOlledj/socks5-proxy. Aug 17 18:05:09 my-test-host systemd[1]: Stopping Golang proxy server in Docker: https://github.com/OOOlledj/socks5-proxy... Aug 17 18:05:09 my-test-host docker[49717]: goproxy Aug 17 18:05:09 my-test-host systemd[1]: goproxy.service: Main process exited, code=exited, status=2/INVALIDARGUMENT Aug 17 18:05:09 my-test-host systemd[1]: goproxy.service: Failed with result 'exit-code'. Aug 17 18:05:09 my-test-host systemd[1]: Stopped Golang proxy server in Docker: https://github.com/OOOlledj/socks5-proxy.
希望服务停止时能进入"exited"正常状态而非"failed",以下是可行解决方案:
解决方案
方案1:标记退出码2为正常状态(推荐)
当执行systemctl stop时,docker run进程会因容器被外部终止返回退出码2,systemd默认将非0退出码视为失败。在服务文件的[Service]段添加配置,告诉systemd该退出码属于正常情况:
SuccessExitStatus=2
同时将原ExecStart中的容器删除命令移到ExecStartPre,更符合systemd启动流程,修改后的完整服务文件:
[Unit] Description=Golang proxy server in Docker After=network.target docker.service Requires=docker.service [Service] Type=simple WorkingDirectory=/home/ooolledj/socks5-proxy ExecStartPre=/usr/bin/docker container rm goproxy --force ExecStart=/usr/bin/docker run --name goproxy -p 12345:12345 goproxy:local ExecStop=/usr/bin/docker container stop -t 1 goproxy Restart=on-failure TimeoutStartSec=10 SuccessExitStatus=2 [Install] WantedBy=multi-user.target
方案2:改用后台运行模式跟踪容器状态
如果希望容器后台运行,可修改ExecStart使用docker run -d,并通过docker wait跟踪容器生命周期,同时将服务类型改为Type=forking:
[Unit] Description=Golang proxy server in Docker After=network.target docker.service Requires=docker.service [Service] Type=forking WorkingDirectory=/home/ooolledj/socks5-proxy ExecStartPre=/usr/bin/docker container rm goproxy --force ExecStart=/usr/bin/docker run -d --name goproxy -p 12345:12345 goproxy:local && /usr/bin/docker wait goproxy ExecStop=/usr/bin/docker container stop -t 1 goproxy Restart=on-failure TimeoutStartSec=10 [Install] WantedBy=multi-user.target
docker wait会阻塞到容器停止,返回容器的退出码,确保systemd正确识别容器的终止状态。
方案3:使用一次性服务模式
若服务仅负责启动容器,无需持续运行主进程,可改用Type=oneshot并开启RemainAfterExit=true,让服务在启动后保持active状态,直到手动停止:
[Unit] Description=Golang proxy server in Docker After=network.target docker.service Requires=docker.service [Service] Type=oneshot WorkingDirectory=/home/ooolledj/socks5-proxy ExecStartPre=/usr/bin/docker container rm goproxy --force ExecStart=/usr/bin/docker run -d --name goproxy -p 12345:12345 goproxy:local ExecStop=/usr/bin/docker container stop -t 1 goproxy && /usr/bin/docker container rm goproxy --force Restart=on-failure TimeoutStartSec=10 RemainAfterExit=true [Install] WantedBy=multi-user.target
内容的提问来源于stack exchange,提问作者ooolledj

