Docker 早已成为运行爬虫、机器人、广告自动化和小型服务的标准工具。但容器有个特点:它们生活在隔离环境中,对你电脑上设置的代理一无所知。结果容器里的脚本用你的真实 IP 访问互联网,而你还以为自己在走移动代理。本指南将一劳永逸地堵上这个漏洞。

引言:你将收获什么,这份指南适合谁

学完本教程后,你将能在 Docker 中所有三个可能层面配置代理:通过环境变量为单个容器配置、通过配置文件为整个 Docker 客户端和守护进程配置,以及通过网络层面用独立网关容器配置。你会明白这些层面有何区别、何时该用哪一个,以及如何确认流量确实走的是代理而非绕过了它。

这份分步指南适合谁

  • 营销人员和社交媒体专员,他们在 Docker 里运行自动发帖、分析或监控服务,希望每个工具用自己的移动 IP 工作。
  • 套利从业者,他们有几十个容器跑追踪器、offer 解析器和 sp y 服务,每个都需要独立的地理位置。
  • 开发者,需要从其他地区测试应用,或通过代理跑集成测试。
  • 企业主,其员工或外包在容器中部署基础设施,需要了解那里代理是怎么运作的。

需要提前知道什么

我们不假设你有深厚知识。只要会打开终端、复制命令并读懂输出就够了。如果你从未接触过 Docker,别怕:在基础概念部分我们会用通俗语言解释所有术语。Kubernetes、集群编排和云平台我们有意不涉及:那是另一个话题,这里我们严格停留在 Docker 层面。

需要多少时间

完整走一遍并验证需要 60 到 120 分钟。如果你只需要一个场景,比如给单个容器配代理,15 分钟就够。如果 Docker 还没装,再加上 20–30 分钟安装时间。

预先准备:工具、访问权限和系统要求

在 Docker 中配置代理之前,先把所需的一切备齐。这样你就不会在过程中为找登录名或装工具而分心。

需要准备什么

  1. 装有 Docker 的电脑或服务器。Linux(Ubuntu 22.04 或 24.04、Debian 12)、装了 Docker Desktop 的 macOS,或装了 Docker Desktop 和 WSL2 的 Windows 10/11 均可。2026 年适用的是 Docker Engine 27 及以上版本,以及用命令 docker compose(空格,无连字符)调用的 Docker Compose v2。
  2. 移动代理数据。你需要四样东西:主机地址(IP 或域名)、端口、登录名和密码。这些在服务商后台能找到。还要确认支持哪种协议:HTTP 还是 SOCKS5。大多数移动代理服务商,包括 mobileproxy.space,都在不同端口上同时提供两种。
  3. 终端。Linux 和 macOS 自带。Windows 上用 PowerShell 或 WSL2 终端(后者更方便,因为命令与 Linux 完全一致)。
  4. 文本编辑器。随便哪个:nano、vim、VS Code、Notepad++。用来改配置文件。
  5. curl 工具。通常已安装。它能帮你检查流量从哪个 IP 出去。

系统要求

  • 至少 2 GB 运行内存和 10 GB 磁盘可用空间给 Docker 和镜像。
  • 管理员权限:Linux 上是 sudo 权限,Windows 和 macOS 上是安装 Docker Desktop 的管理员账户。
  • 用于下载镜像的稳定网络连接。

备份

过程中我们会编辑 Docker 配置文件。里面的错误可能导致 Docker 无法启动。所以改任何文件前先做备份。在 Linux 上就一条命令:

sudo cp /etc/docker/daemon.json /etc/docker/daemon.json.bak

对 ~/.docker/config.json 文件同理。如果文件还不存在,就不必备份,但给自己记一笔:你是从零创建的,那么要回滚只需删除它即可。

建议:建一个文本文件,用 protocol://login:password@host:port 格式存好你的代理数据模板。这个字符串你会粘贴很多次,现成模板能避免打错字。

基础概念:Docker 如何运作,代理在哪里

为了让 Docker 代理配置不变成魔法,我们先厘清术语。如果你已经熟练操作容器,可以快速浏览本节,但请留意关于三个代理层面的小节:大多数错误就藏在那里。

通俗易懂的关键术语

  • 镜像(image)——模板,一套「冻结」的文件和程序。比如带 Python 的镜像或带浏览器的镜像。
  • 容器(container)——镜像的运行副本。一个镜像可以启动任意多个容器,每个都彼此隔离。
  • Docker 守护进程(daemon,dockerd)——后台服务,负责创建容器、下载镜像、管理网络。当你执行 docker pull 时,正是守护进程去互联网拉取镜像。
  • Docker 客户端(docker CLI)——终端里的 docker 命令。它把你的指令发给守护进程。
  • 环境变量(environment variables)——容器内程序可访问的命名值。比如 HTTP_PROXY=http://user:pass@host:port。许多程序会自动读取这类变量并开始走指定代理。
  • Docker 网络(network)——连接容器的虚拟网络。同一用户网络中的容器按名称互相可见。
  • Docker Compose——把多个容器及其变量和网络描述在一个 YAML 文件里、用一条命令启动的工具。

Docker 中代理的三个层面

