无法在 localhost 上的 docker 容器之间进行通信

Posted

技术标签:

【中文标题】无法在 localhost 上的 docker 容器之间进行通信【英文标题】:Unable to communicate between docker containers on localhost 【发布时间】:2020-06-24 05:46:07 【问题描述】:

首先,我是 docker 容器的新手,我不太了解它们是如何运行的,但我有这样的设置:

一个 .NET Core API 的 docker 容器(VS-2019 自动生成的 Docker 文件) 一个用于 Angular 9 应用程序的 docker 容器

API 可通过https://localhost:44384/api/weatherforecast 访问 可通过 https://localhost:4200

访问容器化的 Angular 应用程序

如果我在没有 docker 的情况下运行 angular 应用程序,在使用代理修复 CORS 问题后,我可以连接到 https://localhost:44384/api/weatherforecast。但是,当我运行 dockerized 版本时,我收到以下错误:

Failed to load resource: the server responded with a status of 504 (Gateway Timeout)

在 VS 控制台我看到这个错误:

clearance_1  | [HPM] Error occurred while trying to proxy request /api/weatherforecast from loccoalhost:4200 to https://localhost:44384 (ECONNREFUSED) (https://nodejs.org/api/errors.html#errors_common_system_errors)

这似乎是一个连接问题,所以我在互联网上挖掘了一下,我试图将这两个容器放在同一个网络下。

以下是步骤: 1.创建一个桥接“测试”网络

docker network create test
    将两个容器连接到新创建的网络:

docker 网络连接测试 [容器 id 1]

docker 网络连接测试 [容器 id 2]

    如果我检查网络,一切似乎都很好,但 api 仍然无法在 localhost 上调用

其他潜在有用的东西:

docker-compose.yml:

version: "3.7"
services:
  clearance:
     build:
     # network: host
      context: .
      dockerfile: DockerFileLocal
     ports:
       - 4200:4200
     volumes:
       - /app/node_modules
       - .:/app

proxy.conf.json


   "/api/*": 
      "target": "https://localhost:44384",
      "secure": false,
      "logLevel": "debug",
      "changeOrigin": true
   

我错过了什么?

【问题讨论】:

