chiachan
chiachan
Published on 2025-03-28 / 17 Visits
0

Systemd入门教程

什么是 Systemd?

Systemd 是 Linux 系统的一个系统和服务管理器,它已经成为大多数现代 Linux 发行版的默认初始化系统。它负责启动系统、管理服务、处理日志等核心功能。

基本概念

1. Unit(单元)

Systemd 使用单元(Unit)来管理不同的系统资源。单元文件通常位于以下目录:

  • /etc/systemd/system/ - 系统管理员创建的单元文件
  • /usr/lib/systemd/system/ - 软件包安装的单元文件
  • /run/systemd/system/ - 运行时创建的单元文件

2. 单元类型

  • Service(服务): 守护进程
  • Socket(套接字): 网络或 IPC 套接字
  • Device(设备): 设备单元
  • Mount(挂载): 文件系统挂载点
  • Target(目标): 单元组
  • Timer(定时器): 定时任务

3. Target(目标)详解

Target 是 systemd 中用于组织单元的特殊单元类型,它可以将多个单元组合在一起,形成一个功能组。

常用系统 Target

  • graphical.target: 图形界面模式
  • multi-user.target: 多用户命令行模式
  • rescue.target: 救援模式
  • emergency.target: 紧急模式
  • network.target: 网络服务就绪
  • basic.target: 基本系统服务就绪

Target 管理命令

# 查看当前默认 target
systemctl get-default

# 设置默认 target
sudo systemctl set-default multi-user.target

# 切换到指定 target
sudo systemctl isolate multi-user.target

# 查看 target 依赖关系
systemctl list-dependencies multi-user.target

# 查看所有可用的 target
systemctl list-unit-files --type=target

创建自定义 Target

  1. 创建 target 单元文件 /etc/systemd/system/myapp.target
[Unit]
Description=My Application Target
Requires=network.target
After=network.target

[Install]
WantedBy=multi-user.target
  1. 创建依赖服务单元文件 /etc/systemd/system/myapp-db.service
[Unit]
Description=My Application Database
PartOf=myapp.target

[Service]
Type=simple
ExecStart=/usr/bin/myapp-db
Restart=always

[Install]
WantedBy=myapp.target
  1. 创建主服务单元文件 /etc/systemd/system/myapp-web.service
[Unit]
Description=My Application Web Service
PartOf=myapp.target
Requires=myapp-db.service
After=myapp-db.service

[Service]
Type=simple
ExecStart=/usr/bin/myapp-web
Restart=always

[Install]
WantedBy=myapp.target
  1. 启用并启动 target:
sudo systemctl daemon-reload
sudo systemctl enable myapp.target
sudo systemctl start myapp.target

Target 使用场景

  1. 多服务协同:将相关的服务组织在一起
  2. 环境切换:在不同运行环境间切换
  3. 服务分组:按功能对服务进行分组管理
  4. 启动顺序控制:通过依赖关系控制服务启动顺序

单元文件详解

1. 查看现有服务的单元文件

# 查看服务单元文件路径
systemctl show -p FragmentPath service_name

# 查看所有已加载的单元文件
systemctl list-unit-files

# 查看特定类型的单元文件
systemctl list-unit-files --type=service

2. 单元文件主要部分详解

[Unit] 部分

  • Description: 单元描述,用于显示在日志和状态信息中
  • Documentation: 文档链接,可以是 URL 或本地文件路径
  • Requires: 强依赖关系,如果依赖的服务失败,当前服务也会失败
  • Wants: 弱依赖关系,如果依赖的服务失败,当前服务仍然可以启动
  • Before: 指定当前单元在哪些单元之前启动
  • After: 指定当前单元在哪些单元之后启动
  • Conflicts: 指定与当前单元冲突的单元
  • Condition: 启动条件,例如:
    ConditionPathExists=/path/to/file
    ConditionFileNotEmpty=/path/to/file
    ConditionDirectoryNotEmpty=/path/to/dir
    ConditionUser=root
    ConditionGroup=admin
    

