案件:
在我们的 Ubuntu Xenial(16.04)站上,我托管了一个用 Python 编写的机器人,该机器人连接到我们的 IRC 服务器,该服务器设置为在/etc/network/if-up.d/
尽管脚本设置为仅运行一个实例,但我们的机器人却连接了两次,并且如果其用户名已被占用,它必须关闭当前连接并使用下一个用户名重新连接。
故障排除:
我曾经ps aux | grep bot_name.py$
确定问题不在脚本级别,但由于某种原因,当 Ubuntu 连接到互联网时,该脚本的两个实例正在运行。
我在脚本级别创建了一个解决方法,用于检查有多少个脚本实例在本地运行,如果实例数量除一之外,它将关闭该脚本。
问题:
哪些原因if-up.d/
可能导致启动存储在其目录中的单个脚本的两个实例?
if-up.d/脚本:
中的脚本if-up.d/
是一个 sh 脚本,它包装了对 python 的调用,因此它会等待 irc 的超时时间然后连接:
sleep 269
sh -c "python /dir/to/bot.py >> /dir/to/log" & disown
其他来源:
到目前为止,我已经看到了这个问题的镜像这里在不同的环境中,有人使用 Python 脚本发送电子邮件,并且由于脚本启动了多个实例,收件人收到了多封电子邮件。
我也能找到这个问题在16.04服务器故障有人遇到过类似的问题,列出的答案表明它可能运行了两次,因为有一个实例是为 IPv4 和 IPv6 启动的,但我不确定这是否真的是根本原因,或者如何指定您只想使用 IPv4 并放弃 IPv6 调用(如果是这种情况)。
在我们的 3 个案例中,Ubuntu 16.04 似乎是共同点,这让我怀疑这是否是一个错误。
答案1
除了检查 中的接口之外$IFACE
,您可能还想检查 中的地址系列$ADDRFAM
。IPv4 为“inet”,IPv6 为“inet6”。
中的脚本/etc/network/if-*.d
不仅针对每个接口运行,而且对于每个地址系列也适用如果已配置(IPv4 和 IPv6)。
例如,如果您只需要脚本针对特定接口运行并且仅针对其 IPv4 地址运行,则可以从如下操作开始:
[ "$IFACE" = "eth0" ] || exit 0 # we only want interface "eth0"
[ "$ADDRFAM" = "inet" ] || exit 0 # we only want IPv4 (not "inet6" for IPv6)
答案2
进一步研究后16.04 if-up.d/
,我发现有人在运行 Debian 7.3 时遇到了类似的问题,并找到了答案。这不是一个错误,我遇到的问题在此处进行了解释@Søren Løvborg 的回答:
一般来说,ifup/ifdown 不能保证你的脚本只运行一次。因此你的脚本必须是幂等的;也就是说,包含自己的逻辑,不建立已经存在的隧道。当然,最好的方法是通过实际检查隧道是否已经存在来启动脚本:
# Exit if network device "tun0" exists.
ip link show dev tun0 >/dev/null 2>&1 && exit 0
此外,if-up.d 等脚本针对每个启动的接口运行。正如 Jeff 正确观察到的那样,if-*.d 脚本没有接收任何参数来指定启动哪个接口。
相反,根据接口(5)手册页:
There exists for each of the above mentioned options a directory
/etc/network/if-<option>.d/ [...]
All of these commands have access to the following environment vari‐
ables.
IFACE physical name of the interface being processed
LOGICAL
logical name of the interface being processed
[... and more ...]
解决方案是启动脚本并检查正确的 IFACE 值,例如:
# Exit if we're not starting "eth0".
[ "$IFACE" = 'eth0' ] || exit 0
否则,该脚本将针对每个网络设备(包括 lo)运行,此时建立隧道可能为时过早。
也就是说,我的问题是lo
除了我的eth0
连接之外,它还执行接口的脚本。我采取了其中一个步骤,即使用逻辑对脚本进行编程,使其不运行多次,而只运行一次,而无需调整代码,只需在执行前指定要使用的接口即可。
如果我想返回并在 sh 级别解决这个问题,我只需要用此行更新我的脚本,因为我想要使用的接口是我的以太网连接
[ "$IFACE" = 'eth0' ] || exit 0
sleep 269
sh -c "python /dir/to/bot.py >> /dir/to/log" & disown