这是理论中最重要的部分。当人们说「Docker 中的代理」,可能指三件完全不同的事,配置方式也各不相同。

  1. 守护进程代理。用于让 Docker 自身通过代理下载镜像。这关乎 docker pull 和 docker build 命令拉取基础镜像。这个层面不影响容器内应用的流量。
  2. 通过环境变量为容器配置代理。把 HTTP_PROXY、HTTPS_PROXY 和 NO_PROXY 传入容器,应用自行决定是否使用。这是最流行最简单的方法,但只对尊重这些变量的程序有效。
  3. 网络层面代理。容器流量被引导经过另一个网关容器或专门配置的网络。内部应用可能完全不知道代理存在。这更可靠,但配置更复杂。

开始前需要理解的重点

带代理的环境变量只是给程序的建议。curl 工具、包管理器 pip、Python 的 requests 库、带 global-agent 包的 Node.js、wget、apt——它们都会读取 HTTP_PROXY。但 headless 模式的浏览器、某些 Go 应用和许多二进制工具可能会忽略这些变量。所以配置后一定要验证实际外部 IP,别只依赖变量已设置。

还有一个细节——大小写。历史上形成的惯例是,部分程序读小写 http_proxy,部分读大写 HTTP_PROXY。可靠做法是同时设置两种。NO_PROXY 变量列出不需要走代理的地址:localhost、127.0.0.1、内部域名、相邻容器名。

最后是代理地址格式。HTTP 代理的字符串形如 http://login:password@host:port。SOCKS5 则是 socks5://login:password@host:port 或 socks5h://login:password@host:port。末尾的字母 h 意味着 DNS 查询也走代理,这对移动代理通常更可取:这样目标网站看不到你服务商的 DNS 解析器。

第 1 步:检查 Docker 并准备代理数据

本阶段目标:确认 Docker 在运行,你的代理数据正确且从这台电脑可访问。没有这一步,你可能会花半小时在配置里找错误,而问题其实出在密码打错字上。

检查 Docker

  1. 打开终端。
  2. 输入 docker --version 并回车。你应看到形如 Docker version 27.x.x 的行。如果终端提示找不到命令,说明 Docker 没装:按官方文档安装 Docker Desktop(Windows、macOS)或 Docker Engine(Linux),然后回到这里。
  3. 输入 docker compose version。预期输出:Docker Compose version v2.x.x。
  4. 输入 docker run --rm hello-world。Docker 会下载一个极小的测试镜像,输出带 Hello from Docker 字样的欢迎语。这说明守护进程在运行,你有启动容器的权限。

建议:如果在 Linux 上 docker 命令需要 sudo,把自己加入 docker 组:sudo usermod -aG docker $USER,然后退出系统重新登录。之后本指南所有命令都能不用 sudo 运行。

从主机验证代理

把代理搬进容器之前,先验证它是否响应。用你自己的数据替换示例。示例中我们用地址 185.10.10.10,HTTP 端口 1050,SOCKS5 端口 1051,登录名 user123,密码 secret。当然你的值会不同。

  1. 先查不带代理的常规 IP:curl -s ifconfig.me。记下结果。
  2. 现在通过 HTTP 代理请求:curl -s -x http://user123:secret@185.10.10.10:1050 ifconfig.me
  3. 如果你用 SOCKS5:curl -s -x socks5h://user123:secret@185.10.10.10:1051 ifconfig.me
  4. 把结果与第 1 步对比。IP 应不同,且属于移动运营商。

密码中的特殊字符

如果登录名或密码中含有 @、:、/、#、? 或空格,必须做 URL 编码,否则代理字符串会失效。@ 变成 %40,: 变成 %3A,/ 变成 %2F,# 变成 %23,? 变成 %3F,空格变成 %20。例如密码 pa@ss 在代理字符串中写作 pa%40ss。

✅ 检查:通过代理的 curl 命令返回了与家宽不同的 IP,且一两秒内收到响应。如果报 407,检查登录名和密码。如果 Connection refused 或超时——检查主机、端口,以及你当前 IP 是否已加入服务商后台白名单(某些套餐默认开启 IP 白名单认证)。

第 2 步:通过环境变量为单个容器配置代理

本阶段目标:启动一个所有 HTTP 流量都走移动代理的容器,并通过外部 IP 验证这一点。这是基础场景,适合所有人起步:它不动系统设置,撤销也容易。

用 -e 标志启动

docker run 命令的 -e(或 --env)标志把环境变量传入容器。我们一次传四个变量:HTTP 代理、HTTPS 代理和例外,每个都设两种大小写。

  1. 把下面的命令复制到编辑器,把代理数据替换成你自己的。
  2. 在终端执行命令。它会启动一个临时的 curl 容器,发一次请求然后结束。
docker run --rm -e HTTP_PROXY=http://user123:secret@185.10.10.10:1050 -e HTTPS_PROXY=http://user123:secret@185.10.10.10:1050 -e http_proxy=http://user123:secret@185.10.10.10:1050 -e https_proxy=http://user123:secret@185.10.10.10:1050 -e NO_PROXY=localhost,127.0.0.1 -e no_proxy=localhost,127.0.0.1 curlimages/curl -s ifconfig.me

注意:HTTPS_PROXY 我们也用 http:// 开头。这不是错误。这样指定的是 HTTPS 请求走的代理,而连接代理服务器本身仍是普通连接。HTTPS_PROXY 值里写 https:// 意味着到代理本身要用 TLS 连接,大多数服务商不支持。

