【问题标题】:How do I access my Docker API container from my front-end container?如何从我的前端容器访问我的 Docker API 容器?
【发布时间】:2020-07-09 09:29:57
【问题描述】:

我是 docker 新手,并试图围绕容器之间的网络进行研究。我正在运行两个容器,一个用于我的 Node.js API 服务器的容器,一个用于保存我的前端 React UI 的容器。在本地运行它们时一切正常。 API 服务器公开了 3001 端口,我可以从我的 React 站点调用 localhost:3001/api。

鉴于容器的思想是它可以在任何地方运行,我如何保证这两个容器服务不在本地机器上运行时可以连接?我知道可以在 docker 容器之间设置网络,但这似乎不适用于这种情况,因为反应容器没有发出请求,而是客户端访问反应容器(因此 localhost 现在将引用他们的机器而不是我的 API 容器)。

部署此类架构的最佳做法是什么?

需要什么样的设置来保证这些容器可以在部署时动态生成 API 主机的云部署中进行通信?

如果相关,我希望专门部署到 AWS ECS。

编辑:

package.json 代理仅与开发相关,因为该代理不会在 react 应用的生产构建中生效。

【问题讨论】:

    标签: amazon-web-services docker networking cloud amazon-ecs


    【解决方案1】:

    据我了解,您希望将由 React 前端和 Node.js 后端组成的经典两层应用程序部署到 Amazon ECS(生产环境)。

    我们前段时间为我们的应用程序设置了这个,我想概述一下解决方案。

    部署此类架构的最佳做法是什么?

    这是一个非常棘手的问题,因为它还取决于您的问题中未完全指定的两个层次的某些特征。

    前端

    我想到的第一个问题是,你是否真的需要从 docker 容器运行 React UI?是动态内容吗?如果您的 React 应用程序是为生产而正确构建的 - 如 [1] 中所述 - 它应该是静态内容。静态内容的优点是可以轻松缓存,因此不需要在生产环境中从 Docker 容器提供服务。情况恰恰相反:我认为在生产环境中从 ECS 容器提供静态内容是一种不好的做法。你应该做的是:

    • 创建一个 S3 存储桶并将您的静态资产部署到该存储桶中。
    • 可选,但强烈建议用于生产:使用某种内容交付网络 (CDN) 来分发您的内容并在边缘有效地缓存它。 AWS 服务环境为此提供了 CloudFront 服务。反过来,使用 CloudFront 是否有回报取决于您的应用程序的流量模式。您可以直接从 S3 存储桶中提供静态资产,这可能会导致更高的延迟,但可能更具成本效益。

    总而言之,我建议:如果您计划将一些严肃的应用程序投入生产,预计会收到相当大的流量负载和/或设计为单页应用程序 (SPA),外包您的静态资产到 S3 并通过 CloudFront 提供服务

    后端

    这里的最佳实践很简单:创建一个应用程序负载均衡器 (ALB)、一个目标组并将您的目标组指向您的 ECS 服务。 ECS 为 AWS Elastic Load Balancing 提供集成。在这里使用 AWS ALB 的优势是会自动为您创建 DNS 记录。 [3]

    但是如果我真的需要在 ECS 中使用两个容器呢?

    如果您决定不外包静态资产,因为您的 React 内容中有动态部分,或者上述解决方案的定价不合适,让我回答您的第二个问题:

    需要什么样的设置来保证这些容器可以在云部署中进行通信,其中 API 主机可能在部署时动态生成?

    有多种策略可以在 ECS 中将事物连接在一起。我猜你的 React 容器不需要直接连接到 Node.js 容器,对吧?如果这个假设是错误的,请纠正我。对我来说,场景如下所示:

    1. 客户端 --> Docker 容器 1“React”(加载例如 index.html)
    2. 客户端(例如,使用 index.html 内部的 Ajax)--> Docker 容器 2“Node.js”

    如果这两层真的完全独立,我建议创建两个单独的 ECS 服务 - 每个都运行一个单独的 ECS 任务定义。其次,您创建一个应用程序负载均衡器并在每个服务上激活负载均衡器集成。最后,您需要在负载均衡器上为每个服务创建一个单独的目标组,并在负载均衡器上分配一个单独的侦听器,以将流量重定向到相应的目标组。

    例子:

    • 应用程序负载均衡器,DNS 名称:my-loadbalancer-1234567890.us-west-2.elb.amazonaws.com
    • 带有 React 前端的服务 A
    • 带有 Node.js 后端的服务 B
    • 将流量重定向到服务 A 的目标组 A
    • 将流量重定向到服务 B 的目标组 B
    • ALB 侦听器 A 将负载均衡器端口 80 上的流量重定向到目标组 A
    • ALB 侦听器 B,它将负载均衡器端口 8080 上的流量重定向到目标组 B
    • 可选:您自己的域的自定义 DNS 记录,它指向负载均衡器(通过别名记录),以便在浏览器中提供对客户更友好的名称,而不是在 aws 区域中自动创建的记录elb.amazonaws.com

    现在可以通过负载平衡器域的标准 HTTP 端口访问前端,并且可以通过端口 8080 访问后端。您可以轻松地在负载平衡器上激活 SSL 并改用端口 443。 [4]
    这允许 SSL 在负载均衡器上终止,而不是在您的 docker 容器上终止 - 一种称为 SSL 终止 [5] 的功能。

    但是如果这些容器必须相互通信呢?

    通过上述方法,容器能够通过应用负载平衡器相互通信。但是,如果此通信本质上是内部通信,则这并不理想,因为它是通过公共负载均衡器端点路由的。如果我们想确保流量不会离开专用网络在容器之间,我们可以*将它们放在一起:

    • 在 ECS [6] 中创建任务定义并将两个容器放入其中
    • 为任务指定"NetworkMode": "bridge"
    • 使用属性Links在各自的容器定义(在任务定义内)指定容器之间的链接

    *再次有多种策略可以实现这一点,我在这里概述了我所知道的最简单的一种(例如,任务也可以使用服务发现 [7] 或任务网络模式私下链接在一起 awsvpc )

    我知道这是一个复杂且特别广泛的话题,因为有多种策略各有利弊,但我希望能给你一些有用的参考。

    参考文献

    [1]https://create-react-app.dev/docs/production-build/
    [2]https://docs.aws.amazon.com/AmazonECS/latest/developerguide/service-load-balancing.html
    [3]https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/resource-record-sets-creating.html#resource-record-sets-elb-dns-name-procedure
    [4]https://docs.aws.amazon.com/elasticloadbalancing/latest/application/create-https-listener.html
    [5]https://infra.engineer/aws/36-aws-ssl-offloading-with-an-application-load-balancer
    [6]https://docs.aws.amazon.com/AmazonECS/latest/developerguide/create-task-definition.html
    [7]https://stackoverflow.com/a/57540515/10473469

    【讨论】:

    • 这很有帮助,谢谢!!我同意它应该从 CDN 提供,但是,我的公司希望我们使用 Next.js,据我了解,它不能归结为静态生产构建,因为它使用 SSR。
    【解决方案2】:

    第一件事。由于 Node.js API 服务器和前端 React UI 都运行在两个不同的容器上,所以需要在 react 应用的 package.json 中配置代理。

    "proxy": "http://<docker_container_name>:3001",
    

    如果您仍然想知道这个代理是什么以及为什么需要它,请在进一步阅读之前参考this

    现在,由于我们的服务在两个不同的容器中运行,所以 "proxy": "http://localhost:3001", 将不起作用,因为这会在同一个前端容器中代理请求。 因此,我们需要告诉反应服务器将请求代理到运行在其他容器上的节点服务器。

    因此,docker_container_name 实际上是运行 Node.js API 服务器的 docker 容器名称。

    "proxy": "http://<docker_container_name>:3001",
    

    注意:确保在 Node 服务器容器中公开端口 3001。

    不想暴露节点服务器的端口怎么办?

    为此,我建议使用 docker-compose。 创建一个看起来像这样的 docker-compose.yml

    version: "3.7"
    services:
      frontend:
        # Add other configuration options for frontend service
        depends_on:
          - backend  
      backend:
        # Add configuration options for backend service
    

    请参考this 了解更多关于depends_on

    【讨论】:

    • 谢谢,但是我正在寻找一种可以在生产中使用的解决方案。代理仅作为开发功能,当您构建不生效的反应项目时。当部署到 ECS 或 Heroku 等服务时,它也不能解决动态生成主机的问题。见这里github.com/facebook/create-react-app/issues/…
    【解决方案3】:

    似乎不适用于这种情况,因为反应容器是 不是发出请求,而是客户端访问反应 容器

    我对 React UI 不是很有经验,但是如果我错了,请纠正我,据我了解,客户端浏览器首先连接到 React UI 前端,该前端反过来提供(或响应)网页具有使用嵌入式基本 URL 将 API 请求发送到 Node.js API 服务器的功能,并且它正在侦听端口 3001。在这种情况下,我认为 React UI 前端容器并没有连接到 Node.js API 容器,它只是为客户端浏览器提供方法,让网页将请求发送到正确的位置。

    虽然我觉得package.json 中的proxy 似乎是关键,但这又不是我的专业领域,真正需要做的是配置 React UI 容器以将正确的主机名嵌入到节点所在的位置JS API 是托管的,因此客户端浏览器会将请求发送到正确的目的地。考虑到您已经能够在本地计算机上执行的操作,在我看来,您的容器的端口配置已正确完成,我将假设它们正在“暴露”。因此,在具有 docker 的服务器上以相同的方式运行容器也将正确地公开端口。

    总而言之,您基本上将拥有一个具有公共主机名并在其上运行 docker 的服务器,这反过来将运行您的容器并公开它们的端口。所需的基本配置是让 React UI 容器提供 Node.js API 的正确 URL,这实际上是服务器的公共主机名(因为 docker 在技术上监听服务器上的端口并将它们向内发送到容器,这就是暴露的意思,更像是端口转发)。

    【讨论】:

      【解决方案4】:

      您必须准备一个 VPC,最低要求是。一个子网(路由表、网关、负载均衡器等)。 VPC 和子网将配置一个 IP-Range。

      子网中的所有实例都会获得超出其范围的 IP,并且您的应用程序实例可能会相互通信。您可以为您的应用预设固定 IP。

      关于蓝/绿部署,需要实例化(Fargate)ECS-Cluster。 Docker 容器对应的 AWS 服务是 AWS ECR。 我建议使用 AWS Fargate 而不是纯 AWS ECS 实例 - 成本更低,灵活性更高。

      关于 VPC 和子网的信息: https://docs.aws.amazon.com/AmazonECS/latest/userguide/create-public-private-vpc.html

      关于 Fargate 和集群的信息: https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs-cli-tutorial-fargate.html

      【讨论】:

        猜你喜欢
        • 2023-02-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-01-19
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多