起因
使用夜莺纳管时序库,需要在夜莺里配置时序库的地址,但经常会遇到连不通的问题。使用 Docker Compose 方式部署夜莺时,遇到问题的概率尤其高。
很多朋友解决不了这类问题,核心原因是网络知识太匮乏,尤其是容器网络。本文用大白话介绍 Docker Compose 中两种常见网络模式:host network 和 bridge network,并说明在夜莺里应该如何配置 Prometheus、VictoriaMetrics 等数据源地址。
先给结论:
| 场景 | 数据源地址应该怎么写 | 原因 |
|---|---|---|
| 夜莺和 Prometheus 都使用 host network,且在同一台宿主机 | http://127.0.0.1:9090 |
容器和宿主共享网络命名空间 |
| 夜莺和 VictoriaMetrics 在同一个 bridge network | http://victoriametrics:8428 |
Docker Compose bridge 网络支持服务名访问 |
| bridge network 下访问另一个容器 | 不要写 127.0.0.1 |
127.0.0.1 只指向当前容器自己 |
基础知识:network namespace
容器网络涉及 network namespace。简单来讲,容器被看作轻量级虚拟机,和宿主之间有一些隔离举措,核心就是靠 namespace 实现的。比如 mount namespace 隔离文件系统挂载点,pid namespace 隔离进程,network namespace 隔离网络。
通常来讲,在一台机器上,两个进程的 PID 不会重复。但是你在宿主机可以看到 PID 为 1 的进程,在容器里仍然可以看到 PID 为 1 的进程,这就是 pid namespace 在起作用。
同理,通常一台机器上的同一个端口不能被重复监听;但你在宿主上监听一个 8080 端口,在容器里仍然可以监听一个 8080 端口,这就是 network namespace 在起作用。
host network:和宿主共享网络
host network 是最简单的网络模式。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 进程和宿主上 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 是一个 bridge 网桥。编号为 198 的这一行表示一个虚拟网络接口:
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
这段输出描述了一个虚拟以太网接口 vethccaf5df。它与另一个接口形成 veth pair,并且被挂到了 br-f7e89bf52fb1 网桥上。这种配置常用于 Docker 或 Kubernetes,让容器和宿主机之间能够通过虚拟网络连接起来。
用 nsenter 查看容器内网络
进入 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
# 使用 exit 退出容器
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
从上面的输出可以看出,bridge network 下,容器内部的网络接口信息和宿主机是不一样的,这就是 bridge network 的特点。容器内有个网口,索引编号是 203,对端是 if204,if204 其实就是宿主机上的 veth pair 接口的另一半:veth18aab27@if203。
总结来看,bridge network 下,宿主上会创建一个 bridge,bridge 上挂了很多从设备,各个从设备又分别和某个容器里的 eth0 组成一个 veth pair。
再对比一下 nightingale 进程的 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],明显不同。
bridge network 下如何配置数据源地址
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://victoriametrics:8428。原因是容器 IP 可能变化,而 Compose 服务名更稳定。
千万不要配置为:
http://127.0.0.1:8428
不同的容器有自己各自的回环网卡。bridge network 下,在 nightingale 容器里访问 127.0.0.1,只能访问 nightingale 容器里的服务,无法访问 VictoriaMetrics 容器里的服务。
host network 和 bridge network 对比
| 对比项 | host network | bridge network |
|---|---|---|
| 网络命名空间 | 与宿主共享 | 容器独立 |
宿主 ip link 是否新增接口 |
通常不新增 | 会看到 bridge 和 veth |
| 容器访问自己 | 127.0.0.1 |
127.0.0.1 |
| 容器访问同机其他服务 | 可以按宿主网络理解 | 应使用服务名、容器 IP 或暴露端口 |
| 夜莺配置同机 Prometheus | 可用 http://127.0.0.1:9090 |
不要用 127.0.0.1 访问其他容器 |
| 典型优点 | 简单、少一层网络隔离 | 服务名访问、隔离更清晰 |
| 典型坑点 | 端口冲突、隔离弱,MacOS 不支持 | 初学者容易误用 127.0.0.1 |
FAQ
Q1:Docker Compose 的默认 bridge 网络能用服务名访问吗? 可以。Compose 会为项目创建网络,服务在同一个网络里时,可以通过服务名互相访问。
Q2:为什么 nightingale 容器里访问 127.0.0.1:8428 不通?
因为 bridge network 下,每个容器有自己的 network namespace。127.0.0.1 是当前容器自己,不是宿主机,也不是 VictoriaMetrics 容器。
Q3:容器 IP 能不能写到配置里? 能,但不推荐。容器重建后 IP 可能变化,服务名更稳定,也更符合 Compose 的使用方式。
附
文末请允许我插播一个小广告。本人创业两年了,我们公司也是做可观测性,和本文思路有些相像之处。如果你有这方面的需求,欢迎联系我们做产品技术交流哈。
🎯 关于快猫星云
快猫星云是一家云原生智能运维科技公司,由知名开源项目“夜莺(Nightingale)”的核心开发团队组成,创始团队均来⾃阿⾥、百度、滴滴等互联⽹公司。夜莺是一款开源云原生监控工具,是中国计算机学会接受捐赠并托管的第一个开源项目,在GitHub上有超过8000颗星,迭代发布了超过100多个版本,上百位社区贡献者,是国内领先的开源可观测性解决方案。
快猫星云以开源夜莺为内核打造的“Flashcat平台”,是国内顶级互联⽹公司可观测性实践的产品化落地,致力于让可观测性技术更好的服务企业,保障服务稳定性。Flashcat 平台具有以下特点:
- 统一采集:采用插件化思路,内置集成上百种采集插件,服务器、网络设备、中间件、数据库、应用、业务,均可监控,开箱即用。
- 统一告警:支持几十种数据源对接,收集各类监控系统的告警事件,进行统一的告警收敛、降噪、排班、认领、升级、协同,大幅提升告警处理效率。
- 统一观测:将 Metrics、Logs、Traces、Events、Profiling 等多种可观测性数据融会贯通,并预置行业最佳实践,既提供全局业务视角、技术视角的驾驶舱,也提供层层下钻的故障定位能力,有效缩短故障发现和定位时间。