用变量文件替代长命令

命令变得很臃肿。Docker 能通过 --env-file 标志从文件读取变量。这样更方便也更安全:密码不会留在终端历史里。

  1. 在工作目录创建 proxy.env 文件:nano proxy.env
  2. 写入行,每行一个变量,不加引号,等号两边不留空格:
HTTP_PROXY=http://user123:secret@185.10.10.10:1050 HTTPS_PROXY=http://user123:secret@185.10.10.10:1050 http_proxy=http://user123:secret@185.10.10.10:1050 https_proxy=http://user123:secret@185.10.10.10:1050 NO_PROXY=localhost,127.0.0.1 no_proxy=localhost,127.0.0.1

实际文件里每个变量应各占一行。保存文件(nano 里是 Ctrl+O、Enter,然后 Ctrl+X)并启动容器:

docker run --rm --env-file proxy.env curlimages/curl -s ifconfig.me

从运行中的容器内部验证

常常需要查看长驻容器看到什么。我们以交互模式启动 Alpine Linux 并检查变量。

  1. 执行 docker run -it --rm --env-file proxy.env alpine sh。你会进入容器内部,提示符变成井号或美元符号。
  2. 输入 env | grep -i proxy。你会看到变量列表。
  3. 输入 apk add --no-cache curl。包管理器 apk 会自动读取 https_proxy 并通过代理下载包。
  4. 输入 curl -s ifconfig.me,确认 IP 是移动的。
  5. 输入 exit 退出。容器会因 --rm 标志自动删除。

建议:除 ifconfig.me 外,检查 IP 时也方便用返回国家、城市和运营商 JSON 信息的服务。这样你一眼就能看出 IP 属于所需地区的移动运营商,而不只是「另一个」IP。

✅ 检查:两次 curl 运行都返回了移动代理的 IP。容器内 env 命令显示了带你的数据的 HTTP_PROXY 和 HTTPS_PROXY 变量。

本步可能的问题

  • IP 没变。容器内应用忽略变量。curl 不会这样,所以如果 curl 显示移动 IP 而你的应用不显示,转到第 6 步的网络方式。
  • 报 invalid reference format 错误。通常是命令里多了空格或换行。把命令拼成一行。
  • 看不到变量。env-file 文件里值周围不能有引号:Docker 会原样传入,代理地址就变得无效。

第 3 步:为 Docker 客户端设置代理,让变量自动传递

本阶段目标:让每个新容器和每次镜像构建都自动获得代理变量,无需 -e 标志。如果你经常用同一个移动代理启动不同容器,这能省时间。

原理

Docker 客户端读取 ~/.docker 文件夹里的 config.json 文件(Windows 上是用户配置文件里的 .docker 文件夹)。如果其中有 proxies 段,客户端在每次 docker run 和 docker build 时会给容器加上指定变量。守护进程不受影响,所以 docker pull 仍会直连。

分步配置

  1. 检查文件是否存在:cat ~/.docker/config.json。如果文件存在且已有设置(比如带注册表登录数据的 auths),先备份:cp ~/.docker/config.json ~/.docker/config.json.bak
  2. 在编辑器中打开:nano ~/.docker/config.json。如果文件不存在,编辑器会创建它。
  3. 添加 proxies 段。如果文件原本是空的,其全部内容如下:
{ "proxies": { "default": { "httpProxy": "http://user123:secret@185.10.10.10:1050", "httpsProxy": "http://user123:secret@185.10.10.10:1050", "noProxy": "localhost,127.0.0.1,*.local" } } }

如果文件里已有其他键,把 proxies 作为另一个顶层键用逗号加上,不要删掉已有的。注意花括号和引号成对:JSON 不容忍漏掉逗号。

  1. 保存文件。
  2. 检查语法。Linux 和 macOS 上方便这样做:python3 -m json.tool ~/.docker/config.json。如果输出是你文件的漂亮格式化版本,就没问题。如果出现带行号的错误,修掉它。
  3. 不带任何标志启动验证容器:docker run --rm curlimages/curl -s ifconfig.me。IP 应是移动的。
  4. 查看任意容器的变量:docker run --rm alpine env。输出中会有 HTTP_PROXY、HTTPS_PROXY、NO_PROXY 及其小写版本:Docker 会自己加上两种大小写。

⚠️ 注意:config.json 的 proxies 段影响你以该用户启动的所有容器,包括数据库、本地 Web 服务器等一切。如果某个服务要与通过你代理无法访问的外部 API 通信,它会坏掉。把这类地址加到 noProxy,或临时去掉该段。

为不同连接设置不同代理

default 键适用于所有到守护进程的连接。如果你通过上下文或 DOCKER_HOST 变量管理多个 Docker 主机,可以把 default 换成具体守护进程地址,例如 tcp://192.168.1.50:2376,代理就只适用于它。本地工作用 default 就够了。

构建镜像时的代理

config.json 里的设置也会作为构建参数传给 docker build。这意味着 Dockerfile 里的 RUN apt-get install 或 RUN pip install 命令会走代理。重要一点:这些变量不会保存在最终镜像里,从安全角度是好事:代理密码不会泄露给收到镜像的人。

