Docker 通过网络驱动来支持容器的网络通信,默认情况下,Docker 提供两种网络驱动供我们使用,一个是 bridge,一个是 overlay。我们也可以自己写一个网络驱动插件,如果你足够大牛的话。

Docker 安装时会自动在 host 上创建三个网络,我们可用docker network ls命令查看:

root@ubuntu:~# docker network ls
NETWORK ID NAME DRIVER SCOPE
fe6e820d51e2 bridge bridge local
e91fa0de345b host host local
9fea16767e7a none null local

一、none 网络

故名思议,none 网络就是什么都没有的网络。挂在这个网络下的容器除了 lo,没有其他任何网卡。容器创建时,可以通过--network=none指定使用 none 网络。

docker run -it --network=none busybox

我们不禁会问,这样一个封闭的网络有什么用呢?

其实还真有应用场景。封闭意味着隔离,一些对安全性要求高并且不需要联网的应用可以使用 none 网络。

比如某个容器的唯一用途是生成随机密码,就可以放到 none 网络中避免密码被窃取。

当然大部分容器是需要网络的,我们接着看 host 网络。

二、host 网络

连接到 host 网络的容器共享 Docker host 的网络栈,容器的网络配置与 host 完全一样。可以通过--network=host指定使用 host 网络。

docker run -it --network=host busybox

直接使用 Docker host 的网络最大的好处就是性能,如果容器对网络传输效率有较高要求,则可以选择 host 网络。当然不便之处就是牺牲一些灵活性,比如要考虑端口冲突问题,Docker host 上已经使用的端口就不能再用了。

Docker host 的另一个用途是让容器可以直接配置 host 网路。比如某些跨 host 的网络解决方案,其本身也是以容器方式运行的,这些方案需要对网络进行配置,比如管理 iptables

三、bridge 网络

bridge 是一个很特殊的网络,Docker 安装时会创建一个 命名为 docker0 的 linux bridge。如果不指定 --network,创建的容器默认都会挂到 docker0 上。

接口 docker0 是一个虚拟的以太网桥,用于连接容器和本地宿主网络。

我们停掉所有的容器查看一下。

root@ubuntu:~# brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.0242f47922c9 no

当前 docker0 上没有任何其他网络设备,我们创建一个容器看看有什么变化。

一个新的网络接口 vethcd8f150 被挂到了 docker0 上,vethcd8f150 就是新创建容器的虚拟网卡。

下面看一下容器的网络配置。

容器有一个网卡 eth0@if39。大家可能会问了,为什么不是 vethcd8f150 呢?

Docker 每创建一个容器就会创建一组互联的网络接口。这组接口就像管道的两端(就是说,从一端发送的数据会在另一端接收到)。这组接口其中一端作为容器里的 eth0 接口,而领一端统一命名为类似 vethxxxx 这种名字,作为宿主机的一个端口。可以把 veth 接口认为是虚拟网线的一端。这个虚拟网线一端插在名为 docker0 的网桥上,另一端插到容器里。通过把每个 veth 接口绑定到 docker0 网桥,Docker 创建了一个虚拟子网,这个子网由宿主机和所有的 Docker 容器共享。

所以说 eth0@if39 和 vethcd8f150 是一对 veth pair。veth pair 是一种成对出现的特殊网络设备,可以把它们想象成由一根虚拟网线连接起来的一对网卡,网卡的一头(eth0)在容器中,另一头(vethcd8f150)挂在网桥 docker0 上,其效果就是将 eth0@if39 也挂在了 docker0 上。

我们还看到 eth0@if39 已经配置了 IP 172.17.0.2,为什么是这个网段呢?让我们通过docker network inspect bridge看一下 bridge 网络的配置信息:

root@ubuntu:~# docker network inspect bridge
[
{
"Name": "bridge",
"Id": "fe6e820d51e226b3262ce3bba44ebae6ec7106db36db333b59ffd09dd0608f23",
"Created": "2017-12-01T13:53:34.169111033+08:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "172.17.0.0/16",
"Gateway": "172.17.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {
"c14ec27277f6228eff709c5b5a437214d967247e716daf7186aacb5339497ac9": {
"Name": "unruffled_swanson",
"EndpointID": "5f8034274333c8582645620b238e2ede7c9267a7433369017a576a3aaaa08527",
"MacAddress": "02:42:ac:11:00:02",
"IPv4Address": "172.17.0.2/16",
"IPv6Address": ""
}
},
"Options": {
"com.docker.network.bridge.default_bridge": "true",
"com.docker.network.bridge.enable_icc": "true",
"com.docker.network.bridge.enable_ip_masquerade": "true",
"com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
"com.docker.network.bridge.name": "docker0",
"com.docker.network.driver.mtu": "1500"
},
"Labels": {}
}
]