[Service] 部分

  • Type: 服务类型

    • simple: 默认类型,主进程由 ExecStart 启动
    • forking: 主进程 fork 子进程后退出
    • oneshot: 一次性服务,执行完成后退出
    • dbus: D-Bus 服务
    • notify: 通过 sd_notify 通知 systemd 服务已就绪
    • idle: 等待其他任务完成后再启动
  • ExecStart: 启动服务的主命令

  • ExecStartPre: 主命令执行前的准备工作

  • ExecStartPost: 主命令执行后的清理工作

  • ExecStop: 停止服务的命令

  • ExecReload: 重新加载配置的命令

  • Restart: 重启策略

    • no: 不重启
    • on-success: 仅在服务正常退出时重启
    • on-failure: 仅在服务异常退出时重启
    • on-abnormal: 仅在服务异常退出或超时时重启
    • always: 总是重启
    • on-abort: 仅在服务被强制终止时重启
  • User: 运行服务的用户

  • Group: 运行服务的用户组

  • WorkingDirectory: 工作目录

  • Environment: 环境变量

  • StandardOutput: 标准输出处理

    • inherit: 继承父进程
    • null: 丢弃
    • journal: 写入系统日志
    • syslog: 写入 syslog
    • kmsg: 写入内核日志
    • append:/path/to/file: 追加到文件
  • StandardError: 标准错误处理(选项同上)

  • TimeoutStartSec: 启动超时时间

  • TimeoutStopSec: 停止超时时间

  • TimeoutSec: 通用超时时间

[Install] 部分

  • WantedBy: 指定安装目标,通常是 multi-user.target
  • RequiredBy: 指定强依赖此单元的目标
  • Also: 同时安装的其他单元
  • Alias: 单元的别名

3. 单元文件示例详解

完整的 Web 服务示例

[Unit]
Description=My Web Application
Documentation=https://example.com/docs
Requires=network.target
After=network.target
Before=web-proxy.target
Conflicts=web-proxy.target
ConditionPathExists=/var/www/myapp
ConditionDirectoryNotEmpty=/var/www/myapp

[Service]
Type=simple
User=www-data
Group=www-data
WorkingDirectory=/var/www/myapp
Environment="APP_ENV=production"
Environment="DB_HOST=localhost"
ExecStartPre=/usr/bin/mkdir -p /var/log/myapp
ExecStartPre=/usr/bin/chown www-data:www-data /var/log/myapp
ExecStart=/usr/bin/python3 /var/www/myapp/app.py
ExecStartPost=/usr/bin/curl -X POST http://localhost:8080/health
ExecStop=/usr/bin/kill -TERM $MAINPID
ExecReload=/usr/bin/kill -HUP $MAINPID
Restart=always
RestartSec=10
TimeoutStartSec=30
TimeoutStopSec=30
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target
RequiredBy=web-proxy.target
Also=myapp-backup.service
Alias=web.service

4. 单元文件调试技巧

# 检查单元文件语法
systemd-analyze verify /etc/systemd/system/myapp.service

# 显示单元文件的完整配置
systemctl show myapp.service

# 显示单元文件的依赖关系图
systemd-analyze dot myapp.service | dot -Tsvg > myapp.svg

# 显示单元启动时间
systemd-analyze blame myapp.service

# 显示关键路径
systemd-analyze critical-chain myapp.service

5. 单元文件最佳实践

  1. 使用有意义的描述和文档链接
  2. 正确设置依赖关系
  3. 使用条件语句确保环境正确
  4. 设置适当的超时时间
  5. 配置日志输出
  6. 使用环境变量而不是硬编码值
  7. 实现优雅的停止和重载机制
  8. 定期检查服务状态和日志

常用命令

1. 服务管理

# 启动服务
sudo systemctl start service_name

# 停止服务
sudo systemctl stop service_name

# 重启服务
sudo systemctl restart service_name

# 查看服务状态
sudo systemctl status service_name

# 设置开机自启
sudo systemctl enable service_name

# 禁用开机自启
sudo systemctl disable service_name

2. 系统管理

# 重启系统
sudo systemctl reboot

# 关机
sudo systemctl poweroff

# 休眠
sudo systemctl hibernate

3. 日志查看

# 查看所有日志
sudo journalctl

# 查看特定服务的日志
sudo journalctl -u service_name

# 实时查看日志
sudo journalctl -f

实际案例

1. 创建自定义服务

假设我们要创建一个简单的 Python Web 服务,使用 Flask 框架:

  1. 首先创建 Python 脚本 /opt/myapp/app.py
from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello():
    return 'Hello, World!'

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)
  1. 创建服务单元文件 /etc/systemd/system/myapp.service
[Unit]
Description=My Flask Web Application
After=network.target

[Service]
User=www-data
WorkingDirectory=/opt/myapp
Environment="PATH=/opt/myapp/venv/bin"
ExecStart=/opt/myapp/venv/bin/python app.py
Restart=always

