简介
docker容器网络是为应用程序创造的虚拟环境的一部分,可以让应用内从宿主机操作系统的网络环境中独立出来,默认与宿主机和其他容器相互隔离,形成容器自有的网络设备,IP,端口套接字等与网络相关的模块。
也需要考虑到容器与容器之间/容器与主机之间/容器与外界主机之间的通信,如果不想隔离要怎么实现,所以需要涉及到网络的问题。
架构
CNM

- sandbox :是容器独立的网络隔离环境,包含容器网络栈的所有配置(例如端口,套接字,IP路由表等等),用于实现容器和宿主机,容器和容器之间的网络隔离,让每个容器拥有完全独立的网络视图,不会互相干扰。
在Linux中通过namespace实现,一个sandbox可以关联多个endpoint(也就是说一个容器可以同时接入多个网络)。 - endpoint :是连接sandbox和network的虚拟网络接口,也是容器接入网络的唯一入口,负责将容器接入指定的网络,完成网络连接的配置。
通常采用虚拟网卡计数实现,一端位于sandbox内,另一端接入network对应的网络设备(例如网桥),一个endpoint只能接入一个network,因此容器需要接入多网络的时候,必须配置多个endpoint。 - network:是一组可以直接通信的endpoint集合,逻辑上等同于子网/广播域,用于提供容器间的通信域,一个network的endpoint可以直接互通,不同network的endpoint默认隔离。
Libnetwork
Libnetwork是CNM的一个标准实现,采用Go语言编写,docker网络架构的核心代码都在这个库中,Libnetwork实现了CNM中定义的全部三个组件,此外,还实现了本地服务发现,基于Ingress的容器负载均衡,以及网络控制层和管理层等功能。
驱动
Libnetwork是代码层面上的实现,而驱动是Libnetwork的插件化模块,负责把CNM的抽象组件映射到具体的底层网络上。
这样,Libnetwork就不关心底层用的是Linux网桥,VLAN还是别的,实现解耦抽象和实现。
网络管理命令
docker network create
用于创建自定义网络。
bash
docker network create [options] network
-d指定网络驱动
--gateway指定网关地址
--subnet用于定义网段的CIDR格式的子网,容器会从这个网段分配IP
--ipv6表示启动ipv6
docker network inspect
用于查看网络详情。
bash
docker network inspect [options] network1 [network2...]
-f指定格式
docker network connect
用于将容器连接到网络,可以按名称或者ID连接容器,一旦连接,容器可以与同一网络中的其他容器通信。
bash
docker network connect [options] network container
--ip用于指定IP地址
--ip6用于指定ipv6地址
还可以使用docker run --network=< networkname >选项启动容器并立即将其连接到网络
docker network disconnect
用于断开网络。
bash
docker network disconnect [options] network container
-f表示强制退出
docker network prune
用于删除不使用的网络。
bash
docker network prune [options]
-f表示删除的时候不提示
docker network rm
用于删除一个或者多个网络。
bash
docker network rm network1 [network2...]
-f表示强制退出
docker network ls
用于列出网络。
bash
docker network ls [options]
-f用于指定过滤条件
--format用于指定格式
--no-trunc表示输出不截断
-q表示仅仅显示id
基本操作
- 创建网络并指定ip地址段
bash
docker network create --subnet=172.19.0.0/16 网络名
- 查看创建的网络
bash
docker network ls

- 创建一个容器并加入网络
bash
docker run -d --name 容器名 --network 网络名 nginx
- 查看容器的网络信息
bash
docker inspect 容器名

- 创建一个新的nginx容器,但不加入自定义网络
bash
docker run -d --name 容器名 nginx
- 查看容器的网络配置,可以看到采用的是桥
bash
docker inspect 容器名

- 将第二个容器加入自定义网络
bash
docker network connect 网络名 容器名
- 再次查看容器2的网络配置,发现容器2多了一个网络
bash
docker inspect 容器名

- 让容器2断开网络,再次查看容器2的配置,可以看到只剩下一个bridge网络了
bash
docker network disconnecct 网络名 容器名
docker inspect 容器名

- 删除网络,可以看到网络被使用了,无法删除
bash
docker network rm 网络名

- 删除容器1,再删除网络,可以看到当我们停止了所有容器,就可以正常删除网络了
bash
docker stop 容器名
docker rm 容器名
docker network rm 网络名

网络种类
docker bridge网络
概念
docker bridge驱动底层采用的是Linux内核中Linux bridge技术,bridge(网桥)是工作在链路层的设备,负责在不同网络段之间转发流量,在Docker中,通过软件模拟实现了名为docker0的网桥。
这样,同一网桥网络内的容器可相互通信,未接入该网桥的容器则被隔离,实现了容器间的网络隔离与互通。

- docker container是容器内部的网络接口,对应的设备名为eth0。
- veth是虚拟以太网对,是连接容器和网桥的虚拟网线,连接eth0和docker0。
- docker0是默认的软件网桥,是容器网络转发的核心转发节点。
- host是宿主机,eth0是宿主机的物理网卡,负责与外部进行通信。
- ipv4 ip_forward是内核IP转发功能,允许网桥将流量转发到物理网卡。
默认情况下,创建的容器在没有使用--network参数指定要加入的docker网络时,默认都是加入docker单机桥接网络,即名字为bridge的网络。
docker默认的bridge网络和Linux内核中的docker0网桥是一一对应的关系,bridge是docker对网络的命名,docker0是内核中网桥的名字。
操作
容器间的网络通信
- 使用busybox(集成了一百多个常用的Linux命令和工具)镜像创建两台容器,并且保持在后台运行
bash
docker run -itd --name test1 busybox
docker run -itd --name test2 busybox
- 查看容器通信的现象
bash
docker exec -it test1 ip a
docker exec -it test2 ip a
docker exec -it test1 ping [test2的IP]


