zoukankan      html  css  js  c++  java
  • 网络端口映射的原理

    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规则是本地生成数据包,然后如何重定位
  • 相关阅读:
    JavaScript 闭包究竟是什么
    Javascript闭包简单理解
    使用border做三角形
    负边距在布局中的使用
    direction和unicode
    CSS 实现打字效果
    你可能没注意的CSS单位
    前端模块化
    JavaScript面向对象
    阿里前端两年随想
  • 原文地址:https://www.cnblogs.com/honpey/p/8620639.html
Copyright © 2011-2022 走看看