网络端口映射的原理

docker有容器内外的端口映射,是怎么做到的呢?这是要起一个新的网络的namespace吧;

启动了docker之后,在docker上面还是有确实是有iptables的项,但是此时应该是有两个网络的namespace吧,使用ip netns list是没有看到的,也就是说其实是在一个网络的namespace中的网络端口的映射;

Chain POSTROUTING (policy ACCEPT)
target     prot opt source               destination         
MASQUERADE  all  --  172.17.0.0/16        anywhere            
MASQUERADE  tcp  --  172.17.0.2           172.17.0.2           tcp dpt:1234

Chain DOCKER (2 references)
target     prot opt source               destination         
RETURN     all  --  anywhere             anywhere            
DNAT       tcp  --  anywhere             anywhere             tcp dpt:32769 to:172.17.0.2:1234

在iptables的输出中有如上的两条规则,然后docker会启动一个proxy:

/usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 32769 -container-ip 172.17.0.2 -container-port 1234

把收到的tcp包透传给容器?

这种端口的映射在内核里做个转化就好了吧?0.0.0.0:32769 --> 172.17.0.2:1234

系统中多了这样一张网卡:

veth485dee9 Link encap:以太网  硬件地址 f2:c6:f3:6c:4f:7a  
          inet6 地址: fe80::f0c6:f3ff:fe6c:4f7a/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  跃点数:1
          接收数据包:14 错误:0 丢弃:0 过载:0 帧数:0
          发送数据包:46 错误:0 丢弃:0 过载:0 载波:0
          碰撞:0 发送队列长度:0 
          接收字节:1088 (1.0 KB)  发送字节:4987 (4.9 KB)

这里就是个veth设备了,容器内外各有一个,从容器中来的数据直接就导流到了网桥上

==========

启动了多个docker,

ip netns list 看到的内容和 ip netns list-id看到的内容为啥不是一样的?

如果多个容器把自己的都绑定到宿主机的8080端口咋样?不能做,本地的端口已经被映射掉了;也就是说,不可能实现,同一台主机两个容器内同时监听8080端口;回到原来的问题上,创建一个

ip netns list是从/var/run/netns中去读取内容,但是现在如果没有内容呢?

内核是如何查看网络的参数的? man ip-netns

ip netns list-id - list network namespace ids (nsid)

              Network namespace ids are used to identify a peer network
              namespace. This command displays nsid of the current network
              namespace and provides the corresponding iproute2 netns name
              (from /var/run/netns) if any.

看下这个命令是干嘛的? network namespace ids(nsid) 列出系统中所有的peer网络ns,如果这个对等网络有名字,那么会列出名字;所以ip netns list-id中所有的明明空间呢;

所以命名空间分为两类:named network namespace  & peer network namespace [不纠结这个小事了,因为从/proc/<pid>/ns/net看确实是不同的网络的命名空间了]

下面看在宿主机上如何完成端口的映射;使用iptables;

想下之前的nat的设置,都是在postrouting时转换网络的IP地址,然后在接收到数据包时,系统会用自动生成的reverse功能把数据包给转回来,这样的话宿主机内的路由就会把这包送给容器内地址了【这里有个小gap:宿主机什么时候决定丢包?

step1:收到数据包,发现数据包是本地地址,保留;

step2:然后更换数据包的目的地址;

step3:进入本机的路由;【是哪个参数可以本机路由了呢?ipv4.ip_forward=1?】在协议栈里面是如何处理的呢?是否真的有本机路由这件事情? 从wireshark看确实是有的,这一点是wuyongzhiyi.

//外面的机器不应该能访问到我本机器上的一台虚拟机,因为这台虚拟机是我内网的,要保护起来,现在的各种NAT机制也不支持;但是我这台docker虚拟机是可以对外提供服务的呀;比如所有对1000号端口的访问都引流到我这台机器上就好啦:https://www.cnblogs.com/jjzd/p/6505871.html

作如下试验:网络拓扑结构

树莓派       A:

主机PC       B:

主机上docker容器: C:

sudo iptables -t nat -A PREROUTING -d [B地址] -p tcp -m tcp --dport 10000 -j DNAT --to-destination 【容器C地址】:10000
测试时在容器上启动一个服务器监听10000端口,在主机B上也启动一个进程监听10000端口,如果在容器内监听到数据,那么就说明设置成功了。
------
说一些题外话,刚才我们发现树莓派其实是ping不同我容器的地址的,那么我们能不能hack一下,通过增加一段路由以及icmp的配置来让我的树莓派能平通我的容器呢?
 
我们首先来分析下为啥我的容器收不到这个包,是因为我的主机上并没有172.17.0.2这样的一个设备,所以甚至这个包都进入不了路由。。。所有关键的问题还是变成扔包到底发生在哪一步?!
我宿主机根本就不会去应答这个数据包,是因为宿主机上现在没有一个网卡的地址是172.17.0.2,如果想让我的网卡收到包,是不是配置一个附属的IP地址就可以了?
1) 首先我随便添加了一个一个tap0设备在我的机器山,发现ping是ping通了【注意此时tap0上并没有流量】
2) 去掉tap0,然后试着在这个网卡上配置多个IP试一下:试过同样可以。。。。但是不知道怎么把这个副网卡给删除掉了;
 
好了,今天算是搞明白了在prerouting处加DNAT是怎么把包导入到容器里的;其实,新手看东西总是想象力丰富,之前自己还在prerouting处我如何能把特定的IP地址导入到特定的容器,即根据IP地址路由,。。。这个好像是没有应用场景。。。不,等等,这个不就是负载均衡的实现么,根据IP地址把流量导入到不同的机器中;这个该咋做?答案:sudo iptables -t nat -I PREROUTING -p tcp -m tcp -j DNAT --to-destination 172.17.0.2 【测试通过,符合预期】
 
///-----刚一眨眼,发现,树莓派竟然和我容器里的地址去通信了,所以这里副网卡的作用真是不得了,他告诉了局域网我能达到的网络的能力
说道这里,一个网卡接收到了数据包怎么还会路由呢。因为有的时候这台机器就是个网卡,是路由器,怎么说?
还是那刚才说我怎么样从树莓派ping通我的容器来说,我的目的地址是172.0.0.2,然后我把PC当成了网关,这个时候奇妙的事情发生了,我的PC机收到了一个网络包,但是这个包的目的地址不是自己,于是这个数据包开始在主机内路由了,路由表发现我所有172.0.0.2的数据包都交给docker网桥发送出去,于是把包发给了网桥bridge0,bridge拿到了数据包之后,网桥就开始在自己的域内广播,广播后发下了我的docker容器里的地址了,所以容器里的网卡就接受这个数据包咯,然后应答,通过网桥发送到主机上,主机根据路由选择出口,加上咱们docker里的SNAT,所以这样就发送出去了。
 
还要一个设置DNAT的地方是OUTPUT规则;OUTPUT规则是本地生成数据包,然后如何重定位
原文地址:https://www.cnblogs.com/honpey/p/8620639.html