You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4.9 KiB
4.9 KiB
运维排查记录:容器无法访问外网 + NPM 免费证书签发
场景:阿里云 ECS(lingpu-hk 8.217.193.140),Nginx Proxy Manager(NPM)容器化部署, 给 starpivot-dev.lingpu.net 申请 Let's Encrypt 证书(阿里云 DNS challenge)。 记录于 2026-07-22。
一、问题现象(三层套娃)
- NPM 界面报
data/meta/propagation_seconds must be integer; - 填了整数后变成
Internal Error; - 深挖发现根因:宿主机能上网,但所有容器都无法访问外网——
NPM 点 Save 时要在线 pip 安装
certbot-dns-aliyun插件,没网就 Internal Error。
二、排查方法(分层定位,可复用)
1. 容器网络三连测(先分清"全断"还是"只 DNS 断")
NPM=$(docker ps --format '{{.Names}}' | grep -iE 'proxy|npm' | head -1)
# ① 测 TCP 出网(223.5.5.5 是阿里公共 DNS 官网,必有响应)
docker exec $NPM curl -skI --max-time 5 https://223.5.5.5 ; echo "退出码: $?"
# ② 测 UDP DNS 直连(绕过 Docker 内置 DNS 127.0.0.11)
docker exec $NPM python3 -c "
import socket
q = b'\x12\x34\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x04pypi\x03org\x00\x00\x01\x00\x01'
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM); s.settimeout(3)
s.sendto(q, ('223.5.5.5', 53)); print('DNS 直连成功', len(s.recv(512)), '字节')"
# ③ 测域名解析(走完整链路)
docker exec $NPM curl -sI --max-time 8 https://mirrors.aliyun.com ; echo "退出码: $?"
判读:
- ①②③全失败 → 容器出网通路断(NAT/iptables 问题),见下面第 2 步;
- ①通、③不通 → Docker 内置 DNS 转发问题,daemon.json 加
"dns": ["223.5.5.5","8.8.8.8"]; - 全通 → 网络没问题,另找原因。
关键认知:Docker 内置 DNS(127.0.0.11)转发外部查询时走的是容器的网络路径, 所以容器断网时改 resolv.conf / 换 DNS 都是无效的治标操作。
2. 根因与修复:iptables 规则被冲掉
Docker 运行时把 FORWARD 链默认策略设为 DROP,靠一组 ACCEPT 规则放行各网桥流量。 防火墙(firewalld/ufw)重载会把这些规则冲掉,症状就是"宿主机能上网、容器全断"。
# 一招见效:重启 dockerd 会原样重建全部 iptables 规则(所有容器闪断几秒)
sudo systemctl restart docker
重启无效再查:
sysctl net.ipv4.ip_forward # 必须是 1
sudo iptables -S FORWARD # -P FORWARD DROP 下必须有各网桥的 ACCEPT
sudo iptables -t nat -S POSTROUTING # 必须有各 docker 网段的 MASQUERADE
ip addr | grep "inet " # 对照下一条
docker network inspect $(docker network ls -q) | grep -i subnet
# ↑ 阿里云特有坑:VPC 常用 172.16.x,docker 从 172.17 往上分配,网段重叠路由必乱
三、NPM 申请 Let's Encrypt 证书(阿里云 DNS challenge)
1. 界面填写要点
-
Propagation Seconds 必须填整数(如
120),留空会触发 NPM 前端 bug; -
Credentials File Content(真实 AccessKey,别用界面示例值):
dns_aliyun_access_key = <AccessKey ID> dns_aliyun_access_key_secret = <AccessKey Secret> -
AccessKey 用独立 RAM 子账号创建,授权 AliyunDNSFullAccess; 域名必须托管在该账号的云解析里;
-
NPM 会明文存这份凭证,泄露过的 Key 要及时轮换。
2. 预装插件(国内/证书环境直连 pypi 慢,先手动装好,NPM 就不用在线装了)
docker exec $NPM pip install -i https://mirrors.aliyun.com/pypi/simple certbot-dns-aliyun
docker exec $NPM certbot plugins | grep -A2 aliyun # 确认识别到 dns-aliyun
3. 正式签发前先 dry-run(走 LE 测试环境,不耗正式配额,验证全链路)
docker exec $NPM sh -c 'printf "dns_aliyun_access_key = xxx\ndns_aliyun_access_key_secret = yyy\n" > /tmp/a.ini && chmod 600 /tmp/a.ini && \
certbot certonly --dry-run --non-interactive --agree-tos --email 你的邮箱 \
--authenticator dns-aliyun --dns-aliyun-credentials /tmp/a.ini \
--dns-aliyun-propagation-seconds 60 -d 你的域名; rm -f /tmp/a.ini'
# 看到 "The dry run was successful." 再回 NPM 界面点 Save,一次成功
四、避坑清单
| 坑 | 结论 |
|---|---|
| Propagation Seconds 留空 | NPM 前端 bug,必填整数(120) |
| Internal Error 无详情 | docker logs <NPM容器> 看真实报错,多半是在线装插件失败 |
| 改容器 resolv.conf 治断网 | 无效。断网根因在 iptables,先 systemctl restart docker |
| 阿里云 VPC vs docker 网段 | 172.16.x(VPC)与 172.17+(docker)可能重叠,inspect 对照 |
| 测试目标选错 | 用 https://223.5.5.5(必有响应),别用随机 IP 的 80 端口 |
| SSH 连续失败 | 服务器 sshd 有连接频率保护,间隔 30s 重试 |
| curl 退出码速查 | 0=成功;7=连不上;28=超时(防火墙丢包典型) |