# 运维排查记录:容器无法访问外网 + NPM 免费证书签发 > 场景:阿里云 ECS,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 断") ```bash 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)重载会把这些规则冲掉,症状就是"宿主机能上网、容器全断"。 ```bash # 一招见效:重启 dockerd 会原样重建全部 iptables 规则(所有容器闪断几秒) sudo systemctl restart docker ``` 重启无效再查: ```bash 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,别用界面示例值): ```ini dns_aliyun_access_key = dns_aliyun_access_key_secret = ``` - AccessKey 用独立 RAM 子账号创建,授权 **AliyunDNSFullAccess**; 域名必须托管在该账号的云解析里; - NPM 会明文存这份凭证,泄露过的 Key 要及时轮换。 ### 2. 预装插件(国内/证书环境直连 pypi 慢,先手动装好,NPM 就不用在线装了) ```bash 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 测试环境,不耗正式配额,验证全链路) ```bash 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 ` 看真实报错,多半是在线装插件失败 | | 改容器 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=超时(防火墙丢包典型) |