当同事反馈 Docker 容器无法正常上网时,我们通过分析发现这是由于 IPv4 转发被关闭引起的。最终我们发现,在 Red Hat 系列系统中,使用 /etc/rc.d/init.d/network
来管理网络时,重启网络服务实际上执行的是 stop
和 start
命令的组合。而在执行 stop
命令时,会运行 sysctl -w net.ipv4.ip_forward=0
,从而导致 IPv4 转发被禁用。
这个问题早在 Red Hat Enterprise Linux 5 中就已经被提出(参考 Bug 552653)。实际上,这个问题一直延续到 Red Hat Enterprise Linux 7。
但在执行 start
命令的时候,会执行 apply_sysctl
函数,而 apply_sysctl
函数引用来源于 /etc/init.d/functions
文件。
❯ cat /etc/rc.d/init.d/network
. /etc/init.d/functions
start)
...(略)
apply_sysctl # 应用 sysctl
stop)
...(略)
sysctl -w net.ipv4.ip_forward=0 > /dev/null 2>&1
/etc/init.d/functions
文件中 apply_sysctl
函数,会对 /etc/sysctl.d
目录下的配置文件进行重新加载应用。
❯ cat /etc/init.d/functions
...(略)
# Apply sysctl settings, including files in /etc/sysctl.d
apply_sysctl() {
if [ -x /lib/systemd/systemd-sysctl ]; then
/lib/systemd/systemd-sysctl
else
for file in /usr/lib/sysctl.d/*.conf ; do
is_ignored_file "$file" && continue
[ -f /run/sysctl.d/${file##*/} ] && continue
[ -f /etc/sysctl.d/${file##*/} ] && continue
test -f "$file" && sysctl -e -p "$file" >/dev/null 2>&1
done
for file in /run/sysctl.d/*.conf ; do
is_ignored_file "$file" && continue
[ -f /etc/sysctl.d/${file##*/} ] && continue
test -f "$file" && sysctl -e -p "$file" >/dev/null 2>&1
done
for file in /etc/sysctl.d/*.conf ; do
is_ignored_file "$file" && continue
test -f "$file" && sysctl -e -p "$file" >/dev/null 2>&1
done
sysctl -e -p /etc/sysctl.conf >/dev/null 2>&1
fi
}
因此,如果您在 /etc/sysctl.d
目录下有配置文件启用了 net.ipv4.ip_forward=1
,则不必担心该问题。然而,如果您只是通过 echo 1 > /proc/sys/net/ipv4/ip_forward
手动配置 IPv4 转发,将会受到影响。
实际上,使用 Docker 时,一般不需要手动配置 net.ipv4.ip_forward=1
,因为 Docker 默认会自动启用 IPv4 转发功能(--ip_forward=true
)。但是,如果您使用 /etc/rc.d/init.d/network
管理网络,则必须将以下内容添加到 /etc/sysctl.d/99-sysctl.conf
中:
❯ echo 'net.ipv4.ip_forward=1' >> /etc/sysctl.d/99-sysctl.conf
在 Docker 的源码仓库中可以看到相关实现:moby/libnetwork/drivers/bridge/setup_ip_forwarding.go
const (
ipv4ForwardConf = "/proc/sys/net/ipv4/ip_forward"
ipv4ForwardConfPerm = 0o644
)
func configureIPForwarding(enable bool) error {
var val byte
if enable {
val = '1'
}
return os.WriteFile(ipv4ForwardConf, []byte{val, '\n'}, ipv4ForwardConfPerm)
}
结论:如果您在 Red Hat Enterprise Linux 5-7 系列中使用 Docker 服务,并且使用桥接网络模式 (bridge
) 和 /etc/rc.d/init.d/network
进行网络管理,则必须执行以下配置,以确保 IPv4 转发功能正常:
❯ echo 'net.ipv4.ip_forward=1' >> /etc/sysctl.d/99-sysctl.conf
或者,您可以在重启网络服务后,再次重启 Docker 服务,以确保容器网络正常。当然,如果您使用 host
网络模式,则不受此问题影响。
实际因为 Rocky Linux 最低版本是 Rocky Linux 8,并通过 NetworkManager 进行网络管理,所以就不存在此问题。😂