Docker单机中三种网络模型的简单理解

那些年接触的网络模型

平常开发中,接触最先的是虚拟机(vmware, virtualbox)里的网络模型。而在docker下的网络模型(networkdriver)是docker架构中处理网络虚拟化的重要部分,主要默认的能够看到有如下三种。html

root@volvo:/etc# docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
7235d2d26f6f        bridge              bridge              local
7ea4e817d5fd        host                host                local
1a445ef4688b        none                null                local

另外docker还有Containeroverlaymacvlanoverlay主要是基于多个docker host之间(通常是多个宿主机之间)。其中macvlan是基于虚拟物理地址的mac地址(须要开启网卡的混杂模式promiscuous mode,且支持vlan)的模式。这里主要说单机模式的最多见的以上三种。node

如下操做环境为 Ubuntu 20.04 LTS,若是采用 win或者 macdocker desktop。结果可能没法正常运行~。另外建议你们直接面向 官方文档编程^_^。

virtual box 的网络模式

回顾如下咱们以前用到的最可能是虚拟机(这里指virtualbox vmware),他们有如下几种经常使用网络模式:linux

  • image.png
  • 桥接网络bridge:虚拟机之间能够互通,虚拟机物理机也能够互通。且默认均可以访问外网。
  • 网络地址转换nat:虚拟机所在的网络和物理机不在同一个网段,且(虚拟)网络设备互相隔离。也就是说虚拟机之间不通,可是和物理机能够通,虚拟机经过物理机也能够上网。
  • nat网络:与nat最大不一样是(虚拟)网络设备是共享的,且在一个网段内,也就说虚拟机之间是全通的。通常也会启动一个dhcp服务。另外是nat和nat网络的相同点主机内都不能ping通虚拟机的(能够经过端口映射这个功能实现)
  • 仅主机host-only模式:一样虚拟机之间能够互通,至关于在一个网络里。

docker内none网络

none网络,表明使用容器自身网络,与世隔绝,本身在小黑屋high的。可能用到的场景为是涉及到安全、数据清洗、内部erp之类(我yy的)。参考例子以下:(--rm 表明运行完容器自清理,-ti表明交互模式,并开启一个终端,docker run更多命令解析)nginx

docker run --name busyboxNoneNet --rm -it --network=none busybox
/ # ifconfig
lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

docker内host网络

直接上代码git

docker run  --name 1busyboxHostNet -it --network=host busybox

host网络会看到宿主机的全部网络,注意是全部。这个名字虽然叫host,主机内网络能看到的,在容器内都能看到且访问到。可是主机向容器内访问不到,容器ping 主机ip能够。
一些特色:github

  • 在容器内执行hostname,发现和宿主机是同样的【可是注意权限有明确区分】
  • 执行ps -ef命令或者cat /etc/user 命令,容器内与宿主机就不一样了。能够看出除了 Net和user没有隔离, user ,pid,mount ,ipc都进行了隔离。
  • 以上两点参考如下代码
#宿主机
root@volvo:/home/tb# hostname
volvo
root@volvo:/home/tb# ps -aux |wc -l
306
root@volvo:/home/tb# 
root@volvo:/etc# ls |wc -l
223
root@volvo:/etc# cat /etc/hosts
127.0.0.1    localhost
127.0.1.1    volvo

# The following lines are desirable for IPv6 capable hosts
::1     ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
#容器内
/ # hostname
volvo
/ # ps -a
PID   USER     TIME  COMMAND
    1 root      0:00 sh
   10 root      0:00 ps -a
/ # cd etc/
/etc # ls
group        hostname     hosts        localtime    mtab         network      passwd       resolv.conf  shadow
/etc # 
/etc # cat /etc/hosts
127.0.0.1    localhost
127.0.1.1    volvo

# The following lines are desirable for IPv6 capable hosts
::1     ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
/etc #
  • host其余注意事项docker

    • host容器和宿主机的端口可能会产生冲突,好比容器内监听了80,实际上宿主机的80端口也被占用了。
    • 使用host模式的容器不会被分配ip地址(容器通讯的 IP 地址即为宿主机的地址)。能够用如下命令见证:编程

      docker run --rm -d --network host --name myNginxHostNet nginx
      root@volvo:/home/tb# docker inspect myNginxHostNet -f {{.NetworkSettings.Networks.bridge~~~~.IPAddress}}
      <no value>
