大量的互联网应用服务包括多个服务组件,这往往需要多个容器之间通过网络通信进行相互配合。
一、网络基础
在了解Docker的网络之前,必须先认识Docker所依赖的几个linux技术,这对理解docker的网络有帮助。
Docker的网络实现其实就是利用了 Linux上的网络命名空间和虚拟网络设备(特别是veth pair)。熟悉这两部分的基本概念,可以有助于理解 Docker网络的实现过程。
1.命名空间
Linux Namespaces机制提供一种资源隔离方案。PID,IPC,Network等系统资源不再是全局性的,而是属于特定的Namespace。每个Namespace里面的资源对其他Namespace都不可见。
一个Network Namespace提供了一份独立的网络环境,包括网卡、路由、Iptable规则等都与其他的Network Namespace隔离。一个Docker容器“通常”会分配一个独立的Network Namespace。这样每个docker容器就好像拥有了一套独立的网络环境,甚至以为自己霸占了全部的主机,也许这也是使人们经常认为容器就是虚机的原因之一吧。
现在我们就来演示一下:
1.1我们先运行两个busybox
容器:
docker run -d --name test1 busybox /bin/sh -c "while true; do sleep 3600; done"
docker run -d --name test2 busybox /bin/sh -c "while true; do sleep 3600; done"
1.2 查看2个容器网络
docker exec -it test1 ip a
docker exec -it test2 ip a
我们可以看到test1和test2是两个不同的网络环境。
1.3添加namespace
ip netns add test1
ip netns add test2
1.4查看本机的namespace
ip netns exec test1 ip a
ip netns exec test2 ip a
namespace默认是DOWN的状态
2.Veth设备对
Veth设备对可以在不同的网络命名空间之间通信,用他们可以连接两个网络命名空间。一对veth设备就像网线的两头一样。
1.把namespace变成up状态
ip netns exec test1 ip link set dev lo up
状态变成了 unknown,为什么是unknown呢,因为当前的test1没有被连通。
2.创建并查看link
ip link add veth-test1 type veth peer name veth-test2
查看linkip link
3.添加veth-test1 到namespace
ip link set veth-test1 netns test1
4.添加并查看veth-test2
ip link set veth-test2 netns test2
ip netns exec test2 ip link
3.网桥
简单来说,桥接就是把一台机器上的若干个网络接口“连接”起来。其结果是,其中一个网口收到的报文会被复制给其他网口并发送出去。以使得网口之间的报文能够互相转发。类似交换机。
linux内核支持网口的桥接与交换机有一点点不同不同,交换机只是一个二层设备,对于接收到的报文,要么转发、要么丢弃。而运行着linux内核的机器本身就是一台主机,有可能就是网络报文的目的地。其收到的报文除了转发和丢弃,还可能被送到网络协议栈的上层(网络层),从而被自己消化。
在docker启动时,会在主机上创建一个docker0网桥。通过docker0在同一个主机上的容器之间都可以通信,外部的消息也可以经过docker0进入容器。
1.为test1和test2分配IP地址
ip netns exec test1 ip addr add 192.168.1.1/24 dev veth-test1
ip netns exec test2 ip addr add 192.168.1.1/24 dev veth-test2
2.link并启动
ip netns exec test1 ip link set dev veth-test1 up
ip netns exec test2 ip link set dev veth-test2 up
3.两个namespace可以ping通了
ip netns exec test1 ping 192.168.1.2
二、网络创建过程
Docker创建一个容器的时候,会具体执行如下操作:
- 创建一对虚拟接口,分别放到本地主机和新容器的命名空间中。
- 本地主机一端的虚拟接口连接到默认的 docker0网桥或指定网桥上,并具有一个以veth开头的唯一名字,如veth1234。
- 容器一端的虚拟接口将放到新创建的容器中,并修改名字作为eth0。这个接口只在容器的命名空间可见。
- 从网桥可用地址段中获取一个空闲地址分配给容器的eth0(例如172.17.0.2/16),并配置默认路由网关为 docker网卡的内部接口 docker的IP地址(例如172.17.42./16)。
完成这些之后,容器就可以使用它所能看到的eth0虚拟网卡来连接其他容器和访问外部网络。
三、端口映射
在启动容器的时候,如果不指定对应参数,在容器外部是无法通过网络来访问容器内的网络应用和服务的。
当容器中运行一些网络应用,要让外部访问这些应用时,可以通过-P或-p参数来指定端口映射。当使用-P标记时, Docker会随机映射一个
我们先演示下没有映射端口的情况:
1.启动一个nginx容器
docker run --name web -d nginx
2.查看桥接网络状态
docker network inspect bridge
获得IP:172.17.0.4
- 在容器内访问
curl http://172.17.0.4
- 在容器外访问
curl http://172.17.0.4
访问被拒绝
现在我们添加端口演示:
1.先停止并删除容器
docker stop web
docker rm web
2.重新创建容器,并指定端口映射
docker run --name web -d -p 80:80 nginx
3.再来测试外部访问,为了能更好的感受到,我们使用浏览器访问
四、默认网络
安装Docker时,它会自动创建三个网络:bridge NetWork
(桥接网络)、Host NetWork
(主机网络)、None NetWork
(无网络)
这三个网络都是用于单机。
这三个网络内置于Docker中。运行容器时,可以使用 --network
或者-- net
标志指定容器应连接到的网络。
bridge网络代表docker0所有Docker安装中存在的网络。除非你使用该docker run --network=<NETWORK>
选项指定,否则Docker守护程序默认将容器连接到此网络。。
我们可以使用docker network inspect <net>
来查看某个网络信息。
例:
docker network inspect bridge
五、Docker网络模式
Docker有以下4种网络模式:
- Bridge模式:使用–net=bridge指定,默认设置;
- Host模式:使用–net=host指定;
- Container模式:使用–net=container:NAME_or_ID指定;
- None模式:使用–net=none指定。
你可以使用以docker network ls
命令列出这些网络:
1.Bridge
Bridge是Docker网络默认模式,相当于Vmware中的Nat模式,容器使用独立network Namespace,并连接到docker0虚拟网卡(默认模式)。通过docker0网桥以及Iptables nat表配置与宿主机通信;bridge模式是Docker默认的网络设置,此模式会为每一个容器分配Network Namespace、设置IP等,并将一个主机上的Docker容器连接到一个虚拟网桥上。
在Bridge模式下:
- 主机上面会有一个docker0的网桥;
- 每个容器都与docker0连通,所以同主机上的容器之间也连通;
- 每个主机上容器的地址都是从172.17.0.2开始往后分。
安装brctl
yum -y install brige-utils
查看本机的veth
brctl show
2.Host
相当于Vmware中的桥接模式,与宿主机在同一个网络中,但没有独立IP地址。如果启动容器的时候使用host模式,那么这个容器将不会获得一个独立的Network Namespace,而是和宿主机共用一个Network Namespace。容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口。
3.None
在这种模式下,Docker容器拥有自己的Network Namespace,但是,并不为Docker容器进行任何网络配置。也就是说,这个Docker容器没有网卡、IP、路由等信息。需要我们自己为Docker容器添加网卡、配置IP等。以下情况下是有用的:容器并不需要网络。
4.Container
这个模式指定新创建的容器和已经存在的一个容器共享一个Network Namespace,而不是和宿主机共享。新创建的容器不会创建自己的网卡,配置自己的IP,而是和一个指定的容器共享IP、端口范围等。同样,两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的。