0

我在 grpc.mydomain.com 上使用 LetsEncrypt 在 traefik 上启动并运行了一项服务。但是,由于 CORS 的一些问题( https://github.com/containous/traefik/issues/4210),traefik 不支持路由 grpc-web 请求。Envoy 似乎是与 grpc-web 一起使用的 traefik 的替代品,但我不想重新配置所有内容。

如果我将特使放在 envoy.mydomain.com 上,那么它实际上首先会命中 traefik,而 traefik 无法将 grpc-web 请求路由到特使。所以这行不通。

如果我将特使放在 traefik 之外(mydomain.com:9091),那么特使就没有 traefik 所具有的 TLS 支持。

我需要将所有东西都切换到特使吗?有没有我没有考虑过的替代方案?欢迎任何指导:)

当前 Traefik 设置:

  traefik:
    image: traefik:v2.0.0
    container_name: traefik
    command:
      - --entrypoints.web.address=:80
      - --entrypoints.websecure.address=:443
      - --entrypoints.grpc.address=:8090
      - --providers.docker
      - --api
      - --serversTransport.rootCAs=/certs/grpc.cert
      # Lets Encrypt Resolvers
      - --certificatesresolvers.leresolver.acme.email=${EMAIL}
      - --certificatesresolvers.leresolver.acme.storage=/etc/acme/cert.json
      - --certificatesresolvers.leresolver.acme.tlschallenge=${TLS_CHALLENGE}
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - /etc/acme/:/etc/acme/
      - ./secrets/grpc.cert:/certs/grpc.cert
    # Dynamic Configuration
    labels:
      # Dashboard
      - "traefik.http.routers.traefik.rule=Host(`traefik.${DOMAIN}`)"
      - "traefik.http.routers.traefik.service=api@internal"
      - "traefik.http.routers.traefik.tls.certresolver=leresolver"
      - "traefik.http.routers.traefik.entrypoints=websecure"
      - "traefik.http.routers.traefik.middlewares=authtraefik"

      # https://docs.traefik.io/middlewares/basicauth/
      # password generated from `echo $(htpasswd -nb admin $PASSWORD) | sed -e s/\\$/\\$\\$/g`
      - "traefik.http.middlewares.authtraefik.basicauth.users=admin:$$apr1$$6VzI3S0N$$29FC82dYEbjFN9tPSfWLX1"

      # global redirect to https
      - "traefik.http.routers.http-catchall.rule=hostregexp(`{host:.+}`)"
      - "traefik.http.routers.http-catchall.entrypoints=web"
      - "traefik.http.routers.http-catchall.middlewares=redirect-to-https"

      # middleware redirect
      - "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
    ports:
      - 80:80
      - 443:443
      - 8090:8090
    networks:
      - internal
      - proxied

  grpc_server:
    image: ${GRPC_IMAGE}
    container_name: grpc_server
    volumes:
      - /tmp/keyset.json:/tmp/keyset.json
      - ./secrets/:/secrets/
    working_dir: /app/__main__/
    labels:
      - "traefik.http.routers.combined_server.rule=Host(`grpc.${DOMAIN}`)"
      - "traefik.http.routers.combined_server.entrypoints=grpc"
      - "traefik.http.routers.combined_server.tls=true"
      - "traefik.http.routers.combined_server.tls.certresolver=leresolver"
      # http
      - "traefik.http.services.grpc-svc.loadbalancer.server.scheme=h2c"
      - "traefik.http.services.grpc-svc.loadbalancer.server.port=8090"
    expose:
      - 8090
    networks:
      - internal
      - proxied

我还尝试设置这些以修复 CORS 错误,但无济于事。

      - "traefik.http.middlewares.testheader.headers.accesscontrolallowmethods=GET,PUT,DELETE,POST,OPTIONS"
      - "traefik.http.middlewares.testheader.headers.accesscontrolallowheaders=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"
      - "traefik.http.middlewares.testheader.headers.accesscontrolmaxage=100"
      - "traefik.http.middlewares.testheader.headers.addvaryheader=true"
      - "traefik.http.middlewares.testheader.headers.alloworigin=*"

