이 가이드에서는 네트워크 부하 분산 및 Envoy 프록시를 사용하여 단일 외부 IP 주소의 Google Kubernetes Engine(GKE)에 배포된 여러 gRPC 서비스를 노출하는 방법을 보여줍니다. 이 가이드에서는 Envoy가 gRPC에 제공하는 몇 가지 고급 기능에 대해 중점적으로 설명합니다.
소개
gRPC는 전송 중 효율적인 표현과 빠른 직렬화를 위해 프로토콜 버퍼를 사용하면서 언어에 독립적인 HTTP/2 기반의 오픈소스 RPC 프레임워크입니다. 내부 Google RPC 프레임워크인 Stubby의 영향을 받은 gRPC는 마이크로서비스 간은 물론 모바일 클라이언트와 API 간의 지연 시간이 짧은 통신을 지원합니다.
gRPC는 HTTP/2를 통해 실행되므로 효율적인 바이너리 인코딩, 단일 연결을 통한 요청 및 응답 다중화, 자동 흐름 제어 등 HTTP/1.1에 비해 여러 가지 이점이 있습니다. gRPC에서는 여러 부하 분산 옵션도 제공합니다. 이 가이드는 모바일 클라이언트 및 클라이언트가 서비스 제공업체의 신뢰 경계 외부에서 실행되는 경우와 같이 클라이언트를 신뢰할 수 없는 상황에 대해 집중적으로 다룹니다. 이 가이드에서는 gRPC가 제공하는 부하 분산 옵션 중 프록시 기반 부하 분산을 사용합니다.
이 가이드에서는 Google Cloud에서 전송 레이어(레이어 4) 네트워크 부하 분산으로 노출되는 TYPE=LoadBalancer
의 Kubernetes 서비스를 배포합니다. 이 서비스는 단일 공개 IP 주소를 제공하며 TCP 연결을 구성된 백엔드로 직접 전달합니다. 이 가이드에서 백엔드는 Envoy 인스턴스의 Kubernetes 배포입니다.
Envoy는 여러 고급 기능을 제공하는 오픈소스 애플리케이션 레이어(레이어 7) 프록시입니다. 이 가이드에서는 Envoy를 사용해 SSL/TLS 연결을 종료하고 gRPC 트래픽을 적절한 Kubernetes 서비스로 라우팅합니다. Kubernetes 인그레스와 같은 다른 애플리케이션 레이어 솔루션과 비교할 때 Envoy를 사용하면 다음과 같은 여러 맞춤설정 옵션을 직접 이용할 수 있습니다.
- 서비스 검색
- 부하 분산 알고리즘
- 요청 및 응답 변환(예: JSON 또는 gRPC-Web으로 변환)
- JWT 토큰 확인을 통한 요청 인증
- gRPC 상태 확인
네트워크 부하 분산과 Envoy를 결합하여 GKE 클러스터에서 실행되는 Envoy 인스턴스 집합에 트래픽을 전달하는 엔드포인트(외부 IP 주소)를 설정할 수 있습니다. 그러면 인스턴스에서 애플리케이션 레이어 정보를 사용해 클러스터에서 실행 중인 다른 gRPC 서비스에 대한 요청을 프록시합니다. Envoy 인스턴스는 클러스터 DNS를 사용해 수신되는 gRPC 요청을 식별하여 각 서비스에서 실행 중인 정상 상태의 pod로 부하를 분산합니다. 즉, 클라이언트의 TCP 연결이 아닌 RPC 요청마다 트래픽이 pod로 부하 분산된다는 의미입니다.
비용
이 가이드에서는 비용이 청구될 수 있는 다음과 같은 Google Cloud 구성요소를 사용합니다.
프로젝트 사용량을 기준으로 예상 비용을 산출하려면 가격 계산기를 사용하세요. Google Cloud를 처음 사용하는 사용자는 무료 체험판을 사용할 수 있습니다.
이 가이드를 마치면 만든 리소스를 삭제하여 비용이 계속 청구되지 않게 할 수 있습니다. 자세한 내용은 삭제를 참조하세요.
시작하기 전에
-
Google 계정으로
로그인합니다.
아직 계정이 없으면 새 계정을 등록하세요.
-
Google Cloud Console의 프로젝트 선택기 페이지에서 Google Cloud 프로젝트를 선택하거나 만듭니다.
-
Cloud 프로젝트에 결제가 사용 설정되어 있는지 확인합니다. 프로젝트에 결제가 사용 설정되어 있는지 확인하는 방법을 알아보세요.
- Cloud Build, Container Registry, and Container Analysis API를 사용 설정합니다.
아키텍처
이 가이드에서는 Google Kubernetes Engine(GKE) 클러스터에 두 가지 gRPC 서비스(echo-grpc
및 reverse-grpc
)를 배포하고 공개 IP 주소로 인터넷에 노출합니다. 다음 다이어그램은 단일 엔드포인트를 통해 이 두 서비스를 노출하는 아키텍처를 보여줍니다.
네트워크 부하 분산은 인터넷(예: 기업 외부의 모바일 클라이언트 또는 서비스 소비자)에서 수신되는 요청을 수락합니다. 네트워크 부하 분산에서 수행하는 태스크는 다음과 같습니다.
- 수신되는 연결의 부하를 풀의 워커 노드로 분산합니다. 트래픽은 클러스터의 모든 워커 노드에 노출되는
envoy
Kubernetes 서비스로 전달됩니다. Kubernetes 네트워크 프록시가 Envoy를 실행하는 pod로 해당 연결을 전달합니다. - 클러스터의 워커 노드에 대한 HTTP 상태 확인을 수행합니다.
Envoy에서 수행하는 태스크는 다음과 같습니다.
- SSL/TLS 연결을 종료합니다.
- 내부 클러스터 DNS 서비스를 쿼리하여 gRPC 서비스를 실행 중인 pod를 찾습니다.
- 트래픽을 gRPC 서비스 pod로 라우팅하고 부하를 분산합니다.
- gRPC 상태 확인 프로토콜에 따라 gRPC 서비스의 상태 확인을 수행합니다.
- 네트워크 부하 분산을 사용하여 상태 확인의 엔드포인트를 노출합니다.
gRPC 서비스(echo-grpc
및 reverse-grpc
)는 Kubernetes 헤드리스 서비스로 노출됩니다.
즉, clusterIP
주소가 할당되지 않고 Kubernetes 네트워크 프록시가 pod로 트래픽의 부하를 분산하지 않습니다. 대신 pod IP 주소를 포함한 DNS A 레코드가 클러스터 DNS 서비스에 생성됩니다. Envoy에서는 이 DNS 항목에서 pod IP 주소를 찾아 Envoy에 구성된 정책에 따라 해당 주소로 부하를 분산합니다.
다음 다이어그램은 이 가이드에 사용된 Kubernetes 객체를 보여줍니다.
환경 초기화
이 섹션에서는 가이드 뒷부분에서 사용할 환경 변수를 설정합니다.
Cloud Shell을 엽니다.
Cloud Shell을 사용하여 가이드의 모든 명령어를 실행합니다.
Cloud Shell에서 현재 프로젝트 ID를 표시합니다.
gcloud config list --format 'value(core.project)'
명령어를 실행해서 선택한 프로젝트의 ID가 반환되지 않으면 project-id를 프로젝트 이름으로 바꾸어 프로젝트를 사용하도록 Cloud Shell을 구성합니다.
gcloud config set project project-id
이 가이드에서 사용할 리전 및 영역의 환경 변수를 정의합니다.
REGION=us-central1 ZONE=$REGION-b
이 가이드에서는
us-central1
리전 및us-central1-b
영역을 사용합니다. 하지만 필요에 맞게 리전과 영역을 변경할 수 있습니다.
GKE 클러스터 만들기
gRPC 서비스 실행을 위한 GKE 클러스터를 만듭니다.
gcloud container clusters create grpc-cluster --zone $ZONE
클러스터의 워커 노드를 나열하여
kubectl
컨텍스트가 설정되었는지 확인합니다.kubectl get nodes -o name
출력은 다음과 비슷합니다.
node/gke-grpc-cluster-default-pool-c9a3c791-1kpt node/gke-grpc-cluster-default-pool-c9a3c791-qn92 node/gke-grpc-cluster-default-pool-c9a3c791-wf2h
gRPC 서비스 배포
하나의 부하 분산기를 사용하는 여러 gRPC 서비스로 트래픽을 라우팅하려면 echo-grpc
및 reverse-grpc
라는 간단한 gRPC 서비스 2개를 배포합니다. 두 서비스 모두 콘텐츠 요청 필드의 문자열을 사용하는 단항 메서드를 노출합니다. echo-grpc
는 변경되지 않은 콘텐츠로 응답하는 반면 reverse-grpc
는 역방향 콘텐츠 문자열로 응답합니다.
gRPC 서비스를 포함한 저장소를 클론하고 작업 디렉터리로 전환합니다.
git clone https://github.com/GoogleCloudPlatform/grpc-gke-nlb-tutorial cd grpc-gke-nlb-tutorial
Cloud Build를 사용해 Echo 및 Reverse gRPC 서비스의 컨테이너 이미지를 만들어 Container Registry에 저장합니다.
gcloud builds submit -t gcr.io/$GOOGLE_CLOUD_PROJECT/echo-grpc echo-grpc gcloud builds submit -t gcr.io/$GOOGLE_CLOUD_PROJECT/reverse-grpc reverse-grpc
이 이미지가 Container Registry에 존재하는지 확인합니다.
gcloud container images list --repository gcr.io/$GOOGLE_CLOUD_PROJECT
출력은 다음과 비슷합니다.
NAME gcr.io/grpc-gke-nlb-tutorial/echo-grpc gcr.io/grpc-gke-nlb-tutorial/reverse-grpc
echo-grpc
및reverse-grpc
에 대한 Kubernetes 배포를 만듭니다.sed s/GOOGLE_CLOUD_PROJECT/$GOOGLE_CLOUD_PROJECT/ \ k8s/echo-deployment.yaml | kubectl apply -f - sed s/GOOGLE_CLOUD_PROJECT/$GOOGLE_CLOUD_PROJECT/ \ k8s/reverse-deployment.yaml | kubectl apply -f -
각 배포에서 두 pod를 사용할 수 있는지 확인합니다.
kubectl get deployments
출력은 다음과 비슷합니다. 두 배포 모두
DESIRED
,CURRENT
,UP-TO-DATE
,AVAILABLE
의 값이2
여야 합니다.NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE echo-grpc 2 2 2 2 1m reverse-grpc 2 2 2 2 1m
echo-grpc
및reverse-grpc
에 대한 Kubernetes 헤드리스 서비스를 만듭니다. 이 명령어는 클러스터의 DNS 서비스에 DNS A 레코드를 만들지만 가상 IP 주소를 할당하지 않습니다.kubectl apply -f k8s/echo-service.yaml kubectl apply -f k8s/reverse-service.yaml
echo-grpc
및reverse-grpc
가 Kubernetes 서비스로 존재하는지 확인합니다.kubectl get services
출력은 다음과 비슷합니다.
echo-grpc
및reverse-grpc
모두TYPE=ClusterIP
와CLUSTER-IP=None
이 있어야 합니다.NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE echo-grpc ClusterIP None <none> 8081/TCP 35s kubernetes ClusterIP 10.0.0.1 <none> 443/TCP 47m reverse-grpc ClusterIP None <none> 8082/TCP 21s
네트워크 부하 분산 설정
클러스터에
LoadBalancer
유형의 Kubernetes 서비스를 만듭니다.kubectl apply -f k8s/envoy-service.yaml
이 명령어는 네트워크 부하 분산에 필요한 리소스를 프로비저닝하고 임시 공개 IP 주소를 할당합니다. 공개 IP 주소를 할당하는 데 몇 분이 걸릴 수 있습니다.
다음 명령어를 실행하고
envoy
서비스의EXTERNAL-IP
값이<pending>
에서 공개 IP 주소로 변경될 때까지 기다립니다.kubectl get services envoy --watch
대기를 중지하려면
Control+C
를 누릅니다.
자체 서명 SSL/TLS 인증서 만들기
Envoy는 SSL/TLS 연결을 종료할 때 인증서와 키를 사용합니다. 먼저 자체 서명 SSL/TLS 인증서를 만듭니다.
이전 섹션에서 만든 Envoy 서비스의 공개 IP 주소를 저장할 환경 변수를 만듭니다.
EXTERNAL_IP=$(kubectl get service envoy -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
자체 서명 SSL/TLS 인증서와 키를 만듭니다.
openssl req -x509 -nodes -newkey rsa:2048 -days 365 \ -keyout privkey.pem -out cert.pem -subj "/CN=$EXTERNAL_IP"
자체 서명 SSL/TLS 인증서와 키가 포함된
envoy-certs
라는 Kubernetes TLS 보안 비밀을 만듭니다.kubectl create secret tls envoy-certs \ --key privkey.pem --cert cert.pem \ --dry-run -o yaml | kubectl apply -f -
Envoy 배포
Envoy 구성 파일(
envoy.yaml
)을 저장할 Kubernetes ConfigMap을 만듭니다.kubectl apply -f k8s/envoy-configmap.yaml
Envoy에 사용할 Kubernetes 배포를 만듭니다.
kubectl apply -f k8s/envoy-deployment.yaml
두
envoy
pod가 실행 중인지 확인합니다.kubectl get deployment envoy
출력은 다음과 비슷하게 표시됩니다.
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE envoy 2 2 2 2 1m
이제 gRPC 서비스를 테스트할 준비가 되었습니다.
gRPC 서비스 테스트
서비스를 테스트하려면 grpcurl
명령줄 도구를 사용합니다.
Cloud Shell에서
grpcurl
를 설치합니다.go get github.com/fullstorydev/grpcurl go install github.com/fullstorydev/grpcurl/cmd/grpcurl
Echo gRPC 서비스에 요청을 보냅니다.
grpcurl -d '{"content": "echo"}' -proto echo-grpc/api/echo.proto \ -insecure -v $EXTERNAL_IP:443 api.Echo/Echo
출력은 다음과 비슷합니다.
Resolved method descriptor: rpc Echo ( .api.EchoRequest ) returns ( .api.EchoResponse ); Request metadata to send: (empty) Response headers received: content-type: application/grpc date: Wed, 27 Feb 2019 04:40:19 GMT hostname: echo-grpc-5c4f59c578-wcsvr server: envoy x-envoy-upstream-service-time: 0 Response contents: { "content": "echo" } Response trailers received: (empty) Sent 1 request and received 1 response
hostname
응답 헤더에는 요청을 처리한echo-grpc
pod의 이름이 표시됩니다. 명령어를 몇 차례 반복하면echo-grpc
pod의 이름에 해당하는hostname
응답 헤더에 서로 다른 두 개의 값이 표시됩니다.Reverse gRPC 서비스에서 동일 동작을 확인합니다.
grpcurl -d '{"content": "reverse"}' -proto reverse-grpc/api/reverse.proto \ -insecure -v $EXTERNAL_IP:443 api.Reverse/Reverse
출력은 다음과 비슷합니다.
Resolved method descriptor: rpc Reverse ( .api.ReverseRequest ) returns ( .api.ReverseResponse ); Request metadata to send: (empty) Response headers received: content-type: application/grpc date: Wed, 27 Feb 2019 04:45:56 GMT hostname: reverse-grpc-74cdc4849f-tvsfb server: envoy x-envoy-upstream-service-time: 2 Response contents: { "content": "esrever" } Response trailers received: (empty) Sent 1 request and received 1 response
문제해결
이 가이드에서 문제가 발생하면 다음 문서를 검토하시기 바랍니다.
Envoy 관리 인터페이스를 탐색해 Envoy 구성 문제를 진단할 수도 있습니다.
관리 인터페이스를 열려면 포트 전달을 Cloud Shell에서 Envoy pod 중 하나의
admin
포트로 설정합니다.kubectl port-forward \ $(kubectl get pods -o name | grep envoy | head -n1) 8080:8090
콘솔에 다음 출력이 표시될 때까지 기다립니다.
Forwarding from 127.0.0.1:8080 -> 8090
Cloud Shell에서 웹 미리보기 버튼을 클릭하고 포트 8080에서 미리보기를 선택합니다. 그러면 관리 인터페이스가 표시된 새 브라우저 창이 열립니다.
단계를 모두 수행했다면 Cloud Shell로 다시 전환한 후
Control+C
키를 눌러 포트 전달을 종료합니다.
gRPC 트래픽을 라우팅하는 다른 방법
이 솔루션은 환경에 맞게 다양한 방법으로 수정할 수 있습니다.
대체 애플리케이션 레이어 부하 분산기
Envoy가 제공하는 애플리케이션 레이어 기능 중 일부는 다른 부하 분산 솔루션에서도 제공하고 있습니다.
Kubernetes 인그레스 객체를 사용해 HTTP(S) 부하 분산을 구성하여 네트워크 부하 분산 및 Envoy 대신 사용할 수 있습니다. HTTP(S) 부하 분산을 사용하면 Cloud CDN, IAP와 같은 다른 Google Cloud 제품과의 통합, 관리형 SSL/TLS 인증서 등 네트워크 부하 분산에 비해 여러 가지 이점이 있습니다.
다음 항목을 지원할 필요가 없으면 HTTP(S) 부하 분산을 사용하는 것이 좋습니다.
- gRPC 상태 확인
- 부하 분산 알고리즘에 대한 세밀한 제어
- 50개 이상의 서비스 노출
샘플 gRPC 서비스를 포함한 HTTP(S) 부하 분산 배포 방법에 대한 자세한 내용은 GitHub에서 인그레스 관련 Google Kubernetes Engine 문서 및 GKE gRPC 인그레스 부하 분산 가이드를 참조하세요.
Istio를 사용하는 경우 해당 기능을 사용해 gRPC 트래픽을 라우팅하고 부하를 분산할 수 있습니다. Istio의 Ingress Gateway는 이 가이드의 아키텍처와 유사하게 Envoy 백엔드를 사용하는 네트워크 부하 분산으로 배포됩니다. 단, Envoy 프록시가 Istio의 traffic routing 객체를 통해 구성된다는 것이 가장 큰 차이점입니다. 이 가이드의 예시 서비스를 Istio 서비스 메시에서 라우팅하려면 Kubernetes 서비스 매니페스트(
echo-service.yaml
및reverse-service.yaml
)에서clusterIP: None
줄을 삭제해야 합니다. 즉, Envoy의 유사한 기능 대신 Istio의 서비스 검색 및 부하 분산 기능을 사용합니다. 이미 Istio를 사용 중이라면 Ingress Gateway를 사용해 gRPC 서비스로 라우팅하는 것이 좋습니다.배포로 또는 Kubernetes용 NGINX 인그레스 컨트롤러를 사용해 Envoy 대신 NGINX를 사용할 수 있습니다. 이 가이드에서는 gRPC 상태 확인 프로토콜 지원 등 고급 gRPC 기능을 더 많이 제공하는 Envoy를 사용했습니다.
Kubernetes 인그레스 컨트롤러를 제공하며 Envoy를 기반으로 하는 Ambassador 및 Contour를 사용할 수 있습니다.
HAProxy 기반의 Kubernetes 인그레스 컨트롤러인 Voyager를 사용할 수 있습니다.
내부 VPC 네트워크 연결
서비스를 VPC 네트워크 내에서만 GKE 클러스터 외부로 노출시키려면 네트워크 부하 분산 대신 내부 TCP/UDP 부하 분산을 사용하면 됩니다. 이렇게 하려면 주석 cloud.google.com/load-balancer-type: "Internal"
을 envoy-service.yaml
매니페스트에 추가합니다.
Envoy 배포와 DaemonSet의 비교
이 가이드에서 Envoy는 Kubernetes 배포로 구성됩니다.
이 구성에서는 배포 매니페스트의 replica
설정에 따라 Envoy pod 수가 결정됩니다. 부하 분산기가 수신되는 요청을 Envoy pod를 실행하지 않는 워커 노드로 전달하면 Kubernetes 네트워크 프록시에서 이 요청을 Envoy pod를 실행 중인 워커 노드로 전달합니다.
DaemonSet를 Envoy 배포 대신 사용할 수 있습니다. DaemonSet를 사용하면 Envoy pod가 GKE 클러스터의 모든 워커 노드에서 실행됩니다. 즉, 이 대안을 사용하면 큰 클러스터의 리소스 사용량이 늘어나지만(Envoy pod가 더 많음) 수신되는 요청이 항상 Envoy pod를 실행하는 워커 노드에 도달합니다. 이때 요청이 Envoy pod에 도달할 수 있도록 워커 노드 간에 전달되는 것은 아니므로 클러스터의 네트워크 트래픽과 평균 지연 시간은 줄어듭니다.
삭제
가이드를 완료한 후에는 할당량을 차지하지 않고 이후에 요금이 청구되지 않도록 Google Cloud에서 만든 리소스를 삭제할 수 있습니다. 다음 섹션에서는 리소스를 삭제하거나 사용 중지하는 방법을 설명합니다.
프로젝트 삭제
- Cloud Console에서 리소스 관리 페이지로 이동합니다.
- 프로젝트 목록에서 삭제할 프로젝트를 선택하고 삭제를 클릭합니다.
- 대화상자에서 프로젝트 ID를 입력한 후 종료를 클릭하여 프로젝트를 삭제합니다.
리소스 삭제
이 가이드에서 사용된 Google Cloud 프로젝트를 유지하려면 개별 리소스를 삭제합니다.
Cloud Shell에서 로컬 Git 저장소 클론을 삭제합니다.
cd ; rm -rf ~/grpc-gke-nlb-tutorial
Container Registry의 이미지를 삭제합니다.
gcloud container images list-tags gcr.io/$GOOGLE_CLOUD_PROJECT/echo-grpc \ --format 'value(digest)' | xargs -I {} gcloud container images delete \ --force-delete-tags --quiet gcr.io/$GOOGLE_CLOUD_PROJECT/echo-grpc@sha256:{} gcloud container images list-tags gcr.io/$GOOGLE_CLOUD_PROJECT/reverse-grpc \ --format 'value(digest)' | xargs -I {} gcloud container images delete \ --force-delete-tags --quiet gcr.io/$GOOGLE_CLOUD_PROJECT/reverse-grpc@sha256:{}
Google Kubernetes Engine 클러스터를 삭제합니다.
gcloud container clusters delete grpc-cluster --zone $ZONE --quiet --async
다음 단계
- Google Kubernetes Engine 네트워킹 읽어보기
- Kubernetes 클러스터 내에서 gRPC 서비스를 클라이언트에 노출하는 방법의 예시 살펴보기
- gRPC 부하 분산 옵션 살펴보기
- 프로덕션을 위한 Google Kubernetes Engine 환경 준비 방법 알아보기
- Google Kubernetes Engine 안내 가이드 살펴보기
- Google Cloud로 GitOps 형식의 지속적 배포 파이프라인을 만드는 방법 알아보기
- Cloud Endpoints Extensible Service Proxy 및 Google Kubernetes Engine을 사용한 gRPC 서비스 배포 방법 알아보기
- 다른 Google Cloud 기능을 직접 사용해보세요. 가이드 살펴보기.