七:Docker 网络
Docker 启动后会创建三个默认网络,对应三种网络类型,名称分别为 bridge、host、none:
- bridge:基于 bridge 网络创建的容器为 Bridged container,创建容器时默认使用此网络;桥接网络可以自定义子网;
- host:基于 host 网络创建的容器为 Joined container,容器直接使用宿主机的 IP 地址;
- none:基于 none 网络创建的容器为 Closed container,容器没有任何网络接口,不参加网络通信;
root@ubuntu:~# docker network ls
NETWORK ID NAME DRIVER SCOPE
6856d6ce1ca3 bridge bridge local
8e3a0782cd56 host host local
609d94c11be8 none null local
7.1:指定 Docker 容器网络
7.1.1:–net=bridge
bridge 是 Docker 创建容器时默认使用的网络,桥接网络是使用比较多的网络类型;
基于 bridge 网络创建的容器会拥有自己的虚拟网络接口和 IP,并将容器的虚拟网络接口连接到虚拟网桥,从而与容器的外部网络通信;
查看 bridge 网络的详细信息:
root@ubuntu:~# docker network inspect bridge
[
{
"Name": "bridge",
"Id": "6856d6ce1ca3f724bf684ceb1b91877bd3a8eb1efdad6280bb2eb9d639e1cb24",
"Created": "2021-01-25T10:59:34.772672508+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": {
"25436c07caecaebddd0d2687c0a6002bc8a7f114d1bb98debf3f69d9c9389003": {
"Name": "tomcat1",
"EndpointID": "9caa8cf498abde1287dbefc31679f92ffebef03615ffaa9cda6cf8c3c9fe29c7",
"MacAddress": "02:42:ac:11:00:02",
"IPv4Address": "172.17.0.2/16",
"IPv6Address": ""
},
"287ee1b334fa7e37d2a3418cc101885372c6dfd59b6c314478e87f65c4262aa9": {
"Name": "nginx1",
"EndpointID": "d47b6a9431e91f1ed0c3e88420cf042e54b01741ec9c6b44864b3d5d1d8d0ecb",
"MacAddress": "02:42:ac:11:00:03",
"IPv4Address": "172.17.0.3/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": {}
}
]
7.1.2:–net=host
启动容器时,如果指定网络为 host(–net=host 或 --net=网络 ID),那么容器没有自己的虚拟网卡(Veth),而是直接使用宿主机的物理网卡和 IP 地址,在容器中查看网络信息,也是宿主机的网络信息;
访问 host 网络下的容器,直接使用宿主机的 IP:PORT 即可;
当然,除了使用宿主机的网络,容器的其它资源(如文件系统、进程等)还是和宿主机保持隔离的;
此模式的网络下,性能最高,但限制了容器间端口的复用(同一宿主机的 host 容器间端口不能使用重复);
使用 host 网络时,如果使用 -p
指定端口映射,则指定不生效;
以 host 网络启动一个 Nginx 容器:
root@ubuntu:~# docker run -itd --name nginx-host --net=host centos-nginx:1.16.1
查看容器的网络信息,和宿主机相同:
root@ubuntu:~# docker exec nginx-host ifconfig docker0: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500 inet 172.17.0.1 netmask 255.255.0.0 broadcast 172.17.255.255 inet6 fe80::42:8fff:fe3b:7bf0 prefixlen 64 scopeid 0x20<link> ether 02:42:8f:3b:7b:f0 txqueuelen 0 (Ethernet) RX packets 438 bytes 180114 (175.8 KiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 524 bytes 329307 (321.5 KiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 192.168.1.111 netmask 255.255.255.0 broadcast 192.168.1.255 inet6 fe80::20c:29ff:fea4:22af prefixlen 64 scopeid 0x20<link> inet6 240e:324:79e:f400:20c:29ff:fea4:22af prefixlen 64 scopeid 0x0<global> ether 00:0c:29:a4:22:af txqueuelen 1000 (Ethernet) RX packets 415869 bytes 577631669 (550.8 MiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 32432 bytes 3575782 (3.4 MiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536 inet 127.0.0.1 netmask 255.0.0.0 inet6 ::1 prefixlen 128 scopeid 0x10<host> loop txqueuelen 1000 (Local Loopback) RX packets 376 bytes 36008 (35.1 KiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 376 bytes 36008 (35.1 KiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
未指定端口映射,但直接访问宿主机即可:
root@ubuntu:~# docker exec -it nginx-host bash [root@ubuntu /]# echo "host network test" > /data/nginx/html/index.html
http://192.168.1.111/
7.1.3:–net=none
使用 none 网络创建的容器,没有虚拟网卡、IP、路由,即没有任何网络配置,不参与任何通信;
可以手动添加网卡、配置 IP,但为什么不直接创建 bridge 网络的容器呢?所以很少使用 none 网络,常用的场景是做一些单机数据分析时使用;
以 none 网络启动一个容器:
root@ubuntu:~# docker run -itd --net=none --name nginx-none centos-nginx:1.16.1
查看容器的网络配置,只有一个回环接口:
root@ubuntu:~# docker exec nginx-none ifconfig lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536 inet 127.0.0.1 netmask 255.0.0.0 loop txqueuelen 1000 (Local Loopback) RX packets 0 bytes 0 (0.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 0 bytes 0 (0.0 B) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
7.1.4:–net=container
container 不是一个网络,而是在创建容器时,通过 --net=container:CONTAINER
指定一个已经存在的容器,使新创建的容器共享它的网络;
新创建的容器没有自己的网络、IP 等网络配置,而是和指定的容器共享同一个网络配置,包括端口范围,因此这两个容器中的端口不能重复;
当然,除了网络,两个容器的文件系统、进程等仍然相互隔离,两个容器的进程间可以通过共享的 lo 网卡和容器 IP 进行通信。
先正常启动一个 Nginx 容器,映射 80 和 8080 端口:
root@ubuntu:~# docker run -itd --name nginx1 -p 80:80 -p 8080:8080 centos-nginx:1.16.1
再启动一个 Tomcat 容器,和 Nginx1 容器共享网络:
root@ubuntu:~# docker run -itd --net=container:nginx1 --name tomcat1 testapp:v1
查看 Tomcat 容器的端口监听:
Tomcat 容器中也监听了 80 端口(其实是 Nginx 容器监听的);
root@ubuntu:~# docker exec -it tomcat1 bash [root@3ca347a32d15 /]# ss -tnl
查看 Nginx 容器的端口监听:
两个容器的监听相同;
root@ubuntu:~# docker exec -it nginx1 bash [root@3ca347a32d15 /]# ss -tnl
分别访问宿主机的 80 和 8080 端口:
编辑 Nginx 配置文件,将 .jsp 的请求转发给 8080 端口,验证两个容器间的通信是否正常:
[root@07da373d143c /]# vim /apps/nginx/conf/nginx.conf location ~* \.jsp$ { proxy_pass http://127.0.0.1:8080; } [root@07da373d143c /]# nginx -s reload
访问 http://192.168.1.111/testapp/hello.jsp:
7.2:创建自定义 Docker 网络
可以通过 docker network create
来创建自定义的 Docker 网络;
root@ubuntu:~# docker network create --help
Usage: docker network create [OPTIONS] NETWORK
Create a network
Options:
--attachable Enable manual container attachment
--aux-address map Auxiliary IPv4 or IPv6 addresses used by Network driver (default map[])
--config-from string The network from which copying the configuration
--config-only Create a configuration only network
-d, --driver string Driver to manage the Network (default "bridge")
--gateway strings IPv4 or IPv6 Gateway for the master subnet
--ingress Create swarm routing-mesh network
--internal Restrict external access to the network
--ip-range strings Allocate container ip from a sub-range
--ipam-driver string IP Address Management Driver (default "default")
--ipam-opt map Set IPAM driver specific options (default map[])
--ipv6 Enable IPv6 networking
--label list Set metadata on a network
-o, --opt map Set driver specific options (default map[])
--scope string Control the network's scope
--subnet strings Subnet in CIDR format that represents a network segment
一般只是创建自定义的桥接网络(-d bridge);
host 和 null 驱动的网络在同一宿主机上只能有一个,而默认的 Docker 网络中,host 和 none 网络就是这两种网络类型,所以这两种
7.2.1:创建自定义的桥接网络
-d bridge
,-d 选项用来指定所要创建网络的驱动,bridge 是创建网络的默认驱动,不用指定 -d 选项也可以;
创建网络
创建一个名为 mynet,子网为 10.10.10.0/24,网关(宿主机桥接网卡的地址)为 10.10.10.1 的 Docker网络:
root@ubuntu:~# docker network create -d bridge --subnet 10.10.10.0/24 --gateway 10.10.10.1 mynet
验证网络
查看创建的 Docker 网络:
root@ubuntu:~# docker network ls -f name=mynet
NETWORK ID NAME DRIVER SCOPE
e4bbd123c63d mynet bridge local
查看宿主机桥接网卡:
创建桥接网络后,会在宿主机创建相应的桥接网卡,地址为该桥接网络的网关;
root@ubuntu:~# ifconfig
br-e4bbd123c63d: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500
inet 10.10.10.1 netmask 255.255.255.0 broadcast 10.10.10.255
ether 02:42:76:88:7c:19 txqueuelen 0 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
……
以 mynet 创建容器:
root@ubuntu:~# docker run -itd --name mynet-nginx --net=mynet centos-nginx:1.16.1
root@ubuntu:~# docker exec -it mynet-nginx bash
[root@b994b17d4619 /]# ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 10.10.10.2 netmask 255.255.255.0 broadcast 10.10.10.255
ether 02:42:0a:0a:0a:02 txqueuelen 0 (Ethernet)
RX packets 14 bytes 1172 (1.1 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
7.3:容器间通信
7.3.1:同宿主机同网络的容器间通信
同一个宿主机上,在创建容器时,可以通过 --link 指定容器(名称或 ID 均可),从而实现和它之间的通信;
比如两个容器 Nginx 和 Tomcat,Nginx 需要转发动态请求至 Tomcat 容器,这时在创建 Nginx 容器时,就可以使用 --link 来指定 Tomcat 容器,从而可以将请求转发给 Tomcat 容器;
同宿主机同网络的容器,可以直接通过 IP 进行访问;
启动 Tomcat 应用容器
利用之前制作的 testapp:v1 镜像启动一个容器:
root@ubuntu:~# docker run -itd -p 8080:8080 --name tomcat1 testapp:v1
启动 Nginx 容器
使用挂载配置文件的方式启动一个 Nginx 容器;
准备配置文件:
root@ubuntu:~# mkdir /data/conf
root@ubuntu:~# cp /Dockerfile/Services/nginx/nginx.conf /data/conf
root@ubuntu:~# vim /data/conf/nginx.conf
……
location ~* \.(jsp|do)$ {
proxy_pass http://tomcat1:8080;
}
……
启动 Nginx 容器:
基于之前制作的 centos-nginx:1.16.1 镜像;
–link 链接到 tomcat1 容器;
root@ubuntu:~# docker run -d -p 80:80 --name nginx1 -v /data/conf/nginx.conf:/etc/nginx/nginx.conf:ro --link tomcat1 centos-nginx:1.16.1
验证容器间通信
进入 nginx1 容器,查看 hosts 文件:
–link 指定要链接的容器后,会在 hosts 文件中添加相应的解析;
root@ubuntu:~# docker exec -it nginx1 bash
[root@287ee1b334fa /]# 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 tomcat1 25436c07caec
172.17.0.3 287ee1b334fa
ping 测试:
[root@287ee1b334fa /]# ping 172.17.0.2
PING 172.17.0.2 (172.17.0.2) 56(84) bytes of data.
64 bytes from 172.17.0.2: icmp_seq=1 ttl=64 time=0.085 ms
64 bytes from 172.17.0.2: icmp_seq=2 ttl=64 time=0.138 ms
[root@287ee1b334fa /]# ping tomcat1
PING tomcat1 (172.17.0.2) 56(84) bytes of data.
64 bytes from tomcat1 (172.17.0.2): icmp_seq=1 ttl=64 time=0.119 ms
端口 telnet 测试:
[root@287ee1b334fa /]# telnet tomcat1 8080
Trying 172.17.0.2...
Connected to tomcat1.
Escape character is '^]'.
7.3.2:跨宿主机的容器间通信
跨宿主机的容器间通信,是指宿主机 A 上的容器和宿主机 B 上的容器可以通信,前提是保证两个宿主机之间是可以通信的,然后各容器才能通过桥接网络访问到对方的容器;
跨宿主机的容器间通信,实现原理就是在宿主机上添加路由,比如,使得宿主机 A 上去往宿主机 B 的容器网络的下一跳指向宿主机 B 的物理网卡;
设定整个业务系统的容器网络为 10.10.0.0/16:
- 宿主机 A:
- 物理网络:192.168.1.121/24;
- 容器网络:10.10.1.0/24;
- 宿主机 B:
- 物理网络:192.168.1.122/24;
- 容器网络:10.10.2.0/24;
修改宿主机 A 的桥接 IP 地址
Docker 桥接网络的默认网段都为 172.17.0.0/24,要实现容器间的跨宿主机通信,必须保证各宿主机上的容器网络网段是不同的;
–bip string Specify network bridge IP
指定的是桥接 IP,而不是指定桥接网络,所以要指定的是桥接网卡的 IP 地址(如果指定的是网络,因为是网络地址,会报该地址已被使用的错误,而无法启动 docker);
root@PM-A:~# vim /lib/systemd/system/docker.service
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --bip=10.10.1.1/24
root@PM-A:~# systemctl daemon-reload
root@PM-A:~# systemctl restart docker
root@PM-A:~# ifconfig docker0
docker0: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500
inet 10.10.1.1 netmask 255.255.255.0 broadcast 10.10.1.255
ether 02:42:38:01:7a:7f txqueuelen 0 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
修改宿主机 B 的桥接 IP 地址
root@PM-B:~# vim /lib/systemd/system/docker.service
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --bip=10.10.2.1/24
root@PM-B:~# systemctl daemon-reload
root@PM-B:~# systemctl restart docker
root@PM-B:~# ifconfig docker0
docker0: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500
inet 10.10.2.1 netmask 255.255.255.0 broadcast 10.10.2.255
ether 02:42:50:55:87:31 txqueuelen 0 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
宿主机启动容器
宿主机 A 启动容器:
root@PM-A:~# docker run -itd -p 80:80 --name nginx-a centos-nginx:1.16.1
宿主机 B 启动容器:
root@PM-B:~# docker run -itd -p 80:80 --name nginx-b centos-nginx:1.16.1
宿主机添加路由
宿主机 A 添加路由:
root@PM-A:~# route add -net 10.10.2.0/24 gw 192.168.1.122
宿主机 B 添加路由:
root@PM-B:~# route add -net 10.10.1.0/24 gw 192.168.1.121
宿主机是 centos 的话,除了要加路由,还需要加 iptables 规则;
iptables -A FORWARD -s 192.168.1.0/24 -j ACCEPT
测试通信
容器 nginx-a PING 容器 nginx-b:
root@PM-A:~# docker exec -it nginx-a bash
[root@a967cf2be3b6 /]# ifconfig eth0
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 10.10.1.2 netmask 255.255.255.0 broadcast 10.10.1.255
ether 02:42:0a:0a:01:02 txqueuelen 0 (Ethernet)
RX packets 17 bytes 1354 (1.3 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 5 bytes 378 (378.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
[root@a967cf2be3b6 /]# ping 10.10.2.2
PING 10.10.2.2 (10.10.2.2) 56(84) bytes of data.
64 bytes from 10.10.2.2: icmp_seq=1 ttl=62 time=2.33 ms
64 bytes from 10.10.2.2: icmp_seq=2 ttl=62 time=0.767 ms
64 bytes from 10.10.2.2: icmp_seq=3 ttl=62 time=0.572 ms
容器 nginx-bPING 容器 nginx-a:
root@PM-B:~# docker exec -it nginx-b bash
[root@ce3cd8074ecc /]# ifconfig eth0
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 10.10.2.2 netmask 255.255.255.0 broadcast 10.10.2.255
ether 02:42:0a:0a:02:02 txqueuelen 0 (Ethernet)
RX packets 17 bytes 1354 (1.3 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 5 bytes 378 (378.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
[root@ce3cd8074ecc /]# ping 10.10.1.2
PING 10.10.1.2 (10.10.1.2) 56(84) bytes of data.
64 bytes from 10.10.1.2: icmp_seq=1 ttl=62 time=2.05 ms
64 bytes from 10.10.1.2: icmp_seq=2 ttl=62 time=0.763 ms
64 bytes from 10.10.1.2: icmp_seq=3 ttl=62 time=0.689 ms
7.3.3:同宿主机不同网络的容器间通信
同一宿主机中有两个自定义的桥接网络:
- net1:192.168.16.0/24;
- net2:172.16.16.0/24
实现两个网络的容器间通信;
创建两个自定义网络
net1:
root@ubuntu:~# docker network create -d bridge --subnet 192.168.16.0/24 --gateway 192.168.16.1 net1
net2:
root@ubuntu:~# docker network create -d bridge --subnet 172.16.16.0/24 --gateway 172.16.16.1 net2
创建不同网络的两个容器
基于 net1 创建 nginx1 容器:
root@ubuntu:~# docker run -itd --name nginx1 -p 80:80 --net=net1 centos-nginx:1.16.1
基于 net2 创建 nginx2 容器:
root@ubuntu:~# docker run -itd --name nginx2 -p 10080:80 --net=net2 centos-nginx:1.16.1
删除 iptables 的 DROP 规则
同宿主机不同网络中的容器默认不能通信,是因为存在以下 iptables 规则:
将来自本机的桥接网卡的报文都 DROP 了;
root@ubuntu:~# iptables-save > iptables.sh
root@ubuntu:~# grep "DROP" iptables.sh
-A DOCKER-ISOLATION-STAGE-2 -o br-83d3e22cd0bc -j DROP
-A DOCKER-ISOLATION-STAGE-2 -o br-faaf2fed4d2c -j DROP
-A DOCKER-ISOLATION-STAGE-2 -o docker0 -j DROP
-A DOCKER-ISOLATION-STAGE-2 -o br-e4bbd123c63d -j DROP
注释 DROP 规则,重新导入 iptables规则:
root@ubuntu:~# vim iptables.sh
#-A DOCKER-ISOLATION-STAGE-2 -o br-83d3e22cd0bc -j DROP
#-A DOCKER-ISOLATION-STAGE-2 -o br-faaf2fed4d2c -j DROP
#-A DOCKER-ISOLATION-STAGE-2 -o docker0 -j DROP
#-A DOCKER-ISOLATION-STAGE-2 -o br-e4bbd123c63d -j DROP
root@ubuntu:~# iptables-restore < iptables.sh
测试通信
nginx1 PING nginx2:
root@ubuntu:~# docker exec -it nginx1 bash
[root@21b16e53482b /]# ifconfig eth0
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.16.2 netmask 255.255.255.0 broadcast 192.168.16.255
ether 02:42:c0:a8:10:02 txqueuelen 0 (Ethernet)
RX packets 25 bytes 1942 (1.8 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
[root@21b16e53482b /]# ping 172.16.16.2
PING 172.16.16.2 (172.16.16.2) 56(84) bytes of data.
64 bytes from 172.16.16.2: icmp_seq=1 ttl=63 time=0.357 ms
64 bytes from 172.16.16.2: icmp_seq=2 ttl=63 time=0.156 ms
64 bytes from 172.16.16.2: icmp_seq=3 ttl=63 time=0.076 ms
nginx2 PING nginx1:
root@ubuntu:~# docker exec -it nginx2 bash
[root@188b2a891bfc /]# ifconfig eth0
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.16.16.2 netmask 255.255.255.0 broadcast 172.16.16.255
ether 02:42:ac:10:10:02 txqueuelen 0 (Ethernet)
RX packets 31 bytes 2390 (2.3 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 5 bytes 378 (378.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
[root@188b2a891bfc /]# ping 192.168.16.2
PING 192.168.16.2 (192.168.16.2) 56(84) bytes of data.
64 bytes from 192.168.16.2: icmp_seq=1 ttl=63 time=0.415 ms
64 bytes from 192.168.16.2: icmp_seq=2 ttl=63 time=0.153 ms
64 bytes from 192.168.16.2: icmp_seq=3 ttl=63 time=0.155 ms