Docker在安装后自动提供3种网络,可以使用docker network ls
命令查看
1 [root@localhost ~]# docker network ls # 还有一种容器模式看不到 2 NETWORK ID NAME DRIVER SCOPE 3 acd31ac4f7db bridge bridge local # 桥接 4 77faf4b77bb6 host host local # 主机模式 5 f6caf930adf9 none null local # 孤岛模式
Docker使用Linux桥接,在宿主机虚拟一个Docker容器网桥(docker0),Docker启动一个容器时会根据Docker网桥的网段分配给容器一个IP地址,称为Container-IP,同时Docker网桥是每个容器的默认网关。因为在同一宿主机内的容器都接入同一个网桥,这样容器之间就能够通过容器的Container-IP直接通信。
网络模式 | 配置 | 说明 |
---|---|---|
host(主机模式) | --network host | 容器和宿主机共享Network namespace(就是容器用真机的ip) |
container (容器模式) | --network container:NAME_OR_ID | 容器和另外一个容器共享Network namespace(共用一个ip) |
none(孤岛模式) | --network none | 容器有独立的Network namespace,(没有ip,只有一个lo网卡,只是用来测试协议,不能通信) 但并没有对其进行任何网络设置, 如分配veth pair 和网桥连接,配置IP等 |
bridge (桥接模式) | --network bridge | 默认模式(没有指定模式,默认就使用这个) |
当Docker进程启动时,会在主机上创建一个名为docker0的虚拟网桥,此主机上启动的Docker容器会连接到这个虚拟网桥上。虚拟网桥的工作方式和物理交换机类似,这样主机上的所有容器就通过交换机连在了一个二层网络中。
从docker0子网中分配一个IP给容器使用,并设置docker0的IP地址为容器的默认网关。在主机上创建一对虚拟网卡veth pair设备,Docker将veth pair设备的一端放在新创建的容器中,并命名为eth0(容器的网卡),另一端放在主机中,以vethxxx这样类似的名字命名,并将这个网络设备加入到docker0网桥中。可以通过brctl show命令查看。
1 3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 2 link/ether 02:42:e7:69:ef:af brd ff:ff:ff:ff:ff:ff 3 inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0 4 valid_lft forever preferred_lft forever 5 inet6 fe80::42:e7ff:fe69:efaf/64 scope link 6 valid_lft forever preferred_lft forever 7 8 28: eth0@if29: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue 9 29: vethfe192be@if28: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default 10 # 没有指定模式,运行容器默认是桥接模式,在主机上创建一个docker0网桥,并设置docker0的IP172.17.0.1地址为容器的默认网关,从docker0子网中分配一个IP给容器使用,创建一对虚拟网卡,一个放在新创建的 容器上eth0@if29,一端放在主机中vethfe192be@if28,并将两个网卡加入到docker0网桥中,从而使得达到通信的目的,退出容器,虚拟的网卡就会消失掉。
1 [root@localhost ~]# ip a 2 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 3 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 4 inet 127.0.0.1/8 scope host lo 5 valid_lft forever preferred_lft forever 6 inet6 ::1/128 scope host 7 valid_lft forever preferred_lft forever 8 2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000 9 link/ether 00:0c:29:bc:11:bf brd ff:ff:ff:ff:ff:ff 10 inet 192.168.149.130/24 brd 192.168.149.255 scope global noprefixroute ens33 11 valid_lft forever preferred_lft forever 12 3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 13 link/ether 02:42:e7:69:ef:af brd ff:ff:ff:ff:ff:ff 14 inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0 15 valid_lft forever preferred_lft forever 16 inet6 fe80::42:e7ff:fe69:efaf/64 scope link 17 valid_lft forever preferred_lft forever 18 21: vethd5e32c4@if20: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default 19 link/ether 06:70:7e:78:9a:18 brd ff:ff:ff:ff:ff:ff link-netnsid 0 20 inet6 fe80::470:7eff:fe78:9a18/64 scope link 21 valid_lft forever preferred_lft forever 22 27: veth777bd37@if26: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default 23 link/ether 06:b2:5a:14:da:22 brd ff:ff:ff:ff:ff:ff link-netnsid 1 24 inet6 fe80::4b2:5aff:fe14:da22/64 scope link 25 valid_lft forever preferred_lft forever 26 31: vethbfd6f79@if30: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default 27 link/ether 2e:60:4e:1e:59:68 brd ff:ff:ff:ff:ff:ff link-netnsid 2 28 inet6 fe80::2c60:4eff:fe1e:5968/64 scope link 29 valid_lft forever preferred_lft forever 30 [root@localhost ~]# brctl show 31 bridge name bridge id STP enabled interfaces 32 docker0 8000.0242e769efaf no veth777bd37 # veth777bd37这个网卡桥接到docker0上去了 33 vethbfd6f79 34 vethd5e32c4
1 [root@localhost ~]# docker run -d --name web -p 8080:80 httpd 2 e17ca083887cb26740069cb625509afc39915f05a9a9d9e59524121de01b057e # 在后台运行一个叫web的httpd容器,做下端口号映射8080是真机的端口号,80是容器里用的 3 [root@localhost ~]# docker port web 4 80/tcp -> 0.0.0.0:8080 # 把容器的80映射到真机的8080,也可以说是把真机的8080转发到容器的80 5 80/tcp -> :::8080 6 [root@localhost ~]# iptables -t nat -vnL # 查看规则, 防火墙要打开 7 Chain DOCKER (2 references) 8 pkts bytes target prot opt in out source destination 9 0 0 RETURN all -- docker0 * 0.0.0.0/0 0.0.0.0/0 10 2 104 DNAT tcp -- !docker0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:8080 to:172.17.0.2:80
bridge模式是docker的默认网络模式,不写--network参数,就是bridge模式。使用docker run -p时,docker实际是在iptables做了DNAT规则,实现端口转发功能。可以使用iptables -t nat -vnL查看。
bridge模式如下图所示:
节点上有三个容器,分别有自己配套的网卡(veth对应eth0相互连接),桥接到docker0的网桥上,他们都在一个网段,链接在一起可以通信,容器1,2,3的网关都指向172.17.0.1,
172.17.0.1和真实网卡在同一台主机上面通过ip转发功能就可以出去
假设上图的docker2中运行了一个nginx,大家来想几个问题:
1 [root@localhost ~]# docker ps 2 CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 3 e17ca083887c httpd "httpd-foreground" 30 minutes ago Up 30 minutes 0.0.0.0:8080->80/tcp, :::8080->80/tcp web 4 [root@localhost ~]# docker run -it --rm busybox /bin/sh 5 / # ip a 6 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000 7 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 8 inet 127.0.0.1/8 scope host lo 9 valid_lft forever preferred_lft forever 10 36: eth0@if37: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue 11 link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff 12 inet 172.17.0.3/16 brd 172.17.255.255 scope global eth0 13 valid_lft forever preferred_lft forever 14 / # wget -O - 172.17.0.2 # 0.3访问0.2成功 15 Connecting to 172.17.0.2 (172.17.0.2:80) 16 writing to stdout 17 <html><body><h1>It works!</h1></body></html> 18 - 100% |***********************************************************************************************************| 45 0:00:00 ETA 19 written to stdout
Docker网桥是宿主机虚拟出来的,并不是真实存在的网络设备,外部网络是无法寻址到的,这也意味着外部网络无法通过直接Container-IP访问到容器。如果容器希望外部访问能够访问到,可以通过映射容器端口到宿主主机(端口映射),即docker run创建容器时候通过 -p 或 -P 参数来启用,访问容器的时候就通过[宿主机IP]:[容器端口]访问容器。
这个模式指定新创建的容器和已经存在的一个容器共享一个 Network Namespace,而不是和宿主机共享。新创建的容器不会创建自己的网卡,配置自己的 IP,而是和一个指定的容器共享 IP、端口范围等。同样,两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的。两个容器的进程可以通过 lo 网卡设备通信。
container模式如下图所示:
如果启动容器的时候使用host模式,那么这个容器将不会获得一个独立的Network Namespace,而是和宿主机共用一个Network Namespace。容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口。但是,容器的其他方面,如文件系统、进程列表等还是和宿主机隔离的。
使用host模式的容器可以直接使用宿主机的IP地址与外界通信,容器内部的服务端口也可以使用宿主机的端口,不需要进行NAT,host最大的优势就是网络性能比较好,但是docker host上已经使用的端口就不能再用了,网络的隔离性不好。
Host模式如下图所示:
使用none模式,Docker容器拥有自己的Network Namespace,但是,并不为Docker容器进行任何网络配置。也就是说,这个Docker容器没有网卡、IP、路由等信息。需要我们自己为Docker容器添加网卡、配置IP等。
这种网络模式下容器只有lo回环网络,没有其他网卡。none模式可以在容器创建时通过--network none来指定。这种类型的网络没有办法联网,封闭的网络能很好的保证容器的安全性。
应用场景:
none模式如下图所示:
docker network inspect bridge #查看bridge网络的详细配置
1 [root@localhost ~]# docker network --help 2 3 Usage: docker network COMMAND 4 5 Manage networks 6 7 Commands: 8 connect Connect a container to a network # 连接网络 9 create Create a network # 创建网络 10 disconnect Disconnect a container from a network # 断开连接网络 11 inspect Display detailed information on one or more networks # 显示网络详细信息 12 ls List networks # 列出网络 13 prune Remove all unused networks # 移除没有使用的网络 14 rm Remove one or more networks # 移除网络 15 [root@localhost ~]# docker network inspect bridge # 查看桥接网络模式的信息 16 [ 17 { 18 "Name": "bridge", 19 "Id": "acd31ac4f7db9b56975549cd6188fa988daa3eb22d05466c5e11e2e4e867219b", 20 "Created": "2022-04-26T16:36:15.290732096+08:00", 21 "Scope": "local", 22 "Driver": "bridge", 23 "EnableIPv6": false, 24 "IPAM": { 25 "Driver": "default", 26 "Options": null, 27 "Config": [ 28 { 29 "Subnet": "172.17.0.0/16", 30 "Gateway": "172.17.0.1" 31 } 32 ] 33 }, 34 "Internal": false, 35 "Attachable": false, 36 "Ingress": false, 37 "ConfigFrom": { 38 "Network": "" 39 }, 40 "ConfigOnly": false, 41 "Containers": {}, 42 "Options": { 43 "com.docker.network.bridge.default_bridge": "true", 44 "com.docker.network.bridge.enable_icc": "true", 45 "com.docker.network.bridge.enable_ip_masquerade": "true", 46 "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0", 47 "com.docker.network.bridge.name": "docker0", 48 "com.docker.network.driver.mtu": "1500" 49 }, 50 "Labels": {} 51 } 52 ]