原来 bridge 网络配置的 subnet 就是 172.17.0.0/16,并且网关是 172.17.0.1,网关就是 docker0。

容器创建时,docker 会自动从 172.17.0.0/16 中分配一个 IP,这里 16 位的掩码保证有足够多的 IP 可以供容器使用。

我们也可以把一个容器从一个网络中端口,使用如下命令即可。

docker network disconnect bridge <Container ID>

四、User-defined 网络

1、创建 my_bridge 网络

Docker 本身提供两种网络驱动:bridge 和 overlay。bridge 只能用于单机网络模式,overlay 用于创建跨主机的网络,我们可通过 bridge 驱动创建类似前面默认的 bridge 网络,例如:

root@ubuntu:~# docker network create -d bridge my_bridge
423b660fbdbd5cfacc4cbf591bdf2bc977e7865261349efb6482dbdcbc7e1bd3
root@ubuntu:~# docker network ls
NETWORK ID NAME DRIVER SCOPE
fe6e820d51e2 bridge bridge local
e91fa0de345b host host local
423b660fbdbd my_bridge bridge local
9fea16767e7a none null local

-d 就是 --bridge 的简写,也可以使用 --bridge 替换进行创建。

查看一下当前 host 的网络结构变化:

root@ubuntu:~# brctl show
bridge name bridge id STP enabled interfaces
br-423b660fbdbd 8000.0242cb3347fd no
docker0 8000.0242f47922c9 no vethcd8f150

新增了一个网桥 br-423b660fbdbd

执行docker network inspect查看一下 my_bridge 的配置信息:

root@ubuntu:~# docker network inspect my_bridge
[
{
"Name": "my_bridge",
"Id": "423b660fbdbd5cfacc4cbf591bdf2bc977e7865261349efb6482dbdcbc7e1bd3",
"Created": "2017-12-04T16:29:23.726303365+08:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "172.18.0.0/16",
"Gateway": "172.18.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {},
"Options": {},
"Labels": {}
}
]

这里 172.18.0.0/16 是 Docker 自动分配的 IP 网段。

2、自定义网络 IP 段

如果要自定义网络 IP 段,只需在创建网段时指定--subnet--gateway参数:

root@ubuntu:~# docker network create -d bridge --subnet 192.168.31.0/24 --gateway 192.168.31.1 my_bridge2
43043f6bbc1a74106bef92e158daec3ea376748de2f8695541c8e93964303b5b
root@ubuntu:~# docker network inspect my_bridge2
[
{
"Name": "my_bridge2",
"Id": "43043f6bbc1a74106bef92e158daec3ea376748de2f8695541c8e93964303b5b",
"Created": "2017-12-04T16:39:19.832006374+08:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "192.168.31.0/24",
"Gateway": "192.168.31.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {},
"Options": {},
"Labels": {}
}
]

这里我们创建了新的 bridge 网络 my_bridge2,网段为 192.168.31.0/24,网关为 192.168.31.1。与前面一样,网关在 my_bridge2 对应的网桥 br-43043f6bbc1a 上:

root@ubuntu:~# brctl show
bridge name bridge id STP enabled interfaces
br-423b660fbdbd 8000.0242cb3347fd no
br-43043f6bbc1a 8000.02426266f83e no
docker0 8000.0242f47922c9 no vethcd8f150

容器要使用新的网络,需要在启动时通过--network指定:

root@ubuntu:~# docker run -it --network=my_bridge2 --name busybox1 busybox
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1
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
42: eth0@if43: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
link/ether 02:42:c0:a8:1f:02 brd ff:ff:ff:ff:ff:ff
inet 192.168.31.2/24 scope global eth0
valid_lft forever preferred_lft forever

容器分配到的 IP 为 192.168.31.2。

root@ubuntu:~# brctl show
bridge name bridge id STP enabled interfaces
br-423b660fbdbd 8000.0242cb3347fd no
br-43043f6bbc1a 8000.02426266f83e no vethfffb9d3
docker0 8000.0242f47922c9 no vethcd8f150

3、给容器指定分配静态 IP

我们在启动容器的时候,可以通过参数 --ip 来指定特定的 IP,只有使用--subnet创建的 User-defined 网络才能指定静态 IP。

