NGINX gRPC 지원 발표

NGINX gRPC 트래픽에 대한 최초의 기본 지원을 공유하게 되어 기쁘게 생각합니다.

NGINX는 이미 gRPC TCP 연결을 프록시할 수 있습니다. 이 새로운 기능을 사용하면 gRPC 메서드 호출을 종료, 검사 및 라우팅할 수 있습니다.

다음과 같은 용도로 사용할 수 있습니다.

  • gRPC 서비스를 게시한 다음 NGINX를 사용하여 HTTP/2 TLS 암호화, 속도 제한, IP 주소 기반 액세스 제어 목록 및 로깅을 적용할 수 있습니다.
    암호화되지 않은 HTTP/2 (h2c cleartext)를 사용하여 서비스를 운영하거나 서비스에 TLS 암호화 및 인증을 wrapping 할 수 있습니다.
  • 단일 엔드포인트를 통해 여러 gRPC 서비스를 게시하고, NGINX를 사용하여 각 내부 서비스에 대한 호출을 검사하고 라우팅할 수 있습니다.
    웹 사이트 및 REST 기반 API와 같은 다른 HTTPS 및 HTTP/2 서비스에도 동일한 엔드포인트를 사용할 수 있습니다.
  • Round Robin, 최소 연결 또는 기타 방법을 사용하여 gRPC 서비스 클러스터에 대한 호출을 분산할 수 있습니다.
    추가 용량이 필요할 때 gRPC 기반 서비스를 확장할 수도 있습니다.

목차

1. gRPC 란 무엇입니까?
2. NGINX로 gRPC 서비스 관리
 2-1. 간단한 gRPC 서비스 노출
 2-2. TLS 암호화를 사용하여 gRPC 서비스 캐시
 2-3. 암호화된 gRPC 서비스로 프록시
 2-4. gRPC 트래픽 라우팅
 2-5. Load Balancing gRPC 호출
3. 다음 단계

1. gRPC 란 무엇입니까?

gRPC는 클라이언트와 서버 애플리케이션 간의 통신에 사용되는 원격 프로시저 호출 프로토콜입니다.
이는 여러 언어에서 사용할 수 있도록 설계되었으며, Request-Response 및 스트리밍 상호 작용을 지원합니다. gRPC는 널리 사용되는 언어 지원과 사용자 친화적인 디자인으로 인해 Service Mesh 구현을 포함한 많은 분야에서 인기를 얻고 있습니다.

간단한 gRPC 기반 애플리케이션
간단한 gRPC 기반 애플리케이션

gRPC는 HTTP/2를 통해 전송되며, Cleartext 또는 TLS로 암호화될 수 있습니다. gRPC 호출은 효율적으로 인코딩된 본문을 사용하는 HTTP POST 요청으로 구현됩니다.
gRPC 응답은 유사하게 인코딩된 본문을 사용하며, 응답의 끝에 상태 코드를 전송하기 위해 HTTP 트레일러를 사용합니다.

gRPC 프로토콜은 HTTP/2의 Multi-Flexing 및 스트리밍 기능을 활용하기 위해 HTTP/2를 필수적으로 사용합니다.

2. NGINX gRPC 서비스 관리

다음 예제에서는 간단한 클라이언트 및 서버 애플리케이션을 만들기 위해 gRPC Hello World 빠른 시작 튜토리얼의 변형을 사용합니다.
클라이언트 및 서버 애플리케이션의 구현은 독자의 연습 과제로 남겨두지만 몇 가지 힌트를 공유합니다.

2-1. 간단한 NGINX gRPC 서비스 노출

먼저, 클라이언트와 서버 애플리케이션 사이에 NGINX를 삽입합니다. 이렇게 하면 NGINX가 서버 애플리케이션을 위한 안정적이고 신뢰할 수 있는 Gateway 역할을 합니다.

gRPC 트래픽을 프록시하는 NGINX
gRPC 트래픽을 프록시하는 NGINX

먼저, gRPC 업데이트가 적용된 NGINX를 배포하세요. 소스에서 NGINX를 빌드하는 경우 http_sslhttp_v2 모듈을 포함해야 합니다.

$ auto/configure --with-http_ssl_module --with-http_v2_module

NGINX는 gRPC 트래픽을 수신하기 위해 HTTP 서버를 사용하고, grpc_pass 지시문을 사용하여 트래픽을 프록시합니다. 다음과 같이 NGINX에 대한 다음 프록시 구성을 생성하여 포트 80에서 암호화되지 않은 gRPC 트래픽을 수신하고 포트 50051로 요청을 전달합니다.

