gRPC-Web의 Proxy를 Nginx로 사용해 보았습니다.

17890 단어 nginxgRPCgrpc-web
요 전날 gRPC-Web이 GA되었습니다.
gRPC-Web이 공식 릴리스. 웹 브라우저에서 gRPC를 직접 호출 가능하게

어떤 구조가 되고 있는가 하면···
Client(ブラウザ) -> Proxy -> gRPCサーバー

라고 하는 식으로 Proxy를 개입시켜 브라우저와 gRPC 서버와의 교환을 실시하고 있습니다.
이번에는이 프록시의 이야기입니다.

이 프록시는 공식 문서와 그 예에서도 Envoy를 사용합니다.
htps : // 기주 b. 코 m / grpc / grpc - b

실제로 Envoy를 이용하면 순조롭게 도입할 수 있습니다.
단지 IP 제한을 할 수 없거나 (이쪽 할 수있는 것 같으면 지적 부탁드립니다), Yaml로 쓰지 않으면 안된다거나 뭔가 가려운 곳에 손이 닿지 않습니다.
그래서 프록시를 Nginx로 만들려고합니다.

Nginx에서도 gRPC 프록시를 할 수 있습니다.
Introducing gRPC Support with NGINX 1.13.10

이번 만드는 샘플은 여기에 배치하고 있습니다.
htps : // 기주 b. 코 m/모리 x1500/grp-우우 b 긴긴 x

proto 파일 만들기



먼저 proto 파일을 만들고 인터페이스를 정의합니다.
이름이 건네지면, 「Hello, ○○」라고 반환할 뿐의 API입니다.

proto/hello.proto
syntax = "proto3";

package hello;

message HelloRequest {
  string name = 1;
}

message HelloResponse {
  string message = 1;
}

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

여기에서 클라이언트와 서버 코드를 생성합니다.
이번에는 편리한 znly/protoc을 사용합니다.
$ docker run --rm -v $(pwd):$(pwd) \
-w $(pwd) znly/protoc:0.4.0 \
-I ./proto \
--js_out=import_style=commonjs,binary:./client/assets/_proto \
--go_out=plugins=grpc:./server/proto/ \
--grpc-web_out=import_style=commonjs,mode=grpcweb:./client/assets/_proto \
proto/hello.proto 

$ tree .
.
├── README.md
├── client
│   └── assets
│       └── _proto
│           ├── hello_grpc_web_pb.js
│           └── hello_pb.js
├── proto
│   └── hello.proto
└── server
    └── proto
        └── hello.pb.go

gRPC 서버 만들기



빨리 만들어 봅시다.
Golang에서 만듭니다. main.go는 생략합니다.

server/grpc/server.go
package grpc

import (
        "context"
        "fmt"
        pb "github.com/morix1500/grpc-web-nginx/server/proto"
        "google.golang.org/grpc"
        "net"
)

type HelloService struct{}

func (h HelloService) Run() int {
        s := grpc.NewServer()
        pb.RegisterHelloServiceServer(s, h)

        lis, err := net.Listen("tcp", ":5000")
        if err != nil {
                fmt.Printf("%+v\n", err)
                return 1
        }
        if err := s.Serve(lis); err != nil {
                fmt.Printf("%+v\n", err)
                return 1
        }

        return 0
}

func (h HelloService) Hello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloResponse, error) {
        return &pb.HelloResponse{
                Message: "Hello, " + in.Name,
        }, nil
}

다음 명령으로 시작할 수 있습니다.
$ cd server/
$ go run main.go

gRPC 클라이언트 만들기



클라이언트는 브라우저이므로 JavaScript입니다.
이번에는 Nuxt.js에서 바삭 바삭하게 만들어 보겠습니다.

client/pages/index.vue
<template>
  <div>
    <h1>Test</h1>
    <div>
      <input type="input" v-model="name" />
      <button @click="hello">send</button>
      <p>Message: {{ message }}</p>
    </div>
  </div>