你是如何开始api docker container 的? 不确定 Visual Studio 如何启动容器。这些是启动配置: "Docker": "commandName": "Docker", "launchBrowser": true, "launchUrl": "Scheme://ServiceHost:ServicePort/api/weatherforecast", " environmentVariables": "ASPNETCORE_URLS": "https://+:443;http://+:80", "ASPNETCORE_HTTPS_PORT": "44384" , "httpPort": 52012, "useSSL": true, "sslPort" : 44384 让我们关注代理和api容器。在代理配置中,您指示对/api/* 的请求转发到localhost:44384。但是在代理容器上,localhost 指的是容器本身,而在代理容器上,44384 上没有运行任何内容。您可能会找到解决方案。阅读more 和more。 【参考方案1】:

在为每个容器声明静态 IP 的每个服务下的 compose 文件中添加一个部分:

services:
  webserver:
    build:
      dockerfile: Dockerfile
      context: .
    depends_on:
      - some_other_container
    ports:
      - "443:443"
    environment:
      - MY_VAR='my_val'
########### ADD THIS PART ##############
    networks:
        dev_net:
          ipv4_address: "192.168.0.50"

并为网络添加与services 相同级别的部分:

networks:
  dev_net:
    name: my_dev_net
    driver: overlay
    ipam:
      config:
          - subnet: "192.168.0.0/24"

然后确保托管应用程序配置为使用这些 IP 进行通信。如果 IP 不是手动分配的,docker 将非常像 DHCP 服务器并提供它决定分配的任何 IP。


澄清:我认为您遇到的部分问题是由于 Docker 网络本身:在 Mac 和 Windows 上,Docker 无法真正提供 IP 分配到的完全可路由的虚拟网络容器可以直接从外部访问。无论您采用哪种解决方案,都必须解决该限制。使容器可以从外部访问的唯一方法是将其直接绑定到主机上的端口(使用docker run -p [source_port]:[host_port]

这是一个可以阐明我的建议的演示。这将:

使用单个 docker-compose.yml 文件构建两个容器 Container #1 将使用 openssl 在端口 8080 上提供一个简单的文本文件 Container #1 上的端口 8080 将绑定到主机上的同一端口 Container #2 将使用 curl 使用私有网络 IP 地址从 Container #1 获取文件 从 主机 运行相同的 curl 命令,但使用 localhost 而不是私有 IP(因为无法从外部 docker 访问) 两个容器都分配了 Docker 专用网络上的静态 IP 地址(在 compose 文件中定义)

这样做的目的是提供一个最小的示例:允许两个 Docker 容器进行通信;通过 docker 专用网络;使用私有静态 IP 地址;同时还绑定到主机系统上的端口;允许从 docker 专用网络外部连接到容器。

docker-compose.yml:

version: "3.7"
services:
  c1:
    build:
      dockerfile: Dockerfile
      context: ./c1
    ports:
      - "8080:8080"
    networks:
        dev_net:
          ipv4_address: "192.168.0.50"
  c2:
    build:
      dockerfile: Dockerfile
      context: ./c2
    depends_on:
      - c1
    networks:
      dev_net:
        ipv4_address: "192.168.0.51"
networks:
  dev_net:
    name: test_dev_net
    driver: overlay
    external: false
    ipam:
      config:
          - subnet: "192.168.0.0/24"

容器 #1 Dockerfile:

FROM alpine:3.7
RUN apk update && apk upgrade && apk add openssl
COPY  run.sh /run.sh
CMD ["/run.sh"]

容器#1 run.sh:

#!/bin/sh

echo "HELLO, WORLD!" >> test_file.txt

openssl req -x509 -newkey rsa:2048 -keyout key.pem \
    -out cert.pem -days 365 -nodes -subj /C=\/ST=\/L=\/O=\/OU=\/CN=\/

openssl s_server -key key.pem -cert cert.pem -accept 8080 -HTTP &

sleep 25

容器#2 Dockerfile:

FROM alpine:3.7
RUN apk update && apk upgrade && apk add curl;
COPY  run.sh /run.sh
CMD ["/run.sh"]

容器#2 run.sh:

#!/bin/sh

c1_url="https://192.168.0.50:8080/test_file.txt"

for _ in $(seq 0 2); do
  curl -k -g  $c1_url
  sleep 5
done

构建/运行/测试:

$ docker-compose up & \
  sleep 10 && \
  curl -k -g "https://localhost:8080/test_file.txt"
[3] 63380

Starting docker_c1_1 ... done
Starting docker_c2_1 ... done
Attaching to docker_c1_1, docker_c2_1
c1_1  | Generating a RSA private key
c1_1  | .....................................+++++
c1_1  | writing new private key to 'keys/key.pem'
c1_1  | No value provided for Subject Attribute C, skipped
c1_1  | No value provided for Subject Attribute ST, skipped
c1_1  | No value provided for Subject Attribute L, skipped
c1_1  | No value provided for Subject Attribute O, skipped
c1_1  | No value provided for Subject Attribute OU, skipped
c1_1  | No value provided for Subject Attribute CN, skipped
c2_1  |   % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
c2_1  |                                  Dload  Upload   Total   Spent    Left  Speed
100    42    0    42    0     0   3230      0 --:--:-- --:--:-- --:--:--  3230
c2_1  | HELLO, WORLD!   <<<<------ Container #2 curl output
c2_1  | HELLO, WORLD!
c2_1  | HELLO, WORLD!
HELLO, WORLD!           <<<<------ Host curl output
docker_c2_1 exited with code 0
c1_1  | DONE. Exiting...
docker_c1_1 exited with code 0
[3]   Done                    docker-compose up
$

因此,此解决方案将允许 docker-compose 构建容器,这些容器在私有 docker 网络上使用静态 IP 地址,并且还通过主机上的端口绑定公开。

【讨论】:

容器专用 IP 地址仅适用于几个非常特定的环境;例如,如果 Angular 应用程序在 MacOS 或 Windows 主机上的浏览器中运行,它将根本无法访问此地址。 @DavidMaze 你应该重新阅读这个问题。 OP 使用已发布的端口连接到容器没有问题。在 docker 网络中设置静态 IP 不会改变这一点(正如我发布的示例所示)。这是一个涉及 docker 网络上的容器之间的通信的问题。【参考方案2】:

我在 单独的 Docker 容器中运行的 Angular 应用和 API 遇到了类似的问题。 通过以下设置,这两个应用程序都可以正常工作:

Angular 运行在 http://localhost:4200API 运行在 http://localhost:8080

问题

Angular 应用无法访问 API。 以下代码一直给我与网络相关的错误。

this.http.get('http://localhost:8080').subscribe(console.log);

解决方案

链接链接到另一个服务中的容器。要么指定服务名称和链接别名 (SERVICE:ALIAS),要么只指定服务名称。链接服务的容器可以通过与别名相同的主机名访问,或者如果没有指定别名,则使用服务名。

当一个容器需要通过网络到达另一个容器时,我们需要创建一个链接。 我最终在docker-compose.yml 中的angular 服务定义中创建了指向api 服务的链接

version: "2"
services:
  api:
    build:
      context: .
      dockerfile: ./api/Dockerfile
    volumes:
      - ./api:/usr/src/app
    ports:
      - "8080:8080"

  angular:
    build:
      context: .
      dockerfile: ./angular/Dockerfile
    volumes:
      - ./angular:/usr/src/app
    ports:
      - "4200:4200"
    links:
      - api

然后我通过在 Angular 应用中将 localhost 替换为 api 来修复网络错误。

this.http.get('http://api:8080').subscribe(console.log);

您不需要代理。但是,您可能需要调整配置才能使其正常工作。

【讨论】:

links 已弃用,不应使用。 不推荐使用命令行标志--link,而不是docker compose文件中的links属性。 实际上,@leopal 是对的:链接通常被认为是遗留的,包括在撰写文件中 (docs.docker.com/compose/compose-file/compose-file-v2/#links)

以上是关于无法在 localhost 上的 docker 容器之间进行通信的主要内容,如果未能解决你的问题,请参考以下文章

无法从我的本地主机访问 docker 上的 apache

Docker:无法连接到localhost端口80:连接被拒绝

Docker:服务器是不是在主机“localhost”(::1) 上运行并接受端口 5432 上的 TCP/IP 连接?

无法在 Windows 10 上使用 docker 访问 localhost:8080

RazorSQL 和 sql 开发人员无法使用 docker 连接到 Mac 上的 oracle DB

如何从 docker 容器中的 python 脚本连接到 localhost 上的 mysql 数据库