http {
    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
    '$status $body_bytes_sent "$http_referer" '
    '"$http_user_agent"';

    server {
        listen 80 http2;

        access_log logs/access.log main;

        location / {
            # gRPC 서버의 주소와 포트로 localhost:50051을 대체하세요.
            # 'grpc://' 접두사는 선택 사항이며, 암호화되지 않은 gRPC가 기본값입니다.
            grpc_pass grpc://localhost:50051;
        }
    }
}

grpc_pass 지시문의 주소가 올바른지 확인하세요. 클라이언트가 NGINX의 IP 주소와 수신 포트를 가리키도록 다시 컴파일하세요.

수정한 클라이언트를 실행하면 이전과 동일한 응답이 표시되지만 트랜잭션이 종료되고 NGINX에 의해 전달됩니다. 구성한 액세스 로그(Access Log)에서 확인할 수 있습니다.

$ tail logs/access.log 192.168.20.11 - - [01/Mar/2018:13:35:02 +0000] "POST /helloworld.Greeter/SayHello HTTP/2.0" 200 18 "-" "grpc-go/1.11.0-dev" 192.168.20.11 - - [01/Mar/2018:13:35:02 +0000] "POST /helloworld.Greeter/SayHelloAgain HTTP/2.0" 200 24 "-" "grpc-go/1.11.0-dev"

참고:  NGINX는 평문 (cleartext, TLS가 아닌) 포트에서 HTTP/1.x와 HTTP/2를 동시에 지원하지 않습니다.
어떤 버전의 프로토콜을 사용할지 사전 지식이 필요합니다. 두 프로토콜 버전을 모두 평문 (cleartext)을 통해 처리하려면 각각에 대한 수신 포트를 생성하세요.

2-2. TLS 암호화를 사용하여 gRPC 서비스 게시

Hello World 빠른 시작 예제에서는 암호화되지 않은 HTTP/2 평문 (cleartext)를 사용하여 통신합니다. 테스트 및 배포가 훨씬 간단하지만 프로덕션 배포에 필요한 암호화를 제공하지는 않습니다. NGINX를 사용하여 이 암호화 계층을 추가할 수 있습니다.

SSL-terminating gRPC traffic
NGINX SSL Termination gRPC 트래픽

자체 서명 인증서를 생성하고 다음과 같이 NGINX 서버 구성을 수정합니다.

server {
    listen 1443 ssl http2;
 
    ssl_certificate     ssl/cert.pem;
    ssl_certificate_key ssl/key.pem;
 
    #...
}

gRPC 클라이언트를 수정하여 TLS를 사용하고 포트 1443에 연결하며, 자체 서명 또는 신뢰할 수 없는 인증서를 사용할 때 인증서 확인을 비활성화하세요. 예를 들어, Go 예제를 사용하는 경우 import 목록에 crypto/tlsgoogle.golang.org/grpc/credentials를 추가하고 grpc.Dial() 호출을 다음과 같이 수정해야 합니다

creds := credentials.NewTLS( &tls.Config{ InsecureSkipVerify: true } )
 
// remember to update address to use the new NGINX listen port
conn, err := grpc.Dial( address, grpc.WithTransportCredentials( creds ) )

NGINX를 사용하여 gRPC 트래픽을 보호하는 데 필요한 모든 작업이 완료되었습니다. 프로덕션 배포에서는 자체 서명된 인증서를 신뢰할 수 있는 인증 기관(CA)에서 발급한 인증서로 교체해야 합니다. 그런 다음 클라이언트가 해당 CA를 신뢰하도록 구성해야 합니다.

2-3. 암호화된 gRPC 서비스로 프록시

내부적으로 gRPC 트래픽을 암호화하려면 서버 애플리케이션을 수정하여 암호화된(grpcs) 연결이 아닌 암호화되지 않은(grpc) 연결을 수신 대기하도록 해야 합니다.

cer, err := tls.LoadX509KeyPair( "cert.pem", "key.pem" )
config := &tls.Config{ Certificates: []tls.Certificate{cer} }
lis, err := tls.Listen( "tcp", port, config )

NGINX 구성에서 gRPC 트래픽을 업스트림 서버로 프록시하는 데 사용되는 프로토콜을 변경해야 합니다.

