Docker容器跨主机通信之:直接路由方式
nanshan 2024-10-10 07:16 57 浏览 0 评论
一、Docker网络基本原理
直观上看,要实现网络通信,机器需要至少一个网络接口(物理接口或虚拟接口)与外界相通,并可以收发数据包;此外,如果不同子网之间要进行通信,需要额外的路由机制
Docker中的网络接口默认都是虚拟的接口。虚拟接口的最大优势就是转发效率极高
这是因为Linux通过在内核中进行数据复制来实现虚拟接口之间的数据转发
即发送接口的发送缓存中的数据包将被直接复制到接收接口的接收缓存中,而无需通过外部物理网络设备进行交换
对于本地系统和容器内系统来看,虚拟接口跟一个正常的以太网卡相比并无区别,只是它速度要快得多
Docker容器网络就很好地利用了Linux虚拟网络技术,在本地主机和容器内分别创建一个虚拟接口,并让它们彼此连通(这样的一对接口叫做veth pair)
一般情况下,Docker创建一个容器的时候,会具体执行如下操作:
1.创建一对虚拟接口,分别放到本地主机和新容器的命名空间中;
2.本地主机一端的虚拟接口连接到默认的docker0网桥或指定网桥上,并具有一个以veth开头的唯一名字,如veth1234;
3.容器一端的虚拟接口将放到新创建的容器中,并修改名字作为eth0。这个接口只在容器的命名空间可见;
4.从网桥可用地址段中获取一个空闲地址分配给容器的eth0(例如172.17.0.2/16),并配置默认路由网关为docker0网卡的内部接口docker0的IP地址(例如172.17.42.1/16)。
完成这些之后,容器就可以使用它所能看到的eth0虚拟网卡来连接其他容器和访问外部网络。用户也可以通过docker network命令来手动管理网络
二、Docker网络默认模式
按docker官方的说法,docker容器的网络有五种模式:
1)bridge模式,--net=bridge(默认) 这是dokcer网络的默认设置,为容器创建独立的网络命名空间,容器具有独立的网卡等所有单独的网络栈,是最常用的使用方式。 在docker run启动容器的时候,如果不加--net参数,就默认采用这种网络模式。安装完docker,系统会自动添加一个供docker使用的网桥docker0,我们创建一个新的容器时, 容器通过DHCP获取一个与docker0同网段的IP地址,并默认连接到docker0网桥,以此实现容器与宿主机的网络互通。
2)host模式,--net=host
这个模式下创建出来的容器,直接使用容器宿主机的网络命名空间。 将不拥有自己独立的Network Namespace,即没有独立的网络环境。它使用宿主机的ip和端口
3)none模式,--net=none 为容器创建独立网络命名空间,但不为它做任何网络配置,容器中只有lo,用户可以在此基础上,对容器网络做任意定制。 这个模式下,dokcer不为容器进行任何网络配置。需要我们自己为容器添加网卡,配置IP。 因此,若想使用pipework配置docker容器的ip地址,必须要在none模式下才可以
4)其他容器模式(即container模式),--net=container:NAME_or_ID 与host模式类似,只是容器将与指定的容器共享网络命名空间。 这个模式就是指定一个已有的容器,共享该容器的IP和端口。除了网络方面两个容器共享,其他的如文件系统,进程等还是隔离开的。 5)用户自定义:docker 1.9版本以后新增的特性,允许容器使用第三方的网络实现或者创建单独的bridge网络,提供网络隔离能力。
bridge模式
bridge模式是docker默认的,也是开发者最常使用的网络模式。在这种模式下,docker为容器创建独立的网络栈,保证容器内的进程使用独立的网络环境, 实现容器之间、容器与宿主机之间的网络栈隔离。同时,通过宿主机上的docker0网桥,容器可以与宿主机乃至外界进行网络通信。 其网络模型可以参考下图:
从上面的网络模型可以看出,容器从原理上是可以与宿主机乃至外界的其他机器通信的。同一宿主机上,容器之间都是连接掉docker0这个网桥上的,它可以作为虚拟交换机使容器可以相互通信
然而,由于宿主机的IP地址与容器veth pair的 IP地址均不在同一个网段,故仅仅依靠veth pair和namespace的技术,还不足以使宿主机以外的网络主动发现容器的存在。为了使外界可以发现容器中的进程,docker采用了端口绑定的方式,也就是通过iptables的NAT,将宿主机上的端口流量转发到容器内的端口上
举一个简单的例子,使用下面的命令创建容器,并将宿主机的3306端口绑定到容器的3306端口: docker run -tid --name db -p 3306:3306 MySQL
在宿主机上,可以通过iptables -t nat -L -n,查到一条DNAT规则: DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:3306 to:172.17.0.5:3306
上面的172.17.0.5即为bridge模式下,创建的容器IP。 很明显,bridge模式的容器与外界通信时,必定会占用宿主机上的端口,从而与宿主机竞争端口资源,对宿主机端口的管理会是一个比较大的问题。同时,由于容器与外界通信是基于三层上iptables NAT,性能和效率上的损耗是可以预见的。
三、方案介绍
概述
就目前Docker自身默认的网络来说,单台主机上的不同Docker容器可以借助docker0网桥直接通信,这没毛病,而不同主机上的Docker容器之间只能通过在主机上用映射端口的方法来进行通信,有时这种方式会很不方便,甚至达不到我们的要求,因此位于不同物理机上的Docker容器之间直接使用本身的IP地址进行通信很有必要。再者说,如果将Docker容器起在不同的物理主机上,我们不可避免的会遭遇到Docker容器的跨主机通信问题。本文就来尝试一下
情景构造
如下图所示,我们有两个物理主机1和主机2,我们在各自宿主机上启动一个centos容器,启动成功之后,两个容器分别运行在两个宿主机之上,默认的IP地址分配如图所示,这也是Docker自身默认的网络
此时两台主机上的Docker容器如何直接通过IP地址进行通信?
一种直接想到的方案便是通过分别在各自主机中 添加路由 来实现两个centos容器之间的直接通信。我们来试试吧
方案原理分析
由于使用容器的IP进行路由,就需要避免不同主机上的容器使用了相同的IP,为此我们应该为不同的主机分配不同的子网来保证。于是我们构造一下两个容器之间通信的路由方案,如下图所示。
各项配置如下:
主机1的IP地址为:192.168.91.128
主机2的IP地址为:192.168.91.129
为主机1上的Docker容器分配的子网:10.0.128.0/24
为主机2上的Docker容器分配的子网:10.0.129.0/24
这样配置之后,两个主机上的Docker容器就肯定不会使用相同的IP地址从而避免了IP冲突
我们接下来 定义两条路由规则 即可:
所有目的地址为10.0.128.0/24的包都被转发到主机1上
所有目的地址为10.0.129.0/24的包都被转发到主机2上
综上所述,数据包在两个容器间的传递过程如下:
从container1 发往 container2 的数据包,首先发往container1的“网关”docker0,然后通过查找主机1的路由得知需要将数据包发给主机2,数据包到达主机2后再转发给主机2的docker0,最后由其将数据包转到container2中;反向原理相同,不再赘述。
我们心里方案想的是这样,接下来实践一下看看是否可行。
四、实际试验
环境介绍
操作系统 | 服务器地址 | Dockerd地址 |
ubuntu-16.04.5-server-amd64 | 192.168.91.128 | 10.0.128.2 |
ubuntu-16.04.5-server-amd64 | 192.168.91.129 | 10.0.129.2 |
请确保已经安装好了docker,这里是使用以下命令安装的
apt-get install -y docker.io
docker的版本为 17.03.2-ce
比如主机1,我需要运行一个docker镜像,要求它的网段必须是 10.0.128.0/24 ,怎么设置呢?
有3个办法:
1. 修改docker0的网段
2. 创建一个docker网桥
这里为了快速实现,选用第一种方案。直接修改 /etc/default/docker 文件,添加 DOCKER_OPTS 参数即可
修改docker0
分别对主机1和主机2上的docker0进行配置
主机1
编辑主机1上的 /etc/default/docker 文件,最后一行添加
DOCKER_OPTS="--bip 10.0.128.1/24"
特别注意,DOCKER_OPTS参数后面必须有引号。--bip后面的ip就是docker0的ip地址,一般从第一个ip开始!
不要修改 /etc/docker/daemon.json 文件添加bip,我测试了一些,重启docker会报错!
主机2
编辑主机1上的 /etc/default/docker 文件,最后一行添加
DOCKER_OPTS="--bip 10.0.129.1/24"
重启docker服务
主机1和主机2上均执行如下命令重启docker服务以使修改后的docker0网段生效
systemctl restart docker
查看主机1上docker0的ip地址
查看主机2上docker0的ip地址
添加路由规则
主机1
查看路由表
默认只有自己本身的路由,如果需要访问 10.0.129.0/24 网段,需要添加路由
主机1上添加路由规则如下:
route add -net 10.0.129.0/24 gw 192.168.91.129
gw 表示下一跳地址,这里的地址就是主机2的ip地址
主机2
主机2上添加路由规则如下:
route add -net 10.0.128.0/24 gw 192.168.91.128
在主机1上,ping主机2的docker0地址
在主机2上,ping主机1的docker0地址
Docker Bridge创建创建过程
1)首先宿主机上创建一对虚拟网卡veth pair设备,veth设备总是成对出现的,形成一个通信通道,数据传输就是基于这个链路的,veth设备常用来连接两个网络设备
2)Docker将veth pair设备的一端放在容器中,并命名为eth0,然后将另一端加入docker0网桥中,可以通过brctl show命令查看
3)从docker0字网卡中分配一个IP到给容器使用,并设置docker0的IP地址为容器默认网关
4)此时容器IP与宿主机是可以通信的,宿主机也可以访问容器中的ip地址,在bridge模式下,连接同一网桥的容器之间可以相互通信,同时容器可以访问外网,但是其他物理机不能访问docker容器IP,需要通过NAT将容器的IP的port映射为宿主机的IP和port;
在主机1上面,再开一个窗口,使用ifconfig查看
会发现有一个 veth077daec 的网卡设备。咦,这是个啥?
当运行docker容器后,再次执行ifconfig命令可以看到会多出个网卡驱动veth开头的名字,所以补充下veth
veth
Linux container 中用到一个叫做veth的东西,这是一种新的设备,专门为 container 所建。veth 从名字上来看是 Virtual ETHernet 的缩写,它的作用很简单,就是要把从一个 network namespace 发出的数据包转发到另一个 namespace。veth 设备是成对的,一个是 container 之中,另一个在 container 之外,即在真实机器上能看到的。 VETH设备总是成对出现,一端请求发送的数据总是从另一端以请求接受的形式出现。创建并配置正确后,向其一端输入数据,VETH会改变数据的方向并将其送入内核网络子系统,完成数据的注入,而在另一端则能读到此数据。(Namespace,其中往veth设备上任意一端上RX到的数据,都会在另一端上以TX的方式发送出去)veth工作在L2数据链路层,veth-pair设备在转发数据包过程中并不串改数据包内容
显然,仅有veth-pair设备,容器是无法访问网络的。因为容器发出的数据包,实质上直接进入了veth1设备的协议栈里。如果容器需要访问网络,需要使用bridge等技术,将veth1接收到的数据包通过某种方式转发出去 。 veth参考链接:http://blog.csdn.net/sld880311/article/details/77650937
因此,如果要多台主机之间的docker通信,需要使用NAT转换。那么接下来,就是设置iptables规则了!
配置iptables规则
主机1
在主机1上查看默认的nat 规则
iptables -t nat -L
这些nat规则,都是docker帮你做的。
增加一条规则
iptables -t nat -I PREROUTING -s 10.0.128.0/24 -d 10.0.129.0/24 -j DNAT --to 10.0.128.1
iptables -t nat -I PREROUTING -s 172.34.0.0/16 -d 172.35.0.0/16 -j DNAT --to 172.34.0.1
PREROUTING:可以在这里定义进行目的NAT的规则,因为路由器进行路由时只检查数据包的目的ip地址,所以为了使数据包得以正确路由,我们必须在路由之前就进行目的NAT;
上面那一条路由规则是啥意思呢?就是当源地址为10.0.128.0/24网段 访问 10.0.129.0/24 时,在路由之前,将ip转换为10.0.128.1
注意:一定要加-d参数。如果不加,虽然docker之间可以互通,但是不能访问网站,比如百度,qq之类的!
为什么呢?访问10.0.129.0/24 时,通过docker0网卡出去的。但是访问百度,还是通过docker0,就出不去了!
真正连接外网的是ens32网卡,必须通过它才行!因此必须要指定-d参数!
这个时候,直接ping 主机2上的docker地址
一键脚本
上面已经实现了2台docker之间的通信,如果是3台呢?怎么搞?还是一样的。
只不过每台主机都要增加2条路由规则以及2条iptables规则。
做路由规则时,容器搞混淆,为了避免这种问题,做一个一键脚本就可以了!
环境要求
1. 每台主机已经安装好了docker,并且已经启动
2. 请确保每台主机的 /etc/default/docker 没有被更改过。还是默认的172.17.0.2/16网段
如果是虚拟机,直接还原快照即可!
#!/bin/bash
# 主机ip后缀清单
hosts="128 129 131"
# 循环主机
for i in `echo $hosts`;do
# 写入临时文件
cat >/tmp/dockerc<<EOF
DOCKER_OPTS=\"--bip 10.0.$i.1/24\"
EOF
# 远程执行命令,更改docker0网段
ssh 192.168.91.$i "echo $(cat /tmp/dockerc)>> /etc/default/docker"
# 重启docker服务
ssh 192.168.91.$i "systemctl restart docker"
# 清空nat规则
# ssh 192.168.91.$i "sudo iptables -t nat -F"
# 再次循环
for j in `echo $hosts`;do
# 排除自身
if [ "$j" != "$i" ];then
# 添加路由
ssh 192.168.91.$i "route add -net 10.0.$j.0/24 gw 192.168.91.$j"
# 添加nat规则
ssh 192.168.91.$i "iptables -t nat -I PREROUTING -s 10.0.$i.0/24 -d 10.0.$j.0/24 -j DNAT --to 10.0.$i.1"
fi
done
# 重启docker服务,写入默认的nat规则
ssh 192.168.91.$i "systemctl restart docker"
done
ssh免密登录
在主机1执行以下命令
生成秘钥,并写入到authorized_keys
ssh-keygen -t rsa -P "" -f ~/.ssh/id_rsacat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
复制公钥,执行以下3个命令
ssh-copy-id 192.168.91.128
ssh-copy-id 192.168.91.129
ssh-copy-id 192.168.91.131
正式执行 docker_dr.sh
bash docker_dr.sh
执行之后,是没有啥输出的
3台主机都启动alpine镜像
docker run -it alpine
相关推荐
- 在 Ubuntu 上安装 Zabbix(以 Zabbix 6.4 LTS 版本为例)
-
Zabbix是一个流行的开源监控解决方案,能够监控各种网络参数和服务器健康状态。一、环境准备系统要求Ubuntu20.04/22.04LTS至少2GBRAM(生产环境建议4GB+)至少1...
- 如何在 Ubuntu 24.04 服务器上安装 Apache Solr
-
ApacheSolr是一个免费、开源的搜索平台,广泛应用于实时索引。其强大的可扩展性和容错能力使其在高流量互联网场景下表现优异。Solr基于Java开发,提供了分布式索引、复制、负载均衡及自...
- 如何在 Ubuntu 24.04 LTS 或 22.04/20.04 上安装 Apache Maven
-
Maven是由Apache托管的开源工具,用于管理Java项目。它包含一个项目对象模型(POM):一个配置文件(XML),其中包含项目的基本信息,包括配置、项目依赖项等。Maven可以处理...
- Cursor的终极对手——Trae Pro最新系统提示词
-
前段时间,字节的AI编程神器Trae国际版,终于甩出了Pro订阅计划!很多对它又爱又恨的小伙伴,直呼:终于等到你。爱它,是因为Trae长期免费+体验真香;恨它?还不是那该死的排队等待,...
- AI系统提示词:V0(ai代码提示)
-
以下是对V0系统提示词(SystemPrompt)的分部分讲解与解读,帮助你理解其核心内容和设计意图。V0系统提示词##CoreIdentity-Youarev0,Vercel&...
- 8岁男童失踪第13天,搜救人员发现可疑水库,更恶心的事情发生了
-
Lookingatyourrequest,Ineedtorewritethearticleaboutthe8-year-oldmissingboywhilemaking...
- docker常用指令及安装rabbitMQ(docker安装zabbix)
-
一、docker常用指令启动docker:systemctlstartdocker停止docker:systemctlstopdocker重启docker:systemctlrestart...
- 三步教你用Elasticsearch+PyMuPDF实现PDF大文件秒搜!
-
面对100页以上的大型PDF文件时,阅读和搜索往往效率低下。传统关系型数据库在处理此类数据时容易遇到性能瓶颈,而Elasticsearch凭借其强大的全文检索和分布式架构,成为理想解决方案。通过...
- ElasticSearch中文分词插件(IK)安装
-
坚持原创,共同进步!请关注我,后续分享更精彩!!!前言ElasticSearch默认的分词插件对中文支持很不友好。一段话按规则会以每个中文字符来拆解,再分别建立倒排索引。如"中华人民共和国国歌...
- SpringBoot使用ElasticSearch做文档对象的持久化存储?
-
ElasticSearch是一个基于Lucene的开源搜索引擎,广泛应用于日志分析、全文搜索、复杂查询等领域,在有些场景中使用ElasticSearch进行文档对象的持久化存储是一个很不错的选择...
- Elasticsearch数据迁移方案(elasticsearch copyto)
-
前言最近小编要去给客户部署一套系统涉及到了Mysql和ES数据的迁移,下面就给大家分享一下ES数据迁移的几套方案,根据具体的使用场景来选择不同的迁移方案能使你事倍功半,话多说下面就一一介绍。Elast...
- Rancher部署单体ElasticSearch(rancher2.5部署)
-
Rancher是k8s图形管理界面,之前曾有写文章介绍如何安装。ElasticSearch是热门搜索引擎,很多地方都有用到,常规安装部署略显繁琐,本文介绍在k8s下用rancher简易部署ES。1.在...
- Elasticsearch在Java项目的搜索实践:从零开始构建高效搜索系统
-
Elasticsearch在Java项目中的搜索实践:从零开始构建高效搜索系统在现代的Java项目中,数据量激增,传统的数据库查询方式已经无法满足快速检索的需求。这时,Elasticsearch(E...
- 小白入门-Kibana安装(kibana安装配置)
-
一Kibana基础1.1介绍Kibana是一款免费且开放的前端应用程序,其基础是ElasticStack,可以为Elasticsearch中索引的数据提供搜索和数据可视化功能。Kiban...
- Docker上使用Elasticsearch,Logstash,Kibana
-
在对一个项目做性能测试时我需要处理我们web服务器的访问日志来分析当前用户的访问情况。因此,我想这是试用ELK的一个好机会。ELK栈首先要注意的是使用它是非常简单的。从决定使用ELK到在本机上搭一个...
你 发表评论:
欢迎- 一周热门
-
-
极空间如何无损移机,新Z4 Pro又有哪些升级?极空间Z4 Pro深度体验
-
UOS服务器操作系统防火墙设置(uos20关闭防火墙)
-
如何修复用户配置文件服务在 WINDOWS 上登录失败的问题
-
手机如何设置与显示准确时间的详细指南
-
如何在安装前及安装后修改黑群晖的Mac地址和Sn系列号
-
日本海上自卫队的军衔制度(日本海上自卫队的军衔制度是什么)
-
爱折腾的特斯拉车主必看!手把手教你TESLAMATE的备份和恢复
-
10个免费文件中转服务站,分享文件简单方便,你知道几个?
-
FANUC 0i-TF数据备份方法(fanuc系统备份教程)
-
NAS:DS video/DS file/DS photo等群晖移动端APP远程访问的教程
-
- 最近发表
-
- 在 Ubuntu 上安装 Zabbix(以 Zabbix 6.4 LTS 版本为例)
- 如何在 Ubuntu 24.04 服务器上安装 Apache Solr
- 如何在 Ubuntu 24.04 LTS 或 22.04/20.04 上安装 Apache Maven
- Cursor的终极对手——Trae Pro最新系统提示词
- AI系统提示词:V0(ai代码提示)
- 8岁男童失踪第13天,搜救人员发现可疑水库,更恶心的事情发生了
- docker常用指令及安装rabbitMQ(docker安装zabbix)
- 三步教你用Elasticsearch+PyMuPDF实现PDF大文件秒搜!
- ElasticSearch中文分词插件(IK)安装
- SpringBoot使用ElasticSearch做文档对象的持久化存储?
- 标签列表
-
- linux 查询端口号 (58)
- docker映射容器目录到宿主机 (66)
- 杀端口 (60)
- yum更换阿里源 (62)
- internet explorer 增强的安全配置已启用 (65)
- linux自动挂载 (56)
- 禁用selinux (55)
- sysv-rc-conf (69)
- ubuntu防火墙状态查看 (64)
- windows server 2022激活密钥 (56)
- 无法与服务器建立安全连接是什么意思 (74)
- 443/80端口被占用怎么解决 (56)
- ping无法访问目标主机怎么解决 (58)
- fdatasync (59)
- 405 not allowed (56)
- 免备案虚拟主机zxhost (55)
- linux根据pid查看进程 (60)
- dhcp工具 (62)
- mysql 1045 (57)
- 宝塔远程工具 (56)
- ssh服务器拒绝了密码 请再试一次 (56)
- ubuntu卸载docker (56)
- linux查看nginx状态 (63)
- tomcat 乱码 (76)
- 2008r2激活序列号 (65)