最近自己写的监控网站脚本想要配置成linux服务,让它开机自己动。这绕不开Linux服务管理的问题,太久没用也有点忘记了,今天就让我们一起重新学习下吧!!!顺便把我这几年踩过的坑和总结的经验分享给大家,希望能帮到正在被服务管理折磨的朋友们。

从SysV到systemd的演进历程

要说Linux服务管理,就不得不提到这个历史演进过程。早期的Linux系统使用的是SysV init系统,那时候管理服务主要靠/etc/init.d/目录下的脚本,还有就是service命令。

我记得刚工作那会儿,公司还在用CentOS 6,每次重启服务都是service httpd restart这样的命令。那时候觉得挺简单的,但是随着系统复杂度增加,SysV init的问题就暴露出来了:启动速度慢、服务依赖关系处理不好、资源管理能力弱等等。

到了CentOS 7开始,Red Hat引入了systemd作为新的init系统。刚开始接触systemd的时候,说适应是假的(嘴硬!),毕竟习惯了service命令,突然要用systemctl,各种参数和语法都不一样。

但是用久了就发现systemd真的强大很多。它不仅仅是一个init系统,更像是一个系统管理套件,包含了日志管理、网络管理、时间同步等各种功能。

现在主流的Linux发行版基本都采用了systemd,比如CentOS 7+、Ubuntu 16.04+、Debian 8+等等。不过有些发行版还是保留了service命令的兼容性,这也是为什么有时候你会发现两个命令都能用的原因。

systemctl命令详解

systemctl是systemd的核心命令,基本语法是:systemctl [选项] [命令] [服务名]

基本服务操作

启动服务最常用的就是start命令:

systemctl start nginx

停止服务用stop:

systemctl stop nginx

重启服务用restart,这个命令会先停止服务再启动:

systemctl restart nginx

还有一个reload命令,这个比较有意思,它不会停止服务,而是重新加载配置文件。不过不是所有服务都支持reload,比如nginx支持,但有些服务就不支持。

systemctl reload nginx

有时候你不确定服务是否支持reload,可以用reload-or-restart,如果支持reload就reload,不支持就restart:

systemctl reload-or-restart nginx

查看服务状态

status命令是我用得最多的,它能显示服务的详细状态信息:

systemctl status nginx

输出信息包括服务是否运行、进程ID、最近的日志等等。有时候服务启动失败,通过status命令基本就能看出问题所在。

如果只想知道服务是否在运行,可以用is-active:

systemctl is-active nginx

检查服务是否启用(开机自启动)用is-enabled:

systemctl is-enabled nginx

开机自启动管理

这个功能特别重要,生产环境的服务基本都需要开机自启动。

启用开机自启动:

systemctl enable nginx

禁用开机自启动:

systemctl disable nginx

有时候你想启用服务的同时立即启动它,可以用enable --now:

systemctl enable --now nginx

相应的,disable --now会禁用并停止服务:

systemctl disable --now nginx

列出服务

list-units命令可以列出所有活动的单元:

systemctl list-units

如果只想看服务类型的单元:

systemctl list-units --type=service

list-unit-files可以列出所有单元文件及其状态:

systemctl list-unit-files --type=service

这个命令特别有用,你可以看到哪些服务是enabled、disabled或者static状态。

service命令的使用

虽然systemd已经成为主流,但service命令在很多系统上还是可以用的,特别是一些老的脚本或者文档中经常出现。

service命令的基本语法:service [服务名] [操作]

service nginx start
service nginx stop  
service nginx restart
service nginx status

在支持systemd的系统上,service命令实际上是一个兼容性包装,它会将命令转换为对应的systemctl调用。

不过我建议在新系统上还是尽量使用systemctl,因为功能更强大,信息更详细。

systemd单元文件详解

要深入理解systemd,就必须了解单元文件(unit file)。每个服务都有对应的单元文件,定义了服务的启动方式、依赖关系等信息。

单元文件通常位于以下目录:

  • /usr/lib/systemd/system/ - 系统默认的单元文件
  • /etc/systemd/system/ - 管理员创建或修改的单元文件
  • /run/systemd/system/ - 运行时创建的单元文件

优先级是从高到低的,也就是说/etc/systemd/system/下的文件会覆盖/usr/lib/systemd/system/下的同名文件。

单元文件结构

一个典型的service单元文件包含三个主要部分:

