使用systemd创建Linux的服务

评论 33 浏览 0 2017-09-05

在编写Web应用程序时,我经常需要将计算量大的任务卸载到异步工作脚本中,计划任务,或者甚至编写一个监听套接字的守护程序,直接与客户端进行通信。

虽然有时可能有更好的工具来完成这项工作--总是首先考虑使用现有的软件,如任务队列服务器--但编写你自己的服务可以给你带来一定程度的灵活性,如果受到第三方软件的约束,你将永远无法获得这种灵活性。

最酷的是,创建一个Linux服务是相当容易的:用你最喜欢的编程语言写一个长期运行的程序,然后用systemd把它变成一个服务。

程序

让我们用PHP创建一个小型服务器。我可以看到你的眉毛都竖起来了,但它的效果出奇的好。我们将监听UDP端口10000,并将收到的任何消息以ROT13的形式返回。

<?php

$sock = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
socket_bind($sock, '0.0.0.0', 10000);

for (;;) {
    socket_recvfrom($sock, $message, 1024, 0, $ip, $port);
    $reply = str_rot13($message);
    socket_sendto($sock, $reply, strlen($reply), 0, $ip, $port);
}

让我们开始行动吧。

$ php server.php

并在另一个终端中测试它。

$ nc -u 127.0.0.1 10000
Hello, world!
Uryyb, jbeyq!

很好,它起作用了。现在,我们希望这个脚本能够一直运行,在发生故障(意外退出)时被重新启动,甚至在服务器重启后也能继续运行。这就是systemd发挥作用的地方了。

将其转化为一种service

让我们创建一个名为/etc/systemd/system/rot13.service的文件。

[Unit]
Description=ROT13 demo service
After=network.target
StartLimitIntervalSec=0
[Service]
Type=simple
Restart=always
RestartSec=1
User=centos
ExecStart=/usr/bin/env php /path/to/server.php

[Install]
WantedBy=multi-user.target

你将需要:

  • User=之后设置你的实际用户名
  • ExecStart=中设置你的脚本的正确路径

就这样了。我们现在可以开始服务了。

$ systemctl start rot13

并自动让它在开机时启动。

$ systemctl enable rot13

更进一步

现在,你的服务(希望)已经工作了,可能有必要更深入地研究一下配置选项,并确保它总是按照你所期望的那样工作。

按正确的顺序开始

您可能想知道 After= 指令的作用。它只是意味着您的服务必须在网络准备就绪后启动。如果您的程序希望 MySQL 服务器启动并运行,您应该添加:

After=mysqld.service

在退出时重新启动

默认情况下,如果程序因某种原因退出,systemd 不会重启你的服务。这通常不是你想要的,因为服务必须一直可用,所以我们要指示它总是在退出时重启。

Restart=always

你也可以使用on-failure,只在退出状态不是0的情况下才重新启动。

默认情况下,systemd 会在 100ms 后尝试重启。你可以通过以下方式指定重启前的等待秒数。

RestartSec=1

避开陷阱:开始时的限制

我个人不止一次地陷入这种情况。默认情况下,当你像我们一样配置Restart=always时,如果 systemd 在 10 秒的间隔内启动失败超过 5 次,systemd 将放弃重新启动您的服务。永远。

有两个[Unit]配置选项负责这个问题。

StartLimitBurst=5
StartLimitIntervalSec=10

RestartSec指令对结果也有影响:如果你把它设置为3秒后重启,那么你就永远不可能在10秒内达到5次失败的重试。

简单的解决方法是设置StartLimitIntervalSec=0这样一来,systemd就会永远尝试重启你的服务。

不过,将RestartSec设置为至少1秒是个好主意,以避免在事情开始出错时给你的服务器带来太大的压力。

作为替代方案,你可以保留默认设置,并要求systemd在达到启动限制时重启服务器,方法是使用StartLimitAction=reboot

真的是这样吗?

这就是用systemd创建一个Linux服务的全部过程:写一个小的配置文件,引用你的长期运行的程序。

Systemd几年来已经成为RHEL/CentOS、Fedora、Ubuntu、Debian和其他系统的默认启动系统,所以你的服务器很有可能已经准备好承载你的自制服务了。

最后更新2023-01-13
33 个评论
#1 retrixe 2017-12-02

我为你的完美和简单的解释拍了50次掌。

#2 Dragas 2018-11-21

根据https://wiki.archlinux.org/index.php/systemd,你不应该把用户服务安装到/lib/systemd/system,而应该安装到/etc/systemd/system,因为你的服务不是由软件包安装的。你是否介意修改你的文章以反映这一点?

Benjamin Morel 2018-11-22

说得好,谢谢你!已在文章中修正。

#3 Vincent Cardillo 2017-09-19

伟大的文章。谢谢本杰明。

