大白话浅析容器网络

巴辉特 2024年4月23日

起因

使用夜莺纳管时序库,需要在夜莺里配置时序库的地址,但是经常会遇到连不通的问题,使用 Docker compose 方式部署夜莺遇到问题的概率尤其高。很多朋友解决不了这类问题,核心是因为网络知识太匮乏了,尤其是容器网络,本文用大白话介绍一下 Docker compose 两种网络模式:host network 和 bridge network。希望对大家有帮助。

基础知识

容器网络涉及到 network namespace 的概念,大家可以通过 GPT 或 Google 学习。简单来讲,容器被看作轻量级虚拟机,和宿主之间有一些隔离举措,核心就是靠 namespace 实现的。比如 mount namespace 隔离文件系统挂载点,pid namespace 隔离进程,network namespace 隔离网络等等。

通常来讲,在一台机器上,两个进程的 pid 是不会重复的,但是你在宿主机可以看到 pid 为 1 的进程,在容器里仍然可以看到 pid 为 1 的进程,这就是 pid namespace 在搞鬼;通常来讲,在一台机器上,端口是不能重复监听的,但是你在宿主上监听一个 8080 端口,在容器里仍然可以监听一个 8080 端口,这就是 network namespace 在搞鬼。

host network

host network 是 Docker compose 默认的网络模式,也是最简单的网络模式。host network 模式下,容器和宿主共享网络命名空间,即在网络方面没有隔离。

假设容器里有个进程 Prometheus 监听在 9090 端口,那么在宿主上直接访问 127.0.0.1:9090 就可以访问到 Prometheus 服务。举例:

[root@aliyun-2c2g40g3m compose-host-network]# curl -s http://127.0.0.1:9090/api/v1/labels | python3 -mjson.tool
{
    "status": "success",
    "data": [
        "__name__",
        "branch",
        "call",
        "type",
        "url",
        "version"
    ]
}

我们知道,通过容器的 pid 是可以看到容器的 network namespace 的,我们来对比一下此时 prometheus 进程的 network namespace 和 宿主上的 systemd 进程(1 号进程)的 network namespace,理论上是相同的。

# 通过 pgrep 可以找到 prometheus 进程的 pid
[root@aliyun-2c2g40g3m compose-host-network]# pgrep prometheus
2101063

# /proc/<PID>/ns/net 下面是 <PID> 进程的 network namespace 信息
[root@aliyun-2c2g40g3m compose-host-network]# ll /proc/2101063/ns/net
lrwxrwxrwx 1 nobody nobody 0 Apr 23 15:10 /proc/2101063/ns/net -> 'net:[4026531994]'

# 1 号进程是 systemd,我们看看它的 network namespace 信息
[root@aliyun-2c2g40g3m compose-host-network]# ll /proc/1/ns/net
lrwxrwxrwx 1 root root 0 Apr 23 15:12 /proc/1/ns/net -> 'net:[4026531994]'

很明显,systemd 进程和 prometheus 进程的 network namespace 是相同的,都是 net:[4026531994],这就是 host network 的特点。

另外,如果通过 ip link ls 命令查看网络接口信息,可以看到:host network 模式在 compose 启动前后,网络接口信息是不变的,因为容器和宿主共享网络命名空间。

[root@aliyun-2c2g40g3m compose-host-network]# ip link ls
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
    link/ether 00:16:3e:36:ec:33 brd ff:ff:ff:ff:ff:ff
    altname enp0s5
    altname ens5
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN mode DEFAULT group default
    link/ether 02:42:66:48:4c:0d brd ff:ff:ff:ff:ff:ff

另外要注意,MacOS 不支持 host network 模式。

所以,在这种模式下,如果 Nightingale 和 Prometheus 都是 host network 下的两个容器,在一台宿主上,在夜莺的数据源管理页面,直接配置:http://127.0.0.1:9090 作为数据源地址即可。

bridge network

夜莺的发布包下载之后,进入 docker/compose-bridge 目录下,即可看到 docker-compose.yaml 文件,文件开头部分的内容如下:

version: "3.7"

networks:
  nightingale:
    driver: bridge

这就表示要创建一个 bridge 类型的 network,名字为 nightingale,后面在各个 service 中会引用。我就用夜莺的这个 compose 环境来验证一下 bridge network 的特点。先启动一下:

[root@aliyun-2c2g40g3m compose-bridge]# docker compose up -d
[+] Running 6/6
 ✔ Network compose-bridge_nightingale  Created                                                       0.1s
 ✔ Container mysql                     Started                                                       0.1s
 ✔ Container redis                     Started                                                       0.2s
 ✔ Container victoriametrics           Started                                                       0.1s
 ✔ Container nightingale               Started                                                       0.0s
 ✔ Container categraf                  Started                                                       0.1s

