百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

如何在 Linux 中创建 Systemd 服务?

toyiye 2024-06-30 09:41 12 浏览 0 评论

1. 写在前面

在 Linux 中,有多种方法可以将程序作为后台服务运行,如 crontab、.bashrc 等,本文将首先介绍 systemd 基本情况,并通过二个简单的示例介绍如何编写 systemd 服务脚本,实现 Linux 服务的自启动、启动、停止和重启管理。


2. Systemd 介绍

2.1 什么是 Systemd?

systemd 是 Linux 系统的一组基本构建块。于 2010 年首次推出,用于取代传统的 System V init 系统。它旨在提高启动速度,更有效地管理系统服务。如今,systemd 已成为许多流行 Linux 发行版的默认系统,包括 Ubuntu、Fedora 和 Red Hat Enterprise Linux等。

几乎所有版本的 Linux 都自带 systemd,但如果你的系统没有自带 systemd,可以通过运行以下命令安装:

sudo apt-get install -y systemd

注意:-y 标志表示快速安装软件包和依赖项;

查看 systemd 的版本:systemd --version

root@jpzhang-dev:~# systemd --version
-----------------------------------------------------------------------------------
systemd 245 (245.4-4ubuntu3.23)
+PAM +AUDIT +SELINUX +IMA +APPARMOR +SMACK +SYSVINIT +UTMP +LIBCRYPTSETUP +GCRYPT +GNUTLS +ACL +XZ +LZ4 +SECCOMP +BLKID +ELFUTILS +KMOD +IDN2 -IDN +PCRE2 default-hierarchy=hybrid

在 systemd 之前,Linux 的启动一直采用 init 进程,如用下面的命令启动服务:

sudo /etc/init.d/apache2 start
service apache2 start

这种方法有两个缺点:

  • 一、是启动时间长:init 进程是串行启动,只有前一个进程启动完,才会启动下一个进程。
  • 二、是启动脚本复杂:init 进程只是执行启动脚本,不管其他事情,脚本需要自己处理各种情况,这往往使得脚本变得很长。

2.2 Systemd 是守护进程吗?

systemd 并不是一个守护进程。相反,它是一个为 Linux 提供大量系统组件的软件套件。其中主要组件是一个 "系统和服务管理器",可作为引导用户空间和管理用户进程的系统。还能替代各种守护进程和实用程序,从设备、登录管理以及网络连接管理和事件记录。

设计目标:在不同的 Linux 发行版中实现服务配置和行为的标准化,为系统的启动和管理提供一套完整的解决方案。根据 Linux 惯例,字母 d 是守护进程(daemon)的缩写。Systemd 这个名字的含义,就是它要守护整个系统。使用了 Systemd,就不需要再用 init 了。Systemd 取代了 initd,成为系统的第一个进程(PID 等于 1),其他进程都是它的子进程。

2.3 Systemd 的主要功能

systemd 有很多功能,比如: 积极并行化操作、方便按需启动守护进程、使用 Linux 控制组监控进程、管理挂载点和自动挂载点,以及实现复杂的基于事务依赖关系的服务控制逻辑。

此外,还支持 SysV 和 LSB 启动脚本,可替代 SysVinit,提供一个日志守护进程和用于管理基本系统配置的实用程序。

2.4 系统管理命令汇总

Systemd 并不是一个命令,而是一组命令,涉及到系统管理各个方面;

systemctl:systemd 的主要命令,用于管理系统;

sudo systemctl reboot       # 重启系统
sudo systemctl poweroff     # 关闭系统,切断电源
sudo systemctl halt         # CPU 停止工作
sudo systemctl suspend      # 暂停系统
sudo systemctl hibernate    # 让系统进入冬眠状态
sudo systemctl hybrid-sleep # 让系统进入交互式休眠状态
sudo systemctl rescue       # 启动进入救援状态(单用户状态)

systemd-analyze: 用于查看启动耗时