建议:如果只想为一次构建传代理而不动 config.json,用标志 docker build --build-arg HTTP_PROXY=http://user123:secret@185.10.10.10:1050 --build-arg HTTPS_PROXY=http://user123:secret@185.10.10.10:1050 . Docker 无需在 Dockerfile 里声明 ARG 就能理解这些预定义参数。

✅ 检查:不带 -e 标志启动的容器以代理 IP 访问互联网。docker run --rm alpine env 命令显示了代理变量。

如何回滚

从 config.json 删掉 proxies 段,或从备份恢复:cp ~/.docker/config.json.bak ~/.docker/config.json。不需要重启,改动下次启动容器时生效。

第 4 步:为 Docker 守护进程设置代理,让镜像通过代理下载

本阶段目标:让 Docker(守护进程)本身通过代理拉取镜像。当你的服务器直连镜像仓库受限(企业政策)、太慢,或你希望服务器全部网络活动走一个通道时,就需要这个。对营销任务来说这步常常不需要,但值得了解:守护进程层面的错误经常被误认为容器层面的错误。

方法 1:daemon.json 文件(Linux,Docker 23 及更新版本)

  1. 做备份:sudo cp /etc/docker/daemon.json /etc/docker/daemon.json.bak(如果文件不存在,命令会报错,这正常)。
  2. 打开文件:sudo nano /etc/docker/daemon.json
  3. 添加 proxies 段:
{ "proxies": { "http-proxy": "http://user123:secret@185.10.10.10:1050", "https-proxy": "http://user123:secret@185.10.10.10:1050", "no-proxy": "localhost,127.0.0.1" } }

注意这里键用连字符和小写:这与客户端 config.json 里 httpProxy 风格的键不同。混淆它们是经典错误。

  1. 保存文件并重启守护进程:sudo systemctl restart docker
  2. 检查守护进程已启动:sudo systemctl status docker。输出中应有 active (running)。
  3. 检查应用情况:docker info | grep -i proxy。你会看到 HTTP Proxy 和 HTTPS Proxy 行带你的地址,密码在输出中显示为星号。

方法 2:systemd drop-in 文件(Linux,任意版本)

这是经典方法,即使在旧版 Docker 上也能用。

  1. 创建文件夹:sudo mkdir -p /etc/systemd/system/docker.service.d
  2. 创建文件:sudo nano /etc/systemd/system/docker.service.d/http-proxy.conf
  3. 写入内容,每条指令一行:
[Service] Environment="HTTP_PROXY=http://user123:secret@185.10.10.10:1050" Environment="HTTPS_PROXY=http://user123:secret@185.10.10.10:1050" Environment="NO_PROXY=localhost,127.0.0.1"
  1. 保存,然后重读 systemd 配置:sudo systemctl daemon-reload
  2. 重启 Docker:sudo systemctl restart docker
  3. 检查:sudo systemctl show --property=Environment docker。输出中会有你的变量。

⚠️ 注意:不要同时用两种方法设置守护进程代理。如果 daemon.json 和 systemd 文件里地址不同,行为会不可预测,调试会很痛苦。选一种方法坚持用它。

方法 3:Docker Desktop(Windows 和 macOS)

  1. 打开 Docker Desktop,点右上角的齿轮图标。
  2. 左侧菜单选 Resources,然后 Proxies。
  3. 把 Manual proxy configuration 开关打开。
  4. 在 Web Server (HTTP) 和 Secure Web Server (HTTPS) 字段粘贴代理地址,格式 http://user123:secret@185.10.10.10:1050。
  5. 在 Bypass proxy settings for these hosts 字段填 localhost,127.0.0.1。
  6. 点 Apply and restart。Docker Desktop 会重启,需要 30–60 秒。

Docker Desktop 会把这些设置同时应用到守护进程和容器,所以桌面系统上通常不需要单独改 config.json。

验证结果

  1. 如果有小镜像,删掉它:docker rmi alpine
  2. 重新下载:docker pull alpine。下载应成功。
  3. 如果代理端有流量统计(mobileproxy.space 后台就有),你会看到消耗流量增加了几个兆字节。

✅ 检查:docker info 显示代理地址,docker pull 无错下载镜像,docker 服务处于 active 状态。

可能的问题

  • 改完 daemon.json 后 Docker 不启动。几乎总是 JSON 语法问题。用 python3 -m json.tool /etc/docker/daemon.json 检查文件,或恢复备份。
  • docker pull 卡住。代理不放行到仓库的连接,或套餐流量超限。像第 1 步那样从主机用 curl 检查代理。
  • x509 certificate 错误。代理替换了证书(对 企业代理常见,对移动代理少见)。向服务商确认。

第 5 步:在 Docker Compose 中设置代理

本阶段目标:在 compose.yaml 文件里描述代理,让一组容器用一条命令带上所需设置启动,且不同服务可以用不同移动代理。这正是套利从业者和营销人员最常需要的场景:一个爬虫走莫斯科代理,第二个走喀山代理,而数据库完全不走代理。

准备项目

  1. 创建项目文件夹并进入:mkdir proxy-demo,然后 cd proxy-demo
  2. 创建 .env 文件(注意开头有点)存密钥:nano .env
  3. 写入变量,一行一个:
