Docker中的容器网络和通信原理详解
作者:Sakura,.
Docker 容器网络和通信原理
1. Docker 默认的网络模型
Docker Host : 安装了 docker deamon 的主机
docker0 : 安装了 docker 之后出现的网桥
- 通过网桥可以将 Linux 支持的不同的端口连接起来
- 实现类交换机多对多的通信
veth pair
- 虚拟以太网(Ethernet)设备
- 成对出现,用于解决网络命名空间之间的隔离
- 一端连接 Container network namespace,另一端连接 host network namespace
当创建了一个容器之后 , 会在主机中创建一个虚拟的以太网设备 ( veth )
一端连接主机的网络的命名空间
一端连接容器的网络命名空间
启动一个容器,查看容器的网络设备
docker run -it centos:latest bash
可以看到容器有自己的网络设备,这是连接容器网络命名空间的一端
按住ctrl+p+q
退回容器,使容器在后台运行
这个虚拟的网络设备就是连接 docker host 网络命名空间的
2. Docker 网络模型工作原理
2.1. 容器访问外网
容器中的数据包转发给 eth0 网卡,然后在转发到 docker host 命名空间的虚拟网络设备,虚拟的网络设备会把数据传给 docker0, 然后再转发给 eth0 ( 物理网卡 ),
SNAT 源地址转换:把 docker0 的源地址转换成 docker host 主机所在网段的 IP 地址
iptables -t nat -vnL POSTROUTING
MASQUERADE 就是 SNAT ,底层转发是由 iptables 来承载的
2.2 外网访问容器
首先外网的数据包能够传送到 eth0 网卡,内核接收到数据之后会进行 DNAT (目标地址转换)
所以容器的 port 就是: 但访问宿主主机的 3306 端口,会被转发到容器的某个端口
可以通过 iptables 看到 docker 链
iptables -t nat -nL
不管哪个 IP , 不管目标地址是哪个,只要访问对应端口,就转换到 172.17… 的对应端口
3. Docker 四种网络模型
- bridge [ 桥接式网络 ( Bridge container A ) ]:–network bridge(默认网络模型)
桥接容器,除了有一块本地回环接口 ( Loopback interface ) 外,还有一块私有接口 ( Private interface ) 通过容器虚拟接口 ( Container virtual interface ) 连接到桥接虚拟接口 ( Docker bridge virtual interface ),之后通过逻辑主机接口 ( Logical host interface ) 连接到主机物理网络 ( Physical network interface )。桥接网卡默认会分配到 172.17.0.0/16 的IP地址段。如果我们在创建容器时没有指定网络模型,默认就是 ( Nat ) 桥接网络,这也就是为什么我们在登录到一个容器后,发现 IP 地址段都在 172.17.0.0/16 网段的原因。
容器通过虚拟以太网设备连接到主机的网络命名空间,通过主机的命名空间实现对外网的访问
- host [ 开放式容器 ( Open container ) ]:–network host
比联盟式网络更开放,联盟式网络是多个容器共享网络 ( Net ) , 而开放式容器 ( Open contaner ) 就直接共享了宿主机的名称空间。因此物理网卡有多少个,那么该容器就能看到多少网卡信息。我们可以说 Open container 是联盟式容器的衍生。
让容器和主键共享同一个网络命名空间,容器中的 IP 地址和 Docker host IP 地址是完全一直的,有利于对容器服务的访问
- none [封闭式网络(Closed container)]:–network none
封闭式容器,只有本地回环接口 ( Loopback interface ) ,和服务器看到的 lo 接口类似),无法与外界进行通信。
这种网络模式中只有 Loopback 这种网路接口,不能去连接外网
- container [联盟式网络 ( Joined container A | Joined container B ]:–network container:c1(容器名称或容器ID)
每个容器都各有一部分名称空间 ( Mount , PID , User ),另外一部分名称空间是共享的( UTS , Net , IPC ) 。由于它们的网络是共享的,因此各个容器可以通过本地回环接口 ( Loopback interface ) 进行通信。除了共享同一组本地回环接口 ( Loopback interface ) 外,还有一块一块私有接口 ( Private interface ) 通过联合容器虚拟接口 ( Joined container virtual interface )连接到桥接虚拟接口 ( Docker bridge virtual interface ),之后通过逻辑主机接口 ( Logical host interface ) 连接到主机物理网络 ( Physical network interface )。
可以共享两个容器的命名空间
4. 查看指定类型的网络模型
4.1 查看所有容器的网络模型
# 查看已有的网络模型 docker network ls docker netword list
4.2 查看指定网络模型的详细信息
root@VM-8-13-debian:~# docker network inspect bridge [ { "Name": "bridge", "Id": "6e7985ab54cdd0f9143da410b03f5e0df82146d3e0256fee573e6d3c64de4df5", "Created": "2023-10-12T18:42:05.876320519+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": { "420df0739e938ad67819430697329aa0cd0b9dc83be296995bfdf3a90b74b6e7": { "Name": "etcd", "EndpointID": "52e9e6d2601301c87c5dcaa2d8cfd47e2da9317692d2572e201207e98ca03a3c", # 容器的IP地址和MAC地址 "MacAddress": "02:42:ac:11:00:02", "IPv4Address": "172.17.0.2/16", "IPv6Address": "" }, "e01f2b3407cb1962b15630623e7d4ecd1ac2e40e256e3d2d87390dd31ef38985": { "Name": "quizzical_nobel", "EndpointID": "560ab4c4cf83a011fdeec12e2ac3e3b0c4252a7cb81a5ae115067ba82eea9dfb", "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", # 是否开启上网功能(源地址转换 SNAT) "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": {} } ]
4.3 查看 docker 支持的网络模型
root@VM-8-13-debian:~# docker info | grep Network Network: bridge host ipvlan macvlan null overlay
5. 创建指定类型的网络模型
5.1 bridge
- 查看帮助
docker network create -h
- 创建网络模型
# 1.简单写法 docker network create Mysql # 2.复杂写法 docker network create -d bridge --subnet "192.168.100.0/24" --gateway "192.168.100.1" -o com.docker.network.bridge.name=docker1 Mysql # com.docker.network.bridge.name 就是上面查看网络模型选项信息里面的内容
- 验证是否创建成功
root@VM-8-13-debian:~# docker network ls NETWORK ID NAME DRIVER SCOPE b14e183c0386 1panel-network bridge local 136c9c1912fc Mysql bridge local d60f5e7ed7e9 backend_default bridge local 6e7985ab54cd bridge bridge local affe2ac83252 host host local 20037e8c62b7 kafka_default bridge local df9f2ca51637 logs_default bridge local aeecc2524ba4 none null local # 同时也可以使用 docker network inspect Mysql查看详细信息
- 启动一个容器连接到创建的网络
# docker run -it --network Mysql busybox / # ping wwww.baidu.com PING wwww.baidu.com (110.242.68.66): 56 data bytes 64 bytes from 110.242.68.66: seq=0 ttl=250 time=10.616 ms 64 bytes from 110.242.68.66: seq=1 ttl=250 time=10.601 ms 64 bytes from 110.242.68.66: seq=2 ttl=250 time=10.595 ms --- wwww.baidu.com ping statistics --- 11 packets transmitted, 11 packets received, 0% packet loss round-trip min/avg/max = 10.589/10.612/10.628 ms
可以看出是可以正常 ping 通的
5.2 host
docker run -it --network host --rm busybox # 启动Nginx 服务 docker run -d --network host nginx:latest
# 查看容器运行状态 # docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES f6677b213271 nginx:latest "/docker-entrypoint.…" 7 seconds ago Up 6 seconds youthful_shtern
# 查看docker host 80端口状态 # ss -anput | grep ":80" tcp LISTEN 0 511 *:80 *:* users:(("nginx",pid=42866,fd=7),("nginx",pid=42826,fd=7)) tcp LISTEN 0 511 :::80 :::* users:(("nginx",pid=42866,fd=8),("nginx",pid=42826,fd=8))
host 网络模型用的是和宿主主机同一个命名空间
缺点: 无法启动第二个这样的容器 ( 端口很稀缺 )
5.3 none
root@VM-8-13-debian:~# docker run -it --network none busybox / # ifconfig lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 UP LOOPBACK RUNNING MTU:65536 Metric:1 RX packets:0 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
可以看到是没有网络的,如果连接了只能在本地做一下测试
如果需要有更多的网络,可以创建虚拟的网络设备
5.4 联盟网络
- 创建c1容器,使用默认网络模型
root@VM-8-13-debian:~# docker run -it --name c1 --rm busybox:latest / # ifconfig eth0 Link encap:Ethernet HWaddr 02:42:AC:11:00:03 inet addr:172.17.0.3 Bcast:172.17.255.255 Mask:255.255.0.0 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:6 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:516 (516.0 B) TX bytes:0 (0.0 B) lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 UP LOOPBACK RUNNING MTU:65536 Metric:1 RX packets:0 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
- 创建 c2 容器,与 c1 容器共享网络命名空间
root@VM-8-13-debian:~# docker run -it --name c2 --network container:c1 --rm busybox:latest root@VM-8-13-debian:~# docker run -it --name c2 --network container:c1 --rm busybox:latest / # ifconfig eth0 Link encap:Ethernet HWaddr 02:42:AC:11:00:03 inet addr:172.17.0.3 Bcast:172.17.255.255 Mask:255.255.0.0 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:11 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:866 (866.0 B) TX bytes:0 (0.0 B) lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 UP LOOPBACK RUNNING MTU:65536 Metric:1 RX packets:0 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
可以看到两者的虚拟网卡一模一样
# 在c2容器中创建文件并开启httpd服务 echo "hello world" >> /tmp/index.html ls /tmp index.html httpd -h /tmp # 验证80端口是否打开 / # netstat -npl Active Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 :::80 :::* LISTEN 10/httpd # 在c1容器中进行访问验证 docker exec c1 wget -O - -q 127.0.0.1 hello world
网络共享,文件系统 独立
6. 跨 Docker Host 容器间通信实现
也就是一台主机中的 docker 容器和另一台主机中的 docker 容器在不使用 host 网络模型的情况下如何通信
这个之后有时间我再慢慢写
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。