重启服务器rc.local中的配置没生效

背景

例如通过route add添加一条静态路由后,想要持久化生效,一个简单的做法就是将这条命令加入到/etc/rc.local这个脚本文件中。
image
但是服务器重启后,通过route -n命令查看,发现压根没执行添加路由的命令。
image

原因

早期SysV init 系统中,/etc/rc.local脚本在所有其他 init 脚本执行完毕后自动执行,从而为系统管理员提供了一个在系统启动时运行自定义命令的便捷方法。

后面systemd 成为许多主流 Linux 发行版的默认 init 系统,其使用服务单位(unit files)来控制启动进程,而不是传统的 init 脚本。所以/etc/rc.local 不再自动开机执行。

但为了向后兼容,许多基于 systemd 的系统提供了一个名为 rc-local.service 的特殊服务,它专门用于执行 /etc/rc.local 脚本。

这样,那些还依赖于 /etc/rc.local 的系统管理员或旧的应用程序仍然可以在 systemd 环境中运行它。

解决方法

检查systemd托管rc.local脚本的服务是否正常启动
通过查看rc-local.service(ubuntu2004上)是否正常启动。如果服务一直是启动中的状态,那么说明rc.local中指定的命令或还没有执行完成。因为rc-local.service服务的启动类型是forking,只有rc.local退出后才会显示启动成功。

如果rc.local里面加了个死循环脚本,那么永远不会返回到rc.local,因此后面的命令也将不会执行。
image

优化方法

方法一: 使用 command || true 可以确保即使 command 执行失败(返回非零退出状态)也不会导致整个脚本退出。因为rc.local有两个特点,一是按顺序执行,而是如果某个命令或者脚本返回非0,就认为失败了,会终止下面命令的执行。

command1 || true
command2 || true
command3 || true

方法二: 如果一个命令可能需要很长时间才能完成,或者它需要持续运行(例如,守护进程),将其放入后台执行。这可以通过在命令后面添加 & 来实现,这样就不会阻塞下面命令的继续执行。

#!/bin/sh -e
/path/to/looping_script.sh &
command2
command3

说明

rc-local.service配置文件说明

[Unit]
Description=/etc/rc.local Compatibility
Documentation=man:systemd-rc-local-generator(8)
ConditionFileIsExecutable=/etc/rc.local  # 该文件需要有可执行权限
After=network.target

[Service]
Type=forking
ExecStart=/etc/rc.local start  # 为了兼容性加了start参数
TimeoutSec=0
RemainAfterExit=yes  # 执行一次退出后也显示启动成功
GuessMainPID=no

image

启动类型是forking,forking一般是用来托管守护进程。应该是为了兼容性,因为rc.local脚本里面可以放一系列的命令、或者后台进程、守护进程等。

在forking这种启动模式下,只有当当父进程(由ExecStart启动的原始进程)退出,并且返回退出码0时,systemd会认为该服务已成功启动,也就是当"/etc/rc.local start"这个命令执行完成后,rc-local.service服务才会显示启动成功。

这里“ExecStart=/etc/rc.local start”不是守护进程,为什么也可以使用forking这种启动类型?

因为forking这种启动类型判断是否启动成功是判断ExecStart指定的命令是否退出,如果正常推出了就认为启动成功。故此及时/etc/rc.local脚本并没有进行fork操作,但仍然正常退出(例如返回退出码0),systemd仍然会认为这个服务按预期启动了,即使实际上并没有任何后台进程在运行。

posted on 2023-09-04 17:59  背对背依靠  阅读(519)  评论(0编辑  收藏  举报