PROXY_MSK=http://user123:secret@185.10.10.10:1050 PROXY_KZN=http://user456:secret2@185.10.10.20:1050

Compose 会自动读取 .env 文件,其中的值可用 ${名称} 语法插到 compose.yaml 里。如果项目在版本控制下,把 .env 加入 .gitignore:密码不能进仓库。

compose.yaml 文件

创建 compose.yaml(nano compose.yaml)并描述三个服务。YAML 里缩进很重要:每层用两个空格,不用制表符。

services: parser-msk: image: curlimages/curl command: -s ifconfig.me environment: HTTP_PROXY: ${PROXY_MSK} HTTPS_PROXY: ${PROXY_MSK} http_proxy: ${PROXY_MSK} https_proxy: ${PROXY_MSK} NO_PROXY: localhost,127.0.0.1,db no_proxy: localhost,127.0.0.1,db parser-kzn: image: curlimages/curl command: -s ifconfig.me environment: HTTP_PROXY: ${PROXY_KZN} HTTPS_PROXY: ${PROXY_KZN} http_proxy: ${PROXY_KZN} https_proxy: ${PROXY_KZN} NO_PROXY: localhost,127.0.0.1,db no_proxy: localhost,127.0.0.1,db db: image: postgres:16-alpine environment: POSTGRES_PASSWORD: example

实际文件里每行各占一行,缩进正确:services 在零层,服务名缩进两个空格,其参数缩进四个,环境变量缩进六个。注意 NO_PROXY 里的 db 名:这样爬虫会通过 Docker 内部网络直连数据库,而不是试图通过移动代理访问它,那必然不行。

启动和验证

  1. 检查 Compose 如何代入变量:docker compose config。命令会输出展开值后的最终文件。确认 ${PROXY_MSK} 位置换成了真实地址。
  2. 启动:docker compose up。Compose 会下载镜像并启动三个服务,在终端输出日志。
  3. 日志中会看到形如 parser-msk-1 | 91.xxx.xxx.xxx 和 parser-kzn-1 | 176.xxx.xxx.xxx 的行:来自两个不同代理的两个不同 IP。Postgres 会启动并等待连接。
  4. 按 Ctrl+C 停止全部,然后删除容器:docker compose down

替代方案:为每个服务用 env_file

如果变量很多,用文件比 environment 块更方便:

services: parser-msk: image: curlimages/curl command: -s ifconfig.me env_file: - proxy-msk.env

proxy-msk.env 文件此时包含与第 2 步 proxy.env 相同的六行。这样每个代理放自己文件里,替换时无需打开 compose.yaml。

Compose 中构建时的代理

如果服务是从 Dockerfile 构建而非用现成镜像,通过 build.args 把代理传进构建:

services: app: build: context: . args: HTTP_PROXY: ${PROXY_MSK} HTTPS_PROXY: ${PROXY_MSK}

这样 Dockerfile 里的 pip、npm 或 apt 会走代理,而值不会进入最终镜像。

建议:Compose 支持多个文件。保持基础 compose.yaml 不带代理,在 compose.proxy.yaml 里只描述 environment 块。需要代理时运行 docker compose -f compose.yaml -f compose.proxy.yaml up,不需要时直接 docker compose up。调试时很方便:一秒钟就能在模式间切换。

✅ 检查:docker compose config 显示了代入的代理地址,docker compose up 日志里用不同代理的服务输出了不同 IP。

第 6 步:通过网络层面用网关容器设置代理

本阶段目标:启动一个独立容器,接收 Docker 网络中邻居的连接并把它们转发到移动代理。其他容器按名称访问网关,不保存登录名和密码。这一下解决三个问题:集中管理代理、从几十个配置里去掉密码、无需重启工作容器就能换代理。

既然有变量,为什么还需要网关

设想你有二十个爬虫容器,服务商给了新端口。用环境变量你得改二十个配置并全部重启。用网关你只改一个地方的一行。此外,某些应用不支持代理的登录名密码认证,但与无认证代理配合良好。网关在封闭的 Docker 网络内不需要认证,而它自己连移动代理时带上你的数据。

创建网络

  1. 创建用户网络:docker network create proxynet
  2. 确认它出现了:docker network ls。列表中有 proxynet,驱动为 bridge。

需要用户网络是因为只有它支持名称解析:容器可以按名字 gateway 访问网关,而不是按每次重启都会变的 IP。

启动网关

我们用 gost 做网关——一个紧凑的代理服务器,能在一个协议上接收连接并通过带认证的方式转发到另一个。镜像在公共仓库中以 gogost/gost 名字提供。

  1. 启动网关容器:
docker run -d --name gateway --network proxynet --restart unless-stopped gogost/gost -L=http://:8118 -F=http://user123:secret@185.10.10.10:1050

解析参数。-d 标志让容器后台运行。--name gateway 设置名字,邻居按此名字访问。--network proxynet 把它连接到我们的网络。--restart unless-stopped 让网关在服务器重启后自动拉起。-L=http://:8118 参数让 gost 在 8118 端口接收 HTTP 代理连接,无需认证。-F 参数指定转发目标:你的带登录名密码的移动代理。

  1. 检查网关运行:docker logs gateway。日志中应有服务器监听 8118 端口的行,无错误。

