在企业级服务器上,系统的每一次开机都相当于一次“交接仪式”,而启动脚本则是那位负责递交钥匙的管理员,它决定哪些服务先行、哪些任务随后执行,直接影响业务的可用性与恢复时长。
CentOS 7 采用 systemd 取代了传统的 SysVinit,启动过程被划分为 initrd、kernel、systemd 初始目标(如 graphical.target、multi-user.target)以及各个 service 单元的并行激活。每个目标下的依赖关系由 .service、.target、.socket 等文件在 /etc/systemd/system 与 /usr/lib/systemd/system 中声明。
虽然 systemd 已经提供了更细粒度的管理手段,/etc/rc.d/rc.local 仍被保留为兼容层。它在 multi-user.target 完全激活之后以单线程方式执行,仅在文件拥有可执行权限(chmod +x /etc/rc.d/rc.local)时才会被调用。实际场景中,运维人员常把一次性初始化脚本、临时调试命令塞进这里,以免在系统正式上线前编写完整的 unit 文件。
/etc/rc.d/rc.local:传统兼容入口,适合快速实验。.service 单元:推荐方式,可声明 After=、Requires= 等依赖,支持自动重启与日志收集。.timer 单元:若任务只需在系统启动后延迟执行,使用 timer 更加轻量。udev 规则:硬件层面的触发脚本,常用于磁盘挂载后立即执行检查。下面给出一个最小化的 myapp.service 示例,演示如何把 “开机创建 ~/test.txt” 这件事从 rc.local 移植到 systemd:
[Unit]
Description=Create test file at first boot
After=network.target
[Service]
Type=oneshot
ExecStart=/bin/bash -c 'if [ ! -f /home/%u/test.txt ]; then touch /home/%u/test.txt; fi'
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
将文件保存至 /etc/systemd/system/myapp.service,随后执行 systemctl enable myapp.service 与 systemctl start myapp.service 即可。此方式的优势在于:系统会在网络等关键服务就绪后再运行,若脚本执行失败,systemd 会自动记录日志并提供重试策略,远比 rc.local 的盲目并行更可靠。
从根本上说,启动脚本的“作用”不只是“让命令跑起来”,而是通过依赖图、并行调度以及统一的日志体系,为整套业务提供可预测、可审计的启动路径。把握了这一点,运维人员便能在几行配置里把“开机即服务”做得既安全又优雅——
参与讨论
感觉 systemd 写 service 虽然麻烦点,但日志和依赖管理确实稳多了
新手问下,那个 %u 是自动替换成当前用户吗?还是得手动指定?
前几天刚把 rc.local 里的脚本迁成 service,启动快了不说,出错还能 journalctl 看,真香
这玩意坑不少,以前图省事全塞 rc.local,结果服务依赖乱成一锅粥
要是写错了 .service 文件,systemctl daemon-reload 之后会报错吗?
我之前也踩过这坑,rc.local 没加执行权限,还以为脚本逻辑有问题
吃瓜看运维老哥怎么优雅地开机即服务 👀
rc.local 还真不能乱用,之前塞了个脚本没加权限,开机死活不跑,折腾半天 😅
蛮好的,至少比 CentOS6 那套 init 脚本清晰点
话说 After=network.target 就一定能保证网卡 up 吗?我试过有时候 DNS 还没好
临时调试塞 rc.local 确实方便,但上线前真得改成 unit,不然半夜报警哭死
有人试过用 .timer 做开机延迟任务吗?比如等数据库起来再跑个初始化?
这文章说的对,启动不只是跑命令,关键是“啥时候跑”和“跑挂了咋办”
hhh 我司还在用 rc.local 跑核心服务,老板说能跑就行,不敢动
想问下 RemainAfterExit=yes 到底有啥用?删了会怎样?