将 docker-compose 网络内的主机重定向到 docker 外的本地主机

时间:2021-02-15 09:21:08

标签: docker networking docker-compose

我有一组使用 docker-compose 运行的容器。网络是 docker-compose 创建的默认网络,容器使用 docker-compose 自动设置的主机名相互通信。因此,如果我在 docker-compose 中定义了一个名为“my_service”的服务,我会以主机“my_service”的身份访问这个容器。

在某些情况下,我想禁用特定容器并将所有请求重定向到实际的本地主机,而不是 docker 容器。因此,我不想将主机“my_service”路由到具有相同名称的容器,而是将其路由到实际的本地主机,在那里我运行相同的服务,例如在带有调试器或类似的东西的 IDE 中。

我不确定如何最好地实现这一点,我是否可以修改网络本身来做到这一点,或者我是否必须以某种方式代理这些请求。有没有办法做到这一点,理想情况下只需要在 docker-compose.yml 中进行一些更改,而无需更改我的容器本身?

我在 Linux (Ubuntu 20.04) 上使用 docker-compose 或在 Windows 10 上使用 WSL2。

1 个答案:

答案 0 :(得分:0)

tl;博士

你需要三件事才能让它工作:

  1. 预定义的外部网络,用于通过固定 IP 访问主机
  2. hostname 每个参与服务的条目
  3. 一个额外的 Docker 服务作为路由器,以便将请求重定向到主机。我们还将使用 aliases 条目允许其他服务通过预定义的主机名访问此路由器服务。

如何

注意:针对 Linux 进行了测试。不确定,它是否适用于 Windows。

  1. 使用网关创建外部网络。例如。通过下面定义的自定义 IP 172.30.0.1,每个 docker 服务都应该能够访问主机。
docker network create \
    --driver=bridge \
    --subnet=172.30.0.0/16 \
    --gateway=172.30.0.1 \
    host-net
  1. 为 docker-compose.yml 中的每个参与服务添加自定义 hostname,例如
services:
    service-1:
        hostname: service-1.in.docker

    service-2:
        hostname: service-2.in.docker

因此,service-2 现在可以通过域 service-1.in.docker 访问 service-1,反之亦然。

  1. 创建一个额外的服务,利用众所周知的多用途中继工具 socat 来充当路由器,例如
services:
    router:
        image: alpine/socat
        command: TCP-LISTEN:5900,fork TCP:172.30.0.1:15900

这里基于 TCP,它在 localhost 的 5900 端口侦听并将传入请求重定向到目标 172.30.0.1(网关 -> 主机)的 15900 端口。

示例

version: "3.9"

services:
  router:
    image: alpine/socat
    entrypoint: >
      sh -c "
        socat TCP-LISTEN:5900,fork TCP:172.30.0.1:15900 &
        socat TCP-LISTEN:5901,fork TCP:172.30.0.1:15901 &
        wait
      "
    networks:
      host-net:
      bridged:
        aliases:
          - service-1.in.docker

  service-1:
#   hostname: service-1.in.docker
    image: hashicorp/http-echo
    command: -listen=:5900 -text="hello world"
    networks:
      bridged:

  service-2:
    hostname: service-2.in.docker
    image: curlimages/curl:7.75.0 
    command: ["sh", "-c", "while true; do curl service-1.in.docker:5900 && sleep 5; done"]
    networks:
      bridged:

networks:
  host-net:
    external: true
  bridged:

注意事项:

  1. 这里的router服务包括两个重要的东西:host-net作为网络,以及aliases网络入口bridged,其中还涉及其他服务.

  2. router 服务的工作原理如下:

(请求) -> [localhost:5900] -> (重定向) -> [172.30.0.1:15900]

(请求) -> [localhost:5901] -> (重定向) -> [172.30.0.1:15901]

  1. 由于我们注释了 hostname: service-1.in.docker 行,service-2 (curl service-1.in.docker:5900) 发出的 curl 请求将转到router 服务(由于别名 service-1.in.docker)。随后,这些请求通过端口 15901 转发到主机。

  2. 如果我们注释掉 hostname: service-1.in.docker 行,service-1 会用“hello world”响应 service2

如果您有可能动态更改单个服务使用的主机名,例如环境变量或属性文件等,您可以使用它们,这样您就不必每次都进行注释输入和输出:

services:
  router:
    image: alpine/socat
    networks:
      bridged:
        aliases:
          - service-1.in.router

  service-2:
    hostname: service-2.in.docker
    command: ["sh", '-c', 'while true; do curl ${DEST_SERVICE_HOST_ADDR_SET_BY_ENV_VAR} && sleep 5; done']

DEST_SERVICE_HOST_ADDR_SET_BY_ENV_VAR 可以根据需要提供 service-1.in.routerservice-1.in.docker

脚注:

你可以在你的主机上运行一个测试服务器来测试这个,例如:

python3 -m http.server 15901