OpenRC 快速查询指南:化繁为简的 Init 系统

在 Linux 的世界里,**init 系统是内核启动后运行的第一个进程(PID 1)**,负责初始化系统并管理所有后续进程。虽然 systemd 已成为许多主流发行版的默认选择,但仍有相当一部分用户和发行版(如 Gentoo, Alpine Linux, Artix Linux 等)青睐于更传统、更轻量、更透明的 OpenRC。

OpenRC 遵循 Unix “只做一件事并把做好” 的哲学。是一个基于依赖关系的 init 系统,以其简洁的配置文件、清晰的服务管理和出色的可移植性而闻名。

核心概念

  • Init 脚本 (Init Script): 存放于 /etc/init.d/ 目录下的可执行脚本,负责控制单个服务的启动、停止和重启等操作。
  • 运行级别 (Runlevel): 代表系统在特定状态下运行的服务集合。例如,boot 运行级别在系统引导时运行,default 是正常的多用户操作级别,shutdown 则在关机时运行。
  • 依赖关系 (Dependencies): OpenRC 通过分析 Init 脚本中的依赖关系来决定服务的启动顺序,确保服务在所依赖的服务启动之后才启动。
  • 配置文件:
    • /etc/rc.conf: OpenRC 的主配置文件,控制全局行为。
    • /etc/conf.d/: 存放各个服务特定配置变量的目录。这使得 Init 脚本本身可以保持通用,而将可变配置分离出来。

常用核心命令

掌握以下 rc-* 命令是使用 OpenRC 的关键。

rc-status - 查看服务状态

用于检查不同运行级别中服务的状态。

  • 查看所有运行级别的服务状态:

    1
    rc-status

    输出示例:

    1
    2
    3
    4
    5
    6
    7
    Runlevel: default
    sshd [ started ]
    nginx [ started ]
    cronie [ started ]
    local [ stopped ]
    Dynamic Runlevel: hotplugged
    Dynamic Runlevel: needed
  • 查看特定运行级别的服务状态:

    1
    rc-status --runlevel boot
  • 查看所有服务(包括已停止的):

    1
    rc-status --all

rc-service - 管理单个服务

这是一个直接操作单个服务的便捷命令,是 /etc/init.d/service_name <action> 的封装。

  • 启动服务:

    1
    rc-service nginx start
  • 停止服务:

    1
    rc-service nginx stop
  • 重启服务:

    1
    rc-service nginx restart
  • 查询服务状态:

    1
    rc-service nginx status

    输出会明确显示服务是 “started”, “stopped”, 或 “crashed”。

rc-update - 管理运行级别中的服务

此命令用于将服务添加或移除出特定的运行级别,从而控制其是否开机自启。

  • 将服务添加到 default 运行级别(开机自启):

    1
    rc-update add nginx default
  • 将服务添加到 boot 运行级别(在引导早期启动):

    1
    rc-update add udev boot
  • 从运行级别中移除服务(禁止开机自启):

    1
    rc-update del nginx default
  • 显示所有已添加到运行级别的服务:

    1
    rc-update show -v

rc-runlevel - 查看当前运行级别

  • 显示当前和上一个运行级别:

    1
    rc-runlevel

    输出示例: default (表示当前在 default 级别)

openrc - 切换运行级别

此命令用于手动切换系统的运行级别。

  • 切换到 default 级别:

    1
    openrc default
  • 切换到 nonetwork 级别:

    1
    openrc nonetwork

配置文件深度解析

主配置文件: /etc/rc.conf

这是 OpenRC 的大脑,定义了其全局行为。以下是一些关键的配置项:

配置项 默认值 描述
rc_sys “” 定义一个子系统标识符。例如,在容器(如LXC)中可设为 "LXC",以加载特定的子系统脚本。
rc_parallel "YES" 是否并行启动服务。设置为 "NO" 将串行启动,便于调试。
rc_shell "/sbin/openrc-run" 执行 init 脚本时使用的 shell。
rc_depend_strict "YES" 是否启用严格的依赖检查。YES 意味着如果依赖的服务失败,当前服务也不会启动。
rc_log_path "/var/log/rc.log" OpenRC 日志文件的路径。
rc_log_sev "info" 日志记录级别,可以是 info, warn, err
rc_hotplug "" 定义处理热插拔设备的服务,例如 eudev-postmount
rc_cgroup_mode "hybrid" Cgroup(控制组)模式。可以是 legacy, hybrid, unified
rc_nocolor "NO" 是否禁用彩色输出。
rc_verbose "NO" 是否在启动时显示详细的调试信息。
unicode "YES" 是否启用 Unicode (UTF-8) 支持。在现代系统中建议保持开启。
rc_zap_scanned_disks "NO" 在扫描磁盘前是否清除之前的扫描缓存。
rc_send_sighup "NO" 在切换运行级别时,是否向上一个 init 进程发送 SIGHUP 信号。
rc_conf_d_dirs "/etc/conf.d" conf.d 配置文件的搜索路径。
rc_init_d_dirs "/etc/init.d" init.d 脚本的搜索路径。

示例 /etc/rc.conf 配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
# /etc/rc.conf