systemd-analyze                             # 查看启动耗时
systemd-analyze blame                       # 查看每个服务的启动耗时
systemd-analyze critical-chain              # 显示瀑布状的启动过程流
systemd-analyze critical-chain docker.service  # 显示指定服务的启动流

hostnamectl: 查看当前主机的信息

hostnamectl                         # 显示当前主机的信息
sudo hostnamectl set-hostname xxx   # 设置主机名

localectl: 查看本地化设置

localectl                                   # 查看本地化设置
sudo localectl set-locale LANG=en_GB.utf8   # 设置本地化参数
sudo localectl set-keymap en_GB

timedatectl: 查看当前时区设置

timedatectl                                     # 查看当前时区设置
timedatectl list-timezones                      # 显示所有可用的时区
sudo timedatectl set-timezone America/New_York  # 设置当前时区
sudo timedatectl set-time YYYY-MM-DD
sudo timedatectl set-time HH:MM:SS

loginctl:查看当前登录的用户

loginctl list-sessions      # 列出当前 session
loginctl list-users         # 列出当前登录用户
loginctl show-user jpzhang   # 列出显示指定用户的信息

2.5 Unit

Systemd 可以管理所有系统资源,不同的资源统称为 Unit(单位);

2.5.1 Unit 种类

一共分成 12 种:

Service unit    # 系统服务
Target unit     # 多个 Unit 构成的一个组
Device Unit     # 硬件设备
Mount Unit      # 文件系统的挂载点
Automount Unit  # 自动挂载点
Path Unit       # 文件或路径
Scope Unit      # 不是由 Systemd 启动的外部进程
Slice Unit      # 进程组
Snapshot Unit   # Systemd 快照,可以切回某个快照
Socket Unit     # 进程间通信的 socket
Swap Unit       # swap 文件
Timer Unit      # 定时器

systemctl list-units 命令可以查看当前系统的所有 Unit:

systemctl list-units                        # 列出正在运行的 Unit
systemctl list-units --all                  # 列出所有 Unit,包括没有找到配置文件的或者启动失败的
systemctl list-units --all --state=inactive # 列出所有没有运行的 Unit
systemctl list-units --failed               # 列出所有加载失败的 Unit
systemctl list-units --type=service         # 列出所有正在运行的、类型为 service 的 Unit

2.5.2 Unit 状态

systemctl status 命令用于查看系统状态和单个 Unit 的状态;

systemctl status                                            # 显示系统状态
sysystemctl status docker.service                           # 显示单个 Unit 的状态
systemctl -H jpzhang@10.20.3.215 status docker.service      # 显示远程主机的某个 Unit 的状态

除了 systemctl 命令,status 还提供了三个查询状态的简单方法,主要供脚本内部的判断语句使用。

sudo systemctl is-active docker.service    # 显示某个 Unit 是否正在运行
sudo systemctl is-failed docker.service    # 显示某个 Unit 是否处于启动失败状态
sudo systemctl is-enabled docker.service   # 显示某个 Unit 服务是否建立了启动链接

2.5.3 Unit 管理

对于用户来说,最常用的是下面这些命令,用于启动和停止 Unit(主要是 service);

sudo systemctl start docker.service                     # 立即启动一个服务
sudo systemctl stop docker.service                      # 立即停止一个服务
sudo systemctl restart docker.service                   # 重启一个服务
sudo systemctl kill docker.service                      # 杀死一个服务的所有子进程
sudo systemctl reload docker.service                    # 重新加载一个服务的配置文件
sudo systemctl daemon-reload                            # 重载所有修改过的配置文件
sudo systemctl show docker.service                      # 显示某个 Unit 的所有底层参数
sudo systemctl show -p CPUShares docker.service         # 显示某个 Unit 的指定属性的值
sudo systemctl set-propertydocker.service CPUShares=500 # 设置某个 Unit 的指定属性

2.5.4 依赖关系

Unit 之间存在依赖关系:A 依赖于 B,就意味着 Systemd 在启动 A 的时候,同时会去启动 B。有些依赖是 Target 类型(详见下文),默认不会展开显示。如果要展开 Target,就需要使用 –all 参数;

