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. 排查环境搭建

为了排查问题,我们可以创建一个测试容器,安装常用网络工具如 dignslookupping 等。

创建测试目录

$ 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: SERVFAILNXDOMAIN

4.3. 检查 Docker DNS 设置

查看 Docker daemon 的配置文件:

$ cat /etc/docker/daemon.json

查看 bridge 网络配置:

$ docker network inspect bridge

⚠️ 注意:确保 enable_ip_masqueradetrue,否则容器可能无法访问外部网络。


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 配置,并提供了多种可行的解决方案。

✅ 推荐顺序:

  1. 优先配置 Docker Daemon DNS
  2. 其次在容器启动时指定 DNS
  3. 最后考虑使用 host 网络模式(仅限调试)

⚠️ 注意:host 模式虽能快速解决问题,但会破坏容器网络隔离性,生产环境慎用。


原始标题:Troubleshooting DNS Issues in Docker Containers When Installing pip Packages