docker系列-07docker网络
seefly
seefly
发布于 2020-04-08 / 6 阅读 / 0 评论 / 0 点赞

docker系列-07docker网络

docker网络

1. docker网络概览

​ 参考 docker网络概览

docker如此强大的一个原因是你可以将多个docker容器或服务连接在一起,以至于他们甚至察觉不到自己或其他容器工作在docker网络上,同时屏蔽了底层操作系统的差异.因为docker提供了一套网络操作工具,来实现这种功能

  1. bridge

    桥接网络是docker的默认提供的网络模式,如果你不指定,他会自动创建(默认叫 bredge).桥接网络通常用于单机的docker容器通讯方式 (用户定义的网络桥接模式是在单机docker守护进程中是最好的选择)

  2. host

    对于单机容器来说,host模式排除了容器和主机之间的网络隔离,使容器直接使用主机网络;

    这种方式通常是用于希望在容器在网络方面和宿主机是共享的,但在其他方面是隔离的.

  3. overlay

    overlay网络连接多个docker守护进程,并且是swarm模式中默认的通讯方式,你可以使用overlay网络来让集群和单机模式下单容器进行通讯,也可以让两个docker守护进程使用该网络模式进行通讯.这种网络模式屏蔽了底层操作系统级别的路由细节.

  4. ipvlan

    IPvlan网络让用户完全控制IPv4和IPv6寻址

  5. macvlan

    macvlan网络允许你给容器分配一个MAC地址,让容器像是一个存在于网络中的物理设备一样,docker守护进程通过这个MAC地址将流量路由到指定的容器上.这种网络模式,通常使用在你需要将流量直接通过物理网络传输到容器,而不是再经过宿主机的网络栈再走一圈.(工作在数据链路层??)(当您从VM安装迁移或需要容器看起来像网络上的物理主机时,Macvlan网络是最好的,每个容器都有一个唯一的MAC地址)

总结

​ 对于容器来说,它是不知道自己用的是那种网络类型, 它只能看到一个带有IP地址,网关,路由表和DNS服务等

2. 桥接网络

在网络方面来说,桥接网络是工作在数据链路层的一种网络设备,它可以是具体的硬件,也可以是工作在操作系统内核的软件.

在docker中,桥接网络则是使用软件桥接多个容器在同一个网桥上进行通讯,提供了容器级别的网络隔离.docker的桥接网络自动安装在宿主集中的规则,所以对于不通的桥接网络是不能直接通讯的.

另外,docker的桥接网络运行在同一个docker守护进程中,对于不通docker守护进程之间的通讯,需要使用overlay网络来完成.

当年你启动docker守护进程的时候,一个默认的桥接网络会被创建,后面新创建的容器实例会自动加入进来(除非你指定了它的网络模式)

当然,你也可以自定义自己的桥接网络,自己定义的桥接网络在多个方面都是优于系统默认的.

2.1 用户自定义的网桥和默认网桥之间的区别

所有在同一个自定义网桥上的容器,它的所有端口都是对彼此暴露的. 但是对于不同一个网络下的其他服务则需要使用-p参数来暴露指定端口才行.

  1. 自定义网桥提供不同容器之间的自动DNS解析

​ 默认的桥接网络,对于各个容器他们只能使用IP地址来进行通讯; 但是对于自定义桥接网络来说可以使用 容器名称来通讯,比如连接在同一个自定义网桥上面的web容器和db容器来说,web容器可以直接使用db这个名字来访问数据库(这种功能叫做 automatic service discovery及自动服务发现),而不必关系宿主机的网络情况.(当然你也可以手动管理/etc/hosts或者用--link来实现,但这会造成一些隐藏的bug和额外的工作量),

  1. 自定义网桥提供更好的隔离性

    如果容器没有使用--network来指定使用网桥,而是都连接到默认的网桥上面,可能会有风险,像是一些不相关的容器出现在了自己的网络上.

  2. 容器可以随时在自定义网桥上连接或断开连接

    对于一个正在运行中的容器,你可以直接让他从自定义网桥上断开连接或加入.但是对于默认网桥来说,你需要重启该容器实例才能实现.

  3. 每个自定义网桥都是可以配置的

    由于默认网桥对所有没有指定网桥的容器都是有影响的,所以如果你想要配置这个默认网桥的一些参数例如MTUiptables规则等,他会影响所有容器,并且需要重启docker守护使之生效. 但是如果使用自定义网桥,你可以随便搞;

  4. 连接到默认网桥上面的容器使用了共享变量

    最开始,想要在多个容器间共享变量只能使用--link指令来实现,这种共享变量的方式不能用于自定义网络,但是这里有其他多种方式来实现变量共享

    1. 使用一个挂载
    2. docker-compose实现
    3. swarm services实现