root@ubuntu:~# docker run -it --network=my_bridge2 --ip 192.168.31.25 --name busybox2 busybox
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1
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
44: eth0@if45: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
link/ether 02:42:c0:a8:1f:19 brd ff:ff:ff:ff:ff:ff
inet 192.168.31.25/24 scope global eth0
valid_lft forever preferred_lft forever

root@ubuntu:~# brctl show
bridge name bridge id STP enabled interfaces
br-423b660fbdbd 8000.0242cb3347fd no
br-43043f6bbc1a 8000.02426266f83e no veth8e0d4f7
vethfffb9d3
docker0 8000.0242f47922c9 no vethcd8f150

4、容器联通测试

两个 busybox 容器都挂在 my_bridge2 上,应该能够互通,我们验证一下:

可见同一网络中的容器、网关之间都是可以通信的。

从拓扑图来看,my_bridge2 与默认 bridge 两个网络属于不同的网桥,应该不能通信,我们通过实验验证一下,让 busybox1 容器 ping httpd 容器:

确实 ping 不通,符合预期。

5、不同网桥下的容器互通

不同的网络如果加上路由应该就可以通信了吧?确实,如果 host 上对每个网络的都有一条路由,同时操作系统上打开了 ip forwarding,host 就成了一个路由器,挂接在不同网桥上的网络就能够相互通信。下面我们来看看 docker host 满不满足这些条件呢?

ip r 查看 host 上的路由表:

root@ubuntu:~# ip r
default via 192.168.2.1 dev enp5s0 onlink
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
172.18.0.0/16 dev br-423b660fbdbd proto kernel scope link src 172.18.0.1 linkdown
192.168.2.0/24 dev enp5s0 proto kernel scope link src 192.168.2.206
192.168.31.0/24 dev br-43043f6bbc1a proto kernel scope link src 192.168.31.1

172.17.0.0/16 和 192.168.31.0/24 两个网络的路由都定义好了。再看看 ip forwarding:

root@ubuntu:~# sysctl net.ipv4.ip_forward
net.ipv4.ip_forward = 1

ip forwarding 也已经启用了。

条件都满足,为什么不能通行呢?

我们还得看看 iptables:

root@ubuntu:~# iptables-save
......
-A DOCKER-ISOLATION -i br-423b660fbdbd -o br-43043f6bbc1a -j DROP
-A DOCKER-ISOLATION -i br-43043f6bbc1a -o br-423b660fbdbd -j DROP
-A DOCKER-ISOLATION -i docker0 -o br-43043f6bbc1a -j DROP
-A DOCKER-ISOLATION -i br-43043f6bbc1a -o docker0 -j DROP
-A DOCKER-ISOLATION -i docker0 -o br-423b660fbdbd -j DROP
-A DOCKER-ISOLATION -i br-423b660fbdbd -o docker0 -j DROP
......

原因就在这里了:iptables DROP 掉了网桥 docker0 与 br-43043f6bbc1a 之间双向的流量。

从规则的命名 DOCKER-ISOLATION 可知 docker 在设计上就是要隔离不同的 netwrok。

怎样才能让 busybox 与 httpd 通信呢?我们需要为 httpd 容器添加一块 net_bridge2 的网卡。这个可以通过docker network connect 命令实现。

查看 httpd 容器的 ID。

root@ubuntu:~# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
488e6019f780 busybox "sh" About an hour ago Up 31 minutes busybox2
7bf37788f011 busybox "sh" About an hour ago Up 36 minutes busybox1
c14ec27277f6 httpd "httpd-foreground" 3 hours ago Up 3 hours 80/tcp unruffled_swanson

为 httpd 容器添加网卡。

docker network connect my_bridge2 c14ec27277f6

root@ubuntu:~# brctl show
bridge name bridge id STP enabled interfaces
br-423b660fbdbd 8000.0242cb3347fd no
br-43043f6bbc1a 8000.02426266f83e no veth8e0d4f7
vethf3faf80
vethfffb9d3
docker0 8000.0242f47922c9 no vethcd8f150

我们在 httpd 容器中查看一下网络配置。

root@ubuntu:~# docker exec -ti c14ec27277f6 /bin/bash
root@c14ec27277f6:/usr/local/apache2# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
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
38: eth0@if39: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.2/16 scope global eth0
valid_lft forever preferred_lft forever
52: eth1@if53: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:c0:a8:1f:03 brd ff:ff:ff:ff:ff:ff
inet 192.168.31.3/24 scope global eth1
valid_lft forever preferred_lft forever

容器中增加了一个网卡 eth1,分配了 my_bridge2 的 IP 192.168.31.3。现在 busybox 应该能够访问 httpd 了,验证一下:

