Golang守护进程的配置与实现方法
Golang守护进程的配置与实现方法
我通过Go语言创建了一个简单的套接字服务器。我已经构建并通过 ./commTest 运行它,运行正常。我在谷歌上搜索并在这里找到了一个方法:GoLang: Running a Go binary as a systemd service on Ubuntu 18.04 in 10 Minutes (without Docker) | by Luci Bro | Medium,并相应地遵循了它,修改了必要的内容。
[Unit] Description=Comm Test ConditionPathExists=/usr/bin/commTest After=network.target [Service] Type=simple User=root Group=root WorkingDirectory=/usr/bin/commTest ExecStart=/usr/lib/golang/bin run . Restart=on-failure RestartSec=10 StandardOutput=syslog StandardError=syslog SyslogIdentifier=commTest [Install] WantedBy=multi-user.target
我已经通过 setenforce 0 禁用了selinux。然后当我运行状态检查时,我得到了这个错误信息。
Redirecting to /bin/systemctl status -l commTest.service ● commTest.service - Comm Test Loaded: loaded (/etc/systemd/system/commTest.service; enabled; vendor preset: disabled) Active: activating (auto-restart) (Result: exit-code) since Tue 2023-01-03 20:48:06 +08; 7s ago Process: 48515 ExecStart=/usr/lib/golang/bin run . (code=exited, status=203/EXEC) Main PID: 48515 (code=exited, status=203/EXEC)
Jan 03 20:48:06 localhost.localdomain systemd[1]: commTest.service: main process exited, code=exited, status=203/EXEC Jan 03 20:48:06 localhost.localdomain systemd[1]: Unit commTest.service entered failed state. Jan 03 20:48:06 localhost.localdomain systemd[1]: commTest.service failed.
我想弄清楚的是,除了这个方法,还有其他方法可以将Go程序作为守护进程运行吗?或者这就是最好的方法?
更多关于Golang守护进程的配置与实现方法的实战教程也可以访问 https://www.itying.com/category-94-b0.html
你好 Sibert, 感谢你介绍 Webmin,让我有机会探索和学习更多相关知识。如果我有不确定的地方,可能会向你本人确认。
更多关于Golang守护进程的配置与实现方法的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
你好 Nobbz, 感谢你,我会更深入地研究Linux中的这个权限和用户相关的内容。
我通过将这一行 ExecStart=/usr/lib/golang/bin run . 改为 /usr/bin/commTest/commTest,已经成功让它运行起来了。但无论如何,这是守护进程化的正确方法吗?还是有其他更适合 Go 的更好方法?
你好 Sibert,
谢谢,是的,在我发现我的错误后,我也把我的解决方案放在上面了。我还有几个问题想和你确认一下:关于 User=root,它必须是 root 吗?另外,维护日志的最佳方式是什么?最后,对于 Go 语言,你会推荐这种方法还是其他方法?
关于 User=root 必须是 root 吗?
这完全取决于具体情况,但有一个经验法则:
如果你了解权限、能力以及如何放弃它们,那么可以是任何用户。
如果你不了解,那么它真的不应该是 root,所以去学习一下 Linux 是如何管理这些的吧!
newbiegolang:
ExecStart=/usr/lib/golang/bin run .
路径与可执行文件之间存在不匹配。
两者的路径应该几乎相同: WorkingDirectory=/usr/lib/golang/ ExecStart=/usr/lib/golang/go_executable
并且省略 run .
以下是我设置 systemd 的方式:
[Unit]
Description=gowebdev
[Service]
Type=simple
User=root
Restart=always
RestartSec=5s
WorkingDirectory=/var/www/gowebdev
ExecStart=/var/www/gowebdev/main
[Install]
WantedBy=multi-user.target
编辑:同时检查 Go 可执行文件是否具有 chmod 0755 权限。
在Go中实现守护进程运行有多种方法,systemd只是其中一种。以下是几种常见的实现方式:
1. 使用systemd(推荐的生产环境方案)
你的systemd配置有几个问题需要修正:
[Unit]
Description=Comm Test Socket Server
After=network.target
[Service]
Type=simple
User=root
# 建议使用非root用户,如:User=nobody
WorkingDirectory=/usr/local/bin
# 这里应该是可执行文件的路径,不是目录
ExecStart=/usr/local/bin/commTest
Restart=always
RestartSec=10
StandardOutput=journal
StandardError=journal
SyslogIdentifier=commtest
[Install]
WantedBy=multi-user.target
关键修正:
ExecStart应该指向编译好的二进制文件路径WorkingDirectory应该设置为程序运行的工作目录- 移除了
ConditionPathExists避免路径检查问题
2. 使用daemon库实现程序自守护
在Go程序中集成守护进程逻辑:
package main
import (
"log"
"os"
"os/exec"
"syscall"
)
func daemonize() {
// 第一次fork
if os.Getppid() != 1 {
cmd := exec.Command(os.Args[0], os.Args[1:]...)
cmd.Stdin = nil
cmd.Stdout = nil
cmd.Stderr = nil
cmd.SysProcAttr = &syscall.SysProcAttr{Setsid: true}
if err := cmd.Start(); err != nil {
log.Fatalf("第一次fork失败: %v", err)
}
os.Exit(0)
}
// 第二次fork(Unix双重fork技术)
syscall.Umask(0)
if _, err := syscall.Setsid(); err != nil {
log.Fatalf("创建新会话失败: %v", err)
}
// 更改工作目录
if err := os.Chdir("/"); err != nil {
log.Fatalf("更改工作目录失败: %v", err)
}
// 关闭标准文件描述符
nullFile, err := os.OpenFile("/dev/null", os.O_RDWR, 0)
if err == nil {
syscall.Dup2(int(nullFile.Fd()), int(os.Stdin.Fd()))
syscall.Dup2(int(nullFile.Fd()), int(os.Stdout.Fd()))
syscall.Dup2(int(nullFile.Fd()), int(os.Stderr.Fd()))
nullFile.Close()
}
}
func main() {
daemonize()
// 你的套接字服务器代码
log.Println("守护进程启动成功")
// ... 服务器逻辑
}
3. 使用第三方守护进程库
使用github.com/takama/daemon:
package main
import (
"fmt"
"log"
"net"
"os"
"github.com/takama/daemon"
)
const (
name = "commtest"
description = "Communication Test Server"
port = ":8080"
)
var stdlog, errlog *log.Logger
type Service struct {
daemon.Daemon
}
func (service *Service) Manage() (string, error) {
usage := "Usage: commtest install | remove | start | stop | status"
if len(os.Args) > 1 {
command := os.Args[1]
switch command {
case "install":
return service.Install()
case "remove":
return service.Remove()
case "start":
return service.Start()
case "stop":
return service.Stop()
case "status":
return service.Status()
default:
return usage, nil
}
}
// 运行服务器
listener, err := net.Listen("tcp", port)
if err != nil {
return "无法监听端口", err
}
defer listener.Close()
stdlog.Println("服务器启动在", port)
for {
conn, err := listener.Accept()
if err != nil {
break
}
go handleClient(conn)
}
return "服务停止", nil
}
func handleClient(conn net.Conn) {
defer conn.Close()
// 处理客户端连接
}
func main() {
stdlog = log.New(os.Stdout, "", log.Ldate|log.Ltime)
errlog = log.New(os.Stderr, "", log.Ldate|log.Ltime)
srv, err := daemon.New(name, description, daemon.SystemDaemon)
if err != nil {
errlog.Println("错误: ", err)
os.Exit(1)
}
service := &Service{srv}
status, err := service.Manage()
if err != nil {
errlog.Println(status, "\n错误: ", err)
os.Exit(1)
}
fmt.Println(status)
}
使用supervisord管理:
创建supervisor配置文件 /etc/supervisor/conf.d/commtest.conf:
[program:commtest]
command=/usr/local/bin/commTest
directory=/usr/local/bin
user=root
autostart=true
autorestart=true
startsecs=10
startretries=3
stdout_logfile=/var/log/commtest.out.log
stdout_logfile_maxbytes=10MB
stdout_logfile_backups=10
stderr_logfile=/var/log/commtest.err.log
stderr_logfile_maxbytes=10MB
stderr_logfile_backups=10
environment=GIN_MODE="release"
4. 使用nohup简单后台运行
# 直接运行
nohup /usr/local/bin/commTest > /var/log/commtest.log 2>&1 &
# 使用start-stop-daemon(Debian/Ubuntu)
start-stop-daemon --start --background --make-pidfile \
--pidfile /var/run/commtest.pid \
--exec /usr/local/bin/commTest \
--chuid nobody:nogroup \
--startas /usr/local/bin/commTest
5. 使用容器化部署(Docker)
创建Dockerfile:
FROM golang:alpine AS builder
WORKDIR /app
COPY . .
RUN go build -o commTest .
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /app/commTest .
EXPOSE 8080
CMD ["./commTest"]
创建docker-compose.yml:
version: '3.8'
services:
commtest:
build: .
ports:
- "8080:8080"
restart: always
volumes:
- ./logs:/var/log
总结建议
- 生产环境:使用systemd(修正配置后)是最佳选择,提供完整的进程管理、日志收集和监控
- 开发环境:可以使用supervisord或直接使用daemon库
- 容器环境:使用Docker部署
- 快速测试:使用nohup或start-stop-daemon
对于你的具体问题,首先修正systemd配置中的ExecStart路径,确保指向正确的可执行文件位置。