[Unit]部分定义了单元的基本信息和依赖关系:

[Unit]
Description=The nginx HTTP and reverse proxy server
After=network.target remote-fs.target nss-lookup.target

Description就是服务的描述信息,After指定了启动顺序,表示这个服务要在network.target等启动之后再启动。

[Service]部分定义了服务的具体行为:

[Service]
Type=forking
PIDFile=/run/nginx.pid
ExecStartPre=/usr/sbin/nginx -t
ExecStart=/usr/sbin/nginx
ExecReload=/bin/kill -s HUP $MAINPID
KillSignal=SIGQUIT
TimeoutStopSec=5
KillMode=process
PrivateTmp=true

Type=forking 定义服务为 forking 类型,即主进程启动后会 fork 出子进程然后退出,子进程继续运行;

PIDFile=/run/nginx.pid 指定 PID 文件位置,让 systemd 能够跟踪主进程;

ExecStartPre=/usr/sbin/nginx -t 在启动前先测试配置文件语法,防止配置错误导致启动失败;

ExecStart=/usr/sbin/nginx 定义实际的服务启动命令;

ExecReload=/bin/kill -s HUP $MAINPID 通过向主进程发送 HUP 信号实现配置重载,不中断现有连接;

KillSignal=SIGQUIT 设置停止服务时使用 SIGQUIT 信号进行优雅关闭;

TimeoutStopSec=5 设置 5 秒的停止超时时间,超时后强制杀死进程;

KillMode=process 指定只杀死主进程而不影响子进程;

PrivateTmp=true 为服务创建独立的临时目录以提高安全性和隔离性。整体配置确保了 Nginx 服务能够在 systemd 环境中稳定、安全、可控地运行,支持优雅的启动、重载和关闭操作。

[Install]部分定义了安装信息,主要是开机自启动相关:

[Install]
WantedBy=multi-user.target

WantedBy指定了这个服务被哪个target需要,multi-user.target相当于传统的运行级别3。

创建自定义服务

有时候我们需要为自己的应用创建systemd服务。比如我之前写了一个监控脚本,需要作为服务运行。

首先创建单元文件/etc/systemd/system/myapp.service:

[Unit]
Description=My Application
After=network.target

[Service]
Type=simple
User=myapp
WorkingDirectory=/opt/myapp
ExecStart=/opt/myapp/bin/myapp
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target

然后重载systemd配置:

systemctl daemon-reload

启用并启动服务:

systemctl enable --now myapp

这里有个坑,每次修改单元文件后都要执行daemon-reload,否则systemd不会读取新的配置。我之前就因为忘记这个命令,调试了半天才发现问题。

日志管理与故障排查

systemd集成了journald日志系统,这让服务的日志管理变得更加统一和方便。

journalctl命令使用

查看特定服务的日志:

journalctl -u nginx

查看实时日志(类似tail -f):

journalctl -u nginx -f

查看最近的日志:

journalctl -u nginx -n 50

按时间范围查看日志:

journalctl -u nginx --since "2024-01-01" --until "2024-01-02"

查看启动相关的日志:

journalctl -u nginx --since today

常见故障排查

服务启动失败是最常见的问题。通常的排查步骤是:

  1. 先看服务状态:
systemctl status nginx
  1. 如果状态信息不够详细,查看日志:
journalctl -u nginx -n 100
  1. 检查配置文件语法:
nginx -t
  1. 检查端口占用:
netstat -tlnp | grep :80

我遇到过一个奇怪的问题,nginx服务显示启动成功,但是访问不了。后来发现是防火墙的问题,端口被blocked了。这种情况下systemctl status是看不出问题的,需要结合其他工具排查。

还有一次是因为磁盘空间不足导致服务启动失败,错误信息也不明显,最后通过df -h才发现问题。所以排查问题的时候要多角度思考,不能只盯着服务本身。

高级功能和技巧

Target管理

systemd使用target来替代传统的运行级别概念。常用的target有:

  • graphical.target - 图形界面模式
  • multi-user.target - 多用户命令行模式
  • rescue.target - 救援模式

查看当前target:

systemctl get-default

设置默认target:

systemctl set-default multi-user.target

切换到指定target:

systemctl isolate rescue.target

服务依赖管理

systemd的依赖管理比SysV强大很多。主要的依赖类型有:

  • Requires - 强依赖,依赖的服务失败则本服务也失败
  • Wants - 弱依赖,依赖的服务失败不影响本服务
  • After/Before - 启动顺序依赖