systemctl list-dependencies --all xxx # 命令列出一个 Unit 的所有依赖。

2.6 Target

启动操作系统时,需要启动大量的 Unit。如果每一次启动,都要一一写明本次启动需要哪些 Unit,显然非常不方便。Systemd 的解决方案就是 Target。简单说,Target 就是一个 Unit 组,包含许多相关的 Unit 。启动某个 Target 的时候,Systemd 就会启动里面所有的 Unit。从这个意义上说,Target 这个概念类似于”状态点”,启动某个 Target 就好比启动到某种状态。传统的 init 启动模式里面,有 RunLevel 的概念,跟 Target 的作用很类似。不同的是,RunLevel 是互斥的,不可能多个 RunLevel 同时启动,但是多个 Target 可以同时启动。

systemctl list-unit-files --type=target         # 查看当前系统的所有 Target
systemctl list-dependencies multi-user.target   # 查看一个 Target 包含的所有 Unit
systemctl get-default                           # 查看启动时的默认 Target
sudo systemctl set-default multi-user.target    # 设置启动时的默认 Target

# 切换 Target 时,默认不关闭前一个 Target 启动的进程
# systemctl isolate 命令改变这种行为
sudo systemctl isolate multi-user.target        # 关闭前一个 Target 里面所有不属于后一个 Target 的进程

2.7 日志管理

Systemd 统一管理所有 Unit 的启动日志。带来的好处就是,可以只用 journalctl 一个命令,查看所有日志(内核日志和应用日志),日志的配置文件是 /etc/systemd/journald.conf,journalctl 功能强大,用法非常多。

sudo journalctl                                         # 查看所有日志(默认情况下 ,只保存本次启动的日志)
sudo journalctl -k                                      # 查看内核日志(不显示应用日志)
sudo journalctl -b                                      # 查看系统本次启动的日志
sudo journalctl -b -0
sudo journalctl -b -1                                   # 查看上一次启动的日志(需更改设置)
sudo journalctl --since="2024-03-20 18:17:16"           # 查看指定时间的日志
sudo journalctl --since "20 min ago"
sudo journalctl --since yesterday
sudo journalctl --since "2024-03-10" --until "2024-03-30 03:00"
sudo journalctl --since 09:00 --until "1 hour ago"
sudo journalctl -n                                      # 显示尾部的最新 10 行日志
sudo journalctl -n 20                                   # 显示尾部指定行数的日志
sudo journalctl -f                                      # 实时滚动显示最新日志
sudo journalctl /usr/lib/systemd/systemd                # 查看指定服务的日志
sudo journalctl _PID=1                                  # 查看指定进程的日志
sudo journalctl /usr/bin/bash                           # 查看某个路径的脚本的日志
sudo journalctl _UID=33 --since today                   # 查看指定用户的日志
sudo journalctl -u docker.service                        # 查看某个 Unit 的日志
sudo journalctl -u docker.service --since today
sudo journalctl -u docker.service -f                     # 实时滚动显示某个 Unit 的最新日志
sudo journalctl -u docker.service -u atd.service --since today    # 合并显示多个 Unit 的日志
# 查看指定优先级(及其以上级别)的日志,共有 8 级
# 0: emerg
# 1: alert
# 2: crit
# 3: err
# 4: warning
# 5: notice
# 6: info
# 7: debug
sudo journalctl -p err -b
sudo journalctl --no-pager                              # 日志默认分页输出,--no-pager 改为正常的标准输出
sudo journalctl -b -u docker.service -o json             # 以 JSON 格式(单行)输出
sudo journalctl -b -u docker.serviceqq -o json-pretty    # 以 JSON 格式(多行)输出,可读性更好
sudo journalctl --disk-usage                            # 显示日志占据的硬盘空间
sudo journalctl --vacuum-size=1G                        # 指定日志文件占据的最大空间
sudo journalctl --vacuum-time=1years                    # 指定日志文件保存多久

3. 在 Linux 中创建 Systemd 服务