# 启用并行启动以加快开机速度
rc_parallel="YES"

# 设置更严格的依赖检查
rc_depend_strict="YES"

# 启用 cgroup 支持
rc_cgroup_mode="unified"

# 如果终端不支持UTF-8,可以关闭unicode
# unicode="NO"

服务特定配置: /etc/conf.d/

为了避免直接修改 /etc/init.d/ 中的脚本,OpenRC 将服务的可配置变量提取到 /etc/conf.d/ 目录下的同名文件中。

示例:配置 sshd 服务

sshdinit 脚本 /etc/init.d/sshd 会检查是否存在 /etc/conf.d/sshd 文件,并加载其中的变量。

假设我们要让 sshd 监听一个额外的端口 2222,并传递其他启动参数。我们可以编辑 /etc/conf.d/sshd

1
2
3
4
5
# /etc/conf.d/sshd

# 将额外的启动参数传递给 sshd 守护进程
# -p 2222 表示额外监听 2222 端口
SSHD_OPTS="-p 2222"

rc-service sshd start 执行时,/etc/init.d/sshd 脚本会读取 $SSHD_OPTS 变量,并将其传递给 sshd 二进制文件,从而实现了配置与逻辑的分离。

示例:配置 apache2

类似地,如果你想为 Apache HTTP 服务器传递额外的启动参数,可以编辑 /etc/conf.d/apache2

1
2
3
4
5
# /etc/conf.d/apache2

# -D FOREGROUND 是默认值
# 我们可以添加其他定义,例如 -D MyCustomFlag
APACHE2_OPTS="-D FOREGROUND -D MyCustomFlag"

编写自己的 Init 脚本

声明式风格

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#!/sbin/openrc-run
# Copyright 1999-2013 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2

# 这是一个通用的 OpenRC 声明式 init 脚本模板。
# ${SVCNAME} 是一个由 openrc-run 自动提供的变量,其值等于该脚本的文件名(例如 "myservice")。

# --- 基础配置 (通常需要修改) ---

# 服务的简短描述,将显示在 `rc-status` 等命令中。
description="一个自定义的后台服务模板"

# 要执行的命令或程序的绝对路径。
command="/usr/local/bin/myservice-daemon"

# 传递给命令的参数。
# 最佳实践: 将参数定义在 /etc/conf.d/${SVCNAME} 文件中,如此处所示。
# 这样,用户就可以在不修改 init 脚本本身的情况下调整参数。
command_args="${MYSERVICE_OPTS}"

# PID 文件的路径。这是 openrc 用来跟踪进程是否在运行的关键。
# 使用 ${SVCNAME} 变量可以使模板更通用。
pidfile="/run/${SVCNAME}.pid"


# --- 进程管理 (按需修改) ---

# 是否在后台作为守护进程运行。对于长时间运行的服务,通常设置为 "yes"。
command_background="yes"

# 运行服务的用户和组。为了安全,强烈建议不要使用 root。
# 格式为 "user" 或 "user:group"。
command_user="myservice:myservice"

# (可选) 如果你的程序需要在一个特定的目录下运行。
# directory="/var/lib/myservice"


# --- 日志配置 (可选) ---

# 将标准输出重定向到此文件。
output_log="/var/log/${SVCNAME}.log"

# 将标准错误重定向到此文件。
error_log="/var/log/${SVCNAME}.err"


# --- 依赖关系 (非常重要) ---

depend() {
# `need` 表示强依赖。在启动本服务前,这些服务必须已经成功启动。
need net # 示例:需要网络连接
need localmount # 示例:需要本地文件系统已挂载

# `use` 表示弱依赖。如果这些服务存在于运行级别中,则会先于本服务启动,但即使失败了,本服务也会尝试启动。
use logger # 示例:如果存在 syslog-ng 或其他日志服务,请先启动
use dns # 示例:如果需要域名解析

# `after` 表示顺序。本服务会在这些服务之后启动,但并不依赖它们。
# after firewall # 示例:在防火墙服务之后启动
}

# (可选) 如果服务在停止后需要一些清理工作。
stop_post() {
# 例如,删除 lock 文件
# rm -f /var/lock/subsys/${SVCNAME}
return 0
}

手动风格

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#!/sbin/openrc-run

# 定义服务描述
description="My custom service script"

# 定义依赖关系:在网络和日志服务启动后才启动
depend() {
need net
use logger
}

# 定义如何启动服务
start() {
# ebegin 和 eend 用于打印友好的状态信息
ebegin "Starting My Service"
# 使用 start-stop-daemon 来管理进程
start-stop-daemon --start --quiet --pidfile /var/run/myservice.pid \
--exec /usr/local/bin/myservice -- --config /etc/myservice.conf
eend $?
}

# 定义如何停止服务
stop() {
ebegin "Stopping My Service"
start-stop-daemon --stop --quiet --pidfile /var/run/myservice.pid
eend $?
}

步骤:

  1. 将此文件保存为 /etc/init.d/myservice
  2. 赋予执行权限:chmod +x /etc/init.d/myservice
  3. 现在你就可以用 rc-service myservice start 来管理,并用 rc-update add myservice default 让开机自启了。