Docker 代理配置最佳实践:如何让 Docker 正确使用代理服务

背景

在使用 Docker 时,经常会遇到需要拉取镜像但网络连接不畅的情况。特别是在国内环境下,直接连接 Docker Hub 往往会遇到超时或连接失败的问题。虽然我们本地配置了代理服务(比如在 7890 端口),但 Docker 却无法正常使用这个代理,这是为什么呢?

问题描述

当我们尝试拉取 Docker 镜像时,经常会遇到这样的错误:

$ sudo docker pull gitea/gitea:latest
Error response from daemon: Get "https://registry-1.docker.io/v2/": context deadline exceeded

常见的解决方案尝试

1. 配置 Docker 守护进程配置文件

很多人的第一反应是修改 Docker 的配置文件 /etc/docker/daemon.json

{
    "proxies": {
        "default": {
            "httpProxy": "http://127.0.0.1:7890",
            "httpsProxy": "http://127.0.0.1:7890",
            "noProxy": "localhost,127.0.0.1"
        }
    }
}

但这种方式往往效果不理想,因为这个配置只在 Docker 守护进程层面生效,而且可能会因为配置格式问题导致 Docker 服务无法启动。

2. 使用环境变量

另一种常见的方式是使用环境变量:

export HTTP_PROXY="http://127.0.0.1:7890"
export HTTPS_PROXY="http://127.0.0.1:7890"
export ALL_PROXY="socks5://127.0.0.1:7890"
sudo -E docker pull gitea/gitea:latest

这种方式虽然简单,但只对当前终端会话有效,而且使用 sudo 时可能会丢失环境变量。

最佳解决方案

经过多次尝试,最有效的解决方案是通过 systemd 服务配置来设置代理:

  1. 创建 Docker 服务的代理配置目录:
sudo mkdir -p /etc/systemd/system/docker.service.d/
  1. 创建代理配置文件:
sudo vi /etc/systemd/system/docker.service.d/http-proxy.conf
  1. 添加以下配置:
[Service]
Environment="HTTP_PROXY=http://127.0.0.1:7890"
Environment="HTTPS_PROXY=http://127.0.0.1:7890"
Environment="NO_PROXY=localhost,127.0.0.1"
  1. 重新加载配置并重启 Docker 服务:
sudo systemctl daemon-reload
sudo systemctl restart docker

为什么这种方式最有效?

技术原理解释

这种配置方式之所以最有效,是因为它利用了 Linux 系统的进程环境变量继承机制:

  1. systemd 在启动 Docker 服务时,会先设置这些环境变量
  2. Docker 守护进程(dockerd)作为子进程会继承这些环境变量
  3. Docker 创建的所有子进程也会继承这些环境变量
  4. 这些标准的代理环境变量(HTTP_PROXY/HTTPS_PROXY)会被大多数网络客户端程序识别和使用

进程继承关系图

systemd
  └── dockerd (继承环境变量)
      ├── docker-container-1 (继承环境变量)
      ├── docker-container-2 (继承环境变量)
      └── docker-container-n (继承环境变量)

配置方案对比

配置方式优点缺点
daemon.jsonDocker 原生配置容易导致服务启动失败,配置复杂
环境变量简单直接临时生效,需要每次设置
systemd 服务配置永久生效,作用于整个进程树需要重启 Docker 服务

最佳实践建议

  1. 优先使用 systemd 服务配置方式设置代理
  2. 设置 NO_PROXY 排除本地网络地址
  3. 定期检查代理配置是否生效
  4. 如果使用的是私有镜像仓库,记得将其加入到 NO_PROXY 中

故障排查

如果配置后还是无法正常拉取镜像,可以:

  1. 检查代理服务是否正常工作:
curl -x http://127.0.0.1:7890 https://www.google.com
  1. 查看 Docker 服务状态:

3. 检查 Docker 环境变量:
```bash
sudo systemctl show docker --property Environment

总结

通过 systemd 服务配置的方式设置 Docker 代理是最可靠的解决方案,它能确保整个 Docker 进程树都正确使用代理服务。这种方式不仅配置简单,而且能永久生效,是管理 Docker 代理设置的最佳实践。

参考资料