React + gRPC 未到达特使容器

React + gRPC don't reach envoy container

我试图在 React 中构建一个非常基本的网页,它将 gRPC 请求发送到用 Rust 编写的后端。

我遵循了这些指南:

https://daily.dev/blog/build-a-chat-app-using-grpc-and-reactjs

https://github.com/grpc/grpc-web/tree/master/net/grpc/gateway/examples/helloworld

但是,我无法获得到达 docker 容器中的 envoy 代理的 gRPC 请求(到目前为止,envoy 是容器中唯一的组件 运行)。

这是我的原型文件:

syntax = "proto3";

package Base;

service Hello {
    rpc HelloWorld(HelloRequest) returns (HelloResponse) {}
}

message HelloRequest {}
message HelloResponse {
    string message = 1;
}

这是我的 App.js 文件(基本上它是由 React 引导程序创建的,我只是添加了功能和按钮)

import logo from './logo.svg';
import './App.css';

import { HelloClient } from './base_grpc_web_pb';
import { HelloRequest } from './base_pb';

const client = new HelloClient("http://" + window.location.hostname + ":8080", null, null);

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.js</code> and save to reload.
        </p>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React
        </a>
      </header>
      <button
        onClick={sendRequest}
        style={{
          padding: "7px 38px",
          fontSize: "1.2em",
          boxSizing: "content-box",
          borderRadius: "4px",
        }}
      >
        Join
      </button>
    </div>
  );
}

function sendRequest() {
  const request = new HelloRequest();
  
  console.log("CLICK");

  client.helloWorld(request, {}, (err, response) => {
    if (err) return console.log("BBBBB error", err);
    console.log("AAAAA RESPONSE", response.getMessage());
  });
}


export default App;

这是我的 envoy 配置(我在 Ubuntu 22.04,所以不需要用 host.docker.internal 覆盖地址):

admin:
  access_log_path: /tmp/admin_access.log
  address:
    socket_address: { address: 0.0.0.0, port_value: 9901 }

static_resources:
  listeners:
    - name: proxy
      address:
        socket_address: { address: 0.0.0.0, port_value: 8080 }
      filter_chains:
        - filters:
            - name: envoy.filters.network.http_connection_manager
              typed_config:
                "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
                codec_type: auto
                stat_prefix: ingress_http
                route_config:
                  name: local_route
                  virtual_hosts:
                    - name: local_service
                      domains: ["*"]
                      routes:
                        - match: { prefix: "/" }
                          route:
                            cluster: hellors
                            timeout: 0s
                            max_stream_duration:
                              grpc_timeout_header_max: 0s
                      cors:
                        allow_origin_string_match:
                          - prefix: "*"
                        allow_methods: GET, PUT, DELETE, POST, OPTIONS
                        allow_headers: keep-alive,user-agent,cache-control,content-type,content-transfer-encoding,custom-header-1,x-accept-content-transfer-encoding,x-accept-response-streaming,x-user-agent,x-grpc-web,grpc-timeout
                        max_age: "1728000"
                        expose_headers: custom-header-1,grpc-status,grpc-message
                http_filters:
                  - name: envoy.filters.http.grpc_web
                    typed_config:
                      "@type": type.googleapis.com/envoy.extensions.filters.http.grpc_web.v3.GrpcWeb
                  - name: envoy.filters.http.cors
                    typed_config:
                      "@type": type.googleapis.com/envoy.extensions.filters.http.cors.v3.Cors
                  - name: envoy.filters.http.router
                    typed_config:
                      "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
  clusters:
    - name: hellors
      connect_timeout: 0.25s
      type: logical_dns
      http2_protocol_options: {}
      lb_policy: round_robin
      # win/mac hosts: Use address: host.docker.internal instead of address: localhost in the line below
      load_assignment:
        cluster_name: cluster_0
        endpoints:
          - lb_endpoints:
              - endpoint:
                  address:
                    socket_address:
                      address: 0.0.0.0
                      port_value: 9090

特使的 Dockerfile:

FROM envoyproxy/envoy-dev:latest
COPY envoy.yaml /etc/envoy/envoy.yaml
RUN chmod go+r /etc/envoy/envoy.yaml

在控制台中,单击按钮后出现以下错误消息:

http://localhost:8080/Base.Hello/HelloWorld [HTTP/1.1 503 Service Unavailable 5ms]

其次是

message: "Http response at 400 or 500 level", [...]

如果我尝试 curl 端口,它似乎没有在监听:

>$ curl -v http://localhost:8080
*   Trying 127.0.0.1:8080...
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET / HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.81.0
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 503 Service Unavailable
< content-length: 145
< content-type: text/plain
< date: Thu, 28 Apr 2022 16:03:55 GMT
< server: envoy
< 
* Connection #0 to host localhost left intact
upstream connect error or disconnect/reset before headers. reset reason: connection failure, transport failure reason: delayed connect error: 111

到这里我就卡住了,所有的配置文件似乎都没问题,但我仍然无法访问 envoy 代理。有什么建议吗?

我偶然发现 建议使用 nginx,但是我不明白为什么需要它,特别是因为我正在查看的 gRPC 示例没有使用它。

在您的情况下,Http response at 400 or 500 level 可能是由于您的 gRPC 服务无法从 Envoy 访问。

您说 Envoy 运行 装在 Docker 容器中。如果是这样,您的 Envoy 配置中的集群 hellors

clusters:
- name: hellors
  load_assignment:
    endpoints:
    - lb_endpoints:
      - endpoint:
          address:
            socket_address:
              address: 0.0.0.0
              port_value: 9090

无法访问,因为您指定了地址 0.0.0.0。 Docker 容器使用它们自己的网络,因此在该网络内,0.0.0.0:9090 未定义,因为您的 gRPC 服务 运行 在您的主机上运行。

您可能想在您的主机网络 (--net=host) 中尝试 运行ning Envoy。

或者如果您的 gRPC 服务可以容器化,您可以 运行 Envoy 在同一个 Docker 网络中的 gRPC 服务。