Docker: Set Proxy for Container to Access Proxy from Host

 28th December 2020 at 3:18pm

docker build 时经常有一个场景,比如使用 Debian 系镜像时需要 apt install 装包,使用 Go 时需要 go get 拉库。但是这些操作往往需要访问国外服务器,很慢甚至有些被墙。这时候我们期望容器里的进程可以访问宿主机的代理。

正常的做法是,给容器环境设置 http_proxy 环境变量,使其指向宿主机的代理。大多数程序,无论是 apt 还是 go get,都会理解这个环境变量。所以:

如何设置环境变量?官方文档

  • 对于 Docker 1.13.0(API 1.25+)及以上版本,可以在 ~/.docker/config.json 中写入 "proxies" 配置,Docker 会将其转换为环境变量带入容器内:
    {
      "proxies": {
        "default": {
          "httpProxy": "http://127.0.0.1:8001",
          "httpsProxy": "http://127.0.0.1:8001",
          "noProxy": "localhost,127.0.0.1"
        }
      }
    }
  • 对于 Docker 1.13.0 以下版本,可以在 Dockerfile 中使用 ENV 指令,或者 docker run --env 中指定

上面的方式设置的环境变量,不仅对 docker build 生效,也对 docker run 生效。这导致我曾经在使用 pupeteer 时,未设置 noProxy,脚本总是通过代理去连接 Chrome 开的 DevTools WebSockets 服务上(因为走了代理),从而无法连上,定位了好久。

如何在容器内访问宿主机的端口?

  • 对于 Windows 或 MacOS,Docker 提供了 host.docker.internal 这样一个域名,可以解析成宿主机的 IP。因此你只需要在 Dockerfile 中设置 http_proxy 环境变量,并在使用完后将其重置:
    FROM node:12-alpine
    ENV http_proxy=http://host.docker.internal:8001 
    RUN apk add --update bash
    ENV http_proxy=
  • 对于 Linux 环境下,不支持 host.docker.internal,可以:
    • Docker 1.13.0(API 1.25+)及以上版本,
      • docker build 时,可以使用 docker build --network=host 来使用 host 的网络
      • docker-compose build 时,可以在 3.0 版本的 docker-compose.yml 中写入 network 配置:
        version: '3'
        services:
          web:
            build: 
              context: .
              // add network field
              network: host
    • Docker 1.13.0 以下版本,可以参考这个 SO 答案 使用 ip route 命令来获取。但不一定可靠:
      hostip=$(ip route show | awk '/default/ {print $3}')
      echo $hostip