我想要使用 systemd 启动以下 Golang 代码:
...
func main() {
// main routes
http.HandleFunc("/", hello)
log.Fatalln(http.ListenAndServe(":80", nil))
}
func hello(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Hello")
}
该代码是用go build
生成的二进制文件的名称:MyApp
路径:/home/andrei/MyApp/MyApp
- 第二个MyApp
是二进制文件
在里面我放置了包含内容的/lib/systemd/system
文件:MyApp.service
[Unit]
Description=MyApp service
After=network.target
[Socket]
ListenStream=80
NoDelay=true
[Service]
Type=simple
Restart=on-failure
RestartSec=3
User=andrei
Group=andrei
WorkingDirectory=/home/andrei/MyApp
ExecStart=/home/andrei/MyApp/MyApp
[Install]
WantedBy=multi-user.target
在 Ubuntu 上,我运行命令(在 Debian 上使用 sudo):
sistemctl start MyApp.service
sistemctl status MyApp.service
我得到输出:
● MyApp.service - MyApp service
Loaded: loaded (/lib/systemd/system/MyApp.service; disabled; vendor preset: enabled)
Active: activating (auto-restart) (Result: exit-code) since Wed 2018-11-14 11:30:45 UTC; 1s ago
Process: 14883 ExecStart=/home/andrei/MyApp/MyApp (code=exited, status=1/FAILURE)
Main PID: 14883 (code=exited, status=1/FAILURE)
Nov 14 11:30:45 andrei systemd[1]: MyApp.service: Main process exited, code=exited, status=1/FAILURE
Nov 14 11:30:45 andrei systemd[1]: MyApp.service: Failed with result 'exit-code'.
NOTE:
当我从终端运行应用程序时,一切正常。
如何使用 systemd 启动应用程序?
Update:
它通过执行以下操作来工作:
sudo setcap CAP_NET_BIND_SERVICE=+eip /home/andrei/MyApp/MyApp
更好的单元文件很有帮助 - 不需要使其工作:
[Unit]
Description=MyApp service
After=network.target
[Service]
Type=simple
Restart=always
User=andrei
Group=andrei
WorkingDirectory=/home/andrei/MyApp
ExecStart=/home/andrei/MyApp/MyApp
# make sure log directory exists and owned by syslog
PermissionsStartOnly=true
ExecStartPre=/bin/mkdir -p /var/log/sleepservice
ExecStartPre=/bin/chown syslog:adm /var/log/sleepservice
ExecStartPre=/bin/chmod 755 /var/log/sleepservice
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=sleepservice
[Install]
WantedBy=multi-user.target
现在让它与:
sudo systemctl daemon-reload
sudo systemctl start MyApp.service
sudo systemctl enable MyApp.service # app will start on OS restart
sudo journalctl -f -u MyApp.service # provide information about the app
答案1
您遇到的问题是,您正在使用非 root 用户 ( User=andrei
) 运行应用程序,但应用程序尝试侦听端口 80 ( http.ListenAndServe(":80", nil)
),这是一个特权端口,通常只有 root 用户可以侦听。
当你说:
当我从终端运行应用程序时,一切正常。
无论如何,你是否将其运行为根?因为那时,我希望它能正常工作。
如果您可以使用不同的端口(端口号高于 1024,这将是无特权的),那么这将是让您的服务以非 root 身份运行的最简单的解决方案。
另外,您的单元文件中的此片段:
[Socket]
ListenStream=80
NoDelay=true
这实际上不适用于服务单元,它仅适用于插座单元,所以要使套接字激活工作时,您需要一个MyApp.socket
具有这些设置的单独单元。
但是套接字激活比仅仅创建一个单独的单元更复杂,因为应用程序本身需要支持从 systemd 接收侦听套接字。例如,C 应用程序会调用sd_listend_fds()并链接到 libsystemd 来实现这一点。 Go 中可能有执行相同操作的绑定,请查找coreos/go-systemd例如,那里可能有一些。
套接字激活的优点是您可以使用端口 80 和非 root 用户,但如上所述,需要更改您的应用程序。