首页 > 解决方案 > 将 docker-compose 网络内的主机重定向到 docker 外的 localhost

问题描述

我有一组使用 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。

标签: dockernetworkingdocker-compose

解决方案


tl;博士

你需要 3 件事来使它工作:

  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. hostname为您的 docker-compose.yml 中的每个参与服务添加自定义,例如
services:
    service-1:
        hostname: service-1.in.docker

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

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

  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. 由于我们注释了该行,因此( curl service-1.in.docker:5900hostname: service-1.in.docker )发出的 curl 请求将转到该服务(由于别名service-1.in.docker)。随后,这些请求被转发到端口 15901 上的主机。service-2router

  2. 如果我们注释掉该行hostname: service-1.in.dockerservice-1会以service2“hello world”响应。

如果您有可能动态更改各个服务使用的主机名,例如环境变量或属性文件等,则可以使用它们,因此您不必每次都注释掉:

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

推荐阅读