前戏
事情的起因是我在服务器a中部署了一个用docker实现的java项目,这个项目中依赖于另一台服务器中的elasticsearch,但是一直显示无法连接,
进入容器ping一下试试看
很显然,网络不通
docker中的bridge网络详解
在 docker 中,bridge 网络 是最常用的网络模式之一,它通过虚拟网桥(如默认的 docker0)来连接容器。
容器之间以及容器与外部网络的通信,需要依赖 linux 主机的 ip 转发功能。
这是因为 docker 网络模型本质上是通过 linux 的虚拟网络设备、iptables 和 ip 转发等内核功能来实现的。
1. 什么是 docker bridge 网络?
当 docker 启动时,它会自动创建一个名为 docker0 的虚拟网桥,类似于一个软件交换机。所有使用 bridge 模式运行的容器,都会自动连接到 docker0 上,并被分配一个 ip 地址。
2. ip 转发的作用
当你在 docker 中使用 bridge 网络时,ip 转发 起到以下关键作用:
- 容器与外部网络的通信:当容器 a(在 docker0 网络中)想要访问外部网络(比如互联网或其他主机)时,数据包会通过 docker0 虚拟网桥被发送到主机的网络接口(如 eth0)。在这种情况下,主机必须启用 ip 转发功能,才能将这些数据包从 docker0 转发到外部网络。
- 容器之间的通信:容器 a 和容器 b 在 docker0 虚拟网桥上被分配了不同的 ip 地址,linux 需要启用 ip 转发来在它们之间路由数据包。例如,容器 a 的 ip 地址为 172.17.0.2,容器 b 的 ip 地址为 172.17.0.3,如果 a 需要与 b 通信,数据包需要通过 docker0 网桥进行转发。
- nat(网络地址转换)和外部访问:为了使容器能够访问外部网络(如互联网),docker 通常会通过 nat 来隐藏容器的内部 ip 地址(例如 172.17.0.x),这需要主机具有 ip 转发能力。docker 会通过 iptables 将内部容器网络的流量进行 snat(源地址转换),并转发到主机的物理网络接口上。
3. ip 转发如何帮助 docker 网络工作?
在 linux 主机中,ip 转发的配置位于 /proc/sys/net/ipv4/ip_forward。如果该值为 0,则表示 ip 转发被禁用;如果该值为 1,则表示启用。docker 依赖这个设置来实现容器与外部网络之间的通信。
当 docker 创建 bridge 网络时,内部机制包括以下几步:
- 创建虚拟网桥(如 docker0**)**。
- 给每个连接到该网桥的容器分配一个虚拟网卡(veth 设备)。
- 配置 ip 转发:确保 linux 主机启用了 ip 转发,以便将不同网络接口之间的数据包进行转发。
docker 通过这种方式,将每个容器的 veth 设备与虚拟网桥 docker0 连接,形成一个桥接网络。linux 内核的 ip 转发机制则负责管理这些网络接口之间的通信。
4. 配置 ip 转发的步骤
在 docker 主机上启用 ip 转发通常是自动的,但如果你需要手动启用或进行故障排查,可以参考以下步骤:
- 检查 ip 转发是否启用:
cat /proc/sys/net/ipv4/ip_forward
- 临时启用 ip 转发(在系统重启前有效):
echo 1 > /proc/sys/net/ipv4/ip_forward
- 永久启用 ip 转发:编辑 /etc/sysctl.conf 文件,添加或修改以下内容:
net.ipv4.ip_forward = 1
然后运行以下命令以应用:sysctl -p
5. bridge 网络中的 nat 和 iptables
为了使容器能够访问外部网络,docker 会自动配置 nat 规则。docker 默认会在 docker0 网桥上创建 iptables 规则,使得从内部网络发往外部的流量进行 snat(源地址转换)。
这就意味着,来自容器的请求会以主机的 ip 地址作为源 ip 发往外部网络,这样容器之间和容器与外部网络之间的通信才得以实现。
例如:
docker 会创建如下的 iptables 规则来进行 nat:
iptables -t nat -a postrouting -s 172.17.0.0/16 ! -o docker0 -j masquerade
解决
可以认真看一下上面的,里面就有解决方案,我这里仅临时展示解决一下
执行
echo 1 | sudo tee /proc/sys/net/ipv4/ip_forward
加餐
明明服务器a可以telnet服务器b的es服务,但是还会报错
在服务器b进行抓包可以看到如下
显示被拒绝了,这个情况我是感到很无语的
进入服务器a中的java容器去ping一下服务器b的地址,你会发现它好了
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。
发表评论