文章目录
docker06(docker的网络模式及其构建)
一.引子
docker的使用是非常便捷的,但是其网络一直是弱势。本章,笔者将带大家一起去了解并学习docker的网络模式和docker向外通信的实验搭建。
二.docker网络模式基本概念
(一).namespace
容器之间网络环境和系统网络环境相互交错,导致隔离性极差,而namespace可以将容器的网络环境隔离开来,这样隔离性的问题解决了,稳定性和安全性也就极大地提升了。
namespace 的概念。其目的是将某个特定的全局系统资源(global system resource)通过抽象方法使得namespace 中的进程看起来拥有它们自己的隔离的全局系统资源实例,而namespace不仅仅只是隔离网络环境那么简单。以后有时间我们会详细介绍namespace的功能。
(二).docker网络模式
1.birdge:bridge模式下容器没有一个公有ip,只有宿主机可以直接访问,外部主机是不可见的,但容器通过宿主机的NAT规则后可以访问外网。
2.host:host模式可以让容器共享宿主机网络栈,这样的好处是外部主机与容器直接通信,但是容器的网络缺少隔离性
3.none:网络环境为 none,即不为 Docker Container 任何的网络环境。一旦 Docker Container 采用了none网络模式,那么容器内部就只能使用 loopback 网络设备,不会再有其他的网络资源。
4.container:处于这个模式下的 Docker 容器会共享一个网络栈,这样两个容器之间可以使用localhost高效快速通信。
(三)docker 容器修改常用命令
–dns 用于指定启动的容器的 DNS
–net 用于指定容器的网络通讯方式,有以下四个值
bridge:Docker 默认方式,网桥模式
none:容器没有网络栈
container:使用其它容器的网络栈,Docker容器会加入其它容器的 network namespace --network container:(ContainerName)
host:表示容器使用 Host 的网络,没有自己独立的网络栈。容器可以完全访问 Host 的网络,不安全 --network host
-b, --bridge=”” 指定 Docker 使用的网桥设备,默认情况下 Docker 会自动创建和使用 docker0 网桥设备,通过此参数可以使用已经存在的设备
–bip 指定 Docker0 的 IP 和掩码,使用标准的 CIDR 形式,如 10.10.10.10/24
-p / P 选项的使用格式
> -p :将制定的容器端口映射至主机所有地址的一个动态端口
> -p ::映射至指定的主机端口
> -p :::映射至指定的主机的 IP 的动态端口
> -p :::映射至指定的主机 IP 的主机端口
> -P(大):暴露所需要的所有端口
docker port ContainerName 可以查看容器当前的映射关系
三.docker网络模式实验详解
(一).查看容器网络模式
[root@localhost ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
41fa5f4f66ca bridge bridge local
ab49d5db7bb1 host host local
2df9c496f37d none null local
[root@localhost ~]# ip addr
4: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:fb:83:53:51 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:fbff:fe83:5351/64 scope link
valid_lft forever preferred_lft forever
(二)docker的四种网络模式
1.bridge:bridge模式下容器没有一个公有ip,只有宿主机可以直接访问,外部主机是不可见的,但容器通过宿主机的NAT规则后可以访问外网。
Bridge 桥接模式的实现步骤主要如下:
- Docker Daemon 利用 veth pair 技术,在宿主机上创建两个虚拟网络接口设备,假设为veth0 和veth1。而veth pair 技术的特性可以保证无论哪一个 veth 接收到网络报文,都会将报文传输给另一方。
- Docker Daemon 将 veth0 附加到 Docker Daemon 创建的 docker0
网桥上。保证宿主机的网络报文可以发往 veth0; - Docker Daemon 将 veth1 添加到 Docker Container 所属的 namespace 下,并被改名为eth0。如此一来,保证宿主机的网络报文若发往 veth0,则立即会被 eth0 接收,实现宿主机到Docker Container网络的联通性;同时,也保证 Docker Container 单独使用 eth0,实现容器网络环境的隔离性
Bridge桥接模式的缺陷:
- 最明显的是,该模式下 Docker Container 不具有一个公有 IP,即和宿主机的 eth0
不处于同一个网段。导致的结果是宿主机以外的世界不能直接和容器进行通信。 - 虽然 NAT 模式经过中间处理实现了这一点,但是 NAT
模式仍然存在问题与不便,如:容器均需要在宿主机上竞争端口,容器内部服务的访问者需要使用服务发现获知服务的外部端口等。 - 另外 NAT 模式由于是在三层网络上的实现手段,故肯定会影响网络的传输效率。
实验:
(1).创建并进入vm2容器内部,发现eth0网卡
[root@localhost ~]# docker exec -it vm2 /bin/bash
[root@f7dfee9bee4a /]# ip addr
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
7: eth0@if8: <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 link-netnsid 0
inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
(2).docker0上连接了veth网卡,并且真实机多了一张网卡
[root@localhost ~]# brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.0242fb835351 no veth6f30ba5
[root@localhost ~]# ip addr
8: veth6f30ba5@if7: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether 72:60:78:b6:bf:b2 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet6 fe80::7060:78ff:feb6:bfb2/64 scope link
valid_lft forever preferred_lft forever
#veth设备是成双成对出现的,一端是容器内部命名为eth0,一端是加入到网桥并命名的veth(通常命名为veth),它们组成了一个数据传输通道,一端进一端出,veth设备连接了两个网络设备并实现了数据通信**
2.host:
- host网络模式需要在容器创建时指定–network=host
- host 模式是 bridge 桥接模式很好的补充。采用 host 模式的 Docker Container,可以直接使用宿主机的 IP
- 地址与外界进行通信,若宿主机的 eth0 是一个公有 IP,那么容器也拥有这个公有IP。同时容器内服务的端口也可以使用宿主机的端口,无需额外进行 NAT 转换。host模式可以让容器共享宿主机网络栈,这样的好处是外部主机与容器直接通信,但是容器的网络缺少隔离性。
Host 网络模式的缺陷: - 最明显的是 Docker Container 网络环境隔离性的弱化。即容器不再拥有隔离、独立的网络栈。
- 另外,使用 host 模式的 Docker Container
虽然可以让容器内部的服务和传统情况无差别、无改造的使用,但是由于网络隔离性的弱化,该容器会与宿主机共享竞争网络栈的使用;
另外,容器内部将不再拥有所有的端口资源,原因是部分端口资源已经被宿主机本身的服务占用,还有部分端口已经用以 bridge
网络模式容器的端口映射。
实验:容器vm4中的ip和宿主机(docker0)中的ip一样
[root@localhost ~]# docker run -d -it --name vm4 --network=host centos
884e7e5de2e705de5ea035f713c6bcc5f2baf459890dd7d2226b7e310d8bd6fc
[root@localhost ~]# docker exec -it vm4 /bin/bash
[root@localhost /]# ip addr
4: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:fb:83:53:51 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:fbff:fe83:5351/64 scope link
valid_lft forever preferred_lft forever
3.docker的none模式
- none模式是指禁用网络功能,只有lo接口,在容器创建时使用–network=none指定。
- 网络环境为 none,即不为 Docker Container 任何的网络环境。一旦 Docker Container采用了none网络模式,那么容器内部就只能使用 loopback 网络设备,不会再有其他的网络资源。可以说 none 模式为 DockeContainer 做了极少的网络设定,但是俗话说得好“少即是多”,在没有网络配置的情况下,作为 Docker开发者,才能在这基础做其他无限多可能的网络定制开发。这也恰巧体现了 Docker 设计理念的开放。
实验:创建一个none模式容器,发现只有一个lo接口
[root@localhost ~]# docker run -it --name vm5 --network=none centosr
[root@f44790b17e90 /]# ip addr
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
4.container模式:
- Container 网络模式是 Docker
中一种较为特别的网络的模式。在容器创建时使用–network=container:vm1指定。(vm1指定的是运行的容器名) - 处于这个模式下的 Docker 容器会共享一个网络栈,这样两个容器之间可以使用localhost高效快速通信。
缺陷:它并没有改善容器与宿主机以外世界通信的情况(和桥接模式一样,不能连接宿主机以外的其他设备)。
实验:创建一个container网络模式的容器vm6并查看ip,由于vm4使用的是vm7的网络,所以其查看的网络结果和vm4的相同
4: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:fb:83:53:51 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:fbff:fe83:5351/64 scope link
valid_lft forever preferred_lft forever
8: veth6f30ba5@if7: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether 72:60:78:b6:bf:b2 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet6 fe80::7060:78ff:feb6:bfb2/64 scope link
valid_lft forever preferred_lft forever
(三)docker的网络连接
1 .–link :容器与容器之间的网络连接
docker run --link可以用来链接2个容器,使得源容器(被链接的容器)和接收容器(主动去链接的容器)之间可以互相通信,并且接收容器可以获取源容器的一些数据,如源容器的环境变量。
实验:
#创建容器vm8连接vm1并设置vm1别名为db1
[root@localhost bin]# docker run -it --name vm8 --link vm1:db1 centos
#vm8可以连接vm1及db1
[root@275c9f579235 /]# ping vm1
PING db1 (172.17.0.2) 56(84) bytes of data.
64 bytes from db1 (172.17.0.2): icmp_seq=1 ttl=64 time=0.602 ms
64 bytes from db1 (172.17.0.2): icmp_seq=2 ttl=64 time=0.134 ms
64 bytes from db1 (172.17.0.2): icmp_seq=3 ttl=64 time=0.126 ms
[root@275c9f579235 /]# ping db1
PING db1 (172.17.0.2) 56(84) bytes of data.
64 bytes from db1 (172.17.0.2): icmp_seq=1 ttl=64 time=0.132 ms
64 bytes from db1 (172.17.0.2): icmp_seq=2 ttl=64 time=0.111 ms
64 bytes from db1 (172.17.0.2): icmp_seq=3 ttl=64 time=0.117 ms
#vm8与vm1有不同的IP
[root@localhost ~]# docker exec -it vm1 /bin/bash
[root@ebb56b583bc9 /]# ip a
5: eth0@if6: <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 link-netnsid 0
inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
[root@275c9f579235 /]# ip a
9: eth0@if10: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:11:00:04 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.17.0.4/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
#查看etc/hosts文件
[root@275c9f579235 /]# cat /etc/hosts
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.2 db1 ebb56b583bc9 vm1
172.17.0.4 275c9f579235
2.高级网络配置:网桥
自定义网络模式中,docker提供了三种自定义网络驱动:brdge,overlay,macvlan,bridge驱动类似默认的bridge网络模式,但增加了一些新的功能,overlay和macvlan是用于创建跨主机网络
建议使用自定义的网络来控制哪些容器可以相互通信,还可以自动DNS解析容器名称到IP地址
实验:(将所有容器删除:docker rm -f $(docker pa -a -q))
(1).birdge自定义网络:自定义网桥中会自己分配ip地址和网关地址(可以使用自定义网桥来控制哪些容器可以相互通信)
#创建自定义网桥
[root@localhost ~]# docker network create --driver bridge my_net1
cccd284cdc6b58626e3f4f1f21e351b47b11e8e5e6fb5b99950b39ca94aa73d1
[root@localhost ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
2fb232dda51e bridge bridge local
ab49d5db7bb1 host host local
cccd284cdc6b my_net1 bridge local
2df9c496f37d none null local
#查看IP及自定义网桥IP
[root@localhost ~]# ip a
4: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:e4:1e:a2:4b 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:e4ff:fe1e:a24b/64 scope link
valid_lft forever preferred_lft forever
[root@localhost ~]# docker network inspect my_net1
"Options": {},
"Config": [
{
"Subnet": "172.18.0.0/16",
"Gateway": "172.18.0.1"
}
#创建vm1和vm2容器并测试他们之间的相互通信
[root@localhost ~]# docker run --name vm1 -it --network=my_net1 centos
[root@bd0878a73cf8 /]# ping vm1
PING vm1 (172.18.0.2) 56(84) bytes of data.
64 bytes from bd0878a73cf8 (172.18.0.2): icmp_seq=1 ttl=64 time=0.084 ms
64 bytes from bd0878a73cf8 (172.18.0.2): icmp_seq=2 ttl=64 time=0.062 ms
[root@localhost bin]# docker run --name vm2 -it --network=my_net1 centos
[root@de769084f578 /]# ping vm1
PING vm1 (172.18.0.2) 56(84) bytes of data.
64 bytes from vm1.my_net1 (172.18.0.2): icmp_seq=1 ttl=64 time=0.817 ms
64 bytes from vm1.my_net1 (172.18.0.2): icmp_seq=2 ttl=64 time=0.103 ms
注:在创建容器时,如果器在同一网桥内可以相互通信,可以创建私有网桥来进行容器管理
(2).bridge自定义网络–网段
还可以自己定义网段:在创建时指定参数:–subnet 、–gateway
创建两个容器,并桥接到不同的网桥上(vm1连接的网桥是my_net1),彼此是不通信的。
使用–ip参数可以指定容器ip地址,但必须是在自定义网桥上,默认的bridge模式不支持,同一网桥上的容器是可以互通的。
实验
#创建特定网段
[root@localhost ~]# docker network create --subnet=172.21.8.0/24 my_net3
43478a85395392e4903555a08391d0b6d802ff4cb7e99a3014dea2a860bddce2
[root@localhost ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
2fb232dda51e bridge bridge local
ab49d5db7bb1 host host local
cccd284cdc6b my_net1 bridge local
43478a853953 my_net3 bridge local
2df9c496f37d none null local
#创建vm3并查看其IP
[root@localhost ~]# docker run --name vm3 -it --network=my_net3 centos
[root@ba219d81870b /]# ip a
17: eth0@if18: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:15:08:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.21.8.2/24 brd 172.21.8.255 scope global eth0
valid_lft forever preferred_lft forever
#检测其网络联通性:无法连接vm1
[root@ba219d81870b /]# ping vm3
PING vm3 (172.21.8.2) 56(84) bytes of data.
64 bytes from ba219d81870b (172.21.8.2): icmp_seq=1 ttl=64 time=0.073 ms
64 bytes from ba219d81870b (172.21.8.2): icmp_seq=2 ttl=64 time=0.076 ms
[root@ba219d81870b /]# ping vm1
ping: vm1: Name or service not known
(3).实现不同网桥之间的通信
docker在设计上就是要隔离不同的network的
dokcer作为一个超级厉害的容器,怎么能容忍不同网桥间无法进行通信,它当然有其自己的方法。
前提:
现在使用的容器vm1使用的网桥时my_net1,容器vm3使用的网桥是my_net3,默认是无法进行通讯的.
实验:
1.使用 docker network connect命令为vm1添加一块my_net3的网卡。
[root@localhost ~]# docker network connect my_net3 vm1
[root@localhost ~]# docker start vm1
[root@localhost ~]# docker attach vm1
[root@bd0878a73cf8 /]# ping vm3
PING vm3 (172.21.8.2) 56(84) bytes of data.
64 bytes from vm3.my_net3 (172.21.8.2): icmp_seq=1 ttl=64 time=0.261 ms
64 bytes from vm3.my_net3 (172.21.8.2): icmp_seq=2 ttl=64 time=0.116 ms
#vm1和vm3之间能够通信
1.docker的bridge自定义网络(自定义网络中的网桥)间可以随便添加对方的网卡
2.docker的bridge自定义网络与系统自带的网桥自己间:只能是系统自带的网桥对应的容器添加给自定义的网桥间,而反过来会报错
3.docker的系统自带的网桥之间时可以互相通信的,因为在同一个网络桥接上
3.docker与外网进行访问和连接
(1).容器如何实现访问外网(容器如通过iptables的SNAT实现的)
(2).外网如何访问容器
通过端口映射以及选项指定映射端口
外网访问容器用到了docker-proxy和iptables DNAT 宿主机访问本机容器使用的是iptables DNAT外部主机访问容器或容器之间的访问是docker-proxy实现
[root@localhost ~]# iptables -t nat -nL
1.查看防火墙规则
Chain POSTROUTING (policy ACCEPT)
target prot opt source destination
MASQUERADE all -- 172.21.8.0/24 0.0.0.0/0
MASQUERADE all -- 172.18.0.0/16 0.0.0.0/0
MASQUERADE all -- 172.17.0.0/16 0.0.0.0/0
Chain DOCKER (2 references)
target prot opt source destination
RETURN all -- 0.0.0.0/0 0.0.0.0/0
RETURN all -- 0.0.0.0/0 0.0.0.0/0
RETURN all -- 0.0.0.0/0 0.0.0.0/0
2.运行nginx容器并设置映射端口
docker run -d --name nginx -p 80:80 nginx
d49c1d7cb1649f9847908626754a302a173d0389138404e9665438c1315f9b85
3.查看容器端口
[root@localhost ~]# docker port nginx
80/tcp -> 0.0.0.0:80
[root@localhost ~]# netstat -antp |grep 80
tcp6 0 0 :::80 :::* LISTEN 115609/docker-proxy
4.查看防火墙规则
[root@localhost ~]# iptables -t nat -nL
Chain DOCKER (2 references)
target prot opt source destination
RETURN all -- 0.0.0.0/0 0.0.0.0/0
RETURN all -- 0.0.0.0/0 0.0.0.0/0
RETURN all -- 0.0.0.0/0 0.0.0.0/0
DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:80 to:172.17.0.2:80
5.本机访问容器(成功)
curl localhost
curl 172.17.0.2
6.外网访问容器
$ docker run -d -p 80:80 nginx
$ iptables -t nat -A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER
$ iptables -t nat -A DOCKER ! -i docker0 -p tcp -m tcp --dport 80 -j DNAT --to-destination 172.17.0.2:80
#可以成功
(3)跨主机之间的docker网络连接
跨主机网络解决方案:docker原生的overlay和macvlan,第三方的flannel,weave,calico
-
众多网络方案是如何与docker集成在一起的:libnetwork docker容器网络库;CNM(Container NetworkModel)这个模型对容器网络进行了抽象CNM分三类组件
1.Sandbox:容器网络栈,包含容器接口、dns、路由表。(namespace)
2.Endpoint:作用是将sandbox接入network (veth pair)
3.Network:包含一组endpoint,同一network的endpoint可以通信。
-
macvlan网络方案实现:
Linux kernel提供的一种网卡虚拟化技术。
无需Linux bridge,直接使用物理接口,性能极好。
macvlan本身是linuxkernel的模块,本质上是一种网卡虚拟化技术。其功能是允许在同一个物理网卡上虚拟出多个网卡,通过不同的MAC地址在数据链路层进行网络数据的转发,一块网卡上配置多个MAC 地址(即多个 interface),每个interface可以配置自己的IP,Docker的macvlan网络实际上就是使用了Linux提供的macvlan驱动。 -
macvlan网络间的隔离和连通
(1)macvlan网络在二层上是隔离的,所以不同macvlan网络的容器是不能通信的。
(2)可以在三层上通过网关将macvlan网络连通起来。
(3)docker本身不做任何限制,像传统vlan网络那样管理即可。
实验:将容器IP绑定到网卡IP上
$ cd /etc/sysconfig/network-scripts/
$ cp ifcfg-eth0 ifcfg-br0
$ vi ifcfg-eth0
DEVICE=eth0
HWADDR=00:0C:29:06:A2:35
TYPE=Ethernet
UUID=34b706cc-aa46-4be3-91fc-d1f48c301f23
ONBOOT=yes
BRIDGE=br0 #指定bro文件
NM_CONTROLLED=yes
BOOTPROTO=static
$ vi ifcfg-br0
DEVICE=br0
TYPE=Bridge
ONBOOT=yes
BOOTPROTO=static
IPADDR=192.168.216.131
NETMASK=255.255.255.0
GATEWAY=192.168.216.2
DN1=8.8.8.8
$ yum install -y git
$ git clone https://github.com/jpetazzo/pipework (测试工具)
$ cp pipework/pipework /usr/local/bin/
$ docker run -itd --net=none --name=ff centos-6-x86 bash
$ pipework br0 ff 192.168.216.135/24 (给容器IP)
$ ping 192.168.216.135
以上便是docker的网络配置的基本介绍了,感谢大家阅读。