香橙派调整podman网络
📝 Podman 容器域名解析故障排查与修复笔记
1. 问题现象
在 Orange Pi Zero 3(开发板二,ARM64 架构,Podman 4.3.1)上使用 podman-compose 部署服务(如 alist 和 frpc)并加入同一个自定义桥接网络(znet)时,出现以下异常:
- 在
alist容器内ping frpc无法解析为局域网内部 IP(如10.89.0.2),而是直接穿透到公网公共 DNS,解析出了外网 IP(如74.208.14.186)。 - 在网络配置中查看,该网络明明已显示
"dns_enabled": true。
2. 核心病灶排查与推演
诊断一:容器内解析被拒绝(REFUSED)
在容器内指定网关 IP(10.89.0.1)进行 nslookup 测试:
podman exec alist nslookup frpc 10.89.0.1
错误返回:
** server can't find frpc: REFUSED
推论: 网关 53 端口上有 DNS 服务在运行,但它显式拒绝了容器的请求,或者该服务处于半瘫痪状态。
诊断二:底层 CNI 缺少 dnsname 插件
运行 podman network ls 时系统抛出警告:
WARN[0000] Error validating CNI config file /etc/cni/net.d/znet.conflist: [failed to find plugin "dnsname" in path [...]]
推论: 当前 Podman 使用的是老旧的 CNI 网络后端。自定义网络虽开启了 DNS 开关,但由于系统缺失 dnsname 二进制插件,导致本地 DNS 劫持转发器根本没有正常运转。
诊断三:宿主机 53 端口遭遇“恶霸”占用
补齐 dnsname 插件源码并编译后,启动容器遭遇报错:
Error: plugin type="dnsname" failed (add): ... dnsmasq failed with "\ndnsmasq: failed to create listening socket for 10.89.0.1: Address already in use\n"
通过 netstat -tunlp | grep :53 抓出真凶:
netstat -tunlp | grep :53
# 或者如果 netstat 没装,用 ss 命令:
ss -tunlp | grep :53
tcp 0 0 0.0.0.0:53 0.0.0.0:* LISTEN 1125/dnsmasq
推论: 宿主机上开启了另一个 dnsmasq 服务(通常由 Clash 或系统流控服务拉起),它霸道地监听了 0.0.0.0:53(所有接口)。这导致 Podman 的 dnsname 插件尝试在虚拟网关 10.89.0.1:53 上摆摊时,遭遇端口冲突。
3. 终极解决方案(正攻法)
整个修复流程分为两步:本地编译补齐插件 $\rightarrow$ 隔离宿主机端口霸占。
第一步:在本地编译补齐 dnsname 插件
由于 Orange Pi Zero 3 是 64 位系统(aarch64),不能直接从 32 位系统(armv7l)复制二进制文件,最稳妥的方式是本地源码编译:
- 安装编译工具链(Go 语言与 Make):
下载二进制文件
https://github.com/containers/dnsname/releases/tag/v1.3.0
解压tar -xzf dnsname-1.3.0.tar.gz 后,编译文件
apt update && apt install -y golang-go make
进入
dnsname源码目录并执行编译:make
注:即使遇到 fatal: not a git repository 警告可直接忽略,Go 编译器已成功在 bin/ 目录下生成了对应的二进制文件。
将编译好的二进制可执行文件移动到 CNI 插件专属目录并赋予权限:
mkdir -p /usr/libexec/cni cp bin/dnsname /usr/libexec/cni/ chmod +x /usr/libexec/cni/dnsname
第二步:修改宿主机 dnsmasq 配置,释放虚拟网卡端口
限制宿主机老旧 dnsmasq 的全盘通吃行为,让它交出 Podman 虚拟网卡的 53 端口。
打开或新建宿主机配置文件:
nano /etc/dnsmasq.conf在末尾追加以下两行核心参数:
# 强制 dnsmasq 只绑定它真正监听的物理网卡接口,不要霸占 0.0.0.0 bind-interfaces # 指定只在本地环回口、物理网卡上听包(把 eth0 换成你 ip a 看到的真实物理网卡名) interface=lo,eth0保存退出(
Ctrl+O回车,Ctrl+X),并重启宿主机 DNS 服务:systemctl restart dnsmasq
4. 清理环境与验证启动
完成上述底层网络修复后,建议对 Podman 缓存进行彻底清洗,防止旧的错误配置死灰复燃。
1. 环境大清洗
# 强行删除所有容器
podman rm -fa
# 强行删除所有自定义网络
podman network rm $(podman network ls -q) 2>/dev/null
# 抹除磁盘上的 CNI 残留网络缓存(核心:防止残留配置锁死旧 IP 报错)
rm -rf /var/lib/cni/networks/*
2. 重新拉起 Compose 服务
回到你的项目目录(如 /data/frp),直接执行:
podman-compose up -d
此时,Podman 的 dnsname 成功占领了空闲出来的 10.89.x.1:53 端口,容器顺利启动,不会再报任何 Address already in use 错误。
3. 最终验证
进入 alist 容器,直接通过容器名 ping 同网络下的 frpc:
podman exec alist ping frpc
预期正确结果:
容器内部完美秒通,并准确指向内部私有桥接 IP(如 10.89.x.x),容器域名自动解析大功告成!
💡 备选方案(掀桌子法)
如果未来不想折腾任何底层的 DNS 插件与端口冲突,可以直接在 podman-compose.yml 中利用 extra_hosts 绕过 DNS 解析,在容器层面硬编码写死映射:
services:
alist:
image: docker.io/xhofe/alist:latest
# ... 其他配置
extra_hosts:
- "frpc:10.89.0.2" # 强行在 alist 的 /etc/hosts 中注入映射,不再询问任何 DNS 服务器