在 Docker 容器中运行服务#
最近正在写的某个容器升级了一下架构,在启动容器时需要同时运行一个 frps 服务。根据我的习惯,一开始打算使用 systemctl 添加 frps 服务,然后 enable 一下以实现开机自启。但在我实际使用时,发现服务没有跑起来。查了一下日志,发现了这条报错:
错误:systemctl:frps.service:可执行路径不是绝对路径,忽略:./frps -c /frps.ini
错误:systemctl:Exec 不是绝对路径:ExecStart=./frps -c /frps.ini
错误:systemctl:!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
错误:systemctl:在 /etc/systemd/system/frps.service 中发现 1 个问题
错误:systemctl:根据定义,SystemD 命令必须始终是绝对路径。
错误:systemctl:早期版本的 systemctl.py 确实使用子 shell,因此使用 $PATH
错误:systemctl:但是较新的版本使用与真实 SystemD 守护程序相同的 execve
错误:systemctl:因此,您的仅限于 docker 的服务脚本可能会突然失败。
错误:systemctl:!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
去官网稍微找了下,发现了这段解释:
这是设计如此。Docker 应该在容器中前台运行一个进程,并且它将作为 PID 1 在容器的 pid 命名空间中生成。Docker 设计用于进程隔离,而不是用于操作系统虚拟化,因此容器内没有其他操作系统进程和守护程序运行(如 systemd、cron、syslog 等),只有您的入口点或您运行的命令。
如果它们包含 systemd 命令,您会发现很多事情无法正常工作,因为您的入口点替换了 init。Systemd 还使用 cgroups,而 docker 在容器内限制了对 cgroups 的更改,因为更改 cgroups 的能力可能允许进程逃脱容器的隔离。在容器内没有作为 init 运行的 systemd,没有守护程序来处理您的启动和停止命令。
大意就是:docker 只是提供了进程隔离,不是操作系统的虚拟,所以不能运行 systemd。
如果只想同时运行两个服务,应该用什么办法呢?其实很简单:在 Dockerfile CMD 内运行 start.sh,然后再用 nohup 挂起 frps 服务就好了:
nohup ./frps -c /frps.ini &
python3 /main.py
顺利运行 frps 服务。