⚠️ 注意:不要用 -p 标志对外暴露网关端口,除非确有必要。网关无认证运行,在公网服务器上开放 8118 端口意味着互联网上任何人都能用你的移动代理并消耗你的流量。在 proxynet 网络内它只对你的容器可见,这就够了。

接入工作容器

  1. 在同一网络启动验证容器,把网关作为代理:
docker run --rm --network proxynet -e HTTP_PROXY=http://gateway:8118 -e HTTPS_PROXY=http://gateway:8118 curlimages/curl -s ifconfig.me
  1. 你应看到移动代理 IP。注意:变量里既没有登录名、密码,也没有真实代理地址。这一切只有网关知道。

在 Compose 里做同样的事

长期运行时,把网关和工作服务描述在一个 compose.yaml 里:

services: gateway: image: gogost/gost command: -L=http://:8118 -F=${PROXY_MSK} restart: unless-stopped networks: - proxynet worker: image: curlimages/curl command: -s ifconfig.me environment: HTTP_PROXY: http://gateway:8118 HTTPS_PROXY: http://gateway:8118 NO_PROXY: localhost,127.0.0.1 depends_on: - gateway networks: - proxynet networks: proxynet: driver: bridge

depends_on 指令保证网关比 worker 先启动。PROXY_MSK 的值取自 .env 文件,如第 5 步。

为多个地理位置设多个网关

想为不同容器组用不同代理?启动多个网关:gateway-msk、gateway-kzn、gateway-spb,每个带自己的 -F。工作容器只需在 HTTP_PROXY 里指定所需名字。还能更进一步,为每个地理位置建独立网络,这样莫斯科组的容器物理上就无法误入喀山网关。

隔离:无直接互联网出口的容器

最严格的做法是禁止工作容器除网关外任何互联网出口。为此用 --internal 标志创建内部网络:docker network create --internal isolated。这种网络中的容器没有外出路由。把网关同时连接到两个网络(isolated 和普通 proxynet),而 worker 只连 isolated。现在即使应用忽略代理变量,它也无法直接连互联网,不会发生真实 IP 泄露。

  1. docker network create --internal isolated
  2. docker network connect isolated gateway
  3. docker run --rm --network isolated -e HTTP_PROXY=http://gateway:8118 -e HTTPS_PROXY=http://gateway:8118 curlimages/curl -s ifconfig.me
  4. 作为对照,不带代理变量运行同一容器:docker run --rm --network isolated curlimages/curl -s -m 5 ifconfig.me。请求应超时:没有直接出口。

建议:内部网络加网关的组合是多账号运营中防泄露的最佳保险。即使开发者在某个新服务里忘了配代理,它也照不出服务器 IP:它要么走网关,要么哪儿都去不了。

✅ 检查:proxynet 网络中带 HTTP_PROXY=http://gateway:8118 变量的容器显示移动代理 IP。内部网络中不带代理的容器根本无法访问互联网。

可能的问题

  • Could not resolve host: gateway。工作容器不在同一网络,或跑在默认网络上(那里不解析名称)。检查 --network 标志。
  • 网关反复重启。-F 行有错误:密码打错或端口不对。看 docker logs gateway。
  • 慢。移动代理本就比数据中心代理慢,但如果延迟达到几十秒,检查 DNS 是否绕行:如果服务商提供 SOCKS5,在 -F 行里用 socks5h 而非 socks5。

结果验证:Docker 代理可用性清单

过一遍清单。如果每一项都打勾,你已完整掌握 Docker 代理配置的实践。

清单

  • 从主机经代理的 curl 返回移动 IP。
  • 带 --env-file proxy.env 标志的容器返回移动 IP。
  • 配置 config.json 后,不带标志的容器返回移动 IP(如果你做了第 3 步)。
  • docker info 显示代理地址,docker pull 能工作(如果你做了第 4 步)。
  • docker compose up 启动服务,日志中不同代理显示不同 IP。
  • 网关容器工作,邻居无需登录名密码即可通过它出口。
  • 内部网络中不带代理的容器无法访问互联网。

如何整体测试

  1. 启动长驻容器:docker run -d --name test --network proxynet -e HTTP_PROXY=http://gateway:8118 -e HTTPS_PROXY=http://gateway:8118 alpine sleep 3600
  2. 进入它:docker exec -it test sh
  3. 安装 curl:apk add --no-cache curl。安装应通过代理完成。
  4. 连续发五次请求:for i in 1 2 3 4 5; do curl -s ifconfig.me; echo; done。五次都应返回移动 IP。如果你的代理开启了自动轮换,请求间 IP 可能不同,这正常。
  5. 退出(exit)并删除容器:docker rm -f test

成功指标

成功配置意味着你能在一秒内回答三个问题:某个容器从哪个 IP 出口、代理密码存哪里、要让容器换到另一个代理需要改什么。如果每个问题答案都显而易见——目标达成。

Docker 代理配置的典型错误及解决办法

错误 1:变量已设但 IP 不变

原因:容器内应用不读取代理环境变量。headless 浏览器、某些 Go 程序和使用自己网络栈的工具常有这问题。

解决办法:查应用文档看是否有自己的代理标志(浏览器通常是 --proxy-server)。如果没有,用第 6 步的网关和内部网络,或进阶部分的透明代理。