Tips:为了作实验,防止docker pull的镜像慢,我这贴一个 docker register source^_^, vim /etc/docker/daemon.json添加以下,记得重启 systemctl restart docker
{
   "registry-mirrors": [
   "https://kfwkfulq.mirror.aliyuncs.com",
   "https://2lqq34jg.mirror.aliyuncs.com",
   "https://pee6w651.mirror.aliyuncs.com",
   "https://registry.docker-cn.com",
   "http://hub-mirror.c.163.com"
   ],
   "dns": ["8.8.8.8","8.8.4.4"]
   }
  • 验证host模式

访问:http://192.168.1.9/ 宿主机查看:json

root@volvo:/etc# netstat -anp |grep :80
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      23113/nginx: master 
tcp        0      0 192.168.1.9:80          192.168.1.32:52389      ESTABLISHED 23141/nginx: worker

docker内bridge网络

bridge 都干了啥

docker自己有就存在一个默认的bridge(如最开始咱们看到的),用户也能够自定义建立的self-bridge(经过docker create network xx)。bridge模式是单体宿主机中用于容器通讯最经常使用的方式。Docker bridge` 模式实现基于虚拟机中使用的 NAT 方式以及docker proxy。nat基于linux的即veth pair。简单的说就是docker daemon下配置一个docker0的虚拟网桥,一边链接宿主机,一边连着N多的容器。而后宿主机上会建立一对虚拟的网络接口 veth pair(这个东西能够理解为链接两个net namespace的桥梁)
若是须要宿主机和容器之间通讯,则veth pair的一边挂在docker0上,另外一边挂在不一样的container中。vim

image.png

若是容器要链接外网以及外网到达容器内,那么就是咱们通常概念中的的nat,而docker中最多见的就是端口映射(natp),natp能够根据数据包的进出顺序分为snat(source nat)和dnat(destination nat)。当前这里面docker 还操做了好比iptable,route相关的操做,具体又能够拿来讲说了。。仍是举个简单的例子吧先:好比外界须要访问docker内容器,

  • 大洋彼岸网络要访问嘎子村中起的一个nginxdocker服务.(前提nginx docker已经暴露了给宿主机80,即常见的expose Port 80:80),流程简单理解以下:
  • 大洋彼岸(公网ip)--> 嘎子村(公网ip:80)--> 宿主机docker0网桥--> docker0:veth0->【veth pair】--> docker nginx:80(即veth1)。以上为docker经过natp的方式修改了包的目的地址(修改目的地址为dnat),数据经过容器返回给外部的过程就须要snat
  • 以上例子同时涉及网桥接口的通讯(brctl show)、iptable的一些规则(iptables -t nat -vnL)以及内核层面的数据包转发(cat /proc/sys/net/ipv4/ip_forward),这里就不具体说了,由于我也没实际操做。能够具体用wireshark或者tcpdump去看看~

是用默认的 bridge仍是本身造一个?

另外docker bridge中官方文档,特别指出: 若是使用默认的的bridge网络,容器之间只能用ip,而不能用container Name(除非你显示指定 --link,然而这个link(实际就是在host中增长一条指向)在后期可能就被废掉),并且全部的容器之间默认均可以直接通讯,这也是一个风险点。另外若是你想把一个容器放到其余网络,若是你使用默认的bridge,那你只能先停掉,在修改network,再启用,至关繁琐。因此官方建议使用自建的bridge,本身定义的bridge就直接两条命令实现容器和网络之间的关闭和链接,另一点使用本身建立的bridge ,默认直接可使用container name来访问,无需link了。另外使用自定义的bridge还能够分别自定义不一样子网的网桥的子网掩码,mtu大小,iptable规则等。

bridge的实际例子

咱们先看一下本机网卡配置

root@volvo:/home/tb# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: enp2s0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc fq_codel state DOWN group default qlen 1000
    link/ether d8:c4:97:0f:4b:c2 brd ff:ff:ff:ff:ff:ff
3: wlp3s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether e8:2a:44:f1:fc:61 brd ff:ff:ff:ff:ff:ff
    inet 192.168.1.9/24 brd 192.168.1.255 scope global dynamic noprefixroute wlp3s0
       valid_lft 244150sec preferred_lft 244150sec
    inet6 240e:82:2:cf9:cdf4:d5ce:b3b3:c6fe/64 scope global temporary dynamic 
       valid_lft 258761sec preferred_lft 71149sec
    inet6 240e:82:2:cf9:f5fb:539d:7ef6:5ee2/64 scope global dynamic mngtmpaddr noprefixroute 
       valid_lft 258761sec preferred_lft 172361sec
    inet6 fe80::51b0:b9d3:fa60:7dc6/64 scope link noprefixroute 
       valid_lft forever preferred_lft forever
4: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default 
    link/ether 02:42:d3:86:73:83 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:d3ff:fe86:7383/64 scope link 
       valid_lft forever preferred_lft forever

这里一共四块网卡,docker0lo没必要说,enp2s0wlan分别表明物理网卡和无线网卡,我这里是用wlan
咱们分别启动两个container,注意默认为bridge模式。

docker run --name demo1 --rm -it busybox /bin/sh
docker run --name demo2 --rm -it busybox /bin/sh

再次宿主机中执行ip a发现多了两块。注意编号分别为14,16记住这两个数字

14: vethf42774d@if13: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default 
    link/ether 7e:df:8d:d4:54:a0 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet6 fe80::7cdf:8dff:fed4:54a0/64 scope link 
       valid_lft forever preferred_lft forever
 16: veth21a33a9@if15: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default 
    link/ether ca:35:58:9b:0e:77 brd ff:ff:ff:ff:ff:ff link-netnsid 1
    inet6 fe80::c835:58ff:fe9b:e77/64 scope link

分别在demo1和demo2容器内执行 ip a

  • demo2中:

    / # ip a
    1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
       link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
       inet 127.0.0.1/8 scope host lo
          valid_lft forever preferred_lft forever
    13: eth0@if14: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue 
       link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff
       inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
          valid_lft forever preferred_lft forever
  • demo1中为【eth0@if16】

    # ip a
    1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
        link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
        inet 127.0.0.1/8 scope host lo
           valid_lft forever preferred_lft forever
    15: eth0@if16: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue 
        link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff
        inet 172.17.0.3/16 brd 172.17.255.255 scope global eth0
           valid_lft forever preferred_lft forever

而后咱们在宿主机上查看一下网桥信息 ,具体的对应关系能够参考上面的【13,15】 【14,16】去查找。好比宿主机中的14号显示和13作关联,那么13就表明某一个容器内的网卡编号,而这个容器内的网卡也会关联到宿主机的14。(brctl自己也能够建立网桥,绑定网卡,设置网关、子网掩码等,只不过docker把这些动做至关于都封装了)

root@volvo:/home/tb# brctl show
bridge name    bridge id        STP enabled    interfaces
docker0        8000.0242d3867383    no        veth21a33a9
                                         vethf42774d

咱们停掉demo1以后,再宿主机上查看 ip a,会发现网卡已经少了一个

...
4: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:d3:86:73:83 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:d3ff:fe86:7383/64 scope link 
       valid_lft forever preferred_lft forever
14: vethf42774d@if13: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default 
    link/ether 7e:df:8d:d4:54:a0 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet6 fe80::7cdf:8dff:fed4:54a0/64 scope link 
       valid_lft forever preferred_lft forever
root@volvo:/home/tb#

固然你以为这里很乱也没有关系,早已经有大神简单实现了这个配对的寻找办法。这里有个小工具dockerveth,不用肉眼识别,直接一把梭。

后序

docker的网络与容器之间能够任意搭配,咱们能够根据业务状况建立本身的bridge,并且能够配置cidrgateway 等,但以上介绍的模式是基础,是小打小闹,本身测试环境拿来玩玩儿还能够,在真正的生产环境,是须要跨主机甚至跨平台的,并且机器数量巨大,经过nat这种方式不能知足GDP的快速增长。。利用overlay等技术实现跨宿主机、跨平台的的docker之间的透明通讯才是咱们应该去掌握的。后序会结合swarm和k8s看是如何调度通讯的。

参考资料

  1. 企业级微服务实战
  2. 容器网络隔离技术
  3. docker-proxy存在合理性分析
  4. iptables详解
  5. linux veth pair
相关文章
相关标签/搜索