# TLS로 암호화된 gRPC 트래픽에 grpcs 사용
grpc_pass grpcs://localhost:50051;

2-4. NGINX gRPC 트래픽 라우팅

서로 다른 서버 애플리케이션에 의해 구현된 여러 gRPC 서비스가 있는 경우 어떻게 해야 할까요? 이러한 모든 서비스를 단일 TLS로 암호화된 엔드포인트를 통해 게시할 수 있다면 좋지 않을까요?

NGINX 라우팅 및 SSL Termination gRPC 트래픽

NGINX를 사용하면 서비스 및 메서드를 식별한 다음 location 지시문을 사용하여 트래픽을 라우팅할 수 있습니다.
이미 알고 계실 수도 있지만 gRPC 요청의 URL은 protospec의 패키지, Service 및 Method 이름에서 파생됩니다. 다음과 같은 샘플 SayHello RPC 메서드를 고려해 보세요.

package helloworld;

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

SayHello RPC 메서드를 호출하면 /helloworld.Greeter/SayHello 에 대한 POST 요청이 발생합니다. 다음 로그 항목과 같습니다.

192.168.20.11 - - [01/Mar/2018:13:35:02 +0000] "POST /helloworld.Greeter/SayHello HTTP/2.0" 200 18 "-" "grpc-go/1.11.0-dev"

NGINX를 사용하여 트래픽을 라우팅하는 것은 매우 간단합니다.

location /helloworld.Greeter {
    grpc_pass grpc://192.168.20.11:50051;
}

location /helloworld.Dispatcher {
    grpc_pass grpc://192.168.20.11:50052;
}

location / {
    root html;
    index index.html index.htm;
}

직접 시도해 보세요. 여기서는 샘플 Hello World 패키지(helloworld.proto)를 확장하여 Dispatcher라는 새 서비스를 추가하고, Dispatcher 메서드를 구현하는 새 서버 애플리케이션을 만들었습니다.
클라이언트는 Greeter 및 Dispatcher 서비스에 대한 RPC 호출을 발행하기 위해 단일 HTTP/2 연결을 사용합니다. NGINX는 호출을 분리하여 각각 적절한 gRPC 서버로 라우팅합니다.

이 블록은 알려진 gRPC 호출과 일치하지 않는 요청을 처리합니다.
이와 같은 location 블록을 사용하여 TLS 암호화된 동일한 endpoint에서 웹 콘텐츠 및 기타 비 gRPC 서비스를 전송할 수 있습니다.

2-5. Load Balancing gRPC 호출

gRPC 서비스를 확장하여 용량을 늘리고 고가용성 (HA)을 제공하려면 어떻게 해야 할까요? NGINX의 Upstream 그룹이 이를 가능하게 합니다.

upstream grpcservers {
    server 192.168.20.11:50051;
    server 192.168.20.12:50051;
}
 
server {
    listen 1443 ssl http2;
 
    ssl_certificate     ssl/certificate.pem;
    ssl_certificate_key ssl/key.pem;
 
    location /helloworld.Greeter {
        grpc_pass grpc://grpcservers;
        error_page 502 = /error502grpc;
    }
 
    location = /error502grpc {
        internal;
        default_type application/grpc;
        add_header grpc-status 14;
        add_header grpc-message "unavailable";
        return 204;
    }
}

물론 업스트림이 TLS를 사용하여 수신 대기 중인 경우 grpc_pass grpcs://upstreams를 사용할 수 있습니다.

NGINX는 다양한 로드 밸런싱 알고리즘을 사용하여 업스트림 gRPC 서버에 대한 gRPC 호출을 분산합니다. NGINX의 내장 상태 확인 기능은 서버가 응답하지 않거나 오류를 생성하는지 감지하고 해당 서버를 회전에서 제외합니다. 사용 가능한 서버가 없는 경우 /error502grpc location 블록은 gRPC 호환 오류 메시지를 반환합니다.

3. 다음 단계

이번 릴리스는 gRPC 프록시 지원의 초기 릴리스입니다.  Maxim Dounin은 이 기능의 수석 개발자였으며 ​​초기 패치와 지침을 제공한 Piotr Sikora에게도 감사의 말씀을 전하고 싶습니다.

gRPC 지원에 대한 NGINX의 자세한 정보는 NGINX STORE에 문의하여 상담 받아볼 수 있습니다.

아래 뉴스레터를 구독하고 NGINX와 NGINX STORE의 최신 정보들을 빠르게 전달 받아보세요.