#4 Nicholas Nguyen 2019-05-23

嘿,我鼓掌了,直到它是3K:)。

#5 Dmitri Sinitsa 2018-04-03

不错。谢谢!:)

#6 hossein nemati 2018-07-07

谢谢你。简单明了

#7 Uri Sternik 2018-08-08

这个程序对我来说不起作用了。

我看到这个端口用nmap打开了,但当我在运行netcat命令(nc -u 127.0.0.0 5654)(我用的是端口号5654)后输入时,我没有得到任何输出...

我复制了这个命令,有什么建议吗?

Deepak Kapiswe 2020-08-08

真的真的很有帮助,简洁明了的解释,对我很有帮助,非常感谢,+50次掌声!。

#8 Tiago S. Martins 2018-07-03

你好!请问你是谁?

我在使用你的.service归档模板时遇到了一个问题。

我的Ubuntu发行版16.04.4 LTS向我抛出了一个错误:未知的lvalue 'StartLimitIntervalSec'在[Service]。

我在这里找到了解决方案,https://lists.freedesktop.org/archives/systemd-devel/2017-July/039254.html

它说,StartLimitIntervalSec 应该是StartLimitInterval instead。

还有一件事,StartLimitInterval是在[服务]中,而不是在[单位]中。

但除此以外,非常好的教程,非常感谢!

Benjamin Morel 2018-07-04

你好,看起来StartLimitIntervalSec新名称,在systemd-230中引入。Xenial似乎使用的是229版本,所以你必须使用旧名称。

此外,我不知道以前的版本,但在目前的版本中,StartLimitIntervalSec [Unit]

https://www.freedesktop.org/software/systemd/man/systemd.unit.html

#9 Cnly Chen 2018-07-02

误以为`StartLimitAction=reboot`是服务的自动重启,直到有一天我发现整个系统进入重启循环......XD

不过,这篇文章很不错。

#10 Jonah Uka 2022-09-09

我试着在我的ubuntu 20.04 LTS终端上运行php代码,但它没有显示任何东西。

#11 JeremyStarTM 2022-08-16

我只是复制了一些系统服务配置并将其用于我自己的服务。但仍然是很好的指南!

#12 Nadzeya Gut 2022-07-26

谢谢你的文章!

#13 Jānis Elmeris 2021-12-03

停止服务的情况如何?我们如何实现优雅的停止,以便服务不会(通常)在会导致数据损坏或不一致或类似情况的时刻被中断?

#14 Luisa Grigorescu 2021-03-25

很好的文章。

我想保持一个C++网络服务器的运行,但它总是在执行后退出。

在execStart命令中,我放置了可执行文件的绝对路径。我做错了什么?

#15 Dep. Producción (Grupo 9Lands) 2020-12-14

很棒的文章。 你能用 Photoshop 把一个螺栓合成到你在页眉上使用的剪贴画图片中吗? 给我 TOC,一个螺栓不见了(在金属平台上)。 哈哈哈..这是个玩笑,...好吧,半开玩笑,半认真 >:(

Benjamin Morel 2020-12-14

我认为这反映了大多数软件的编写方式。2/3完成;-)

#16 Roger Tranchez 2020-09-25

是的!我终于找到了对系统配置文件中的mo**3rf*****s选项的一个很好的解释;那个5-倍的限制突发让我非常头疼。10分!!!。

#17 Arun Menon 2020-09-07

谢谢。这很有帮助。

#18 midix 2020-08-31

我想这就是为什么我看到的一些例子中的

重启秒数=10

并且没有任何额外的重新启动时间设置。

因此,你永远不会在10秒内达到5个故障,你的服务会在10秒内自动重新启动,给你足够的时间来解决一些配置问题等。

#19 William C Bonner 2020-08-25

我喜欢这句话,因为如果没有一个正常的网络,我的程序就无法运行。

After=network-online.target

#20 Josh B 2020-07-13

优秀的帖子。很快就起来了!我鼓掌,直到它不再计数。

#21 Rômulo Costa 2020-06-09

我有一个无法理解的问题。 我在网站上用wss运行一个聊天的websocket。 如果我在终端运行。"php my-file.php",聊天就能正常工作。但当我创建服务以保持这个相同的websocket处于活动状态时,我在浏览器中得到错误ERR_SSL_VERSION_OR_CIPHER_MISMATCH

#22 Pitter 2020-05-19

首先,祝贺你的文章/教程非常棒(而且客观)。其次,一个提示:你总是要告知执行命令的绝对路径(你可以使用 "which "命令来发现它在哪里)。

#23 Kuly 2020-04-08

惊人的教程Edward Burton你能帮助我解决这个问题吗?我在使用Golang。

进程。8316 ExecStart=/usr/bin/env go /home/testbed/Desktop/test/test.go(代码=exited,状态=127)。

