
装个包遇到NewConnectionError: [Errno -2] Name or service not known第一反应是不是网断了我第一次在服务器上碰到这个报错时也这么想重启了网络服务、重新 DHCP、甚至重启了机器折腾了一圈包还是装不上。后来才反应过来这根本不是网络连通性的问题而是DNS 解析失败——你的机器压根没把pypi.org这个域名翻译成 IP 地址自然就建立不了连接。这个报错在 Python 包管理里属于高频问题尤其是刚接触 pip、在云服务器、公司内网或 Docker 容器里装包的时候几乎隔几天就能在社区里看到一次。核心里就一句话pip 默认依赖系统 DNS 去解析 PyPI 域名只要 DNS 配置不对、解析超时或 hosts 被干扰你就会看到这个报错。这篇文章我会从报错的底层逻辑讲起再给出几套能直接用的排查和修复方案最后录一段完整的排障过程。适合刚入门 Python 的朋友快速止血也适合运维同学当排查手册参考。1. 先搞清楚这个报错到底在说什么1.1 NewConnectionError 的完整拆解先看报错的完整形态常见的会长这样$ pip install requests WARNING: Retrying (Retry total4, connectNone, readNone, redirectNone, statusNone) after connection broken by NewConnectionError(pip._vendor.urllib3.connection.HTTPConnection object at 0x7f...: Failed to establish a new connection: [Errno -2] Name or service not known): /simple/requests/ WARNING: Retrying (Retry total3, connectNone, readNone, redirectNone, statusNone) after connection broken by ...这里面的关键信息分几层NewConnectionError是 urllib3 抛出的异常pip 内部的核心 HTTP 客户端就是 urllib3所以报错类型是 urllib3 体系里的。Failed to establish a new connection表示 TCP 连接建立前的某个环节就失败了。[Errno -2] Name or service not known来自系统底层的getaddrinfo()调用对应 POSIX 规范里的EAI_NONAME翻译成人话就是系统查不到这个主机名对应的 IP 地址。所以这个报错的本质不是“连不上”而是“不知道要去连哪里”。你可以理解成快递员拿到了一个写着“北京中关村”的收件地址但地图导航里根本搜不到“北京中关村”这个地名那快递自然送不出去。对 pip 来说“北京中关村”就是pypi.org这个域名“地图导航”就是你的系统 DNS。注意一个细节报错重试了 4 次才最终失败这是 pip 默认的机制。它默认retries5就是给自己多次尝试的机会。如果每次都秒失败基本可以锁定就是解析问题如果时好时坏可能还夹杂着网络抖动。1.2 为什么 pip 特别容易踩中 DNS 问题很多人会觉得奇怪浏览器上网好好的Python 装个包怎么就不行这里有个认知盲区——你浏览器能打开网页不代表系统 DNS 是健康的。浏览器有自身的 DNS 缓存、有 HTTP 代理配置、甚至有的浏览器用 DoH基于 HTTPS 的 DNS 查询它能绕开系统 DNS 去解析域名。但 pip 不一样pip 默认直连系统 DNS不会自动走浏览器的代理设置。pip 对 PyPI 域名的解析结果没有本地缓存每次安装都要现查。很多服务器在初始化时/etc/resolv.conf是残留的虚拟化平台配置迁移或重启后可能指向一个已经不存在的内网 DNS。在没有明确配置的情况下系统可能走了默认路由的 DNS而这个 DNS 恰好是内网专用的对公网域名解析支持很差。另一个常见场景是公司内网或学校机房办公网访问百度、GitHub 都没问题但解析 PyPI 和一些 CDN 域名时内网 DNS 会返回异常结果甚至拒绝解析。这种隔离环境下pip 装包失败几乎是必然的。这也是为什么很多企业内部的 Python 开发者都习惯先配一个镜像源或私有 PyPI。2. 动手之前5分钟快速定位故障层2.1 用“分而治出”的办法判断问题在哪一层修复之前先别急着改配置先用三个命令做基础判断。这一步要建立“分层排查”的思维DNS 解析是一层网络连通性是另一层应用软件配置是再往上一层。挨个排除才能少走弯路。第一步看域名能不能解析$ ping pypi.org -c 3 ping: pypi.org: Temporary failure in name resolution或者用专门的 DNS 查询工具$ nslookup pypi.org Server: 10.0.0.2 Address: 10.0.0.2#53 ** server cant find pypi.org: NXDOMAIN看到Temporary failure in name resolution或者server cant find就说明解析确实失败了。也可以用更现代的dig工具$ dig pypi.org 223.5.5.5这里223.5.5.5意思是直接用指定的 DNS 服务器查询不走系统默认配置。如果这条命令能返回 IP而走系统默认 DNS 查不到那问题就锁定在“系统配置的 DNS 服务器不可靠”上。第二步跳过 DNS 直接拿 IP 测试连通性。先去网上查一下pypi.org当前的真实 IP或者用之前解析出来的 IP然后直接 curl$ curl -I --resolve pypi.org:443:151.101.0.223 https://pypi.org/simple/如果这样能通那就进一步确认网络本身没问题问题出在名字翻译环节。第三步看系统配置文件。Linux 上核心就是/etc/resolv.conf$ cat /etc/resolv.conf nameserver 10.0.0.2 nameserver 10.0.0.3如果里面只有一两个内网地址甚至内容为空那基本破案了。2.2 排查过程中要留意的几个细节几个容易误导判断的细节我这里单独列一下全是实操中见过的问题ping 通了但不是走 DNS 查到的有些系统里 ping 会读/etc/hosts而 hosts 里早就有人写死了 pypi.org 的记录。这时候 ping 通不代表 DNS 正常。要验证就临时把 hosts 里的条目注释掉再看。curl 能通但 pip 不行你系统上可能有 HTTP 代理环境变量http_proxy、https_proxy对 curl 是生效的但 pip 如果没有显式配置trusted-host或proxy可能不会正确识别。先执行env | grep -i proxy看看有没有代理变量。pip 里残留了旧的 index-url这不是 DNS 问题但表现很像——pip 报错指向的域名可能不是 PyPI 官方而是某个已经失效的私有源。执行pip config list看看全局配置里有没有global.index-url。IPv6 干扰有的机器解析返回了 IPv6 地址但网络环境根本不支持 IPv6 路由导致连接卡住超时。这种场景下可以测试强制走 IPv4pip install requests --force-ipv4或者关闭系统的 IPv6 解析偏好。诊断阶段的核心理念就是先确认“能不能解析”再确认“能不能连通”最后再看“pip 自己的配置有没有捣乱”。按这个顺序来10 分钟之内基本能定位。3. 一套能直接抄作业的修复方案3.1 方案 A给系统配置一个可靠的 DNS这是最直接的解法。既然问题出在 DNS那就换一个能正常解析的 DNS。不同平台的改法不一样Linux 临时生效直接改/etc/resolv.conf$ sudo tee /etc/resolv.conf /dev/null EOF nameserver 223.5.5.5 nameserver 119.29.29.29 EOFWindows 的话在“网络和共享中心 → 更改适配器设置 → IPv4 属性”里把 DNS 服务器改成公共 DNS。macOS 则在“系统设置 → 网络 → 高级 → DNS”里添加服务器。这里推荐两个国内公共 DNS阿里云的223.5.5.5和腾讯云的119.29.29.29。它们的共同特点是解析速度快、在国内网络环境下访问稳定、对主流开发站点支持完善而且不劫持 NXDOMAIN就是一个不存在的域名该返回什么就返回什么。顺带说一句很多人念叨的114.114.114.114是国内老牌的公共 DNS由南京信风运营也可以作为备选。我个人的习惯是写两个 nameserver这样可以避免单个节点故障导致解析中断。改完之后别急着说“好了”先验证$ nslookup pypi.org $ pip install requests这里有个 Linux 上的大坑很多云服务器、虚拟化平台会自动重置/etc/resolv.conf特别是你用了 NetworkManager、systemd-resolved 或者 cloud-init 这类工具时。有时候即使你改了 resolv.conf下一次重启又会被覆盖回原来的内网 DNS。这种场景下建议用系统原生的网络配置来设置 DNS。以使用 netplan 的 Ubuntu 服务器为例应在/etc/netplan/下的 .yaml 文件中配置nameservers段再执行sudo netplan apply而不是直接改 resolv.conf。3.2 方案 B换一个不依赖官方域的镜像源如果你不想折腾系统级配置或者系统 DNS 就是内网强制指定、改不了另一个行之有效的方案是给 pip 换源。基本原理是让 pip 访问一个解析稳定、网络路径更短的镜像站从源头避开 DNS 解析不稳定的问题。pip 配置源的方式非常简单一行命令即可$ pip config set global.index-url https://mirrors.aliyun.com/pypi/simple/也可以用清华 TUNA 的源$ pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple/这两条命令会把配置写入 pip 的全局配置文件。Linux 下通常是~/.config/pip/pip.confWindows 下是%APPDATA%\pip\pip.ini。除了命令行设置也可以手写配置文件效果一样。选镜像源时有几个经验供参考阿里云镜像在大部分运营商网络下速度都不错包同步也比较及时。清华 TUNA 同样稳定但偶尔在高峰期会有流量压力。如果你在中国大陆之外的网络环境其实官方 PyPI 就挺好不一定非要换源。认准一个源长期用不要今天换阿里明天换清华。换源之后的索引缓存是独立的有些包需要重新下载索引元数据反复横跳只会增加无谓的等待时间。还有一点要注意早期 pip 在访问非官方源时如果源缺少有效的 HTTPS 证书链需要加--trusted-host参数绕过验证。但现在的知名镜像源证书都是齐全的正常不需要这个参数。我见过不少教程一上来就让用户加--trusted-host这其实掩盖了真正的证书问题不建议无脑加。3.3 方案 C用 hosts 文件固定解析记录还有一种适合特殊场景的办法直接改 hosts 文件把域名和 IP 的对应关系写死。应用场景主要两类一类是内网有私有 PyPI 服务器域名只在内网 DNS 里存在但有时内网 DNS 抽风解析失败导致 pip 装不了包。这时候在 hosts 里加一条内网服务器的记录绕过 DNS 查询最直接。另一类是做自动化镜像同步的机器希望每次请求都固定到同一个 IP避免 DNS 轮询导致的连接不稳定。Linux / macOS 的 hosts 文件在/etc/hostsWindows 在C:\Windows\System32\drivers\etc\hosts。加一行151.101.0.223 pypi.org files.pythonhosted.org改 hosts 是一个有效的急救手段但我不建议作为长期方案。原因很简单IP 变了你就得手动改。尤其是pypi.org背后有 CDN不同区域、不同时间的解析结果可能不一样写死一个 IP 很可能让下载速度变慢甚至失败。所以 hosts 方案的定位是“临时救急”真正常态化使用还是建议用系统 DNS 镜像源配合。3.4 方案 D给 pip 加上超时和重试参数如果你面对的是间歇性网络抖动比如 DNS 有时候正常、有时候超时直接加大超时时间和重试次数能明显提高装包成功率。这个方案不能根治问题但很“止血”。命令行的做法$ pip install requests --timeout 60 --retries 10--timeout单位是秒表示每个连接阶段的超时阈值--retries是重试次数。如果你的网络环境不稳定把超时加到 60 秒、重试加到 10 次通常能撑过短时抖动。更推荐的做法是把这两个参数写进配置文件$ pip config set global.timeout 60 $ pip config set global.retries 10这样后续所有 pip 命令都自动带上重试和超时参数不用每次手写。我自己实践下来这两个参数在 Docker 构建镜像时特别好用。Docker build 过程中网络如果出现瞬时抖断默认的 15 秒超时可能直接让构建失败而调大超时和重试之后构建稳定性明显提升。4. 一次真实的排查全程复盘4.1 现场环境与报错记录为了讲得更透我把一个实际的排障过程完整复现出来。背景是公司一台内网 Linux 服务器系统是 Ubuntu 22.04需要安装一个 Python 包用来做数据处理但服务器本身只能通过内网 DNS 访问公网且这台机器之前已经跑过不少 Python 项目。执行安装命令后终端输出$ pip install pandas Collecting pandas Retrying (Retry total4, connectNone, readNone, redirectNone, statusNone) after connection broken by NewConnectionError(pip._vendor.urllib3.connection.HTTPConnection object at 0x7f20b910c460: Failed to establish a new connection: [Errno -2] Name or service not known): /simple/pandas/ Retrying (Retry total3, connectNone, readNone, redirectNone, statusNone) after connection broken by NewConnectionError(pip._vendor.urllib3.connection.HTTPConnection object at 0x7f20b910c460: Failed to establish a new connection: [Errno -2] Name or service not known): /simple/pandas/看到[Errno -2] Name or service not known我第一反应就是解析问题。马上验证$ ping pypi.org -c 3 ping: pypi.org: Temporary failure in name resolution果然如此。接着看/etc/resolv.conf$ cat /etc/resolv.conf # Generated by NetworkManager nameserver 10.0.0.2内网 DNS 指向10.0.0.2但问题是这个 DNS 服务器已经下线了解析任何公网域名都返回失败。4.2 从 DNS 到镜像源一步步解决第一步临时修改/etc/resolv.conf指向公共 DNS$ sudo tee /etc/resolv.conf /dev/null EOF nameserver 223.5.5.5 nameserver 119.29.29.29 EOF再验证$ ping pypi.org -c 3 PING pypi.org (151.101.0.223) 56(84) bytes of data. 64 bytes from 151.101.0.223: icmp_seq1 ttl53 time28.2 ms解析恢复pip 装包也能正常开始了。但这里有个隐患这台服务器是 NetworkManager 管理的下次重启后/etc/resolv.conf大概率会被重置回内网 DNS。为了根治我直接编辑/etc/netplan/00-installer-config.yaml把 DNS 配置写进网络配置文件里network: ethernets: ens18: dhcp4: true nameservers: addresses: [223.5.5.5, 119.29.29.29] version: 2然后执行sudo netplan apply这就保证了重启后配置不会丢失。第二步考虑到服务器网络到 PyPI 官方域名的链路质量一般我把 pip 默认源切到了阿里云镜像$ pip config set global.index-url https://mirrors.aliyun.com/pypi/simple/这一步是为了后续所有包下载都能稳定快速而不只是解决当前这一个包。设置完后用pip config list确认$ pip config list global.index-urlhttps://mirrors.aliyun.com/pypi/simple/第三步重新安装 pandas$ pip install pandas这次顺利拉取依赖并完成安装。整个过程从开始排查到装完包大概用了 20 分钟左右其中大部分时间花在确认 DNS 故障和修改 netplan 配置上。4.3 踩坑记录说说这次排障踩到的坑给后来者提个醒改 resolv.conf 不一定会被系统尊重。我之前直接在服务器上改 resolv.conf当时生效了但过了两天再连服务器发现又变回了原样。后来才意识到是 NetworkManager 的接管逻辑在捣乱光改 resolv.conf 不能持久化。镜像源也不是万能的。切到阿里云镜像后有一次装一个比较冷门的包发现镜像上没有这个包报的是No matching distribution found。后来用官方源临时装了一次才好。所以建议把镜像源当作默认配置遇到镜缺失时再用-i https://pypi.org/simple/临时切换回官方源。别忘记虚拟环境的 pip 是独立的。如果你在 venv 环境里执行pip config set会改变全局配置但pip install时实际生效的配置层级要看你有没有在 venv 里再建一层配置。为了在多个环境之间保持一致的安装体验我后来直接把 index-url 写进了项目文件夹下的pip.conf跟随代码仓库一起走别人拉到代码后不用手动改源。5. 容易被误判成 DNS 的几种报错场景5.1 把 DNS 报错当成网络断连[Errno -2] Name or service not known这个错最容易被误判成“服务器上不了网”。我在群里见过不止一个朋友遇到这报错就去重启网卡、重启网络服务、甚至重装系统。其实判断是不是网络断连很简单拿一个不依赖 DNS 的地址做连通性测试比如直接 ping 一个 IP$ ping 223.5.5.5 -c 3如果 IP 能通但域名解析失败那网络就没断问题只在 DNS。先把这两个场景分清楚能省很多没必要的折腾。5.2 和“must give at least one requirement”这类提示混淆有相当多的提问是把网络报错和另一个不同报错混在一起说的。比如这种$ pip install ERROR: You must give at least one requirement to install (see pip help install)这个报错和 DNS 一点关系都没有纯粹是因为pip install后面没写要装的包名。但实际问问题的人往往会说“我装包失败报错里说必须给一个 requirement是不是网络问题”其实只要看完报错信息就知道这是最基本的命令行使用问题。解决方式也很简单正确格式是$ pip install 包名如果想一次装多个包$ pip install requests pandas numpy如果想从 requirements 文件安装$ pip install -r requirements.txt这里我多说一句遇到任何 pip 报错第一件事是完整读报错信息。pip 的报错通常写得很直白前两行就点明了问题所在。DNS 问题会提示Name or service not known命令行缺参会提示must give at least one requirementSSL 证书问题会提示CERTIFICATE_VERIFY_FAILED超时会提示Read timed out。每种报错的解决办法都不一样无脑搜报错关键字很容易被带偏。5.3 其他容易和 DNS 混淆的连接类报错我把平时踩过的几个高频 pip 报错放在一起对比帮你快速区分报错关键字实际含义常见原因首选处理思路[Errno -2] Name or service not knownDNS 解析失败系统 DNS 不可用、域名拼写错误、hosts 异常修复系统 DNS、换源、检查 hosts[Errno 111] Connection refused目标端口拒绝连接防火墙拦截、代理端口错误、服务未监听检查防火墙、确认代理配置[Errno 110] Connection timed out连接超时网络路径不通、目标高延迟、IP 被限换网络环境、加超时重试、换镜像源CERTIFICATE_VERIFY_FAILEDHTTPS 证书校验失败pip 版本过旧、系统 CA 证书不完整升级 pip、安装 ca-certificatesNo matching distribution found包源里找不到目标包镜像源缺失、包名拼写错误、Python 版本与包不兼容切回官方源、检查包名、检查 Python 版本Read timed out读取响应超时网络抖动、大文件下载慢调大--timeout、增加--retries这张表是我根据自己的排障经验整理的不一定覆盖所有可能但命中率很高。遇到问题先对号入座再决定处理路径比乱试一通高效得多。6. 预防与日常维护习惯6.1 让 DNS 配置“自愈”DNS 问题有一个让人防不胜防的特点配置被重置时往往悄无声息。今天装包是好的明天换了个网络环境或者系统重启了一次配置就变了。为了尽量避免这种情况我平时会做两件事第一把 DNS 配置固化到系统网络管理层的配置里而不是只改 resolv.conf。前面已经提过 netplan 的改法这里补充一下其他主流平台的做法Ubuntu 18.04 以前的版本用/etc/network/interfaces配置dns-nameservers字段。CentOS / RHEL 系列可以用 NetworkManager 的nmcli命令修改连接的 DNSnmcli con mod System eth0 ipv4.dns 223.5.5.5 119.29.29.29再nmcli con up System eth0。使用 systemd 纯内建网络服务的机器可以用/etc/systemd/resolved.conf里的DNS字段。第二用一个简单的定时检查脚本把 DNS 健康检查变成日常习惯。比如这样一个脚本逻辑尝试解析pypi.org如果失败就自动重置 resolv.conf 并记录日志。不用做得太复杂几行 shell 就够#!/bin/bash if ! nslookup pypi.org /dev/null 21; then echo nameserver 223.5.5.5 /etc/resolv.conf echo DNS is broken, reset to 223.5.5.5 at $(date) /var/log/dns-check.log fi配合 cron 每 5 分钟跑一次基本能保证服务器长期稳定。6.2 pip 配置的职责划分pip 的配置文件有优先级之分搞不清楚会带来很隐蔽的问题。按优先级从低到高排列系统级配置/etc/pip.conf或/etc/xdg/pip/pip.confLinux覆盖所有用户。用户级配置~/.config/pip/pip.conf或~/.pip/pip.confLinux作用于当前用户。虚拟环境内配置位于 venv 根目录下pip.conf只作用于这个虚拟环境。命令行参数优先级最高实时生效。我强烈建议用pip config list -v来查看当前所有配置来源$ pip config list -v ...global... /usr/etc/pip.conf, /etc/xdg/pip/pip.conf, /etc/pip.conf ... ...user... /root/.config/pip/pip.conf ... ...site... /root/.venv/pip.conf ...看到所有层级就能快速判断哪一层配置在起作用。一个常见问题是你在全局配了阿里云镜像但某个虚拟环境里残留了旧的私有源配置导致镜像源设置“不生效”。用这条命令一看就清楚。6.3 顺手把 pip 本身也升级一下最后聊一个容易被忽略但很实际的建议升级 pip 本身。旧版 pip 在解析 HTTPS 证书时偶尔有兼容性问题会在网络正常的情况下报各种各样的连接错误。而且新版 pip 对依赖解析、重试机制的实现都更稳。在 Python 3 的环境里建议用模块方式调用 pip$ python -m pip install --upgrade pip这里强调用python -m pip而不是直接pip原因是能保证 pip 和你当前使用的 Python 解释器严格匹配。很多人机器上同时存在 Python 2 和 Python 3或者装了多个版本直接用pip可能调到一个“不属于当前解释器”的 pip导致装完包后 import 依然失败。配合镜像源升级$ python -m pip install -i https://mirrors.aliyun.com/pypi/simple/ --upgrade pip如果你已经在配置文件里设置了源就不需要每次带-i参数了。回到开头那个报错。我现在再看到Name or service not known大脑会直接进入一套肌肉记忆式的检查流程先看 hosts 有没有被改再看 resolv.conf 指向谁然后nslookup pypi.org试一下最后顺手pip config list看有没有历史残留的源配置。整套流程下来用不了 5 分钟基本能干掉 90% 的同类问题。最后分享一个我自己的小习惯服务器上长期备一个~/.pip/pip.conf里面只写三样东西——index-url、timeout、retries。这样不管当时网络环境多糟糕至少装包的容错率高很多。如果你也经常在三台以上机器或容器里折腾 Python不妨现在就打开终端执行一下pip config list -v看看自己手头的配置是不是符合预期。有问题早点发现总比深夜上线时被一个莫名的连接报错搞得焦头烂额强。