查看服务的依赖关系:

systemctl list-dependencies nginx

这个命令会显示服务的依赖树,对理解服务启动顺序很有帮助。

资源控制

systemd支持cgroup资源控制,可以限制服务的CPU、内存等资源使用。

在单元文件的[Service]部分添加:

[Service]
CPUQuota=50%
MemoryLimit=1G

这样就限制了服务最多使用50%的CPU和1GB内存。

定时任务

systemd还可以替代cron来执行定时任务,通过timer单元实现。

创建一个service单元文件/etc/systemd/system/backup.service:

[Unit]
Description=Backup Script
[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh

然后创建对应的timer单元文件/etc/systemd/system/backup.timer:

[Unit]
Description=Run backup daily
Requires=backup.service

[Timer]
OnCalendar=daily
Persistent=true

[Install]
WantedBy=timers.target

启用定时器:

systemctl enable --now backup.timer

查看所有定时器状态:

systemctl list-timers

相比cron,systemd timer的优势是可以更好地与系统集成,支持更复杂的时间表达式,还能处理系统休眠等情况。

性能监控和优化

服务启动时间分析

systemd提供了很好的启动时间分析工具。查看系统启动时间:

systemd-analyze

查看详细的启动时间分解:

systemd-analyze blame

这个命令会列出每个服务的启动时间,按时间长短排序。如果系统启动慢,通过这个命令基本就能找到罪魁祸首。

生成启动过程的SVG图表:

systemd-analyze plot > boot.svg

这个图表可以直观地看到各个服务的启动时间线和依赖关系。

服务状态监控

除了基本的status命令,还可以用show命令查看服务的详细属性:

systemctl show nginx

这会输出大量信息,包括内存使用、CPU时间、文件描述符数量等等。

如果只想看特定属性:

systemctl show nginx -p MainPID,LoadState,ActiveState

批量操作

有时候需要对多个服务进行批量操作。比如重启所有web相关的服务:

systemctl restart nginx php-fpm mysql

或者使用通配符:

systemctl restart 'php*'

不过通配符要小心使用,特别是在生产环境,避免误操作。

实际应用场景和案例

Web服务器环境搭建

我经常需要搭建LNMP环境,这里分享一下完整的流程。

首先安装软件包(以CentOS为例):

yum install nginx mysql-server php-fpm

启动并设置开机自启动:

systemctl enable --now nginx
systemctl enable --now mysqld  
systemctl enable --now php-fpm

检查服务状态:

systemctl status nginx mysqld php-fpm

这样基本的LNMP环境就搭建好了。但实际使用中还会遇到各种问题,比如nginx和php-fpm的socket通信配置、MySQL的初始化等等。

服务监控脚本

我写过一个简单的服务监控脚本,定期检查关键服务的状态:

#!/bin/bash
services=("nginx" "mysqld" "php-fpm")

for service in "${services[@]}"; do
    if ! systemctl is-active --quiet $service; then
        echo "$(date): $service is not running, attempting to restart..."
        systemctl restart $service
        if systemctl is-active --quiet $service; then
            echo "$(date): $service restarted successfully"
        else
            echo "$(date): Failed to restart $service"
            # 这里可以添加告警逻辑,比如发邮件或者钉钉通知
        fi
    fi
done

把这个脚本配置成systemd timer,每5分钟执行一次,基本就能保证服务的高可用性。

容器化环境的服务管理

现在容器化越来越普及,但传统的虚拟机环境还是很常见。在容器化环境中,systemd的使用场景有所不同。

Docker容器内部通常不运行systemd,而是直接运行应用进程。但在宿主机上,Docker本身就是一个systemd服务:

systemctl status docker

Kubernetes环境中,kubelet也是通过systemd管理的:

systemctl status kubelet

这种混合环境下,需要同时掌握systemd和容器编排工具的使用。

常见问题和解决方案

服务启动失败

最常见的问题就是服务启动失败。排查步骤我前面提到过,这里补充几个具体的案例。

配置文件语法错误
这是最常见的原因,比如nginx配置文件有语法错误:

nginx -t
systemctl status nginx
journalctl -u nginx

端口被占用
有时候服务启动失败是因为端口被其他进程占用:

netstat -tlnp | grep :80
lsof -i :80

权限问题
服务运行用户没有足够的权限访问文件或目录:

ls -la /var/log/nginx/
chown -R nginx:nginx /var/log/nginx/

依赖服务未启动
有些服务依赖其他服务,比如php-fpm可能依赖MySQL:

systemctl list-dependencies php-fpm

开机自启动失效

有时候明明设置了enable,但重启后服务还是没有自动启动。

检查服务是否真的被启用:

systemctl is-enabled nginx

检查target依赖关系:

systemctl list-dependencies multi-user.target | grep nginx

有时候是因为服务的依赖没有满足,导致启动失败。这种情况下需要检查依赖的服务是否正常。

服务假死问题

有些服务进程还在,但实际上已经不响应请求了。这种情况systemctl status可能显示正常,但服务实际不可用。

可以通过健康检查脚本来检测:

curl -f http://localhost/ || systemctl restart nginx

或者在systemd单元文件中配置健康检查:

[Service]
ExecStartPost=/bin/sleep 5
ExecStartPost=/usr/bin/curl -f http://localhost/

日志轮转问题

systemd journal的日志可能会占用大量磁盘空间。可以配置日志轮转:

编辑/etc/systemd/journald.conf:

SystemMaxUse=1G
SystemMaxFileSize=100M
MaxRetentionSec=1month

然后重启journald:

systemctl restart systemd-journald

手动清理旧日志:

journalctl --vacuum-time=7d
journalctl --vacuum-size=500M

最佳实践和建议

服务配置方面

  • 尽量使用系统包管理器安装的软件,它们通常已经包含了合适的systemd单元文件
  • 自定义服务的单元文件放在/etc/systemd/system/目录下
  • 修改单元文件后记得执行systemctl daemon-reload
  • 合理设置服务的重启策略,生产环境建议使用Restart=always

监控和维护方面

  • 定期检查服务状态,可以写脚本自动化
  • 关注系统日志,特别是启动失败的服务
  • 定期清理journal日志,避免占用过多磁盘空间
  • 备份重要的配置文件和单元文件

安全方面

  • 服务尽量不要以root用户运行
  • 使用systemd的安全特性,比如PrivateTmp、NoNewPrivileges等
  • 定期更新系统和软件包
  • 监控异常的服务重启和失败

性能优化方面

  • 使用systemd-analyze分析启动性能
  • 禁用不必要的服务
  • 合理配置服务的资源限制
  • 优化服务的依赖关系

说实话,systemd刚出来的时候争议很大,很多人觉得它太复杂了,违背了Unix的简单哲学。但用了这么多年下来,我觉得它确实解决了很多传统init系统的问题,特别是在复杂的生产环境中。

当然,学习systemd确实需要一定的时间投入,特别是对于习惯了SysV init的人来说。但掌握了之后,你会发现系统管理变得更加高效和可靠。

最后想说的是,工具只是手段,关键还是要理解系统的运行原理。不管是systemd还是其他的init系统,核心都是进程管理、资源控制和服务编排。掌握了这些基本概念,学习任何新的工具都会事半功倍。

现在云原生技术发展很快,Kubernetes、Docker等容器技术越来越普及,但传统的虚拟机和物理机环境还是会长期存在。systemd作为现代Linux系统的核心组件,掌握它的使用对于运维工程师来说还是很有必要的。

希望这篇文章能帮到正在学习Linux服务管理的朋友们。如果你在实际使用中遇到问题,欢迎留言讨论,大家一起交流学习。毕竟运维这个行业,踩坑是难免的,但踩过的坑如果能帮到别人,那就很有价值了。

记得当年我刚开始做运维的时候,网上的资料没现在这么丰富,很多问题都要自己摸索。现在信息获取容易多了,但也要注意甄别,有些过时的文档可能会误导人。特别是Linux发行版更新很快,一些老的命令和配置方法可能已经不适用了。

总的来说,systemd虽然学习曲线有点陡峭,但掌握了之后确实能提高工作效率。而且随着容器化和云原生技术的发展,理解systemd的原理对学习Kubernetes等技术也很有帮助,因为很多概念是相通的。

如果这篇文章对你有帮助,别忘了点赞转发支持一下!想了解更多运维实战经验和技术干货,记得关注微信公众号@运维躬行录,领取学习大礼包!!!我会持续分享更多接地气的运维知识和踩坑经验。让我们一起在运维这条路上互相学习,共同进步!

公众号:运维躬行录

个人博客:躬行笔记

标签: none