需求背景

服务器配置有多个网卡,拥有多个外网IP地址,业务需求是:某一个docker容器主动访问外网时,使用指定的IP地址。

我们知道默认情况下,从外界进来的数据包,我们是可以通过-p参数来指定使用哪一个IP地址的,但容器主动访问外界的时候就有所不同了。

问题在哪

容器outgoing packets(主动访问网络)时数据包怎么走是由默认路由决定的,而docker官方目前并没有一种可行的方式以指定固定的IP地址。

要解决这个问题,我们需要使用策略路由,通常来说是set-mark,但如果手动去修改容器内的容器以设定mark就有些不太现实了。所以,我把目标放在了iptables上对于cgroup的控制上,通过man iptables得知如下信息:

这张图片所展示的信息让我兴奋不已,如此一来,我只需要给容器设定一个classid,再使用iptables根据这个classid进行SNAT一下,不就解决问题了吗,哈哈。

解决办法

简单来说,两步搞定:

① 设定容器的net_cls.classid

1
2
echo 256 > /sys/fs/cgroup/net_cls,net_prio/docker/<ID>/net_cls.classid
# 这个256是人为给定的,范围是0-4294967295(但不能为0,系统默认初始的是0)

② 设定匹配这个 net_cls.classid 的源地址

1
iptables -t nat -A POSTROUTING -p all -m cgroup --cgroup 256 -o em2 -j SNAT --to-source 192.168.79.18

容器的ID需要使用长ID,不能是短ID 短:6f4267d1d900 长:6f4267d1d900c5b889d93f8713cf19c16faa00c200417195ec173ae84897f0e1

如此一来,凡是容器为ID为6f4267d1d900的数据包出口IP将被固定为192.168.79.18,问题解决。~

参考链接:http://man7.org/linux/man-pages/man8/iptables-extensions.8.html