启动之后,我们来查看网络接口,立刻就会看到一些新接口:

[root@aliyun-2c2g40g3m compose-bridge]# ip link ls
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
    link/ether 00:16:3e:36:ec:33 brd ff:ff:ff:ff:ff:ff
    altname enp0s5
    altname ens5
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN mode DEFAULT group default
    link/ether 02:42:66:48:4c:0d brd ff:ff:ff:ff:ff:ff
196: br-f7e89bf52fb1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default
    link/ether 02:42:1c:ee:72:51 brd ff:ff:ff:ff:ff:ff
198: vethccaf5df@if197: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-f7e89bf52fb1 state UP mode DEFAULT group default
    link/ether de:10:51:ed:c1:f3 brd ff:ff:ff:ff:ff:ff link-netnsid 0
200: veth4be2140@if199: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-f7e89bf52fb1 state UP mode DEFAULT group default
    link/ether 22:1d:46:e9:51:ca brd ff:ff:ff:ff:ff:ff link-netnsid 1
202: vethe500119@if201: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-f7e89bf52fb1 state UP mode DEFAULT group default
    link/ether 32:cb:7e:9a:14:af brd ff:ff:ff:ff:ff:ff link-netnsid 2
204: veth18aab27@if203: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-f7e89bf52fb1 state UP mode DEFAULT group default
    link/ether f2:03:0d:e8:5f:76 brd ff:ff:ff:ff:ff:ff link-netnsid 3
206: vethd2c49b3@if205: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-f7e89bf52fb1 state UP mode DEFAULT group default
    link/ether a2:65:bd:bf:04:ba brd ff:ff:ff:ff:ff:ff link-netnsid 4

编号为 196 的是 br-f7e89bf52fb1,br 这个前缀表示这是一个 bridge 网桥。编号为 198 的那一行,具体表示什么我让 Kimi 给大家解释一下,解释一下下面这行:

198: vethccaf5df@if197: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-f7e89bf52fb1 state UP mode DEFAULT group default
    link/ether de:10:51:ed:c1:f3 brd ff:ff:ff:ff:ff:ff link-netnsid 0

这段输出是 ip link ls 命令的一部分,描述了一个特定的虚拟网络接口 vethccaf5df 的详细信息。下面是对输出中各项内容的解释:

  1. 接口索引198 是这个网络接口的索引号。

  2. 接口名称vethccaf5df@if197 表示这是一个虚拟以太网对(veth pair),它与另一个接口 if197 配对。@if197 表示这个接口是一对中的一个端点,另一个端点是 if197

  3. 接口状态标志

    • <BROADCAST,MULTICAST,UP,LOWER_UP>:这些标志描述了接口的能力:
      • BROADCAST:接口能够发送广播消息。
      • MULTICAST:接口能够发送多播消息。
      • UP:接口已经被激活。
      • LOWER_UP:接口的物理层(比如网线)也是激活状态的。
    • mtu 1500:接口的最大传输单元(MTU)是 1500 字节,这是以太网的标准 MTU 大小。
  4. 队列规则qdisc noqueue 表示这个接口没有使用任何队列规则,即它没有排队任何数据包。

  5. 控制器master br-f7e89bf52fb1 表示这个接口是 br-f7e89bf52fb1 网桥的从设备。

  6. 状态state UP 表示接口处于激活状态。

  7. 模式mode DEFAULT 表示接口处于默认模式。

  8. group default 表示接口属于默认的网络组。

  9. 硬件地址link/ether de:10:51:ed:c1:f3 是这个接口的 MAC 地址。

  10. 广播地址brd ff:ff:ff:ff:ff:ff 是这个接口的广播地址,用于广播传输。

  11. 网络命名空间 IDlink-netnsid 0 表示这个接口位于编号为 0 的网络命名空间中。在 Linux 中,网络命名空间用于隔离网络设备和网络状态,0 通常表示全局或宿主机的网络命名空间。

这个输出表明 vethccaf5df 是一个虚拟接口,它与另一个接口 if197 形成了一对,并且它被配置为一个网桥的从设备。这种配置通常用于容器技术,如 Docker 或 Kubernetes,以便在容器和宿主机之间提供网络连接。

我们进入 nightingale 容器,查看一下容器内的网络接口信息:

# 使用 docker exec 进入容器
[root@aliyun-2c2g40g3m ~]# docker exec -it nightingale /bin/bash

# 使用 ip link ls 查看容器内部的网络接口信息,很遗憾,容器内没有 ip 这个命令
root@nightingale:/app# ip link ls
bash: ip: command not found