2.2 管理自定义网桥

当你创建或删除自定义网桥以或将容器连接或断开连接网桥时,docker使用操作系统相关的工具,去管理底层网络基础设施(例如在linux上添加或删除桥接网络时docker实际上是操作iptables规则来实现的)

创建一个自定义网桥,这里省略了-d bridge参数

$ docker network create my-net

删除自定义网桥(删除之前需要先断开所有连接在该网桥上的容器)

$ docker network rm my-net

将容器连接到自定义网桥上

// 创建一个容器,并将该容器连接到my-net网桥上,同时发布8080端口在docker宿主机上
// 这样外部的客户端可以通过8080端口访问该容器,同时网桥上的其他容器可以访问该容器的所有端口
$ docker run -d --name demo --network my-net --publish 8080:80 nginx:latest

// 进入该容器,并假设该容器在my-net上分配到了IP: 172.20.0.3
$ docker exec -it demo /bin/bas

// 此时假设你有另一个容器名为web,也运行在my-net网络上,ip为: 172.20.0.2
// 其内运行了一个web容器并监听80端口,但是没用-p对外暴露

// 你可以在demo容器中使用telnet命令来验证,是否像文档中说的那样:在同一个网桥上的容器对彼此暴露所有端口
// 我试了是真的!!
root@45da95e4f08f:/# telnet 172.20.0.2 80
Trying 172.20.0.2...
Connected to 172.20.0.2.
Escape character is '^]'

// 将该web容器从net网络上退出
$ docker network disconnect my-net web

// 再进入demo,使用telnet验证是否还能联调web容器
root@45da95e4f08f:/# telnet 172.20.0.2 80
Trying 172.20.0.2...
telnet: Unable to connect to remote host: No route to host
// 发现已经无法联通了,按照文档说的,这一切应该是使用`iptables`来完成的.

总结:

单机桥接网络中演示了,默认桥接网络和自定义桥接网络的区别;比较重要的一点是自定义桥接网络可以使在该网络上的不同容器之间通过容器名称来访问彼此,同时所有端口都是彼此暴露的,但是默认网络不行,只能通过ip地址来访问.

2.3 Docker网桥原理

参考:

  1. linux网桥原理
  2. 官方文档-docker网桥概述
  3. [docker0和eth0的关系](networking - What is the relation between docker0 and eth0? - Stack Overflow)
  4. liunx网桥源码解析
  5. iptables介绍
2.3.1 linux系统中的网桥

linux系统中的网桥是一种特殊的网络接口(数据结构为net_bridge extend net_device),其内用双向链表维护了一个名为port_listnet_bridge_port列表,同时还有一个以mac地址的散列值为key,以net_bridge_fdb_entry为值的哈希表;另外,net_device中有一个指向net_bridge_port的指针(这也说明了一个网络接口只能绑定到一个网桥端口上)

linux_bridge

网络接口处理数据的过程

  1. net_device接收到数据的时候他会判断自身有没有关联到网桥上
  2. 如果有则将这个数据交给网桥处理
  3. 网桥先记住数据的源MAC地址和数据从哪个网络接口进来的,关系保存到hash表中
  4. 在从数据包中提取目的MAC,查hash表
    1. 如果找到对应的端口,则向这个端口发送数据
    2. 如果没有,则向所有端口发送(除了当前的这个端口)

这个hash跟java的HashMap实现差不多,都是桶上面挂链表为了解决hash冲突

2.3.2 docker的网桥

docker0-2

Networking your docker containers using docker0 bridge 文章中指出

The Docker server creates and configures the host system’s docker0 interface as an Ethernet bridge inside the Linux kernel that could be used by the docker containers to communicate with each other and with the outside world;(Docker在宿主机的Linux内核中创建并配置了一个名为docker0的以太网网桥,用于容器之间以及和外部的通讯.)

docker在启动时会自动创建一个名为docker0的网桥,所有没有指定网络模式的容器默认都通过虚拟设备对(veth-pair)将容器和网桥连接起来; 虚拟设备对在容器中的一端始终会被命名为eth0,在宿主机的一端则命名为vethxxxxx,并挂在docker0网桥上. 这样就实现了同一个网桥上容器之间的互联.并借助iptables实现不同网桥之间的隔离.