4

2 回答 2

3

解决此问题的一种方法是使用 2 个不同的 URL,它们最初都由 traefik 处理。一个 URL 用于“直接 grpc”(grpc.mydomain.com),另一个用于 grpc-web(我们称之为 grpc-web.mydomain.com)。Traefik 为两者执行 TLS 终止。

grpc.mydomain.com 流量直接传递给运行 grpc_server 的容器。grpc-web.mydomain.com 流量被传递给充当 grpc-web-proxy 的 envoy,然后将流量传递给 grpc_server。

因此,当您使用 docker-compose 时,您需要向 docker-compose.yml 添加一个特使服务:

---
version: '3'
services:
  traefik:
    # traefik configuration from your question
    # ...
   grpc-server:
    # grpc_server configuration from your question
    # ...
  envoy:
    image: envoyproxy/envoy:v1.14.1
    restart: unless-stopped
    volumes:
      - ./envoy.yaml:/etc/envoy/envoy.yaml
    labels:
      - traefik.enable=true
      - traefik.http.routers.envoy.rule=Host(`grpc-web.mydomain.com`)
      - traefik.http.services.envoy.loadbalancer.server.port=8080
      - traefik.http.routers.envoy.tls=true
      - traefik.http.routers.envoy.tls.certresolver=leresolver

envoy.yaml 配置(安装在上面的卷部分)如下所示:

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

static_resources:
  listeners:
    - name: listener_0
      address:
        socket_address: { address: 0.0.0.0, port_value: 8080 }
      filter_chains:
        - filters:
            - name: envoy.http_connection_manager
              config:
                codec_type: auto
                stat_prefix: ingress_http
                route_config:
                  name: local_route
                  virtual_hosts:
                    - name: local_service
                      domains: ["*"]
                      routes:
                        - match: { prefix: "/" }
                          route:
                            cluster: grpc_service
                            max_grpc_timeout: 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.grpc_web
                  - name: envoy.cors
                  - name: envoy.router
  clusters:
    - name: grpc_service
      connect_timeout: 0.25s
      type: logical_dns
      http2_protocol_options: {}
      lb_policy: round_robin
      hosts: [{ socket_address: { address: grpc-server, port_value: 8090 }}]

这是 envoy 的一个非常基本的 grpc-web 配置。需要注意的重要部分是,我们address: grpc-server, port_value: 8090在“grpc_service”集群配置的配置中设置了 docker-compose.yml 中的服务名称和 grpc-server 正在侦听的端口。请注意,我将您的服务从 grpc_server 重命名为 grpc-server,因为下划线不是主机名中的有效字符。

在客户端,使用:

  • javascript (grpc-web) 代码中的“grpc-web.mydomain.com”。
  • “grpc.mydomain.com” 用另一种语言(例如 golang)编写客户端时。

我创建了一个工作示例,可以在以下位置找到:https ://github.com/rbicker/greeter

于 2020-04-18T13:49:16.123 回答
0

如果您想摆脱特使中已弃用的警告,您可以通过以下三个更改envoy.yaml 从此答案更新:

  1. 代替:
            - name: envoy.http_connection_manager
              config:

和:

            - name: envoy.filters.network.http_connection_manager
              typed_config:
                "@type": type.googleapis.com/envoy.config.filter.network.http_connection_manager.v2.HttpConnectionManager
  1. 代替
                  - name: envoy.grpc_web
                  - name: envoy.cors
                  - name: envoy.router

                  - name: envoy.filters.http.grpc_web
                  - name: envoy.filters.http.cors
                  - name: envoy.filters.http.router
  1. 代替
hosts: [{ socket_address: { address: grpc-server, port_value: 8090 }}]

      load_assignment:
        cluster_name: cluster_0
        endpoints:
          - lb_endpoints:
              - endpoint:
                  address:
                    socket_address:
                      address: grpc-server
                      port_value: 8090
于 2020-06-20T09:18:50.710 回答