参考文档:https://docs.docker.com/engine/tutorials/networkingcontainers/

https://docs.docker.com/engine/userguide/networking/#related-information

Docker 网络(十一)的更多相关文章

  1. 理解Docker(5):Docker 网络

    本系列文章将介绍 Docker的相关知识: (1)Docker 安装及基本用法 (2)Docker 镜像 (3)Docker 容器的隔离性 - 使用 Linux namespace 隔离容器的运行环境 ...

  2. docker网络配置方法总结

    docker启动时,会在宿主主机上创建一个名为docker0的虚拟网络接口,默认选择172.17.42.1/16,一个16位的子网掩码给容器提供了65534个IP地址.docker0只是一个在绑定到这 ...

  3. 【转】Docker网络详解及pipework源码解读与实践

    好文必转 原文地址: http://www.infoq.com/cn/articles/docker-network-and-pipework-open-source-explanation-prac ...

  4. Docker网络模式

    [编者的话] 本文是<Docker网络及服务发现>一书的一个章节,介绍了搭建Docker单主机网络的基础内容.关于Docker网络的更多内容,包括多主机的网络,请参考该书的其他章节. @C ...

  5. docker网络-如何让外部网络访问容器资源

    docker网络-如何让外部网络访问容器资源 安装httpd 服务: docker:/root# docker exec -it f63b2633d146 bash bash-4.1# yum ins ...

  6. docker网络解析

    Docker概念和默认网络 什么是Docker网络呢?总的来说,网络中的容器们可以相互通信,网络外的又访问不了这些容器.具体来说,在一个网络中,它是一个容器的集合,在这个概念里面的一个容器,它会通过容 ...

  7. docker网络访问(三)

    docker网络访问 ifconfig查看网卡,启动docker的时候,docker会帮我们创建一个docker0的网桥. 1.随机映射 docker run -P 2.指定映射 -p hostPor ...

  8. [Docker网络]模拟一台交换机的拓扑

    [Docker网络]模拟一台交换机的拓扑 本例主要对Docker网络进行实际运用. 背景介绍 一台虚拟机如何模拟成一台多端口交换机分别连接多台虚拟机? bridge网桥技术 实验准备 docker d ...

  9. Docker 网络之理解 bridge 驱动

    笔者在前文<Docker 网络之进阶篇>中介绍了 CNM(Container Network Model),并演示了 bridge 驱动下的 CNM 使用方式.为了深入理解 CNM 及最常 ...

随机推荐

  1. [GraphQL] Query Lists of Multiple Types using a Union in GraphQL

    Unions are used when we want a GraphQL field or list to handle multiple types of data. With a Union ...

  2. RabbitMQ消息队列+安装+工具介绍

    1.MQ为Message Queue,消息队列是应用程序和应用程序之间的通信方法 2. 多种开发语言支持,其实就是一个驱动,如连接数据库的mysql驱动,oracle驱动等. 3. 4.采用以下语言开 ...

  3. react页面跳转 window.location.href和window.open的几种用法和区别

    https://www.cnblogs.com/Qian123/p/5345298.html

  4. jQuery相关方法8-----解绑事件

    一.解绑事件方法unbind() 用什么方式绑定的事件,最好用对应的方式解绑事件 unbind("事件名字")括号里写上事件名字,就会解除这个事件 unbind()括号里没有参数就 ...

  5. C#中的线程(一)入门 转载

    文章系参考转载,英文原文网址请参考:http://www.albahari.com/threading/ 转载:http://www.cnblogs.com/miniwiki/archive/2010 ...

  6. linux系列(十九):firewall-cmd命令

    1.命令格式 firewall-cmd [选项] [参数] 2.命令功能: 简单来说是一个防火墙管理工具. 3.简单使用: systemctl start firewalld # 启动, system ...

  7. 【原创】go语言学习(四)流程控制

    目录: 1.if else语句块 2.for语句 3.switch语句 if else语句块 1.基本语法 if condition { //do something } if statement; ...

  8. 在docker容器中编译hadoop 3.1.0

    在docker容器中编译hadoop 3.1.0 优点:docker安装好之后可以一键部署编译环境,不用担心各种库不兼容等问题,编译失败率低. Hadoop 3.1.0 的源代码目录下有一个 `sta ...

  9. jmeter+ant+jenkins构建自动化测试

    背景目的: 持续更新.... 参考文档:https://blog.csdn.net/cherish0123/article/details/79339732

  10. python eval的用法

    >>>x = >>> eval( '3 * x' ) >>> eval('pow(2,2)') >>> eval('2 + 2' ...