错误 2:407 Proxy Authentication Required

原因:登录名或密码错误,或其中特殊字符未编码,或代理开启了 IP 认证而服务器 IP 不在白名单。

解决办法:从主机用 curl 检查数据。编码特殊字符。在后台把服务器 IP 加入白名单,或把代理切换为登录名密码认证。

错误 3:改完 daemon.json 后 Docker 不启动

原因:JSON 语法错误:多余逗号、漏引号、用了 config.json 风格而非 daemon.json 风格的键。

解决办法:看日志:sudo journalctl -u docker -n 50。那里会指示出错行。修掉或恢复备份并重启服务。

错误 4:容器互相看不到了

原因:在 config.json 全局代理设置后,对相邻容器的请求也走了移动代理,而它不知道什么是 db 或 redis。

解决办法:把服务名和内部子网加入 NO_PROXY:localhost,127.0.0.1,db,redis,172.16.0.0/12。注意并非所有程序都理解子网掩码,所以更可靠的是明确列名字。

错误 5:docker build 在 apt-get 或 pip 时失败

原因:构建时没有代理,因为变量是为容器设的而非构建,或守护进程代理设了但不影响 RUN 步骤。

解决办法:传 --build-arg HTTP_PROXY 和 HTTPS_PROXY,或在客户端 config.json 配置 proxies 段:它也适用于构建。

错误 6:代理密码在 docker inspect 和日志里可见

原因:环境变量以明文存在容器元数据里,任何有 Docker 访问权的人都能通过 docker inspect 看到。

解决办法:用网关:工作容器只知道 gateway:8118 地址。密码只留在一个容器和权限受限的 .env 文件里(chmod 600 .env)。

错误 7:服务器重启后代理失效

原因:网关容器未用重启策略启动,或移动代理主机 IP 变了。

解决办法:给网关加 --restart unless-stopped。如果服务商提供域名,用域名替代 IP。检查 docker ps -a:如果网关状态是 Exited,看它的日志。

错误 8:HTTPS 网站打不开而 HTTP 能用

原因:只设了 HTTP_PROXY,而 HTTPS_PROXY 为空,或 HTTPS_PROXY 里写了 https:// 而非 http://。

解决办法:始终把两个变量设为相同值和 http:// 协议。

进阶附加能力:透明代理、IP 轮换和安全

本节面向已走完基础步骤、想从 Docker 和移动代理组合中榨取最大价值的人。这里分步清单少,带关键命令的思路多。

透明代理:应用完全不知道代理存在

如果你的应用完全不支持代理,可以在网络栈层面把它全部 TCP 流量改道。思路是:工作容器用 network_mode: service:gateway 参数(Compose 里)或 --network container:gateway(docker run 里)启动。这样它完全使用网关容器的网络栈:同一 IP、同一接口、同一路由规则。

网关里此时运行 redsocks 之类的程序,它监听本地端口并把连接转发到 SOCKS5 代理,而 iptables 规则把所有出站 TCP 流量重定向到该端口。网关需要权限:cap_add: NET_ADMIN。工作容器里应用向网站发普通请求,内核拦截并导向 redsocks,后者再导向移动代理。没有任何环境变量。配置需要细心:错误的 iptables 规则可能让流量死循环,所以在单独机器上测试。还要注意,用 network_mode: service 时工作容器失去自己的端口和到其他网络的连接,这些都要在网关上描述。

从容器里轮换 IP

移动代理有个特点,人们正是为它才买:IP 可按请求更换。服务商提供特殊的换 IP 链接,打开它就足以让调制解调器重连。从容器里用同样的 curl 就能做。有用的模式:Compose 里一个小的独立服务按计划定时请求轮换链接。它不应走代理(否则换 IP 后它自己会丢连接),所以不带代理变量启动,或明确把 HTTP_PROXY 设为空。注意换 IP 后工作容器的活跃连接会断开:在爬虫里设计重试机制。

网关健康检查:healthcheck

在 Compose 里加一项检查,确认网关真在代理而不只是进程活着:

healthcheck: test: ["CMD", "gost", "-V"] interval: 30s timeout: 10s retries: 3

这是最低限度的进程存活检查。要检查真实互联网出口,最好用单独的监控容器,每分钟通过网关发一次请求并把结果写入日志或发通知。如果 IP 突然变成服务器 IP,那就是警报:网关挂了,容器走了直连。第 6 步的内部网络正好防这种情况。

安全存储密码

.env 文件适合本地工作,但在多用户服务器上应保护它:chmod 600 .env,所有者是运行 Compose 的用户。Compose 也支持通过 secrets 指令管理密钥,它们以文件形式挂载到容器 /run/secrets/ 而非环境变量。Gost 不能直接读文件里的密码,但你可以写个小包装脚本,启动时从密钥文件拼出 -F 字符串。这样密码既不会进 docker inspect,也不会进 docker compose config 输出。

多项目与一套代理基础设施

如果你有多个 Compose 项目而代理是共用的,把网关放到独立项目配外部网络:该项目里用 name: proxynet 参数声明 networks,其他项目以 external: true 接入。这样网关独立存在,工作项目可以随意重启而不碰代理。

限制流量

