Docker网络
Docker网络
docker容器之间连接,之前我们使用的是link命令,但是这个命令其实已经不被官方所推荐,在以后新的版本中,也可能会被移除,这就有一个问题了?
问题:我们怎么打通容器之间的通信呢?
接下来,我们了解一下docker的网络,随之解决遇到的问题。
网络模式
docker 自身有四种网络工作模式以及一种自定义网络模式
安装Docker时,它会自动创建三个网络,bridge(创建容器默认连接到此网络)、 none 、host
默认网络模式 | 简介 | 设置方式 |
---|---|---|
bridge | 启动容器的默认网络模式,会为每一个容器都分配、设置IP等,将容器连接到虚拟网桥docker0,通过虚拟网桥以及Iptables nat表配置和宿主机通信 | --net=bridge 【默认】 |
host | 容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口。 | --net=host |
Container | 创建的容器不会创建自己的网卡,配置自己的IP,而是和一个指定的容器共享IP、端口范围。 | --net=container:NAME_or_ID |
none | 关闭了容器的网络功能 | --net=none |
查询网络模式命令:
docekr network ls
bridge 网络模式
相当于Vmware中的Nat模式,容器使用独立network Namespace,并连接到docker0虚拟网卡(默认模式)。通过docker0网桥以及Iptables nat表配置与宿主机通信;bridge模式是Docker默认的网络设置,此模式会为每一个容器分配Network Namespace、设置IP等,并将一个主机上的Docker容器连接到一个虚拟网桥上。下面着重介绍一下此模式。
理解:
bridge是docker默认的网络模式,在docker安装成功以后,会在主机上创建一个名为docker0的虚拟网桥,当我们启动docker容器的时候,并且未设置网络模式的情况下,容器会默认连接到docker0虚拟网桥,docker守护进程会创建一对对等虚拟设备接口 veth pair
,将其中一个接口设置为容器的 eth0
接口(容器的网卡),另一个接口放置在宿主机的命名空间中,以类似 vethxxx
这样的名字命名,从而将宿主机上的所有容器都连接到这个内部网络上。
docker0虚拟网桥,就像是物理交换机类似,在宿主机和容器之间起到交换数据作用。
整个通信流程是:
- Docker Daemon 利用 veth pair 技术,在宿主机上创建一对对等虚拟网络接口设备,假设为 veth0 和 veth1。而 veth pair 技术的特性可以保证无论哪一个 veth 接收到网络报文,都会将报文传输给另一方。
- Docker Daemon 将 veth0 附加到 Docker Daemon 创建的 docker0 网桥上。保证宿主机的网络报文可以发往 veth0;
- Docker Daemon 将 veth1 添加到 Docker Container 所属的 namespace 下,并被改名为 eth0。如此一来,宿主机的网络报文若发往 veth0,则立即会被 Container 的 eth0 接收,实现宿主机到 Docker Container 网络的联通性;同时,也保证 Docker Container 单独使用 eth0,实现容器网络环境的隔离性。
通过实际操作,我看一下通信的流转:
- 运行一个
nginx
镜像容器nginx01
,查看ip addr
:
# 启动nginx 容器
docker run -it --name nginx01 -p 80:80 nginx
# 查看 ip addr
ip addr
启动nginx01容器后,守护进创建了一对对等虚拟设备接口 veth pair
,将其中一个接口设置为容器的 eth0
接口(容器的网卡),另一个接口放置在宿主机的命名空间中,以类似 vethxxx
这样的名字命名。
守护进程还会从网桥 docker0
的私有地址空间中分配一个 IP 地址和子网给该容器,并设置 docker0 的 IP 地址为容器的默认网关。也可以安装 yum install -y bridge-utils
以后,通过 brctl show
命令查看网桥信息。
对于容器的相信网络信息我们可以通过docker inspect 容器ID查看:
docekr inspect nginx01
# 查看网络配置模块
"Networks": {
"bridge": {
"IPAMConfig": null,
"Links": null,
"Aliases": null,
"NetworkID": "2302db010e784147f4e8cb53e668ebfcd932394559fcfff3c65b079e5e9bf827",
"EndpointID": "bde65f813752db5877e77ae0fe49fcbf251b8168d27e8caf861568e3da0ae434",
"Gateway": "172.17.0.1", # 网关
"IPAddress": "172.17.0.2", # 容器IP
"IPPrefixLen": 16,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": "02:42:ac:11:00:02",
"DriverOpts": null
}
}
bridge
网络模式下的容器
docker network inspect bridge
[root@bogon ~]# docker network inspect bridge
[
{
"Name": "bridge", # 网络模式
"Id": "2302db010e784147f4e8cb53e668ebfcd932394559fcfff3c65b079e5e9bf827",
"Created": "2021-11-16T22:28:55.525405758-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": {
"36b76f065b823df2f9847acee0837ec5d3e21136d795f6e9af264ccb4108addd": {
"Name": "nginx01", # 该网络下的容器
"EndpointID": "bde65f813752db5877e77ae0fe49fcbf251b8168d27e8caf861568e3da0ae434",
"MacAddress": "02:42:ac:11:00:02",
"IPv4Address": "172.17.0.2/16", # 容器IP
"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": {}
}
]
注意:
- 在bridge模式下,连在同一网桥上的容器可以相互通信(若出于安全考虑,也可以禁止它们之间通信,方法是在DOCKER_OPTS变量中设置–icc=false,这样只有使用
–link
才能使两个容器通信[–link
官方不在推荐使用])。 - Docker可以开启容器间通信(意味着默认配置--icc=true),也就是说,宿主机上的所有容器可以不受任何限制地相互通信,这可能导致拒绝服务攻击。进一步地,Docker可以通过--ip_forward和--iptables两个选项控制容器间、容器和外部世界的通信。
host 网络模式
host网络模式相当于Vm中的桥接模式,与宿主机在同一个网络中,但没有独立IP地址。众所周知,Docker使用了Linux的Namespaces技术来进行资源隔离,如PID Namespace隔离进程,Mount Namespace隔离文件系统,Network Namespace隔离网络等。一个Network Namespace提供了一份独立的网络环境,包括网卡、路由、Iptable规则等都与其他的Network Namespace隔离。一个Docker容器一般会分配一个独立的Network Namespace。但如果启动容器的时候使用host模式,那么这个容器将不会获得一个独立的Network Namespace,而是和宿主机共用一个Network Namespace。容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口。
理解:
- host网络模式不是默认网络模式,需要通过在创建容器时通过参数
--net host
或者--network host
指定; - 通过host网络模式启动的docker容器,可以直接使用宿主机的 IP 地址与外界进行通信,若宿主机的 eth0 是一个公有 IP,那么容器也拥有这个公有 IP。同时容器内服务的端口也可以使用宿主机的端口,无需额外进行 NAT 转换;【这里需要注意,端口冲突问题】
- host 网络模式可以让容器共享宿主机网络栈,这样的好处是外部主机与容器直接通信,但是容器的网络缺少隔离性。
基于hsot网络模式启动nginx容器
[root@bogon ~]# docker run --name=nginx_host --net=host -p 80:80 -d nginx
WARNING: Published ports are discarded when using host network mode
3b579b9f40bb47c2ecd382b3b8a8842232caf6d086bbc3fc4093f32e807f8e08
[root@bogon ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
3b579b9f40bb nginx "/docker-entrypoint.…" 33 seconds ago Up 33 seconds nginx_host
查ip addr
ip addr
# 下图,可以看到并没有新增虚拟网卡设备
查nginx容器的网络inspect
docker inspect nginx_host
# 可以看到并没有给容器分配ip
"Networks": {
"host": {
"IPAMConfig": null,
"Links": null,
"Aliases": null,
"NetworkID": "00fa104f6ce11949adc376735ed4ceb61e25af56487832370faa1a7a0201f9f1",
"EndpointID": "22fceed66317ea06498b9df4c38818c222f35bbb797144877ee71ee9eb2e825c",
"Gateway": "",
"IPAddress": "",
"IPPrefixLen": 0,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": "",
"DriverOpts": null
}
}
查host
网络模式下的容器
docker network inspect host
# 当前我们 的nginx容器使用host网络模式
测试访问
curl localhost:80
# http://192.168.8.128/ 直接访问宿主机的ip即可
none 网络模式
-
none 网络模式是指禁用网络功能,只有 lo 接口 local 的简写,代表 127.0.0.1,即 localhost 本地环回接口。在创建容器时通过参数
--net none
或者--network none
指定; -
none模式将容器放置在它自己的网络栈中,但是并不进行任何配置。实际上,none模式关闭了容器的网络功能,在以下两种情况下是有用的:容器并不需要网络(例如只需要写磁盘卷的批处理任务)。
-
none 网络模式即不为 Docker Container 创建任何的网络环境,容器内部就只能使用 loopback 网络设备,不会再有其他的网络资源。可以说 none 模式为 Docke Container 做了极少的网络设定,但是俗话说得好“少即是多”,在没有网络配置的情况下,作为 Docker 开发者,才能在这基础做其他无限多可能的网络定制开发。这也恰巧体现了 Docker 设计理念的开放。
container 网络模式
-
container 模式指定新创建的容器和已经存在的一个容器共享一个Network Namespace,而不是和宿主机共享。新创建的容器不会创建自己的网卡,配置自己的IP,而是和一个指定的容器共享IP、端口范围等。同样,两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的。两个容器的进程可以通过lo网卡设备通信
-
Container 网络模式是 Docker 中一种较为特别的网络的模式。在创建容器时通过参数
--net container:已运行的容器名称|ID
或者--network container:已运行的容器名称|ID
指定; -
处于这个模式下的 Docker 容器会共享一个网络栈,这样两个容器之间可以使用 localhost 高效快速通信。
自定义网络模式
Docker 提供的默认网络使用比较简单,但是为了保证各容器中应用的安全性,在实际开发中更推荐使用自定义的网络进行容器管理,以及启用容器名称到 IP 地址的自动 DNS 解析。
从 Docker 1.10 版本开始,docker daemon 实现了一个内嵌的 DNS server,使容器可以直接通过容器名称通信。方法很简单,只要在创建容器时使用
--name
为容器命名即可。
但是使用 Docker DNS 有个限制:只能在 user-defined 网络中使用。也就是说,默认的 bridge 网络是无法使用 DNS 的,所以我们就需要自定义网络。
- Docker提供了创建这些网络的默认网络驱动程序,你可以创建一个新的Bridge网络,Overlay或Macvlan网络。你还可以创建一个网络插件或远程网络进行完整的自定义和控制
- 可以自动DNS解析容器名称到IP地址
- 创建任意数量的网络,并且可以在任何给定时间将容器连接到这些网络中的零个或多个网络
- 可以连接并断开网络中的运行容器,而无需重新启动容器。当容器连接到多个网络时,其外部连接通过第一个非内部网络以词法顺序提供
创建一个自定义网络
- 查看现有网络
docker network ls
# 现有网络
[root@bogon ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
2302db010e78 bridge bridge local
00fa104f6ce1 host host local
3a55f6933f38 none null local
- 创建网络 docker network create
docker network create new_network
# docker network create --driver bridge --subnet 192.168.1.0/16 --gateway 192.168.1.0 mynet
# --driver 网络模式 [默认bridge]
# --subnet 网段子网
# --gateway 网关
ERROR:守护进程无法创建新网络,由于,我在docker启动之后,关闭了宿主机的防火墙,造成的原因
解决:重启docker环境
上图,可以看到创建网络成功
注意:
-
docker network create
命令使用详情 -
网络模式有:bridge 、overlay 、MACVLAN
-
自定义网络也可以指定网关,网段,子网,但是不要和宿主机在同一个网段
- 查看自定义网络
docker inspect new_network
#
[
{
"Name": "new_network",
"Id": "1800a809a0f2bf16da20345a27df9ff79dc27f3108d505b03b4379e07872ed18",
"Created": "2021-11-17T01:18:36.838971866-08:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "172.18.0.0/16",
"Gateway": "172.18.0.1" # 网关
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {},
"Options": {},
"Labels": {}
}
]
- 启动nginx容器使用自定义网络
docker run -p 80:80 --name nginx01 --net new_network -d nginx
# -p 映射宿主机端口
# --neame 容器名
# --net 指定容器启动使用的网络 【这里我们使用自定义的网络】
# -d 后台运行
- 查看启动容器的IP
docker inspect nginx01
# 查看 Networks 模块
"Networks": {
"new_network": {
"IPAMConfig": null,
"Links": null,
"Aliases": [
"0bc7518d16b9"
],
"NetworkID": "1800a809a0f2bf16da20345a27df9ff79dc27f3108d505b03b4379e07872ed18",
"EndpointID": "740cdf7efb0d7bda920e8670e8fa5ffb446663774289e6eb1312f184ce93765f",
"Gateway": "172.18.0.1", # 网关
"IPAddress": "172.18.0.2", # 容器的IP地址
"IPPrefixLen": 16,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": "02:42:ac:12:00:02",
"DriverOpts": null
}
}
# 这里的IP网关也就是我们创建网络的网关
- 启动第二个nginx容器使用自定义网络
docker run -p 81:80 --name nginx02 --net new_network -d nginx
- 查看自定义网络下的容器
docker inspect new_network
可以看到二个nginx容器一个都在自定义网络下了
- ping 测试
# ping ip IP能正常ping通
[root@bogon /]# ping 172.18.0.2
PING 172.18.0.2 (172.18.0.2) 56(84) bytes of data.
64 bytes from 172.18.0.2: icmp_seq=1 ttl=64 time=0.328 ms
64 bytes from 172.18.0.2: icmp_seq=2 ttl=64 time=0.061 ms
64 bytes from 172.18.0.2: icmp_seq=3 ttl=64 time=0.037 ms
64 bytes from 172.18.0.2: icmp_seq=4 ttl=64 time=0.045 ms
64 bytes from 172.18.0.2: icmp_seq=5 ttl=64 time=0.103 ms
64 bytes from 172.18.0.2: icmp_seq=6 ttl=64 time=0.031 ms
[1]+ 已停止 ping 172.18.0.2
[root@bogon /]# ping 172.18.0.3
PING 172.18.0.3 (172.18.0.3) 56(84) bytes of data.
64 bytes from 172.18.0.3: icmp_seq=1 ttl=64 time=0.062 ms
64 bytes from 172.18.0.3: icmp_seq=2 ttl=64 time=0.098 ms
64 bytes from 172.18.0.3: icmp_seq=3 ttl=64 time=0.102 ms
64 bytes from 172.18.0.3: icmp_seq=4 ttl=64 time=0.039 ms
64 bytes from 172.18.0.3: icmp_seq=5 ttl=64 time=0.096 ms
[2]+ 已停止 ping 172.18.0.3
[root@bogon /]#
- 进入容器nginx01,测试ping容器02
# 进入容器nginx01
docker exec -it nginx01 /bin/bash
# 安装ping工具 docker镜像只保留核心命令
apt update && apt install iputils-ping
# 安装完成 ping 容器2
root@0bc7518d16b9:/# ping nginx02
PING nginx02 (172.18.0.3) 56(84) bytes of data.
64 bytes from nginx02.new_network (172.18.0.3): icmp_seq=1 ttl=64 time=0.114 ms
64 bytes from nginx02.new_network (172.18.0.3): icmp_seq=2 ttl=64 time=0.121 ms
64 bytes from nginx02.new_network (172.18.0.3): icmp_seq=3 ttl=64 time=0.245 ms
64 bytes from nginx02.new_network (172.18.0.3): icmp_seq=4 ttl=64 time=0.073 ms
64 bytes from nginx02.new_network (172.18.0.3): icmp_seq=5 ttl=64 time=0.094 ms
不使用link命令即可实现容器通信,这也是我们在实际常使用自定义网络的原因之一
- 使用默认网络启动新nginx容器03
# 使用默认网络启动nginx03
docker run -p 82:80 --name nginx03 -d nginx
# 查ip
"Networks": {
"bridge": {
"IPAMConfig": null,
"Links": null,
"Aliases": null,
"NetworkID": "989e32212a22d0958ca0d854b662555339333f20ea2ce463f29c8919c119f0c5",
"EndpointID": "1985f0b431527548739d133fef18eb4cbc6fc3943ba2f936be5d1a33c63fc09d",
"Gateway": "172.17.0.1",
"IPAddress": "172.17.0.2", # 可以看到这与自定义的网络不在同一个网段
"IPPrefixLen": 16,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": "02:42:ac:11:00:02",
"DriverOpts": null
}
}
- 测试nginx01中ping容器03
root@0bc7518d16b9:/# ping nginx02 #测试同一个网段02容器可以正常通信
PING nginx02 (172.18.0.3) 56(84) bytes of data.
64 bytes from nginx02.new_network (172.18.0.3): icmp_seq=1 ttl=64 time=0.119 ms
64 bytes from nginx02.new_network (172.18.0.3): icmp_seq=2 ttl=64 time=0.135 ms
^Z
[1]+ Stopped ping nginx02
root@0bc7518d16b9:/# ping nginx03 #测试不同网段03容器不可以正常通信
ping: nginx03: Temporary failure in name resolution
root@0bc7518d16b9:/#
问题:那么我们如何让不在同一个网段的容器贯通呢?
- docker network connect
# 使用dockr network connect 贯通两个不在同一个网段的容器
# 将容器三放到与容器1和2的同一个网段即可,也就是我们自定义的网络
docker network connect new_network ngin03
# 查看自定义网络
docker inspect new_network
再次到nginx01中 ping容器03
可以看到,此时,容器都在一个网段就可以通信了
总结:一个容器两个ip地址;与阿里云服务,公网ip 私网ip类似
- 移除网络
docker network rm
# 主要注意的是:如果通过某个自定义网络模式创建了容器,则该网络模式无法删除