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

如何在 Linux 中创建 Systemd 服务?

sinye56 2024-11-11 13:34 4 浏览 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

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

感谢您花时间阅读文章!

相关推荐

Linux两种光驱自动挂载的方法

环境:CentOS6.4西昆云服务器方式一修改fstab文件/etc/fstab是系统保存文件系统信息?静态文件,每一行描述一个文件系统;系统每次启动会读取此文件信息以确定需要挂载哪些文件系统。参...

linux系统运维,挂载和分区概念太难?在虚机下操作一次全掌握

虚拟机的好处就是可以模拟和学习生产环境的一切操作,假如我们还不熟悉磁盘操作,那先在虚机环境下多操作几次。这次来练习下硬盘扩容操作。虚拟机环境:centos8vm11linux设备命名规则在linux中...

Linux 挂载 NFS 外部存储 (mount 和 /etc/fstab)

mount:手工挂载,下次重启需再重新挂载,操作命令:mount-tnfs-ooptionsserver:/remote/export/local/directory上面命令中,本地目录...

在Linux中如何设置自动挂载特定文件系统(示例)

Linux...

Linux环境中的绑定挂载(bind mount)

简介:Linux中的mount命令是一个特殊的指令,主要用于挂载文件目录。而绑定挂载(bindmount)命令更为特别。mount的bind选项将第一个目录克隆到第二个。一个目录中的改变将会在...

Linux挂载CIFS共享 临时挂载 1. 首先

如何解决服务器存储空间不足的问题?大家好,欢迎回来。在上一期视频中,我为大家介绍了如何利用Linux挂载来扩容服务器存储空间。这一期视频,我将以Linux为例,教大家如何进行扩容。群辉使用的是Linu...

Linux 硬盘挂载(服务器重启自动挂载)

1、先查看目前机器上有几块硬盘,及已挂载磁盘:fdisk-l能够查看到当前主机上已连接上的磁盘,以及已经分割的磁盘分区。(下面以/dev/vdb磁盘进行分区、挂载为例,挂载点设置为/data)df...

linux 挂载磁盘

在Linux中挂载硬盘的步骤如下:...

笨小猪教您Linux磁盘挂载

本教程针对Linux系统比较熟悉或者想学习Linux基础的用户朋友,本教程操作起来比较傻瓜式,跟着步骤就会操作,本文使用的工具是XShell同时多多注意空格(文中会有提示)。【问答】什么是磁盘挂载?答...

Linux 磁盘挂载和docker安装命令

本篇给大家介绍Linux磁盘挂载和docker安装的相关内容,Linux服务器的操作是一个手熟的过程,一些不常用的命令隔断时间就忘记了,熟话说好记性不如烂笔头,还需在平时的工作中多练习记录。...

Linux设置开机自动挂载分区

有时候,我们在安装完Linux系统之后,可能在使用过程中添加硬盘或者分区进行使用,这时候就需要手动把磁盘分区挂载到某个路径,但是开机之后就会消失,需要重新挂载,非常麻烦,那么我们应该如何设置开机自动挂...

在linux挂载一个新硬盘的完整步骤

以下是在Linux中挂载新原始磁盘的完整步骤,包括分区、创建文件系统以及使用UUID在/etc/fstab中启动时挂载磁盘:将新的原始磁盘连接到Linux系统并打开电源。运行以下命令,...

Linux系统如何挂载exFAT分区

简介:Linux系统中不能像Windows系统那样自动识别加载新设备,需要手动识别,手动加载。Linux中一切皆文件。文件通过一个很大的文件树来组织,文件树的根目录是:/,从根目开始录逐级展开。这些文...

Linux系统挂载硬盘

fdisk-l查看可挂载的磁盘都有哪些df-h查看已经挂载的磁盘...

WSL2发布,如何在Win10中挂载Linux文件系统

WSL2是最新版本的架构,它为Windows子系统提供支持,使其能够在Windows上运行ELF64Linux二进制文件。通过最近的更新,它允许使用Linux文件系统访问存储在硬盘中的文件。如果你...

取消回复欢迎 发表评论: