Blog·Tanky WooABOUTRSS

Sysctl加载配置顺序问题

29 Jan 2015

系统Gentoo, 最近遇到一个比较离奇的问题.

网关机器的sysctl运行时配置中,net.ipv4.ip_forward被改为0, 原来是1; net.ipv4.conf.default.rp_filternet.ipv4.conf.all.rp_filter也都被设置为1了.

因为最近系统出现过几次异常, 所有服务不可用, 怀疑是某个服务造成, 不排除影响到这块, 虽然感觉不太可能.

昨天恰好又出现了, 于是今天准备写一个监控net.ipv4.ip_forward值的脚本, 才写到一半, 跳闸了, 网关机器启动后, 发现无法转发, nat表都是正常的, 继而发现ip_forward也被改为0了.

改回来后, 过了一会, 又跳闸, 启动后确认, 基本确定了是重启会造成这个问题. 因为之前网关机器的服务异常时, 同事都是重启了机器.

继续排查, Gentoo的默认/etc/sysctl.conf配置(主要部分):

# Disables packet forwarding
net.ipv4.ip_forward = 0
# Disables IP dynaddr
#net.ipv4.ip_dynaddr = 0
# Disable ECN
#net.ipv4.tcp_ecn = 0
# Enables source route verification
net.ipv4.conf.default.rp_filter = 1
# Enable reverse path
net.ipv4.conf.all.rp_filter = 1

可以看到, 这里恰好都是被修改的配置.

继续看/etc/sysctl.d/default.conf配置, 里面有对这几个项的修改. 不过检查发现其它项的值都是和配置的一样, 唯独这几个被修改为和/etc/sysctl.conf中一样了.

按理来说, 应该是先加载/etc/sysctl.conf, 然后加载/etc/sysctl.d/*.conf中以ascii首字母排序的配置, 后者会覆盖前者的配置.

本地直接测试:

sysctl --system

这个会加载所有的sysctl配置(sysctl -p默认无参数只加载/etc/sysctl.conf)

发现它的顺序是先加载/etc/sysctl.d/*.conf, 最后加载/etc/sysctl.conf.

man sysctl:

  --system
         Load settings from all system configuration files.
         /run/sysctl.d/*.conf
         /etc/sysctl.d/*.conf
         /usr/local/lib/sysctl.d/*.conf
         /usr/lib/sysctl.d/*.conf
         /lib/sysctl.d/*.conf
         /etc/sysctl.conf

但是以前重启都是没问题的.

继续看/etc/init.d/sysctl:

# ... 省略其它代码

start()
{
        ebegin "Configuring kernel parameters"
        sysctl --system
        eend $? "Unable to configure some kernel parameters"
}

启动这块是调用的这个sysctl --system命令.

那为何之前没问题?

后来同事发现近期这个脚本更新过, 于是我看了下我本地Gentoo系统上的这个启动脚本(主要代码):

# ... 省略其它代码

start()
{
        local conf= retval=0 err errs

        ebegin "Configuring kernel parameters"
        eindent

        for conf in /etc/sysctl.conf /etc/sysctl.d/*.conf; do
                if [ -r "$conf" ]; then
                        vebegin "applying $conf"
                        if ! err=$(sysctl -p "$conf" 2>&1 >/dev/null) ; then
                                errs="${errs} ${err}"
                                sysctl -e -p "${conf}" >/dev/null
                        fi
                        veend $? || retval=1
                fi
        done

        eoutdent
        if [ ${retval} -eq 0 ] && [ -n "${errs}" ] ; then
                ewarn "Unknown keys:${errs}"
        fi
        eend $retval "Some errors were encountered: ${errs}"
}

这个是老的sysctl启动脚本, 里面就设置了导入配置的顺序, 先导入/etc/sysctl.conf, 然后用/etc/sysctl.d/*.conf中的配置覆盖.

感觉后者是合理的顺序, 不明白为何更新后, 将/etc/sysctl.conf作为最后导入覆盖的配置文件了.

如果这么做, 那么这么文件就最好全部注释掉. 感觉这块挺坑的.