# 使用 exti 退出容器
root@nightingale:/app# exit
exit

# 既然容器里没有相关命令,我们就使用 nsenter 进入容器的 network namespace,相当于变相进入了容器
# 通过 inspect 找到 Pid
[root@aliyun-2c2g40g3m ~]# docker inspect nightingale | grep Pid
            "Pid": 2104897,
            "PidMode": "",
            "PidsLimit": null,

# 把 pid 作为参数传给 nsenter,-n 表示进入 network namespace
[root@aliyun-2c2g40g3m ~]# nsenter -t 2104897 -n

# 在容器的 network namespace 里,执行 ip link ls 查看网口信息
[root@aliyun-2c2g40g3m ~]# ip link ls
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
203: eth0@if204: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default
    link/ether 02:42:ac:1f:00:05 brd ff:ff:ff:ff:ff:ff link-netnsid 0

上面的命令我做了详细解释,其中比较关键的是 nsenter 的使用,之前录制过一个小视频讲解,大家可以到视频号:SRETalk 观看。

从上面的输出可以看出,bridge network 下,容器内部的网络接口信息和宿主机是不一样的,这就是 bridge network 的特点。容器内有个网口,索引编号是 203,对端是 if204,if204 其实就是宿主机上的 veth pair 接口的另一半:veth18aab27@if203

总结来看,bridge network 下,宿主上会创建一个 bridge,bridge 上挂了很多从设备,各个从设备又分别和某个容器里的 eth0 组成了一个 veth pair。

通过 docker inspect nightingale | grep Pid 我们可以看到 Pid 是 2104897,我们也对比一下这个进程的 network namespace 和宿主上的 systemd 进程的 network namespace,理论上是不同的。

[root@aliyun-2c2g40g3m ~]# ll /proc/2104897/ns/net
lrwxrwxrwx 1 root root 0 Apr 23 16:03 /proc/2104897/ns/net -> 'net:[4026532543]'
[root@aliyun-2c2g40g3m ~]# ll /proc/1/ns/net
lrwxrwxrwx 1 root root 0 Apr 23 16:04 /proc/1/ns/net -> 'net:[4026531994]'

一个是 net:[4026532543],另一个是 net:[4026531994],明显不同。

Docker compose bridge network 模式下,服务之间的访问,可以通过服务名访问,也可以通过容器 IP 访问,比如在 nightingale 里配置 VictoriaMetrics 数据源地址,可以配置为:http://victoriametrics:8428,也可以配置为:http://172.31.0.3:8428。其中 172.31.0.3 就是 VictoriaMetrics 的容器 IP,通过 nsenter 进入 VictoriaMetrics 容器的 network namespace,执行 ip addr 或者 ifconfig 都可以看到。

千万不要配置为:http://127.0.0.1:8428,不同的容器是有自己各自的回环网卡的,访问 127.0.0.1 就只能访问自己容器里的服务,无法访问其他容器的服务。

文末请允许我插播一个小广告。本人创业两年了,我们公司也是做可观测性,和本文思路有些相像之处。如果你有这方面的需求,欢迎联系我们做产品技术交流哈。

🎯 关于快猫星云

快猫星云是一家云原生智能运维科技公司,由知名开源项目“夜莺(Nightingale)”的核心开发团队组成,创始团队均来⾃阿⾥、百度、滴滴等互联⽹公司。夜莺是一款开源云原生监控工具,是中国计算机学会接受捐赠并托管的第一个开源项目,在GitHub上有超过8000颗星,迭代发布了超过100多个版本,上百位社区贡献者,是国内领先的开源可观测性解决方案。

快猫星云以开源夜莺为内核打造的“Flashcat平台”,是国内顶级互联⽹公司可观测性实践的产品化落地,致力于让可观测性技术更好的服务企业,保障服务稳定性。Flashcat 平台具有以下特点:

  • 统一采集:采用插件化思路,内置集成上百种采集插件,服务器、网络设备、中间件、数据库、应用、业务,均可监控,开箱即用。
  • 统一告警:支持几十种数据源对接,收集各类监控系统的告警事件,进行统一的告警收敛、降噪、排班、认领、升级、协同,大幅提升告警处理效率。
  • 统一观测:将 Metrics、Logs、Traces、Events、Profiling 等多种可观测性数据融会贯通,并预置行业最佳实践,既提供全局业务视角、技术视角的驾驶舱,也提供层层下钻的故障定位能力,有效缩短故障发现和定位时间。

快猫星云,让可观测性数据更有价值!
https://flashcat.cloud/

标签: Docker
开源版
Flashcat
Flashduty