查看容器内的eth0和外部vethxx的关系

$ docker exec <CONTAINER_NAME OR ID> cat /sys/class/net/eth0/iflink
17
$ grep -l 17 /sys/class/net/veth*/ifindex
/sys/class/net/veth06c005a/ifindex

上例说明,容器内部的eth0和外部的veth06c005a关联.

疑问是,不管是默认网桥还是自定义网桥,为什么也能联通宿主机局域网中的其他主机? 这其中肯定在哪里和宿主机的eth0网卡发生了关系.利用brctl show并没有看到宿主机的eth0挂在这个网桥上.怎么回事?

2.3.3 docker0和宿主机的eth0

找到一个答案: whta is the relation between docker0 and eth0

可以在iptabls中看到,对于从容器中出来到局域网中的其他主机的流量,一路都是放行的. 最后还用SNAT给转了一下;

在iptables的nat表里面可以看到docker对于从容器中访问宿主机所在局域网的其他主机的规则配置

Chain POSTROUTING (policy ACCEPT 4543 packets, 272K bytes)
 pkts bytes target     prot opt in     out     source               destination                  
   24  2611 MASQUERADE  all  --  *      !docker0  172.17.0.0/16        0.0.0.0/0                   
    0     0 MASQUERADE  tcp  --  *      *       172.17.0.2           172.17.0.2   tcp dpt:3306
    0     0 MASQUERADE  tcp  --  *      *       172.17.0.3           172.17.0.3   tcp dpt:8090

路线: 容器内eth0 --> vethxx --> docker0 --> 路由选择 --> SNAT --> 局域网其他主机

2.4 默认网桥管理

默认网桥(bridge)被认为是docker的历史遗留功能,当前不再推荐使用在生产环境上,在该网络上你只能通过ip地址来访问;

可以通过编辑deamon.json文件来管理默认网桥配置,当然这需要重启docker使之生效

{
  "bip": "192.168.1.1/24",
  "fixed-cidr": "192.168.1.0/25",
  "fixed-cidr-v6": "2001:db8::/64",
  "mtu": 1500,
  "default-gateway": "192.168.1.254",
  "default-gateway-v6": "2001:db8:abcd::89",
  "dns": ["10.20.1.2","10.20.1.3"]
}

3. overlay

官方文档

The overlay network driver creates a distributed network among multiple Docker daemon hosts. This network sits on top of (overlays) the host-specific networks, allowing containers connected to it (including swarm service containers) to communicate securely when encryption is enabled. Docker transparently handles routing of each packet to and from the correct Docker daemon host and the correct destination container.(overlay基于特定于主机的(host-specific)的网络之上在多个docker守护进程中创建了分布式的网络.并允许这些容器在该网络上进行加密通讯.docker会透明的处理并路由这些分组/数据包(packet)到正确的容器或docker守护进程中;)

简单总结一下,overlay网络的目的是联通多个docker守护进程,使分散在不同docker宿主机上的容器能够在这个网络上彼此通讯. 这是和bridge的最大区别. 一个用于多机集群,一个用于单机.所以如果你只有一台主机,就完全用不到overlay网络(除非在这个主机上装虚拟机跑多个docker)

swarm集群中

  1. 和桥接网络一样,在同一个overlay网络上的容器,彼此暴露所有端口
  2. 初始化swarm集群或者加入swarm集群时,会在宿主机上创建两个网络(即使你用不到也会自动创建)
    1. 名为ingress的网络,类型是overlay,用于处理数据流量和集群控制信息
    2. 名为docker_gwbridge的网络,类型是bridge,是当前docker守护进程的物理网络连接到overlay网络的虚拟网桥(The docker_gwbridge is a virtual bridge that connects the overlay networks (including the ingress network) to an individual Docker daemon’s physical network)

3.1 创建ovarlay

创建overlay网络需要当前docker节点以manager的身份加入一个已经存在的swarm集群,或者在当前节点上初始化一个swarm集群.否则会创建失败.

创建一个可连接的overlay网络, 单机容器可以通过docker network connect my-overlay some-container的方式连接上去.

$ docker network create -d overlay --attachable my-overlay

4. host

如果你使用host网络,那么容器的网络栈不会和docker的宿主机隔离,同时也不会获得一个自己的ip,因为他使用宿主机的ip地址;

在该模式下如果使用-p --publish -P等命令,不会生效,并且会产生一个警告信息,因为它直接绑定在宿主机上.


评论