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

运维排查记录:容器无法访问外网 + NPM 免费证书签发

场景:阿里云 ECS(lingpu-hk 8.217.193.140),Nginx Proxy Manager(NPM)容器化部署, 给 starpivot-dev.lingpu.net 申请 Let's Encrypt 证书(阿里云 DNS challenge)。 记录于 2026-07-22。

一、问题现象(三层套娃)

  1. NPM 界面报 data/meta/propagation_seconds must be integer;
  2. 填了整数后变成 Internal Error;
  3. 深挖发现根因:宿主机能上网,但所有容器都无法访问外网—— 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=超时(防火墙丢包典型)