一、Docker默认的网络通信
1.1 Docker安装后默认的网络配置
Docker服务安装完成之后,默认在每个宿主机会生成一个名称为docker0的网卡其IP地址都是 172.17.0.1/16。
1.2 创建容器后的网络配置
每次新建容器后:
- 宿主机就会多了一个虚拟网卡,和容器的网卡组合成一个网卡,比如:27: veth7a2c992@if26,而在容器内的网卡名为26,可以看出和宿主机的网卡之间的关联
- 容器会自动获取一个172.17.0.0/16网段的随机地址,默认从172.17.0.2开始,第二个容器为 172.17.0.3,以此类推
- 容器获取的地址并不固定。每次容器重启,可能会发生地址变化
创建容器后,宿主机多了个新网卡
docker run -d --name test-web01 -p 81:80 centos.7.9.2009-nginx:1.22.1
查看新建容器后的桥接状态
创建容器,容器会自动获取ip地址
1.3 容器间的通信
默认情况下,同一个宿主机的不同容器之间可以相互通信。不同宿主机之间的容器IP地址重复,默认不能相互通信。
在docker.service配置文件中的fd://后面添加--icc=false 选项可以禁止同一个宿主机的不同容器间通信。
vim /lib/system/system/docker.service
…
ExecStart=/usr/bin/dockerd -H fd:// --cnotallow=/run/containerd/containerd.sock --icc=false
…
systemctl daemon-reload && systemctl restart docker
在同一宿主机创建俩容器,最后无法通信
二、容器名称互联
新建容器时,docker会自动分配容器名称,容器ID和IP地址,导致容器名称,容器ID和IP都不固定,那么如何区分不同的容器,实现和确定目标容器的通信呢?解决方案是给容器起个固定的名称,容器之间通过固定名称实现确定目标的通信。
有两种固定名称的方法: 容器名称;容器名称的别名。
注意: 两种方式都最少需要两个容器才能实现
2.1 通过容器名称互联
2.1.1 容器名称介绍
即在同一个宿主机上的容器之间可以通过自定义的容器名称相互访问,比如:一个业务前端静态页面是使用nginx,动态页面使用的是tomcat,另外还需要负载均衡调度器,如::haproxy 对请求调度至nginx 和tomcat的容器,由于容器在启动的时候其内部IP地址是DHCP 随机分配的,而给容器起个固定的名称,则是相对比较固定的,因此比较适用于此场景。
注意: 如果被引用的容器地址变化,必须重启当前容器才能生效
2.1.2 容器名称实现
docker run 创建容器,可使用--link选项实现容器名称的引用,其本质就是在容器内的/etc/hosts中添加--link后指定的容器的IP和主机名的对应关系,从而实现名称解析。
格式:
docker run --name #先创建指定名称的容器
docker run --link #再创建容器时引用上面容器的名称
创建指定名称的容器。由于刚才已创建,在新建第二个容器时引用第一个容器的名称即可。创建容器时会自动将第一个容器的名称加入/etc/hosts文件,从而可以利用第一个容器名称进行访问。
docker run -d --name test-web02 --link test-web01 centos.7.9.2009-nginx:1.22.1
2.2 通过自定义容器别名互联
2.2.1 容器别名介绍
自定义的容器名称可能后期会发生变化,一旦名称发生变化,容器内程序之间也必须要随之发生变化,比如:程序通过固定的容器名称进行服务调用,但是容器名称发生变化之后再使用之前的名称肯定是无法成功调用,每次都进行更改的话又比较麻烦,因此可以使用自定义别名的方式解决,即容器名称可以随意更改,只要不更改别名即可。
2.2.2 容器别名实现
#先创建指定名称的容器
docker run --name <容器名称>
docker run --name <容器名称> --link <目标容器名称>:"<容器别名1> <容器别名2> ..."
给上面创建的容器起别名,来创建新容器
docker run -d --name test-web04 -p 84:80 --link test-web01:test-web01-alias centos.7.9.2009-nginx:1.22.1
三、Docker网络连接模式
Docker 的网络支持5种网络模式: none、bridge、host、container和network-name。
默认新建的容器使用Bridge模式,创建容器时,docker run 命令使用以下选项指定网络模式:
docker run --network <mode>
docker run --net=<mode>
<mode>: 可是以下值:
• none
• bridge
• host
• container:<容器名或容器ID>
查看默认的网络模式,发现有三个
3.1 Bridge网络模式
3.1.1 Bridge网络模式架构
本模式是Docker的默认模式,即不指定任何模式就是Bridge模式,也是使用比较多的模式,此模式创建的容器会为每一个容器分配自己的网络 IP 等信息,并将容器连接到一个虚拟网桥与外界通信。可以和外部网络之间进行通信,通过SNAT访问外网,使用DNAT可以让容器被外部主机访问,所以此模式也称为NAT模式。此模式宿主机需要启动ip_forward功能。
Bridge网络模式特点如下:
- 网络资源隔离:不同宿主机的容器无法直接通信,各自使用独立网络
- 无需手动配置:容器默认自动获取172.17.0.0/16的IP地址,此地址可以修改
- 可访问外网:利用宿主机的物理网卡,SNAT连接外网
- 外部主机无法直接访问容器:可以通过配置DNAT接受外网的访问
- 性能较低:因为可通过NAT,网络转换带来更的损耗
- 端口管理繁琐:每个容器必须手动指定唯一的端口,容器产生端口冲突
要想查看Bridge模式的详细信息,可用docker network inspect bridge命令查看。此外,安装Docker后会默认启用ip_forward。
3.1.2 修改默认的 bridge 模式网络配置
有两种方法均可修改默认的bridge 模式的网络配置,但两种方式只能选一种,否则会导致冲突,docker服务无法启动。
方法1: docker.service修改桥接地址
vim /lib/systemd/system/docker.service
…
ExecStart=/usr/bin/dockerd -H fd:// --cnotallow=/run/containerd/containerd.sock --bip=172.18.0.1/16
…
systemctl daemon-reload && systemctl restart docker
此后再创建容器就会为容器分配172.18网段的ip。第一个容器ip为172.18.0.2,第二个容器ip为172.18.0.3,以此类推。
方法2: daemon.json修改桥接地址
vim /etc/docker/daemon.json
{
"registry-mirrors": ["https://mirror.ccs.tencentyun.com"],
"bip": "172.17.0.1/16",
"dns": ["8.8.8.8"]
}
systemctl daemon-reload && systemctl restart docker
3.2 Host网络模式
如果指定host模式启动容器,那么新创建的容器不会创建自己的虚拟网卡,而是直接使用(共享)宿主机的网卡和IP地址,因此在容器里面查看到的IP信息就是宿主机的信息,访问容器的时候直接使用宿主机IP+容器端口即可,不过容器内除网络以外的其它资源(eg:文件系统、系统进程等)仍然和宿主机保持隔离。
此模式由于直接使用宿主机的网络无需转换,网络性能最高,但是各容器内使用的端口不能相同,适用于运行容器端口比较固定的业务。
Host 网络模式特点:
- 使用参数--network host 指定
- 共享宿主机网络
- 网络性能无损耗
- 网络故障排除相对简单
- 各容器网络无隔离
- 网络资源无法分别统计
- 端口管理困难,容易产生端口冲突
- 不支持端口映射
先创建一个host模式的nginx容器,之后宿主机的80端口打开,nginx容器也能通过宿主机ip访问
之后再创建一个nginx容器就会出现端口冲突的情况,也不能用端口映射。因为都是共用宿主机的80端口
3.3 none网络模式
在使用none 模式后,Docker 容器不会进行任何网络配置,没有网卡、没有IP也没有路由,因此默认无法与外界通信,需要手动添加网卡配置IP等,所以极少使用。
none网络模式特点:
- 使用参数 --network none 指定
- 默认无网络功能,无法和外部通信
- 无法实现端口映射
- 适用于测试环境
3.4 Container网络模式
使用此模式创建的容器需指定和一个已经存在的容器共享一个网络,而不是和宿主机共享网络,新创建的容器不会创建自己的网卡也不会配置自己的IP,而是和一个被指定的已经存在的容器共享IP和端口范围,因此这个容器的端口不能和被指定容器的端口冲突,除了网络之外的文件系统、进程信息等仍然保持相互隔离,两个容器的进程可以通过lo网卡进行通信。
简单的说,就是第一个容器使用的网络模式决定了第二个容器使用的网络模式。比如第一个容器用Bridge网络模式,则第二个容器也用Bridge网络模式。
Container 模式特点:
- 使用参数 –-network container:名称或ID 指定
- 与宿主机网络空间隔离
- 容器间共享网络空间
- 适合频繁的容器间的网络通信
- 直接使用对方的网络,较少使用
eg:基于容器模式实现wordpress的部署
docker run -d -p 81:80 --name wordpress -v /data/wordpess:/var/www/html wordpress:php8.1-apache
docker run --network container:wordpress -e MYSQL_ROOT_PASSWORD=123456 -e MYSQL_DATABASE=wordpress -e MYSQL_USER=wordpress -e MYSQL_PASSWORD=123456 --name mysql -d -v /data/mysql:/var/lib/mysql mysql:8.0.32
3.5 自定义网络模式
自定义网络模式,类似于划分子网,实现不同集群应用的独立网络管理且互不影响。而且在同一个网络内,可以直接利用容器名相互访问,无需使用--link,非常便利。
语法:docker network COMMAND
Commands:
• connect 将容器连接到网络
• create 创建网络
• disconnect断开容器与网络的连接
• inspect 显示一个或多个网络的详细信息
• ls 列出网络列表
• prune 删除所有未使用的网络
• rm 删除一个或多个网络
#创建自定义网络。注意mode不支持host和none,默认是bridge模式
docker network create -d <mode> --subnet <CIDR> --gateway <网关> <自定义网络名称>
#查看自定义网络信息
docker network inspect <自定义网络名称或网络ID>
#引用自定义网络
docker run --network <自定义网络名称> <镜像名称>
#删除自定义网络。注意:内置的三个网络bridge、host、none无法删除
doccker network rm <自定义网络名称或网络ID>
3.5.1 自定义网络实战
3.5.1.1 创建自定义网络
docker network create -d bridge --subnet 172.27.0.0/16 --gateway 172.27.0.1 test-net
此时会新添加一个虚拟网卡和网桥。由于尚未利用自定义网络创建容器,故而用brctl show查看新的虚拟网卡对应的interfaces显示为空。
3.5.1.2 利用自定义网络创建容器
docker run -d --network test-net -p 82:80 --name test-web01 centos.7.9.2009-nginx:1.22.1
3.5.1.3 自定义网络中的容器间通信
docker run -it --network test-net --name alpine alpine:3.17.2 sh
#进入容器后执行
ping -c 2 test-web01
3.6 同一个宿主机之间不同网络的容器通信
开两个容器,一个使用默认bridge网络的容器(以172.17网段为例),一个是使用自定义网络的容器(网络名称:test-net,以172.27网段为例),默认因iptables规则导致无法通信。
#第一台宿主机执行
docker run --it --name test1 alpine sh
#第二台宿主机执行
docker run –it –name test2 –network test-net alpine sh
方法1:修改iptables实现同一宿主机上的不同网络的容器间通信
iptables-save > iptables.rule
vim iptables.rule
#修改下面两行的规则
-A DOCKER-ISOLATION-STAGE-2 -o br-0af11889deee -j ACCEPT
-A DOCKER-ISOLATION-STAGE-2 -o docker0 -j ACCEPT
iptables-restore < iptables.rule
修改之后两个不同网段的容器可通信
方法2:通过docker network connect 实现同一个宿主机不同网络的容器间通信
#将CONTAINER连入指定的NETWORK中,使此CONTAINER可以与NETWORK中的其它容器进行通信
docker network connect [OPTIONS] NETWORK CONTAINER
Options:
• --alias strings 为容器添加网络范围的别名
• --driver-opt strings 网络的驱动程序选项
• --ip string IPv4地址 (e.g:172.30.100.104)
• --ip6 string IPv6 地址 (e.g:2001:db8::33)
• --link list 将链接添加到另一个容器
• --link-local-ip strings 为容器添加链接本地地址
#让Bridge网络模式中的容器test1可以连通自定义网络test-net的容器test2
docker network connect test-net test1
此时在test1容器中可以看到新添加了一个网卡,并设置test-net网络的IP信息
在test1容器中可以Ping通test2容器
在test2容器中ping不通test1容器
#让自定义网络中容器test2可以连通默认网络的容器test1
docker network connect bridge test2
此时,自定义网络的容器test2添加了新网卡,并设置了Bridge网络的IP信息
在test2容器中可以ping通test1容器
docker network disconnect命令用于断开不同网络中容器的通信,操作方式、参数与docker network connect命令类似。
#断开test1容器与test-net网络中其它容器的通信。此时在test1容器中ping不通test2容器,但test2容器能ping通test1容器
docker network disconnect test-net test1
#断开test2容器与Bridge网络模式中其它容器的通信。此时在test2容器中也ping不通test1容器
docker network disconnect bridge test2
四、跨宿主机的容器之间网络互联
4.1 方式1:利用桥接实现跨宿主机的容器间互联
##在两个宿主机均执行如下操作,需要自定义网络
apt-get -y install bridge-utils
docker network create -d bridge --subnet 172.20.0.0/16 --gateway 172.20.0.1 net20
##将ens33网卡加入自定义网络net20所对应的网桥
#第一台宿主机执行
brctl addif br-bb6615fee423 ens33
#第二台宿主机执行
brctl addif br-afdab1700e8b ens33
##两个宿主机各启动一个容器。需要确保ip不同,相互测试访问
#第一台宿主机执行
docker run -it --network net20 --name a1 --ip 172.20.0.2 alpine sh
#第二台宿主机执行
docker run -it --network net20 --name a2 --ip 172.20.0.4 alpine sh
4.2 方式2:利用NAT实现跨主机的容器间互联
4.2.1 Docker跨主机互联实现说明
跨主机互联是说A宿主机的容器可以访问B主机上的容器,但是前提是保证各宿主机之间的网络是可以相互通信的,然后各容器才可以通过宿主机访问到对方的容器。
实现原理:在宿主机做一个网络路由即可实现A宿主机的容器访问B主机的容器的目的。
注意:此方式只适合小型网络环境,复杂的网络或者大型的网络可以使用k8s进行互联。
4.2.2 修改各宿主机网段
Docker默认网段是172.17.0.x/16,而且每个宿主机都是一样的,因此要做路由的前提就是各个主机的网络不能一致。这里修改第二个宿主机网段为172.18网段。
4.2.3 两个宿主机各启动一个容器
#第一个宿主机启动a1容器
docker run -it --name a1 alpine sh
#第二个宿主机启动a2容器
docker run -it --name a2 alpine sh
此时,第一个宿主机的容器a1不能与第二个宿主机的容器a2通信
4.2.4 添加静态路由和iptables规则
在各宿主机添加静态路由,网关指向对方宿主机的IP。
##第一台宿主机添加静态路由和iptables规则
route add -net 172.18.0.0/16 gw 192.168.131.12
#注意:这里的192.168.131.0/24是宿主机使用的网段
iptables -A FORWARD -s 192.168.131.0/24 -j ACCEPT
##第二台宿主机添加静态路由和iptables规则
route add -net 172.17.0.0/16 gw 192.168.131.11
iptables -A FORWARD -s 192.168.131.0/24 -j ACCEPT
4.2.5 测试跨宿主机之间容器通信
第一台宿主机的容器a1访问第二台宿主机的容器a2,结果可以访问
第二台机器输入tcpdump -i ens33 –nn icmp命令抓包观察
但是这样的方式需要手动添加多条路由和IPTABLES规则,管理不便。实际工作中多用k8s解决容器之间的网络问题。