[Install]
WantedBy=multi-user.target
  1. 启用并启动服务:
sudo systemctl daemon-reload
sudo systemctl enable myapp
sudo systemctl start myapp

2. 定时任务

创建定时器单元文件 /etc/systemd/system/backup.timer

[Unit]
Description=Daily backup timer

[Timer]
OnCalendar=daily
Persistent=true

[Install]
WantedBy=timers.target

创建对应的服务单元文件 /etc/systemd/system/backup.service

[Unit]
Description=Backup service

[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup-script.sh

[Install]
WantedBy=multi-user.target

启用定时器:

sudo systemctl enable backup.timer
sudo systemctl start backup.timer

3. 挂载点管理

创建挂载单元文件 /etc/systemd/system/data.mount

[Unit]
Description=Mount data partition

[Mount]
What=/dev/sdb1
Where=/data
Type=ext4
Options=defaults

[Install]
WantedBy=multi-user.target

启用挂载点:

sudo systemctl enable data.mount
sudo systemctl start data.mount

4. 多服务协同示例

假设我们要部署一个完整的 Web 应用,包含数据库、缓存、Web 服务器和应用服务:

  1. 创建应用 target 文件 /etc/systemd/system/webapp.target
[Unit]
Description=Web Application Stack
Requires=network.target
After=network.target

[Install]
WantedBy=multi-user.target
  1. 创建数据库服务 /etc/systemd/system/webapp-db.service
[Unit]
Description=Web Application Database
PartOf=webapp.target
Requires=network.target
After=network.target

[Service]
Type=simple
User=postgres
Group=postgres
ExecStart=/usr/bin/postgres -D /var/lib/postgres/data
Restart=always

[Install]
WantedBy=webapp.target
  1. 创建缓存服务 /etc/systemd/system/webapp-cache.service
[Unit]
Description=Web Application Cache
PartOf=webapp.target
Requires=network.target
After=network.target

[Service]
Type=simple
User=redis
Group=redis
ExecStart=/usr/bin/redis-server /etc/redis/redis.conf
Restart=always

[Install]
WantedBy=webapp.target
  1. 创建 Web 服务器服务 /etc/systemd/system/webapp-nginx.service
[Unit]
Description=Web Application Nginx
PartOf=webapp.target
Requires=network.target
After=network.target

[Service]
Type=forking
ExecStart=/usr/bin/nginx -g 'daemon on; master_process on;'
ExecReload=/usr/bin/nginx -g 'daemon on; master_process on;' -s reload
ExecStop=/usr/bin/nginx -s stop
Restart=always

[Install]
WantedBy=webapp.target
  1. 创建应用服务 /etc/systemd/system/webapp-app.service
[Unit]
Description=Web Application Service
PartOf=webapp.target
Requires=webapp-db.service webapp-cache.service
After=webapp-db.service webapp-cache.service

[Service]
Type=simple
User=webapp
Group=webapp
WorkingDirectory=/opt/webapp
Environment="APP_ENV=production"
ExecStart=/opt/webapp/venv/bin/python app.py
Restart=always

[Install]
WantedBy=webapp.target
  1. 启用并启动整个应用栈:
sudo systemctl daemon-reload
sudo systemctl enable webapp.target
sudo systemctl start webapp.target
  1. 查看应用栈状态:
# 查看所有相关服务状态
sudo systemctl status webapp.target

# 查看具体服务状态
sudo systemctl status webapp-db.service
sudo systemctl status webapp-cache.service
sudo systemctl status webapp-nginx.service
sudo systemctl status webapp-app.service

常见问题解决

1. 服务启动失败

# 查看详细日志
sudo journalctl -u service_name -n 50

# 检查服务状态
sudo systemctl status service_name

2. 单元文件修改后

# 重新加载单元文件
sudo systemctl daemon-reload

# 重启服务
sudo systemctl restart service_name

3. 依赖问题

# 查看服务依赖
sudo systemctl list-dependencies service_name

# 查看服务被哪些其他服务依赖
sudo systemctl list-dependencies --reverse service_name

最佳实践

  1. 始终使用 systemctl 命令而不是直接编辑配置文件
  2. 在修改单元文件后记得运行 systemctl daemon-reload
  3. 使用 journalctl 查看日志以诊断问题
  4. 为服务设置适当的依赖关系
  5. 使用 Restart=always 确保服务崩溃时自动重启
  6. 定期检查服务状态和日志

参考资源