systemd 服务在 unit 文件中定义(unit 是服务和系统资源,如设备、套接字、挂载点等的表示形式)。自定义服务 unit 文件应存储在 /etc/systemd/system/ 目录中,扩展名必须是 .service。例如,自定义 test-app 服务使用 /etc/systemd/system/test-app.service unit文件。

unit 文件是一种纯文本 ini-style 文件,以 .service 结尾,由 Unit、Service 和 Install 三部分组成;

  • [Unit] => 启动顺序与依赖关系;
  • [Service] => 启动行为定义;
  • [Install] => 服务安装定义;

例如:

[Unit]   
Description=test        # 简单描述服务
After=network.target    # 描述服务类别,表示本服务需要在network服务启动后在启动
Before=xxx.service      # 表示需要在某些服务启动之前启动,After和Before字段只涉及启动顺序,不涉及依赖关系

[Service] 
Type=forking            # 设置服务的启动方式
User=USER               # 设置服务运行的用户
Group=USER              # 设置服务运行的用户组
WorkingDirectory=/PATH  # 设置服务运行的路径(cwd)
KillMode=control-group  # 定义systemd如何停止服务
Restart=no              # 定义服务进程退出后,systemd的重启方式,默认是不重启
ExecStart=/start.sh     # 服务启动命令,命令需要绝对路径(采用sh脚本启动其他进程时Type须为forking)

[Install]   
WantedBy=multi-user.target  # 多用户

3.1 在 Linux 中创建自定义 Systemd 服务文件

3.1.1 示例1:定义使用 Gunicorn 运行 django 应用程序的服务

要在 systemd 下以服务形式运行应用程序、或脚本,可以按如下方法创建一个新的 systemd 服务。

首先,在 /etc/systemd/system/ 下创建名为 django-myapps.service 的服务 unit 文件(切记用服务或应用程序的实际名称替换 django-myapps):

vi /etc/systemd/system/django-myapps.service

以下配置用于定义使用 Gunicorn(UNIX 下 Python WSGI HTTP 服务器)运行 django 应用程序的服务。

创建 django 应用:django-admin startproject myapps

/etc/systemd/system/django-myapps.service:

[Unit]
Description=Gunicorn daemon for serving myapps
After=network.target

[Service]
User=root
Group=root
WorkingDirectory=/home/workspace/examples/myapps
Environment="PATH=/home/workspace/py-env/py3.11-dev-env/bin"
ExecStart=/home/workspace/py-env/py3.11-dev-env/bin/gunicorn --worker-class=gevent -w 2 -b 0.0.0.0:8000 myapps.wsgi:application --log-level=info --access-logfile=/tmp/gunicorn/access.log --error-logfile=/tmp/gunicorn/error.log
ExecReload=/bin/kill -s HUP $MAINPID
RestartSec=5

[Install]
WantedBy=multi-user.target

简要介绍上述配置指令:

  • Description: 指定服务的描述;
  • After: 定义了与 network.target 的关系, 在这种情况下,django-myapps.service 会在 network.target 启动后启动;
  • User: 指定服务运行用户;
  • Group: 指定服务运行组;
  • WorkingDirectory: 设置执行服务的工作目录;
  • Environment: 设置执行服务环境变量;
  • ExecStart: 定义启动该服务时要执行的命令及其参数;
  • ExecReload: 定义触发服务配置重载时要执行的命令;
  • WantedBy: 当使用 systemctl enable 命令启用 django-myapps.service unit时,在列出的每个unit的 .wants/ 或 .requires/ 目录中创建一个符号链接,本例中为 multi-user.target;

保存 django-myapps.service 文件并关闭,运行以下命令,重新加载 systemd:

# sudo systemctl daemon-reload

注意:在编辑 unit 文件后需要运行此命令;

启动/激活服务:

# systemctl start django-myapps.service

ubuntu@jpzhang-dev:~/workspace/examples/myapps/myapps$ systemctl start django-myapps.service
==== AUTHENTICATING FOR org.freedesktop.systemd1.manage-units ===
启动“django-myapps.service”需要认证。
Authenticating as: Ubuntu (jpzhang)
Password:
==== AUTHENTICATION COMPLETE ===