</template>
<script>
import {HelloServiceClient} from "~/assets/_proto/hello_grpc_web_pb"
import {HelloRequest, HelloResponse} from "~/assets/_proto/hello_pb"
 export default {
  asyncData() {
    return {
      name: "",
      message: "",
    }
  },
  methods: {
    hello() {
      let req = new HelloRequest()
      req.setName(this.name)

      const client = new HelloServiceClient("https://127.0.0.1:8080", {}, {})
      client.hello(req, {}, (err, ret) => {
        this.message = ret.getMessage()
      })
    }
  }
}
</script>
https://127.0.0.1:8080 이번 Proxy에 gRPC 통신을 시도하고 있습니다.
무엇인가 입력하고, Send 버튼을 누르면, 「Hello ○○」라고 하는 것이 화면에 표시됩니다.

아직 프록시가 없기 때문에 이것은 작동하지 않습니다.

그건 그렇고,이 응용 프로그램을 이동하려면 다음 명령입니다.
$ cd client/
$ npm install
$ npm run dev

# ブラウザでアクセス
http://localhost:3000

Nginx 구축



자 본제입니다.

Nginx는 Docker로 해보자.

docker-compose.yaml
version: "3"
services:
  proxy:
    image: nginx:1.15.6-alpine
    ports:
      - "8080:8080"
    expose:
      - "8080"
    volumes:
      - ./proxy/nginx/common:/etc/nginx/common
      - ./proxy/nginx/conf.d:/etc/nginx/conf.d
      - ./proxy/keys:/ssl

proxy/nginx/conf.d/sample.conf
server {
  listen 8080 ssl http2;
  server_name host.docker.internal;

  ssl_certificate /ssl/localhost+1.pem;
  ssl_certificate_key /ssl/localhost+1-key.pem;
  ssl_protocols TLSv1.2 TLSv1.3;
  ssl_prefer_server_ciphers on;

  access_log /dev/stdout;
  error_log /dev/stderr debug;

  location ~ \.(html|js)$ {
    root /var/www/html;
  }
  location /hello.HelloService/Hello {
    grpc_set_header Content-Type application/grpc;
    grpc_pass host.docker.internal:5000;
    include common/cors.conf;
  }
}

여기 간은 여기
  location /hello.HelloService/Hello {
    grpc_set_header Content-Type application/grpc;
    grpc_pass host.docker.internal:5000;
    include common/cors.conf;
  }

location에서 서비스명과 함수명을 기재하면, 그 마다의 설정을 할 수 있으므로
서비스나 함수 단위로 액세스 제어 등을 할 수 있습니다.

그리고 grpc_set_header Content-Type application/grpc; 이것이 없으면 gRPC-Web에서 gRPC 서버로의 프록시가 올바르지 않습니다.

그리고는 로컬용의 증명서도 발행합니다. 다음은 Mac의 예입니다.
$ brew install mkcert
$ mkcert -install
$ mkcert localhost 127.0.0.1
$ mv localhost+1* proxy/keys/.

그럼 Nginx도 기동해 갑니다.
$ docker-compose up

확인



브라우저로 만든 클라이언트에 액세스합니다.



「타로」라고 입력하면, 「Hello, 타로」라고 돌아왔습니다.
이제 gRPC-Web과 gRPC 서버에서 소통 확인할 수 있었습니다!

마지막으로



익숙한 Nginx에서도 무사히 사용할 수 있다는 것을 알았습니다.
단지 gRPC-Web은 제한이 있으며 사용에는 주의가 필요합니다.
웹 브라우저에서 gRPC 양방향 통신이 가능한지 조사했다

현재 저는 double jump.tokyo 주식회사님으로부터 gRPC 기반의 구축의 업무 위탁을 받아 가고 있습니다.
그 업무의 일환으로서 이러한 기술 검증을 실시했습니다.

좋은 웹페이지 즐겨찾기