• Systemd


    Systemd 服务

    构建 Systemd 服务

    让我们从构建一个普通用户可以(手动)运行的 systemd 服务开始,然后再逐步增加内容。

    不需要管理员权限即可运行的服务位于 ~/.config/systemd/user/,故首先需要创建这个目录:

    cdmkdir -p ~/.config/systemd/user/
    

    有很多类型的 systemd 单元 (曾经叫做 systemd 脚本),包括“计时器”和“路径”等,但我们这里关注的是“服务”类型。在 ~/.config/systemd/user/ 目录中创建 minetest.service 文件,使用文本编辑器打开并输入如下内容:

    # minetest.service
    [Unit]
    Description= Minetest 
    serverDocumentation= https://wiki.minetest.net/Main_Page
    [Service]
    Type= simple
    ExecStart= /usr/games/minetest --server
    

    [Unit] 段主要为用户提供信息,给出该单元的描述及如何获得更多相关文档。

    [Service] 段使用 Type 指令确定服务类型。服务有多种类型,下面给出两个示例。

    如果你运行的进程设置环境变量、调用另外一个进程(主进程)、退出运行,那么你应该使用的服务类型为 forking

    如果你希望在你的单元对应进程结束运行前阻断其他单元运行,那么你应该使用的服务类型为 oneshot

    如果你希望启动服务器并使其在后台持续运行;这种情况下应该使用 simple 类型。

    ExecStart 指令给出 systemd 需要运行的程序。在本例中,你希望在后台运行 minetest 服务器。如上所示,你可以在可执行程序后面添加参数。

    注意:

    1. 不能将一系列 Bash 命令通过管道连接起来。下面给出的例子无法工作:

      ExecStart: lsmod | grep nvidia > videodrive.txt
      

      如果你需要将 Bash 命令通过管道连接起来,可以将其封装到一个脚本中,然后运行该脚本。

    2. systemd 要求你给出程序的完整路径。如果你想使用 simple 类型运行类似 ls 的命令,你需要使用 ExecStart= /bin/ls

    ExecStop 指令用于定制服务终止的方式。如果你没有指定 ExecStop,systemd 会帮你尽可能友好地终止进程。

    systemd.directives 的帮助页中包含完整指令列表,另外你可以在该网站上找到同样的列表,点击即可查看每个指令的具体信息。

    虽然只有 6 行,但你的 minetest.service 已经是一个有完整功能的 systemd 单元。

    执行如下命令启动服务:

    systemd --user start minetest
    

    执行如下命令终止服务:

    systemd --user stop minetest
    

    选项 --user 告知 systemd 在你的本地目录中检索服务并用你的用户权限执行服务。

    进阶

    让我们更进一步,让其可以向玩家发邮件,包括在服务器可用时通知玩家,在服务器关闭前警告玩家:

    # minetest.service
    [Unit]
    Description= Minetest server
    Documentation= https://wiki.minetest.net/Main_Page
    [Service]Type= simple
    ExecStart= /usr/games/minetest --server
    ExecStartPost= /home/<username>/bin/mtsendmail.sh "Ready to rumble?" "Minetest Starting up"
    TimeoutStopSec= 180
    ExecStop= /home/<username>/bin/mtsendmail.sh "Off to bed. Nightie night!" "Minetest Stopping in 2 minutes"
    ExecStop= /bin/sleep 120
    ExecStop= /bin/kill -2 $MAINPID
    

    ExecStartPost 指令在主进程启动后马上执行任何你指定的操作。在本例中,你执行了一个自定义脚本 mtsendmail (内容如下),该脚本以邮件形式通知你的朋友服务器已经启动。

    #!/bin/bash
    # mtsendmail
    echo $1 | mutt -F /home/<username>/.muttrc -s "$2" my_minetest@mailing_list.com
    

    我们使用 Mutt 这个命令后邮件客户端发送消息。虽然从实际效果来看,上述脚本仅有 1 行,但 systemd 单元的参数中不能包含管道及重定向操作,故我们需要将其封装到脚本中。

    ExecStartPre 指令用于在服务主进程执行之前进行指定操作。

    接下来我们看到,关闭服务器涉及了好几条指令。

    TimeoutStopSec 指令用于设置 systemd 友好关闭服务的最大等候时间,默认值大约是 90 秒。超过这个最大等候时间,systemd 会强制关闭服务并报错。考虑到你希望在彻底关闭服务器前给用户预留几分钟的时间,你需要将超时时间提高至 3 分钟,这样 systemd 就不会误认为服务关闭时出现问题。

    接下来是关闭服务的具体指令部分。虽然没有 ExecStopPre 这样的指令,但你可以通过多次使用 ExecStop 指令实现关闭服务器前执行操作的目标。多个 ExecStop 指令按从上到下的顺序依次运行,这样你就可以在服务器真正关闭前向用户发送消息。

    通过这个特性,你首先应该向你的朋友发邮件,警告其服务器即将关闭,然后等待两分钟,最后关闭服务器。可以使用 Ctrl + c 关闭 Minetest 服务器,该操作会被转换为一个中断信号(SIGINT);当你执行 kill -2 $MAINPID 时就会发送该中断信号,其中 $MAINPID 是 systemd 变量,用于记录你服务中主进程的 PID 信息。

    开机自启动

    下一步我们让你的服务在主机启动后立即可用,在主机关闭时自动关闭。

    我们需要将你的服务文件移动到系统服务目录,即 /etc/systemd/system/

    sudo mv /home/<username>/.config/systemd/user/minetest.service /etc/systemd/system/
    

    如果你希望此时启动该服务,你需要拥有超级用户权限:

    sudo systemctl start minetest
    

    另外,可以使用如下命令检查服务状态:

    sudo systemctl status minetest
    

    你会发现服务很糟糕地处于失败状态,这是因为 systemd 不能通过上下文信息、特征、配置文件得知具体使用哪个用户运行该服务。在单元文件中增加 User 指令可以解决这个问题。

    # minetest.service
    [Unit]
    Description= Minetest server
    Documentation= https://wiki.minetest.net/Main_Page
    [Service]
    Type= simple
    User= <username>
    ExecStart= /usr/games/minetest --server
    ExecStartPost= /home/<username>/bin/mtsendmail.sh "Ready to rumble?"  "Minetest Starting up"
    TimeoutStopSec= 180
    ExecStop= /home/<username>/bin/mtsendmail.sh "Off to bed. Nightie night!"  "Minetest Stopping in 2 minutes"
    ExecStop= /bin/sleep 120
    ExecStop= /bin/kill -2 $MAINPID
    

    systemd 从 User 指令中得知应使用哪个用户的环境变量来正确运行该服务。你可以使用 root 用户,但这可能产生安全风险;使用你的个人用户会好一些,但不少管理员的做法是为服务单独创建一个用户,这样可以有效地将服务与其它用户和系统组件相互隔离。

    下一步我们让你的服务在系统启动时自动启动,系统关闭时自动关闭。要达到这个目的,你需要 启用 你的服务;但在这之前,你还需要告知 systemd 从哪里 安装 它。

    对于 systemd 而言,安装 意味着告知 systemd 在系统启动的具体哪个步骤激活你的服务。以通用 Unix 打印系统(cups.service)为例,它的启动在网络框架启动之后、其它打印服务启动之前。又如,minetest.server 需要使用用户邮件(及其它组件),需要等待网络和普通用户对应的服务就绪后才可启动。

    你只需要在单元文件中添加一个新段和新指令:

    ...
    [Install]
    WantedBy= multi-user.target
    

    你可以将其理解为“等待多用户系统的全部内容就绪”。systemd 中的“目标”类似于旧系统中的运行级别,可以用于将主机转移到一个或另一个状态,也可以像本例中这样让你的服务等待指定状态出现后运行。

    你的最终 minetest.service 文件如下:

    # minetest.service
    [Unit]
    Description= Minetest server
    Documentation= https://wiki.minetest.net/Main_Page
    
    [Service]
    Type= simple
    User= <username>
    ExecStart= /usr/games/minetest --server
    ExecStartPost= /home/<username>/bin/mtsendmail.sh "Ready to rumble?"  "Minetest Starting up"
    TimeoutStopSec= 180
    ExecStop= /home/<username>/bin/mtsendmail.sh "Off to bed. Nightie night!"  "Minetest Stopping in 2 minutes"
    ExecStop= /bin/sleep 120
    ExecStop= /bin/kill -2 $MAINPID
    
    [Install]
    WantedBy= multi-user.target
    

    在尝试新的服务之前,你还需要对邮件脚本做一些调整:

    #!/bin/bash
    # mtsendmail
    sleep 20
    echo $1 | mutt -F /home/<username>/.muttrc -s "$2" my_minetest@mailing_list.com
    sleep 10
    

    这是因为系统需要一定的时间启动邮件系统(这里等待 20 秒),也需要一定时间完成邮件发送(这里等待 10 秒)。注意脚本中的等待时间数值适用于我的系统,你可能需要针对你的系统调整数值。

    大功告成啦。执行如下操作:

    sudo systemctl enable minetest
    

    你的 Minetest 服务将在系统启动时自动启动,在系统关闭时友好关闭并通知你的用户。

  • 相关阅读:
    Web开发利器Webstorm导入多个文件夹或者项目
    js react 全选和反选
    nginx的配置文件 【nginx.conf】
    nginx 服务器重启命令,关闭
    Nginx反向代理新篇-使用location对多个URL做反向代理
    Windows下Nginx的安装与配置
    es6 递归 tree
    自定义table样式
    数据库(7)
    数据库(6)
  • 原文地址:https://www.cnblogs.com/backups/p/systemd.html
Copyright © 2020-2023  润新知