浏览器访问即可打开默认 django 页面;

检查服务状态:

# systemctl status django-myapps.service

ubuntu@jpzhang-dev:~/workspace/examples/myapps/myapps$ systemctl status django-myapps.service
● django-myapps.service - Gunicorn daemon for serving myapps
     Loaded: loaded (/etc/systemd/system/django-myapps.service; disabled; vendor preset: enabled)
     Active: active (running) since Sun 2024-04-28 17:41:00 CST; 4s ago
   Main PID: 320060 (gunicorn)
      Tasks: 3 (limit: 38470)
     Memory: 77.3M
... ...

系统启动时启动:

使用 systemctl enable 命令设置启动时启动,同时可以使用 systemctl is-enable 命令检查服务是否已启用,如下所示:

# 开启随系统启动而启动
# systemctl enable django-myapps.service
# 检查是否开启
# systemctl is-enabled django-myapps.service

或者,也可以同时启用和启动服务。

# systemctl enable --now django-myapps.service

停止/停用该服务:

# systemctl stop django-myapps.service

重启服务:

# systemctl restart django-myapps.service

禁用服务:

使用 systemctl disable 命令禁用服务,以防止它在系统启动时启动,通过 systemctl is-enable 命令检查服务是否已禁用:

# systemctl disable django-myapps.service
# systemctl is-disabled django-myapps.service

或者,也可以同时禁用和停止它:

# systemctl disable --now django-myapps.service

3.1.2 示例2:定义使用 python 脚本的服务

将 python 脚本作为后台服务运行,这样即使服务器因某种原因重启,脚本也能在后台运行。

注意:除了 Python 脚本其他脚本配置方式一样,这里仅通过 Python 举例说明;

准备一个测试脚本 test.py:

import time
from datetime import datetime
while True:
    with open("/tmp/timestamp.txt", "a") as f:
        f.write("The current timestamp is: " + str(datetime.now()))
        f.close()
    time.sleep(10)

上述脚本将每隔 10 秒在文件中写入当前的时间戳;

创建 unit 文件:

vi /etc/systemd/system/python-test.service

添加内容如下:

[Unit]
Description=My test python service
After=multi-user.target
[Service]
Type=simple
Restart=always
ExecStart=/home/workspace/py-env/py3.11-dev-env/bin/python3.11  /home/workspace/examples/test.py
[Install]
WantedBy=multi-user.target

重载修改过的配置文件:sudo systemctl daemon-reload

启动 Python 脚本服务:

启动:

sudo systemctl start python-test.service

查看状态:

sudo systemctl status python-test.service

检查时间戳输出:

ubuntu@jpzhang-dev:~/workspace/examples$ tail -f /tmp/timestamp.txt
The current timestamp is: 2024-04-28 18:03:14.978850The current timestamp is: 2024-04-28 18:03:24.979337

其他命令与上节一样,这里不在继续说明;

感谢您花时间阅读文章!

相关推荐

为何越来越多的编程语言使用JSON(为什么编程)

JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

何时在数据库中使用 JSON(数据库用json格式存储)

在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

JSON对象花样进阶(json格式对象)

一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

深入理解 JSON 和 Form-data(json和formdata提交区别)

在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...

JSON 语法(json 语法 priority)

JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...

JSON语法详解(json的语法规则)

JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...

MySQL JSON数据类型操作(mysql的json)

概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...

JSON的数据模式(json数据格式示例)

像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...

前端学习——JSON格式详解(后端json格式)

JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...

什么是 JSON:详解 JSON 及其优势(什么叫json)

现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...

PostgreSQL JSON 类型:处理结构化数据

PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...

JavaScript:JSON、三种包装类(javascript 包)

JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...

Python数据分析 只要1分钟 教你玩转JSON 全程干货

Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...

比较一下JSON与XML两种数据格式?(json和xml哪个好)

JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码