1. 简介
在 Docker 容器内使用 pip 安装 Python 包时,有时会因为 DNS 解析问题导致安装失败。常见的症状包括连接 PyPI 超时、域名解析失败、下载速度极慢、连接被拒绝等。
本文将探讨几种排查和解决 Docker 容器中 pip 安装包时 DNS 问题的方法。所有命令均在 Ubuntu 22.04 + Docker Engine 27.5.0 环境下测试通过。
2. 常见 DNS 问题错误
2.1. 连接错误
pip 无法连接 PyPI 时,可能会出现如下报错:
Retrying (Retry(total=4, connect=None, read=None, redirect=None, status=None)) after connection broken by
'NewConnectionError('<pip._vendor.urllib3.connection.VerifiedHTTPSConnection object at 0x7f6d5d343d30>:
Failed to establish a new connection: [Errno -3] Temporary failure in name resolution',)': /simple/django/
✅ 关键点:提示“Temporary failure in name resolution”,说明容器无法解析域名。
2.2. SSL 证书错误
在尝试验证 SSL 证书时,也可能因为域名无法解析而失败:
Could not fetch URL https://pypi.org/simple/requests/: There was a problem confirming the ssl certificate:
HTTPSConnectionPool(host='pypi.org', port=443): Max retries exceeded with url:
/simple/requests/ (Caused by NameResolutionError("<urllib3.connection.HTTPSConnection object at 0x7f8c1c9b6d90>:
Failed to resolve 'pypi.org' ([Errno -2] Name or service not known)")) - skipping
✅ 关键点:提示“Failed to resolve 'pypi.org'”,说明容器无法解析该域名。
3. 排查环境搭建
为了排查问题,我们可以创建一个测试容器,安装常用网络工具如 dig
、nslookup
、ping
等。
创建测试目录
$ mkdir -p pip-dns-demo && cd pip-dns-demo
编写 Dockerfile
FROM python:3.9-slim
# 安装网络工具
RUN apt-get update && apt-get install -y \
iputils-ping \
dnsutils \
curl \
net-tools \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
CMD ["bash"]
构建镜像
$ docker build -t app .
启动交互式容器
$ docker run -it app
进入容器后即可开始排查。
4. 诊断步骤
4.1. 验证容器网络连通性
在容器内尝试 ping 通 Google 的 DNS 服务器:
$ ping -c 4 8.8.8.8
再尝试 ping 通 PyPI:
$ ping -c 4 pypi.org
✅ 正常输出:有响应、无丢包
❌ 异常输出:ping 不通、丢包严重
4.2. 检查 DNS 配置
查看容器的 DNS 配置文件:
$ cat /etc/resolv.conf
使用 dig
检查域名解析是否正常:
$ dig pypi.org
✅ 正常输出:包含 ANSWER SECTION
和 IP 地址
❌ 异常输出:返回 status: SERVFAIL
或 NXDOMAIN
4.3. 检查 Docker DNS 设置
查看 Docker daemon 的配置文件:
$ cat /etc/docker/daemon.json
查看 bridge 网络配置:
$ docker network inspect bridge
⚠️ 注意:确保 enable_ip_masquerade
为 true
,否则容器可能无法访问外部网络。
5. 解决方案汇总
5.1. 配置 Docker Daemon 使用固定 DNS
修改 /etc/docker/daemon.json
:
{
"dns": ["8.8.8.8", "8.8.4.4"]
}
重启 Docker 服务:
$ sudo systemctl restart docker
✅ 优点:适用于所有容器
⚠️ 注意:需要 root 权限
5.2. 容器运行时指定 DNS
启动容器时手动指定 DNS:
$ docker run -it --dns 8.8.8.8 --dns 8.8.4.4 app
验证 DNS 设置:
$ cat /etc/resolv.conf
✅ 优点:灵活,适合临时调试
✅ docker-compose.yml 示例:
services:
demo:
image: app
dns:
- 8.8.8.8
- 8.8.4.4
5.3. 使用 host 网络模式
直接使用宿主机网络:
$ docker run --network=host -it app
✅ 优点:快速解决 DNS 问题
❌ 缺点:牺牲容器网络隔离性,不推荐生产使用
6. 验证示例应用
6.1. 编写测试代码
from flask import Flask
import socket
app = Flask(__name__)
@app.route('/')
def check_dns():
domains = ['pypi.org', 'google.com']
results = []
for domain in domains:
try:
ip = socket.gethostbyname(domain)
results.append(f"{domain}: ✓ Resolved to {ip}")
except socket.gaierror:
results.append(f"{domain}: ✗ Failed to resolve")
return "<br>".join(results)
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
6.2. 添加依赖
flask==2.2.5
werkzeug==2.2.3
6.3. 修改 Dockerfile
FROM python:3.9-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app.py .
EXPOSE 5000
CMD ["python", "app.py"]
6.4. 构建并测试
$ docker build -t app .
$ docker run -p 5000:5000 app &
$ curl localhost:5000
✅ 预期输出:两个域名都成功解析
7. 其他注意事项
企业网络代理配置
若在公司网络中使用代理,需在 Dockerfile 中设置环境变量:
ENV HTTP_PROXY="http://example:8080"
ENV HTTPS_PROXY="http://example:8080"
或在 pip 命令中指定:
RUN pip install -r requirements.txt --proxy=http://addr:port
获取宿主机 DNS 地址
使用 nmcli
查看 DNS:
$ nmcli dev show | grep 'DNS'
将其写入 daemon.json
:
{
"dns": ["<server_ip>", "8.8.8.8", "8.8.4.4"]
}
✅ 优点:优先使用本地 DNS,失败时回退到 Google DNS
8. 总结
本文介绍了如何排查和解决 Docker 容器中 pip 安装包时的 DNS 问题。我们从基础网络测试入手,逐步深入排查 DNS 配置,并提供了多种可行的解决方案。
✅ 推荐顺序:
- 优先配置 Docker Daemon DNS
- 其次在容器启动时指定 DNS
- 最后考虑使用 host 网络模式(仅限调试)
⚠️ 注意:host 模式虽能快速解决问题,但会破坏容器网络隔离性,生产环境慎用。