Docker/Symfony/Reactjs/Keycloak : 如何使用分隔的 docker-compose 文件从一个容器向另一个容器发出 HTTP 请求?

Docker/Symfony/Reactjs/Keycloak : How to make an HTTP request from a container to another with separated docker-compose files?

我有 3 项服务:我在 Symfony 中的后端(后台和 Api)、我在 Reactjs 中的前端应用程序和用于 SSO 身份验证的 Keycloak 服务。

我想为我的后台和前端应用程序使用 Keycloak 身份验证。

我有 3 个分开的 docker-compose 文件:

我的后端docker-撰写文件:

version: "3.3"
services:
  web:
    container_name: web_app
    build:
      context: ./.docker/web
    ports:
      - 8000:80
    volumes:
      - .:/var/www/backend
    working_dir: /var/www/backend
    depends_on:
      - mysql

  mysql:
    container_name: mysql_app
    image: mysql:5.7
    command: mysqld --sql_mode=NO_ENGINE_SUBSTITUTION --character-set-server=utf8 --collation-server=utf8_general_ci --skip-character-set-client-handshake
    restart: on-failure
    ports:
      - 3307:3306
    volumes:
      - mysql_data:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: app

  phpmyadmin:
    container_name: phpmyadmin_app
    image: phpmyadmin/phpmyadmin
    ports:
      - 8001:80
    environment:
      PMA_HOST: mysql
      PMA_USER: root
      PMA_PASSWORD: root
    depends_on:
      - mysql

volumes:
  mysql_data:
    driver: local

networks:
  default:
    external:
      name: app-network

我的前端docker-编写文件:

version: "3.3"
services:
  nodejs:
    container_name: nodejs_app
    image: "node:10"
    ports:
      - 3001:3000
    user: "node"
    environment:
      - NODE_ENV=development
    volumes:
      - .:/usr/src/frontend
    working_dir: /usr/src/frontend
    command: >
      sh -c "npm install && npm start"

networks:
  default:
    external:
      name: app-network

我的 Keycloak docker-编写文件:

version: "3.3"
services:
  keycloak_mysql:
    image: mysql:5.7
    volumes:
      - keycloak_data:/var/lib/mysql
    ports:
      - 3308:3306
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: keycloak
  keycloak:
    image: quay.io/keycloak/keycloak:latest
    environment:
      DB_VENDOR: MYSQL
      DB_ADDR: keycloak_mysql
      DB_DATABASE: keycloak
      DB_USER: root
      DB_PASSWORD: root
      KEYCLOAK_USER: admin
      KEYCLOAK_PASSWORD: admin
    ports:
      - 8080:8080
    depends_on:
      - keycloak_mysql

volumes:
  keycloak_data:
    driver: local

networks:
  default:
    external:
      name: app-network

我的后端 .env 文件:

OAUTH_KEYCLOAK_SERVER_URL=http://localhost:8080/auth
OAUTH_KEYCLOAK_CLIENT_ID=app
OAUTH_KEYCLOAK_CLIENT_SECRET=
OAUTH_KEYCLOAK_REALMS=app

以及使用以下命令创建的网络:docker network create app-network

首先,当我尝试在后台进行身份验证时,我被重定向到 Keycloak 登录页面。 然后,在输入我的凭据后,我被重定向回我的后台并出现以下错误:

cURL error 7: Failed to connect to localhost port 8080: Connection refused (see https://curl.haxx.se/libcurl/c/libcurl-errors.html) for http://localhost:8080/auth/realms/app/protocol/openid-connect/token

我对 docker 和网络不是很熟悉。

我该如何解决这个问题?


更新 1:

现在我已经在我的网络中添加了一个子网:docker network create --subnet 172.23.0.0/24 app-network

并向我的容器添加了静态 IP:

services:
  # Backend docker-compose file
  web:
    networks:
      default:
        ipv4_address: 172.23.0.11
    ...
    ...
  mysql:
    networks:
      default:
        ipv4_address: 172.23.0.15
    ...
    ...
  phpmyadmin:
    networks:
      default:
        ipv4_address: 172.23.0.14

  # Frontend docker-compose file
  nodejs:
    networks:
      default:
        ipv4_address: 172.23.0.12

  # Keycloak docker-compose file
  keycloak_mysql:
    networks:
      default:
        ipv4_address: 172.23.0.16
    ...
    ...
  keycloak:
    networks:
      default:
        ipv4_address: 172.23.0.13

下面的命令:docker network inspect app-network 给我:

[
    {
        "Name": "app-network",
        "Id": "31984c890cfbf8206dacf18dd3e831c45cb133ab5391b45edf609cd2232eaffa",
        "Created": "2021-07-14T12:19:07.6486215Z",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "172.23.0.0/24"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "14ab20cf0c5517d9ec9b9d136d65bc07e847d154364ab831a7825c913943a768": {
                "Name": "phpmyadmin",
                "EndpointID": "81d31a65c37e121dbb4948e933e8edf37213612e9b3f6eb4ac66e4a0443a0117",
                "MacAddress": "02:42:ac:17:00:0e",
                "IPv4Address": "172.23.0.14/24",
                "IPv6Address": ""
            },
            "1cec44b30b8f942803d1bc3525ba5875d73f7253fbac021c67bf76ebf4488f7c": {
                "Name": "web",
                "EndpointID": "9a58e55dd679585af744af01b69d21eb94995a54ead0d24a5fe64e306c599452",
                "MacAddress": "02:42:ac:17:00:0b",
                "IPv4Address": "172.23.0.11/24",
                "IPv6Address": ""
            },
            "21f54bb4a453e701f1628b4c5e122d3a1e32411608603d193bd5787e2ff1ac38": {
                "Name": "nodejs",
                "EndpointID": "3272a80565ea0ff627c947635c04f5ad8fa47d1822db62e3e0d5f9052270d062",
                "MacAddress": "02:42:ac:17:00:0c",
                "IPv4Address": "172.23.0.12/24",
                "IPv6Address": ""
            },
            "7d0a8dec8ff6baac31e208c5ccea6fca6954d088a87b884ddd96c95d1c6a2930": {
                "Name": "keycloak_mysql_1",
                "EndpointID": "2517e766e4af674185c4d8612c5e169e6e12ab4658497dfe95cd08ba3d3f6153",
                "MacAddress": "02:42:ac:17:00:10",
                "IPv4Address": "172.23.0.16/24",
                "IPv6Address": ""
            },
            "822abcc2e5b7e2231f08534050618bc4842405a0f7a287274b9240c291ce87e3": {
                "Name": "mysql",
                "EndpointID": "5b3f47483495e48d0026c84dffe86d4cecd529b9f87ca0d40902280cca01be86",
                "MacAddress": "02:42:ac:17:00:0f",
                "IPv4Address": "172.23.0.15/24",
                "IPv6Address": ""
            },
            "998210a92b66d1342c9df35b30e341f7631c62604f6b7d0d0d3ac128d2c78420": {
                "Name": "keycloak_1",
                "EndpointID": "3e7e346a159593e4863abd7bc20d2b7f398a6fd2763981b2584ecc02d325c92e",
                "MacAddress": "02:42:ac:17:00:0d",
                "IPv4Address": "172.23.0.13/24",
                "IPv6Address": ""
            }
        },
        "Options": {},
        "Labels": {}
    }
]

我可以通过以下操作访问 web 容器中的 keycloak 容器: curl http://keycloak:8080curl http://172.23.0.13:8080

所以我将我的后端 .env 文件更新为:OAUTH_KEYCLOAK_SERVER_URL=http://keycloak:8080/auth

但是我的浏览器在发生重定向时无法识别 http://keycloak:8080 url。

在我的浏览器上,我只能访问:

My backend: http://localhost:8000
My frontend: http://localhost:3001
My keycloak: http://localhost:8080

是否也可以通过 Ips 从主机访问我的容器?


解决方案:

我编辑了我的 /etc/hosts 并添加了:

127.0.0.1 keycloak
127.0.0.1 web
127.0.0.1 nodejs

现在一切正常

从后端的角度(或从上下文中的所有其他容器)来看,localhost 是容器本身。所以如果你想在你的主机上使用端口转发,你必须使用网桥 ip 地址。

应该是 172.17.0.1.

所以你的配置应该是这样的:
OAUTH_KEYCLOAK_SERVER_URL=http://172.17.0.1:8080/auth

默认情况下,由 docker-compose 创建的容器都有自己的网络命名空间——即它们自己的(虚拟)网络接口和它们自己的 IP 地址——因此尝试从应用程序连接到 http://localhost:8080/容器不会连接到 Keycloak 容器,而是连接到应用程序容器本身。

要连接到 Keycloak 容器,您需要使用映射/转发到 Keycloak 容器的主机名/IP。由于所有容器都连接到 app-network,因此您可以为此使用容器名称:

OAUTH_KEYCLOAK_SERVER_URL=http://keycloak:8080/auth

有关更多信息,请参阅 docker-compose networking docs。 (简短的旁注:您不需要在主机上公开 mysql 容器的端口,应用程序容器就可以连接)

如果相同的基础 url 用于重定向您的浏览器,但请务必使用可以从您的主机解析的 host/IP ,其中浏览器 运行 打开,例如主机本身的主机名,然后将相应的端口转发到正确的容器。