gRPC-웹 요청용 Envoy Proxy

28732 단어 gRPCenvoytech
다음은 gRPC-웹의 요청 Proxy를 gRPC 서버에 연결하는 Envoy의 설정 절차를 소개합니다.

샘플 코드


샘플 코드 준비됐으니 가능하면 손 옆에서 읽어보세요.[1]

디렉토리 구조


목차의 구성은 다음과 같다.
./
├── Makefile # 各種コマンド
├── client # gRPC-Web Client 関連
│   ├── client.js
│   ├── dist
│   ├── index.html
│   └── generated
│       └── helloworld
│           ├── helloworld_grpc_web_pb.js
│           └── helloworld_pb.js
├── docker-compose.yml # Envoy の起動設定
├── proto # protobuf
│   └── helloworld
│       └── helloworld.proto
├── proxy # Envoy 関連
│   ├── Dockerfile
│   └── envoy.yaml
└── server # gRPC Server 関連
    ├── go.mod
    ├── go.sum
    └── main.go
    └── generated
        └── helloworld
            ├── helloworld.pb.go
            └── helloworld_grpc.pb.go

prootobuf의 정의


prootobuf의 정의는 다음과 같다.
proto/helloworld/helloworld.proto
syntax = "proto3";

package helloworld;

service Greeter {
  rpc SayHello (HelloRequest) returns (HelloReply);
}

message HelloRequest {
  string name = 1;
}

message HelloReply {
  string message = 1;
}

gRPC 서버 준비


위의 protoo에 해당하는 gRPC 서버를 준비합니다.이번에는 gRPC의 문서QuickStart를 참고하여 실시하였으며, 상세한 내용은 생략하였다.
cd server
go run main.go
evans -r --host localhost -p 50051

  ______
 |  ____|
 | |__    __   __   __ _   _ __    ___
 |  __|   \ \ / /  / _. | | '_ \  / __|
 | |____   \ V /  | (_| | | | | | \__ \
 |______|   \_/    \__,_| |_| |_| |___/

 more expressive universal gRPC client


helloworld.Greeter@localhost:50051> call SayHello
name (TYPE_STRING) => Envoy
{
  "message": "Hello Envoy"
}

gRPC-웹 센터 준비


JavaScript를 사용하여 gRPC-Web Client를 설치합니다.

코드 자동 생성


프로토에 대응하는 클라이언트 코드를 자동으로 생성합니다.
protoc -I=./proto ./proto/helloworld/helloworld.proto \
	--js_out=import_style=commonjs:client/generated \
	--grpc-web_out=import_style=commonjs,mode=grpcwebtext:client/generated

클라이언트 구현


자동 생성된 파일을 사용하여 gRPC-웹 클라이언트 구현
client/client.js
const {HelloRequest} = require('./generated/helloworld/helloworld_pb.js');
const {GreeterClient} = require('./generated/helloworld/helloworld_grpc_web_pb.js');

const client = new GreeterClient('http://localhost:9000', null, null);

const request = new HelloRequest();
request.setName('World');

client.sayHello(request, {}, (err, response) => {
    if (err) {
        console.log(`Unexpected error for sayHello: code = ${err.code}` +
            `, message = "${err.message}"`);
    } else {
        console.log(response.getMessage());
    }
});
또한 브라우저에서 위의 설치 코드를 호출하기 위해 간단한 HTML을 설치합니다.
client/index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>gRPC-Web Example</title>
    <script src="./dist/main.js"></script>
</head>
<body>
<p>Open up the developer console and see the logs for the output.</p>
</body>
</html>

Envoy 준비


Envoy.yaml


Envoy.yaml에서 Proxy 설정을 설명합니다.
  • Proxy는Port9000에서gRPC-웹으로부터요청을 받았습니다.
  • Port 50051의 gRPC Server에서 Forward.
  • proxy/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: 9000 }
        filter_chains:
        - filters:
          - name: envoy.filters.network.http_connection_manager
            typed_config:
              "@type": type.googleapis.com/envoy.config.filter.network.http_connection_manager.v2.HttpConnectionManager
              codec_type: auto
              stat_prefix: ingress_http
              access_log:
                - name: envoy.access_loggers.file
                  typed_config:
                    "@type": type.googleapis.com/envoy.config.accesslog.v2.FileAccessLog
                    path: "/dev/stdout"
              route_config:
                name: local_route
                virtual_hosts:
                - name: local_service
                  domains: ["*"]
                  routes:
                  - match: { prefix: "/" }
                    route:
                      cluster: greeter_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.filters.http.grpc_web
              - name: envoy.filters.http.cors
              - name: envoy.filters.http.router
      clusters:
      - name: greeter_service
        connect_timeout: 0.25s
        type: logical_dns
        http2_protocol_options: {}
        lb_policy: round_robin
        dns_lookup_family: V4_ONLY
        upstream_connection_options:
          tcp_keepalive:
            keepalive_time: 300
        load_assignment:
          cluster_name: cluster_0
          endpoints:
            - lb_endpoints:
                - endpoint:
                    address:
                      socket_address:
                        address: host.docker.internal
                        port_value: 50051
    

    Docker 설정


    Docker를 사용하여 Envoy를 시작할 준비를 합니다.
    proxy/Dockerfile
    FROM envoyproxy/envoy:v1.15.0
    COPY ./envoy.yaml /etc/envoy/envoy.yaml
    CMD /usr/local/bin/envoy -c /etc/envoy/envoy.yaml
    
    이어서 docker-compose.예.
    docker-compose.yaml
    version: '3'
    services:
      envoy:
        build:
          context: ./proxy
        container_name: envoy-grpc-proxy
        ports:
          - 9000:9000
    

    다양한 시작 명령 준비


    gRPC Server, gRPC-Web Client의 시작 명령을Makefile에 취합하면 다음과 같습니다.
    Makefile
    server:
    	cd ./server && go run main.go
    
    client:
    	cd ./client && \
    		npx webpack --mode=development client.js && \
    		yarn static -p 8081
    
    .PHONY: server client
    
    이렇게 준비됐습니다.

    동작 확인


    실기 한번 돌려봐.
    터미널 창을 각각 열고 다음 명령을 실행합니다.
    # Start gRPC Server
    make server
    
    # Start gRPC-Web Client
    make client
    
    # Start Envoy
    docker-compose up
    
    브라우저로http://localhost:8081/에 접근해 보면 gRPC 서버가 요청한 것을 알 수 있습니다.

    또한 Envoy 로그에서 받은 요청 정보의 출력도 확인할 수 있습니다.

    Envoy를 통해 브라우저의 요청이 gRPC 서버에서 Forward에 의해 확인되었습니다.

    푹 빠진 곳


    우선gRPC-Web github의 샘플 코드 같은 절차를 시도했지만 자바스크립트로 실행된 gRPC 서버가 잘 작동하지 않거나 Envoy가 잘 작동하지 않습니다.yaml의 기술이 매우 낡아서 원인을 조사하는 데 많은 시간이 걸렸다😢
    결과적으로 문서 Envoy를 찾아다녔습니다.yaml의 기술을 고쳐 쓰면 Go로 gRPC 서버를 다시 쓰면 잘 됩니다.

    참고 자료

  • https://developers.google.com/protocol-buffers/docs/reference/go-generated
  • https://grpc.io/docs/languages/go/quickstart/
  • https://github.com/grpc/grpc-go/tree/master/examples
  • https://github.com/grpc/grpc-web/tree/master/net/grpc/gateway/examples/helloworld
  • 각주
    노드, go,protoc의 설치 완료를 전제로 합니다.↩︎

    좋은 웹페이지 즐겨찾기