记一次在openEuler系统/etc/rc.local配置添加路由策略不生效问题
记一次在openEuler系统/etc/rc.local配置添加路由策略不生效问题
问题现象
最近有一个场景是需要给主机添加路由,通过命令行配置只能临时生效,系统重启后就丢失没有了,需要进行永久配置。于是在openEuler操作系统/etc/rc.local开机启动脚本中配置添加路由策略,/etc/rc.local 配置内容如下:
[root@localhost ~]# cat /etc/rc.local
......
ip route add 192.168.2.1 via 192.168.77.2
然而在对操作系统进行reboot重启后检查发现路由策略未生效,检查如下:
[root@localhost ~]# netstat -rn
Kernel IP routing table
Destination Gateway Genmask Flags MSS Window irtt Iface
0.0.0.0 192.168.77.2 0.0.0.0 UG 0 0 0 ens33
172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0
192.168.77.0 0.0.0.0 255.255.255.0 U 0 0 0 ens33
192.168.250.0 0.0.0.0 255.255.255.0 U 0 0 0 br-ebcf487aed7d
原因分析
/etc/rc.local下配置未成功执行,首先想到的最常见的原因也是很容易忽略的原因是/etc/rc.d/rc.local文件没有执行权限。因为早期SysV init 系统中,/etc/rc.local脚本在所有其他 init 脚本执行完毕后自动执行,从而为系统管理员提供了一个在系统启动时运行自定义命令的便捷方法。后面systemd 成为许多主流 Linux 发行版的默认 init 系统,其使用服务单位(unit files)来控制启动进程,而不是传统的 init 脚本。为了向后兼容,许多基于 systemd 的系统也提供了一个名为 rc-local.service 的特殊服务,它专门用于执行 /etc/rc.local 脚本。/etc/rc.local为/etc/rc.d/rc.local的软链接文件,/etc/rc.d/rc.local文件默认没有执行权限,所以/etc/rc.local 不再自动开机执行。如果你留意过/etc/rc.local 文件内容,也会发现注释说明你必须执行chmod +x /etc/rc.d/rc.local
命令来确保该文件能够被正常引导执行,如下:
[root@localhost ~]# cat /etc/rc.local
#!/bin/bash
......
# Please note that you must run 'chmod +x /etc/rc.d/rc.local' to ensure
# that this script will be executed during boot.
检查该文件权限,确认/etc/rc.d/rc.local被添加了执行权限,故而排除这个原因
[root@localhost ~]# ll /etc/rc.local /etc/rc.d/rc.local
-rwxr-xr-x. 1 root root 515 Nov 16 10:54 /etc/rc.d/rc.local
lrwxrwxrwx. 1 root root 13 Jun 23 2021 /etc/rc.local -> rc.d/rc.local
前面提到systemd 的系统通过 rc-local.service 服务来管理执行 /etc/rc.local 脚本,查看该服务状态:
[root@localhost ~]# systemctl status rc-local
● rc-local.service - /etc/rc.d/rc.local Compatibility
Loaded: loaded (/usr/lib/systemd/system/rc-local.service; enabled-runtime; vendor preset: disabled)
Active: failed (Result: exit-code) since Thu 2023-11-16 10:55:59 CST; 6min ago
Docs: man:systemd-rc-local-generator(8)
Process: 761 ExecStart=/etc/rc.d/rc.local start (code=exited, status=2)
Nov 16 10:55:59 localhost.localdomain systemd[1]: Starting /etc/rc.d/rc.local Compatibility...
Nov 16 10:55:59 localhost.localdomain rc.local[766]: Error: Nexthop has invalid gateway.
Nov 16 10:55:59 localhost.localdomain systemd[1]: rc-local.service: Control process exited, code=exited, status=2/INVALIDARGUMENT
Nov 16 10:55:59 localhost.localdomain systemd[1]: rc-local.service: Failed with result 'exit-code'.
Nov 16 10:55:59 localhost.localdomain systemd[1]: Failed to start /etc/rc.d/rc.local Compatibility.
通过检查发现该服务状态为failed,并且发现一条关键日志Error: Nexthop has invalid gateway.
,无效的网关。这里大概猜测/etc/rc.local配置路由重启不生效的原因是由于添加路由依赖网络服务,而在当前系统启动过程中内核读取rc.local时网络服务尚未启动,导致添加路由失败。
解决方案
解决方案1
/etc/rc.d/rc.local开机启动脚本由rc-local服务引导执行,配置修改在network-online.target后启动该服务,network.target并不能保证网络服务已经启动,并通过Requires指定本unit与其它unit之间存在强依赖关系。rc-local服务默认配置路径为/usr/lib/systemd/system/rc-local.service。在该文件的[Unit]模块中添加或修改Requires和After项值为network-online.target,修改内容如下:
[root@localhost ~]# vim /usr/lib/systemd/system/rc-local.service
# SPDX-License-Identifier: LGPL-2.1+
#
# This file is part of systemd.
#
# systemd is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
# This unit gets pulled automatically into multi-user.target by
# systemd-rc-local-generator if /etc/rc.d/rc.local is executable.
[Unit]
Description=/etc/rc.d/rc.local Compatibility
Documentation=man:systemd-rc-local-generator(8)
ConditionFileIsExecutable=/etc/rc.d/rc.local
#After=network.target
Requires=network-online.target
After=network-online.target
[Service]
Type=forking
ExecStart=/etc/rc.d/rc.local start
TimeoutSec=0
RemainAfterExit=yes
GuessMainPID=no
network-online.target是主动等待直到网络“启动”的target,其中“启动”的定义由网络管理软件定义。通常,它表示某种已配置的、可路由的IP地址。其主要目的是主动延迟服务的激活,直到建立网络为止。
通知systemd重新加载配置文件。
systemctl daemon-reload
重启rc-local.service,使/etc/rc.d/rc.local脚本立即生效。
systemctl restart rc-local.service
再次检查路由情况或重启主机确认路由配置已正常添加完成。
[root@localhost ~]# netstat -rn
Kernel IP routing table
Destination Gateway Genmask Flags MSS Window irtt Iface
0.0.0.0 192.168.77.2 0.0.0.0 UG 0 0 0 ens33
172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0
192.168.2.1 192.168.77.2 255.255.255.255 UGH 0 0 0 ens33
192.168.77.0 0.0.0.0 255.255.255.0 U 0 0 0 ens33
192.168.250.0 0.0.0.0 255.255.255.0 U 0 0 0 br-ebcf487aed7d
解决方案2
不使用/etc/rc.local配置路由,改为将路由写入静态路由配置文件(该方法即使重启网卡路由也不会丢失)。在/etc/sysconfig/network-scripts/目录中创建或修改route-文件。
该目录是与路由相关的接口的名称。
<network/prefix> via <gateway>
其中<network/prefix>是带有前缀的远程网络,是下一跳的IP地址。
例如,要添加一条通过192.168.77.2到192.168.2.1/24网络的路由,以便在ens33启用时都处于活动状态:
# cat /etc/sysconfig/network-scripts/route-ens33
192.168.2.1/24 via 192.168.77.2
执行以下命令,使修改生效。
ifup ens33
附:****ansible批量添加路由脚本
[root@ansible-server playbook]# cat route-add.yml
---
- name: 添加vpn路由并立即生效
hosts: all
tasks:
- name: 添加vpn路由
copy:
content: "192.168.2.1/24 via 192.168.77.2 dev ens33"
dest: /etc/sysconfig/network-scripts/route-ens33
backup: yes
- name: 启用vpn路由策略
shell: ifup ens33