在现代的CentOS系统(如CentOS 7、8及后续的Stream版本)中,systemd
已成为标准的初始化系统和服务管理器,取代了传统的SysVinit
。systemctl
是与systemd
交互的主要命令行工具,它提供了强大的功能来管理系统服务,包括启动、停止、重启、开机自启以及添加自定义服务,掌握如何使用systemctl
添加服务,是系统管理员和开发者的必备技能,本文将详细、系统地介绍在CentOS中创建和管理自定义服务的完整流程。
理解Systemd服务单元文件
在添加服务之前,我们首先需要理解systemd
是如何定义一个服务的。systemd
通过“单元文件”来管理各种资源,其中服务就是由后缀为.service
的单元文件定义的,这些文件包含了服务的启动命令、依赖关系、行为描述等信息。
系统自带的服务单元文件通常存放在/usr/lib/systemd/system/
目录下,而我们创建的自定义服务,推荐放置在/etc/systemd/system/
目录中,这样做的好处是,该目录下的文件具有更高的优先级,并且不会在系统包更新时被覆盖。
一个典型的服务单元文件包含三个主要部分:
[Unit]
:单元的元信息部分,用于描述服务。Description
:一段简短的描述,用于说明该服务的作用。After
:定义该服务应在哪些其他服务启动之后才启动。After=network.target
表示在网络服务启动后再启动本服务。Requires
:定义强依赖关系,如果这里列出的服务启动失败,那么本服务也不会启动。
[Service]
:服务的核心配置部分,定义了服务如何运行。Type
:服务的启动类型,这是非常重要的一个参数,常见的有:-
simple
(默认):ExecStart
启动的进程就是服务的主进程。 -
forking
:ExecStart
启动的进程会通过fork()
系统调用创建一个子进程作为主进程,然后父进程退出,传统的Unix守护进程常使用此类型。 -
oneshot
:一次性任务,执行完ExecStart
指定的命令后服务就退出了。 -
notify
:与simple
类似,但服务进程会在启动就绪后通过sd_notify
函数通知systemd
。
-
User
和Group
:指定运行该服务的用户和用户组,出于安全考虑,强烈建议不要使用root
用户运行非必要的服务。WorkingDirectory
:设置服务启动前的工作目录。ExecStart
:指定启动服务时要执行的命令或脚本的完整路径。ExecStop
:指定停止服务时要执行的命令。Restart
:定义服务在何种情况下会自动重启,常用值有no
(不重启)、on-success
(仅在成功退出时重启)、on-failure
(在非正常退出时重启)、always
(总是重启)。RestartSec
:在服务重启前等待的秒数。
[Install]
:定义如何安装服务,即如何实现开机自启。WantedBy
:指定服务在哪个“目标”下被启用,最常用的是multi-user.target
,它对应于传统的多用户命令行模式,当执行systemctl enable
时,systemd
会在/etc/systemd/system/multi-user.target.wants/
目录下创建一个指向本服务文件的符号链接。
分步实战:创建一个自定义服务
为了更直观地理解,我们来创建一个简单的自定义服务,这个服务的作用是每分钟将当前时间追加写入到一个日志文件中。
创建可执行脚本
我们需要一个可执行的脚本作为服务的核心。
# 创建一个脚本文件 sudo vim /usr/local/bin/time_logger.sh
在文件中输入以下内容:
#!/bin/bash # 一个简单的日志记录脚本 LOG_FILE="/var/log/time_app.log" # 将当前时间和日期追加到日志文件 echo "$(date): Service is running." >> $LOG_FILE
保存并退出后,赋予该脚本执行权限:
sudo chmod +x /usr/local/bin/time_logger.sh
创建服务单元文件
在/etc/systemd/system/
目录下创建服务单元文件。
sudo vim /etc/systemd/system/time-logger.service
这里我们将结合前面介绍的各个指令:
[Unit] Description=A simple service that logs the current time every minute After=network.target [Service] Type=simple User=nobody Group=nobody ExecStart=/usr/local/bin/time_logger.sh Restart=on-failure RestartSec=5s [Install] WantedBy=multi-user.target
配置解析:
- 我们使用
nobody
用户运行此服务,这是一个安全的实践。 Type=simple
,因为我们的脚本运行一次就结束,符合此类型。Restart=on-failure
,如果脚本执行出错(例如权限问题),5秒后会尝试重启。WantedBy=multi-user.target
,使其在系统进入多用户模式时自动启动。
管理服务
服务文件创建好后,systemd
还不知道它的存在,我们需要重新加载配置文件,然后就可以管理我们的服务了。
重新加载systemd配置
这一步至关重要,它让systemd
读取新创建或修改过的服务文件。sudo systemctl daemon-reload
启动服务
sudo systemctl start time-logger.service
查看服务状态
这是排错的关键步骤,可以查看服务是否正在运行、最近的日志以及是否有错误。systemctl status time-logger.service
输出应类似如下,显示
active (exited)
,因为oneshot
或simple
类型的服务执行完命令后就正常退出了。● time-logger.service - A simple service that logs the current time every minute Loaded: loaded (/etc/systemd/system/time-logger.service; disabled; vendor preset: disabled) Active: active (exited) since Mon 2025-10-23 10:30:00 CST; 5s ago Process: 12345 ExecStart=/usr/local/bin/time_logger.sh (code=exited, status=0/SUCCESS) Main PID: 12345 (code=exited, status=0/SUCCESS) CPU: 2ms
设置开机自启
sudo systemctl enable time-logger.service
执行后,系统会提示创建了符号链接。
停止和禁用服务
sudo systemctl stop time-logger.service sudo systemctl disable time-logger.service
进阶技巧与最佳实践
- 使用Timer实现定时任务:上述例子中,我们只运行了一次,要实现每分钟运行,
systemd
提供了更优雅的timer
单元,你可以创建一个.timer
文件来定时激活.service
,这比cron
更集成、日志更清晰。 - 日志管理:
systemd
将服务的标准输出和标准错误重定向到了系统日志,使用journalctl
可以方便地查看。# 查看time-logger服务的实时日志 journalctl -u time-logger.service -f
- 环境变量:如果服务需要复杂的环境变量,可以在
[Service]
部分使用Environment="VAR=value"
来定义,或者使用EnvironmentFile=/path/to/env_file
来从一个文件中加载所有环境变量。
为了方便查阅,以下小编总结了常用的systemctl
服务管理命令:
命令 | 功能 |
---|---|
systemctl start [service] | 立即启动一个服务 |
systemctl stop [service] | 立即停止一个服务 |
systemctl restart [service] | 重启一个服务 |
systemctl reload [service] | 重新加载服务配置(服务需支持) |
systemctl status [service] | 查看服务状态 |
systemctl enable [service] | 设置服务开机自启 |
systemctl disable [service] | 取消服务开机自启 |
systemctl is-enabled [service] | 检查服务是否已设置开机自启 |
journalctl -u [service] | 查看指定服务的日志 |
相关问答FAQs
为什么我执行 systemctl start my-service.service
后,状态显示 failed
,并且日志提示 code=exited, status=203/EXEC
?
解答:这个错误码(203/EXEC
)通常意味着systemd
无法执行你在ExecStart
中指定的命令,最常见的原因有以下几点:
- 脚本或程序没有执行权限:请检查你的脚本文件是否已经使用
chmod +x
命令赋予了执行权限。 - 脚本缺少Shebang:如果你的
ExecStart
指向一个shell脚本(如.sh
文件),请确保脚本的第一行是#!/bin/bash
或#!/bin/sh
,没有这一行,系统不知道该用什么解释器来运行它。 - 路径错误:
ExecStart
中的路径必须是绝对路径,并且确保该路径下的文件确实存在。 - 架构不兼容:在极少数情况下,如果你运行的二进制文件与当前系统CPU架构不匹配(例如在ARM架构上运行x86的程序),也会出现此错误。
我修改了.service
,如何让更改生效?
解答:修改了服务单元文件后,必须执行以下命令来让systemd
重新加载所有配置文件:
sudo systemctl daemon-reload
这个命令会通知systemd
守护进程重新扫描其配置目录,并加载所有更改。请注意,仅仅执行daemon-reload
并不会对正在运行的服务本身产生任何影响,如果你想让正在运行的服务应用新的配置,你需要根据配置的更改类型来决定后续操作:
- 如果只是修改了
Restart
策略、环境变量等不影响服务核心逻辑的配置,执行sudo systemctl restart my-service.service
即可。 - 如果修改了
ExecStart
等核心执行命令,同样需要restart
。 systemctl reload my-service.service
命令通常用于那些支持在不重启的情况下重新加载自身配置文件的服务(如Nginx、Apache),它会向服务发送一个SIGHUP
信号,这要求服务本身能处理此信号,如果服务不支持,reload
会失败,对于不确定的服务,使用restart
是最稳妥的方式。
【版权声明】:本站所有内容均来自网络,若无意侵犯到您的权利,请及时与我们联系将尽快删除相关内容!
发表回复