移动流量通常计费,一个失控的爬虫一夜能跑掉几十 GB。Docker 层面没有硬性流量配额,但有间接手段:应用里限制请求频率、启动命令里用 timeout 限制容器寿命,以及用 docker stats 实时监控每个容器的 NET I/O。定期把这些数字与服务商后台统计核对。

不带密钥的日志

许多应用启动时把环境变量打印到日志,包括带密码的 HTTP_PROXY。如果日志流向集中系统,密码就泄露到那里。网关也解决这个问题:工作容器日志里只有 gateway:8118。

建议:每季度重发一次代理密码并更新 .env。用网关这只要一分钟:改一行,docker compose up -d gateway,所有 worker 无需重启继续工作。

FAQ:Docker 代理配置常见问题

应用新的代理变量需要重启容器吗?

是的。环境变量在容器创建时设定,无法在运行中修改。停止、删除并重新创建容器(Compose 里是 docker compose up -d --force-recreate 服务名)。如果重启碍事,用网关:它的设置可以独立更换。

为 docker pull 配置代理与为容器内应用配置代理有何不同?

这是两个不同层面。docker pull 由守护进程执行,其代理在 daemon.json 或 systemd 里设置。容器内应用是独立进程有自己的环境,其代理用变量、客户端 config.json 或网络设置。两者不能互相替代。

环境变量里能用 SOCKS5 替代 HTTP 代理吗?

如果应用支持 SOCKS 就可以。curl、Python requests(安装了 PySocks 包)、git 支持。apt 和许多其他工具不支持。通用出路是 gost 网关,它输入接收 HTTP、输出发送到 SOCKS5:-L=http://:8118 -F=socks5://user:pass@host:port。

如何检查运行中的容器在用哪个代理?

执行 docker inspect -f '{{.Config.Env}}' 容器名。你会看到所有环境变量。要检查实际 IP,用 docker exec 容器名 curl -s ifconfig.me(如果容器里有 curl),或 wget -qO- ifconfig.me。

为什么 Docker Desktop 上代理能用,而 Linux 服务器上同样设置不行?

Docker Desktop 把 Proxies 窗口里的设置同时应用到守护进程和容器。Linux 上是两个独立位置:daemon.json 给守护进程,~/.docker/config.json 给容器。如果需要两者,检查是否都配置了。

如何只为一个域名设代理,其余直连?

环境变量做不到:它们按「除 NO_PROXY 外全部走代理」原则工作。如果需 要相反逻辑,在应用端用 PAC 文件(浏览器支持)或 gost 的路由规则,它能按域名把流量导向不同出口通道。

把代理密码存在 compose.yaml 里安全吗?

最好不要。存到权限 600 的 .env 里并用 ${名称} 代入。不要把 .env 提交到仓库。服务器上用手网关,让密码只在一处。

如果移动代理换了 IP 导致容器里连接中断怎么办?

这是轮换时的正常行为。应用应能重试请求。如果轮换按服务商计划进行,了解间隔并让重任务与之同步。如果是按你的链接轮换,在任务批次之间调用,不要在中间调用。

这些设置在不用 WSL2 的 Windows 上能用吗?

Windows 上的 Docker Desktop 底层用 WSL2 或 Hyper-V,所有 docker run 和 docker compose 命令从 PowerShell 运行方式相同。只有路径不同:config.json 在 C:\Users\用户名\.docker\config.json,守护进程设置通过 Docker Desktop 窗口做而非手动改 daemon.json。

一个移动代理可以跑多少容器?

技术上任意多,限制只在移动通道带宽和套餐限额。实践中对账号相关任务,合理做法是一个代理对应一个逻辑实体(账号、项目、地区),让行为看起来自然,且一个容器的错误不影响其他。

结语:你做了什么,接下来往哪走

我们来总结。你验证了代理从主机的可用性,搞清了连接字符串格式。通过环境变量和 env-file 文件为单个容器配置了代理。通过客户端 config.json 让变量自动传递。弄清了为什么以及如何为 Docker 守护进程本身配置代理的三种方式。在 Docker Compose 里描述了几个带不同移动代理的服务,把密钥放进 .env。最后搭建了带隔离网络的网关容器——生产环境最可靠的方案,即使应用忽略变量也能防真实 IP 泄露。

现在 Docker 中的代理对你来说不再是黑箱,而是三个边界清晰的层面:守护进程、容器、网络。你知道如果 IP 突然不对该去哪找问题,也能用一条命令验证。

接下来做什么

  1. 把工作项目迁移到网关加内部网络方案。从一个非关键服务开始,确认一切正常后再扩展。
  2. 加一个监控容器,每分钟通过网关检查外部 IP,一旦与服务器 IP 相同就报警。
  3. 按你的任务设置定时 IP 轮换,并让应用能承受连接中断。
  4. 整理密钥:权限 600 的 .env,compose.yaml 和 Dockerfile 里不放任何密码。

往哪发展

下一个合乎逻辑的步骤是把 Docker 与指纹浏览器和多账号工具结合,让每个配置文件对应自己的容器和移动代理。另一个方向是通过服务商 API 自动化:获取代理列表、检查状态并直接从你的服务里轮换。两个话题都超出本指南范围,但你现在打下的基础让它们容易得多。祝启动顺利、IP 稳定!