- 确定两个容器可以通信以后,查看一下bridge网络的信息,此时可以看到,该网络已经连接了2个容器,test1和test2就是通过docker0这个网桥来进行通信的
bash
docker network inspect bridge

- 当我们停止或者删除掉一个容器的时候,会发现该容器会自动和docker0切断连接
bash
docker stop test1
docker network inspect bridge

创建自定义bridge
在默认情况下,我们创建的容器都会连接在docker0这个bridge上,我们也可以创建一些自定义的bridge,让运行的容器通过自定义bridge进行通信。
- 通过create命令来创建新的bridge
bash
docker network create -d bridge new-bridge
- 查看new-bridge的网络信息
bash
docker network inspect new-bridge

- 创建容器,使用--network选项指定要连接的网络,如果不指定,默认连接的是bridge
bash
docker run -itd --name test1 --network new-bridge busybox
#过滤网络相关信息
docker inspect test1 | grep "Networks" -A 17
#查看自定义bridge网络详细信息
docker network inspect new-bridge


DNS解析
docker自定义桥接网络是支持通过docker DNS服务进行域名解析的,也就是说我们可以直接使用容器名进行通信,因为DNS服务可以解析容器名到IP地址的映射,但默认的bridge网络是不支持DNS的。
- 创建四个容器,test1和test2都默认连接bridge网络,test3和test4默认连接new-bridge网络。
bash
docker network create -d bridge new-bridge
docker run -itd --name test1 busybox
docker run -itd --name test2 busybox
docker run -itd --name test3 --network new-bridge busybox
docker run -itd --name test4 --network new-bridge busybox
#查看两个网络里面连接的容器
docker network inspect bridge
docker network inspect new-bridge

- 验证test1和test2能否使用dns解析服务
bash
docker exec -it test1 ip a
docker exec -it test2 ip a
#test1容器pingtest2的IP地址是可以ping通的
docker exec -it test1 ping [test2的IP]
#test1容器pingtest2的容器名是找不到IP地址的
docker exec -it test1 ping test2

- 验证test3和test4能否使用dns解析服务
bash
docker exec -it test3 ip a
docker exec -it test3 ip a
#test1容器pingtest2的IP地址是可以ping通的
docker exec -it test3 ping [test4的IP]
#test1容器pingtest2的容器名是找不到IP地址的
docker exec -it test3 ping test4

端口暴露和转发
- 端口暴露有两种方式,在启动容器的时候添加端口参数,一种是-P暴露所有端口,将暴露的所有端口映射到主机所有地址的一个动态端口,另外一种是-p暴露指定端口。
- 端口转发,连接bridge容器的端口映射到宿主机的端口上,那么任何发送到宿主机该端口的流量,都会转发到容器的端口中。
例如下图,任何发送到8088端口的流量都会被转发到container1的80端口,任何发送到8089端口的流量都会转发到container2的80端口。

docker host网络
概念
docker容器运行默认都会分配network namespace隔离子系统,如果是基于host网络模式,容器将不再获得一个独立的network namespace,而是和宿主机共用同一个network namespace,容器将不再虚拟出自己的网卡,IP等,而是直接使用宿主机的IP端口。

bridge网络在通信的时候需要进行端口转发以及NAT地址转换,肯定会消耗掉一些资源以及性能。直接使用host网络最大的好处就是性能好,如果容器对网络传输有较高的要求,建议选择host网络,但host也有一些不足,比如说要考虑端口冲突的问题。
操作
- 创建test1容器使用bridge网络,test2容器使用host网络
bash
docker run --name test1 -itd busybox
docker run --name test2 -itd --network=host busybox
- 查看test1容器的网络信息
bash
docker exec test1 ip a

- 查看test2容器的网络信息
bash
docker exec test2 ip a
可以看到test1容器是独立的网络配置,test2容器是和宿主机共享网络配置

docker container网络
概念
docker container的other container网络模式是docker中一种比较特别的网络模式,这个模式下的docker container会使用其他容器的网络环境,网络隔离性会处于bridge桥接模式和host模式之间,docker container共享其他容器的网络环境,则至少这两个容器之间不存在网络隔离,而这两个容器又与宿主机以及其他的容器存在网络隔离。

实现这种模式需要两步
- 查找other container(即需要被共享网络环境的容器)的网络namespace
- 让新创建的docker container(需要共享其他网络的容器)的namespace使用other container的namespace
操作
- 创建一个busybox容器
bash
docker run -itd --name test1 busybox
- 使用test1的网络搭建另外一个容器
bash
docker run -itd --name test2 --network container :test1 busybox
- 查看两个容器的ip信息,会发现两个ip和mac完全一样
bash
docker exec -it test1 ip a
docker exec -it test2 ip a

- 停止test1容器,查看test2容器的网络,会发现eth0网卡消失了,只有一个本地网络了
bash
docker stop test1
docker exec -it test2 ip a

- 再次重启test1容器和test2容器
bash
docker restart test1
docker restart test2
- 再次查看test2容器,会发现eth0回来了,网络恢复

这种模式下的docker container可以通过localhost来访问namespace下的其他容器,传输效率较高,但两个容器之间存在依赖,如果依赖的容器重启了,就会导致另外一个服务不可用。
docker none网络
none网络就是指没有网络,挂在这个网络下的容器出来lo(本地环回),没有任何其他网卡。
针对一些对安全性要求比较高且不需要联网的应用,可以使用none网络,比如说生成随机密码,避免生成的密码被第三方获取,一些第三方的应用也可能需要docker创建一个没有网络的容器,网络由第三方自己配置。