使用systemd创建Linux的服务
在编写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和其他系统的默认启动系统,所以你的服务器很有可能已经准备好承载你的自制服务了。
根据https://wiki.archlinux.org/index.php/systemd,你不应该把用户服务安装到/lib/systemd/system,而应该安装到/etc/systemd/system,因为你的服务不是由软件包安装的。你是否介意修改你的文章以反映这一点?
说得好,谢谢你!已在文章中修正。
伟大的文章。谢谢本杰明。
嘿,我鼓掌了,直到它是3K:)。
不错。谢谢!:)
谢谢你。简单明了
这个程序对我来说不起作用了。
我看到这个端口用nmap打开了,但当我在运行netcat命令(nc -u 127.0.0.0 5654)(我用的是端口号5654)后输入时,我没有得到任何输出...
我复制了这个命令,有什么建议吗?
真的真的很有帮助,简洁明了的解释,对我很有帮助,非常感谢,+50次掌声!。
你好!请问你是谁?
我在使用你的.service归档模板时遇到了一个问题。
我的Ubuntu发行版16.04.4 LTS向我抛出了一个错误:未知的lvalue 'StartLimitIntervalSec'在[Service]。
我在这里找到了解决方案,https://lists.freedesktop.org/archives/systemd-devel/2017-July/039254.html。
它说,StartLimitIntervalSec 应该是StartLimitInterval instead。
还有一件事,StartLimitInterval是在[服务]中,而不是在[单位]中。
但除此以外,非常好的教程,非常感谢!
你好,看起来StartLimitIntervalSec
是新名称,在systemd-230中引入。Xenial似乎使用的是229版本,所以你必须使用旧名称。
此外,我不知道以前的版本,但在目前的版本中,StartLimitIntervalSec 是在[Unit]
。
https://www.freedesktop.org/software/systemd/man/systemd.unit.html
误以为`StartLimitAction=reboot`是服务的自动重启,直到有一天我发现整个系统进入重启循环......XD
不过,这篇文章很不错。
我试着在我的ubuntu 20.04 LTS终端上运行php代码,但它没有显示任何东西。
我只是复制了一些系统服务配置并将其用于我自己的服务。但仍然是很好的指南!
谢谢你的文章!
停止服务的情况如何?我们如何实现优雅的停止,以便服务不会(通常)在会导致数据损坏或不一致或类似情况的时刻被中断?
很好的文章。
我想保持一个C++网络服务器的运行,但它总是在执行后退出。
在execStart命令中,我放置了可执行文件的绝对路径。我做错了什么?
很棒的文章。 你能用 Photoshop 把一个螺栓合成到你在页眉上使用的剪贴画图片中吗? 给我 TOC,一个螺栓不见了(在金属平台上)。 哈哈哈..这是个玩笑,...好吧,半开玩笑,半认真 >:(
我认为这反映了大多数软件的编写方式。2/3完成;-)
是的!我终于找到了对系统配置文件中的mo**3rf*****s选项的一个很好的解释;那个5-倍的限制突发让我非常头疼。10分!!!。
谢谢。这很有帮助。
我想这就是为什么我看到的一些例子中的
重启秒数=10
并且没有任何额外的重新启动时间设置。
因此,你永远不会在10秒内达到5个故障,你的服务会在10秒内自动重新启动,给你足够的时间来解决一些配置问题等。
我喜欢这句话,因为如果没有一个正常的网络,我的程序就无法运行。
After=network-online.target
优秀的帖子。很快就起来了!我鼓掌,直到它不再计数。
我有一个无法理解的问题。 我在网站上用wss运行一个聊天的websocket。 如果我在终端运行。"php my-file.php",聊天就能正常工作。但当我创建服务以保持这个相同的websocket处于活动状态时,我在浏览器中得到错误ERR_SSL_VERSION_OR_CIPHER_MISMATCH
首先,祝贺你的文章/教程非常棒(而且客观)。其次,一个提示:你总是要告知执行命令的绝对路径(你可以使用 "which "命令来发现它在哪里)。
惊人的教程Edward Burton你能帮助我解决这个问题吗?我在使用Golang。
进程。8316 ExecStart=/usr/bin/env go /home/testbed/Desktop/test/test.go(代码=exited,状态=127)。
我小心翼翼地按照这个手册操作,但在运行systemctl start watcher(watcher是我的服务名称)后,错误出现了
随着越来越多的公司转向云计算,基于Linux的服务器正日渐流行起来。云服务器是安全的,而且数据可以在任何地方提取。基于Linux的云服务器帮助开发者轻松启动应用程序,定义工作流程,并托管各种服务。
在创建服务后,你还必须生成系统的依赖关系树
> sudo systemctl daemon-reload
我在本地又试了一次,在创建服务后,我不需要发出守护进程的重载,我可以直接启用它并启动它。
是不是因为在这个特定的案例中没有依赖性问题?
感谢这篇令人敬畏的文章。
我能够用$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
有什么建议吗?
伟大而有用的文章。
谢谢你
我是一个老的死硬的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方便用户使用。
如果有任何帮助,任何有关这种修改的例子的参考链接,我们将不胜感激。
嗨,Joseph,我绝不是Systemd专家,恐怕无法帮助你解决这个问题。
根据手册(https://www.freedesktop.org/software/systemd/man/systemd.service.html)的规定
有一些部分,如。ExecStartPre, ExecStartPost, ExecCondition, ....,使其更加灵活。
伟大的文章本杰明-莫雷尔,我们可以在linkedin上联系吗,我还有一个问题想听听你的专家意见。 你的linkedin id是什么?
系统说:Unknown lvalue ‘StartLimitIntervalSec’ in section ‘Unit’
你好,上面已经讨论过这个问题了。
有什么办法可以运行修改过的ExecStart。
ExecStart=/usr/bin/npm npm run start
ExecStart2=/usr/bin/npm npm run clean-start
在这种情况下,运行 "ExecStart2 "的命令会是什么呢?
嗨,本杰明。
谢谢你的解释。但我有一个问题,当我对服务进行状态检查时(我为postgresql创建了一个服务),它也试图启动该服务。这在Red Hat 7上是否正常?
事先谢谢你。
问候语。
玛丽安-
你好!请问你是什么人?
谢谢你提供的有用的帖子!
这对我来说是有效的。然而,我的需求是,我应该能够以普通用户而不是root身份运行该服务。
但是,我的rot13.service是以root身份运行的。
有人能帮助我吗?
你好,它不应该以root身份运行,你是否将这一行设置为你的用户名?
User=centos
在启动你的服务后,检查以下命令。
ps aux | grep "\.php"
与你的server.php文件相匹配的第一列应该与上面设置的用户名相匹配!
我为你的完美和简单的解释拍了50次掌。