我小心翼翼地按照这个手册操作,但在运行systemctl start watcher(watcher是我的服务名称)后,错误出现了

#24 Cwadamsmith 2019-11-11

随着越来越多的公司转向云计算,基于Linux的服务器正日渐流行起来。云服务器是安全的,而且数据可以在任何地方提取。基于Linux的云服务器帮助开发者轻松启动应用程序,定义工作流程,并托管各种服务。

#25 Chris Stigas 2019-09-28

在创建服务后,你还必须生成系统的依赖关系树

> sudo systemctl daemon-reload

Benjamin Morel 2019-10-03

我在本地又试了一次,在创建服务后,我不需要发出守护进程的重载,我可以直接启用它并启动它。

是不是因为在这个特定的案例中没有依赖性问题?

#26 shubham singla 2019-07-14

感谢这篇令人敬畏的文章。

我能够用$sudo systemctl start myservice.service来启动和运行服务。

但无法通过$sudo systemctl enable myservice.service使其在系统启动后自动启动。

日志中。

ubuntu@ip-xxx–xx–xx–xxx:~$ sudo systemctl enable myservice.service

Failed to enable unit: File mongooseim.service: Invalid argument

ubuntu@ip-xxx–xx–xx–xxx:~$ sudo systemctl — 用户启用 myservice.service

Failed to connect to bus: No such file or directory

有什么建议吗?

#27 Muflone Ovinis 2019-05-24

伟大而有用的文章。

谢谢你

#28 Joseph Fisher 2019-03-26

我是一个老的死硬的Unix/Linux管理员。

我有多年的编写shell脚本的经验。

有了init.d,我就可以自由地创建我想要的复杂的服务脚本。

例如,我在工作中的每一台服务器上都有Tomcat的启动脚本。

在我的脚本中包括了以下一些 "选项"。

Start)

Stop)

Restart)

Status)

Errors) # 搜索所有的Tomcat日志文件,以查找错误。

Tail)# 主应用程序日志文件的tail -f,以监视应用程序的出现。

Help)# 列出每一个可用的选项。

所有这些,都是通过一个简单的shell脚本实现的。

我没有看到用systemd来做这件事的方法,对我来说,这很令人沮丧。

我的命令可以是:

服务tomcat启动

服务tomcat停止

服务Tomcat重新启动

服务tomcat的错误

服务汤姆猫的尾巴

服务Tomcat的帮助

如此容易实现。

调试起来要容易得多。

有没有办法让我在systemd中实现这些相同的功能?

如果有的话,我还没找到呢!"。

Systemd甚至不如init.d方便用户使用。

如果有任何帮助,任何有关这种修改的例子的参考链接,我们将不胜感激。

Benjamin Morel 2019-03-26

嗨,Joseph,我绝不是Systemd专家,恐怕无法帮助你解决这个问题。

Martin Dobrev 2022-02-18

根据手册(https://www.freedesktop.org/software/systemd/man/systemd.service.html)的规定

有一些部分,如。ExecStartPre, ExecStartPost, ExecCondition, ....,使其更加灵活。

#29 Ram Subramanian 2019-02-20

伟大的文章本杰明-莫雷尔,我们可以在linkedin上联系吗,我还有一个问题想听听你的专家意见。 你的linkedin id是什么?

#30 Demo2 2019-01-15

系统说:Unknown lvalue ‘StartLimitIntervalSec’ in section ‘Unit’

Benjamin Morel 2019-01-15

你好,上面已经讨论过这个问题了。

https://medium.com/@heimdhall/hello-45e8fa06cdd7

#31 Nick C 2018-11-22

有什么办法可以运行修改过的ExecStart。

ExecStart=/usr/bin/npm npm run start

ExecStart2=/usr/bin/npm npm run clean-start

在这种情况下,运行 "ExecStart2 "的命令会是什么呢?

#32 Marian Forums 2018-10-30

嗨,本杰明。

谢谢你的解释。但我有一个问题,当我对服务进行状态检查时(我为postgresql创建了一个服务),它也试图启动该服务。这在Red Hat 7上是否正常?

事先谢谢你。

问候语。

玛丽安-

#33 Subramanian Sridharan 2018-07-25

你好!请问你是什么人?

谢谢你提供的有用的帖子!

这对我来说是有效的。然而,我的需求是,我应该能够以普通用户而不是root身份运行该服务。

但是,我的rot13.service是以root身份运行的。

有人能帮助我吗?

Benjamin Morel 2018-07-26

你好,它不应该以root身份运行,你是否将这一行设置为你的用户名?

User=centos

在启动你的服务后,检查以下命令。

ps aux | grep "\.php"

与你的server.php文件相匹配的第一列应该与上面设置的用户名相匹配!

标签