Istio를 사용하여 내부 gRPC 서비스 부하 분산

이 가이드에서는 Google Kubernetes Engine(GKE)에서 실행되는 gRPC 서비스용 Istio를 사용하여 내부 TCP/UDP 부하 분산을 설정하는 방법을 보여 줍니다. 이 설정은 Istio가 gRPC 서비스를 실행하는 Kubernetes Pod에서 라우팅과 부하 분산을 처리하는 동안 VPC 네트워크의 다른 리소스가 비공개 내부(RFC 1918) IP 주소를 사용하여 gRPC 서비스와 통신할 수 있게 합니다.

이 가이드에서는 gRPC와 GKE 또는 Kubernetes에 대한 기본 지식을 알고 있다고 가정합니다.

소개

Istio는 서비스 메시에 들어오고 나가는 트래픽을 관리하기 위한 게이트웨이를 제공합니다. Istio 내부 부하 분산기(ILB) 게이트웨이는 내부 VPC 네트워크의 소스에서 서비스 메시의 Kubernetes Pod로 인바운드 트래픽을 라우팅합니다. 이 아키텍처에서 Google Cloud 내부 TCP/UDP 부하 분산은 GKE 클러스터의 노드 간에 레이어 4(전송 레이어) 부하 분산을 수행합니다. Istio ILB Gateway는 트래픽을 수신하고 레이어 7(애플리케이션 레이어) 부하 분산을 수행하여, 가상 서비스대상 규칙에 정의된 규칙을 사용하여 트래픽을 Istio 서비스 메시의 서비스로 분산합니다.

이 가이드에서 사용하는 샘플 gRPC 서비스는 요청을 처리한 Kubernetes Pod의 이름을 포함하는 응답 헤더를 반환합니다. 이 정보를 사용하면 Istio ILB Gateway에 의한 부하 분산이 클라이언트가 생성한 요청을 단일 연결을 통해 GKE 클러스터의 여러 Kubernetes Pod로 분산하는 것을 확인할 수 있습니다.

목표

  • Istio 및 Istio ILB Gateway로 GKE 클러스터 생성
  • 샘플 gRPC 서비스 배포
  • 내부 연결 확인

비용

이 가이드에서는 비용이 청구될 수 있는 다음과 같은 Google Cloud 구성요소를 사용합니다.

프로젝트 사용량을 기준으로 예상 비용을 산출하려면 가격 계산기를 사용하세요. Google Cloud를 처음 사용하는 사용자는 무료 체험판을 사용할 수 있습니다.

이 가이드를 마치면 만든 리소스를 삭제하여 비용이 계속 청구되지 않게 할 수 있습니다. 자세한 내용은 삭제를 참조하세요.

시작하기 전에

  1. Google 계정에 로그인하거나 Google 계정이 없으면 새 계정에 가입합니다.
  2. Cloud Console에서 프로젝트 선택기 페이지로 이동합니다.

    프로젝트 선택기 페이지로 이동

  3. Cloud 프로젝트를 선택하거나 만듭니다.

  4. Google Cloud 프로젝트에 결제가 사용 설정되어 있는지 확인합니다. 프로젝트에 결제가 사용 설정되어 있는지 확인하는 방법을 알아보세요.

환경 초기화

  1. Cloud Console의 프로젝트 선택 드롭다운에서 사용할 프로젝트를 선택합니다.

  2. Cloud Shell을 엽니다.

    Cloud Shell로 이동

    Cloud Shell을 사용하여 가이드의 모든 명령어를 실행합니다.

  3. Cloud Build API, Google Kubernetes Engine API, Container Analysis API, Cloud API를 사용 설정합니다.

    gcloud services enable \
        cloudapis.googleapis.com \
        cloudbuild.googleapis.com \
        container.googleapis.com \
        containeranalysis.googleapis.com
    
  4. 이 가이드에 사용할 Compute Engine 영역의 gcloud 기본값을 설정합니다.

    gcloud config set compute/zone us-central1-b
    

    이 가이드에서는 us-central1-b 영역을 사용합니다. 필요에 맞게 영역을 변경할 수 있습니다.

  5. 샘플 gRPC 서비스가 포함된 Git 저장소를 클론하고 작업 디렉터리로 전환합니다.

    git clone https://github.com/GoogleCloudPlatform/istio-samples.git
    cd istio-samples/sample-apps/grpc-greeter-go
    

Istio로 GKE 클러스터 만들기

  1. Cloud Shell에서 GKE 클러스터를 만듭니다.

    gcloud beta container clusters create grpc-istio-ilb-tutorial \
        --machine-type n1-standard-2 \
        --enable-ip-alias
    
  2. 자신에게 클러스터 관리자 권한을 부여합니다.

    kubectl create clusterrolebinding cluster-admin-binding \
        --clusterrole cluster-admin \
        --user $(gcloud config get-value account)
    

    Istio를 설치하려면 cluster-admin Kubernetes 클러스터 역할에 정의된 권한이 있어야 합니다.

  3. istio-system이라는 Kubernetes 네임스페이스를 만듭니다.

    kubectl create namespace istio-system
    
  4. Istio를 다운로드하고 압축을 풉니다.

    ISTIO_VERSION=1.2.7
    curl -L https://github.com/istio/istio/releases/download/$ISTIO_VERSION/istio-$ISTIO_VERSION-linux.tar.gz | tar -zxf -
    
  5. Helm의 로컬 템플릿 렌더링을 사용하여 Istio 커스텀 리소스 정의(CRD)를 설치합니다.

    helm template \
        istio-$ISTIO_VERSION/install/kubernetes/helm/istio-init \
        --namespace istio-system | kubectl apply -f -
    
  6. Helm을 사용하여 ILB Gateway(istio-ilbgateway)와 함께 Istio를 설치합니다.

    ISTIO_PACKAGE=$ISTIO_VERSION-gke.0
    
    helm template \
        istio-$ISTIO_VERSION/install/kubernetes/helm/istio \
        --set gateways.istio-ingressgateway.enabled=false \
        --set gateways.istio-ilbgateway.enabled=true \
        --set gateways.istio-ilbgateway.ports[0].name=grpc-pilot-mtls \
        --set gateways.istio-ilbgateway.ports[0].port=15011 \
        --set gateways.istio-ilbgateway.ports[1].name=grpc \
        --set gateways.istio-ilbgateway.ports[1].port=443 \
        --set gateways.istio-ilbgateway.ports[2].name=tcp-citadel-grpc-tls \
        --set gateways.istio-ilbgateway.ports[2].port=8060 \
        --set gateways.istio-ilbgateway.ports[2].targetPort=8060 \
        --set gateways.istio-ilbgateway.ports[3].name=tcp-dns \
        --set gateways.istio-ilbgateway.ports[3].port=5353 \
        --set global.hub=gcr.io/gke-release/istio \
        --set global.tag=$ISTIO_PACKAGE \
        --namespace istio-system | kubectl apply -f -
    
  7. istio-ilbgateway Kubernetes 서비스용 외부 IP 주소 생성 상태를 확인합니다.

    kubectl get services istio-ilbgateway -n istio-system --watch
    

    EXTERNAL-IP 값이 <pending>에서 IP 주소로 변경될 때까지 기다립니다. Control+C를 눌러 대기를 중지합니다.

Istio ILB Gateway의 TLS 인증서 만들기

  1. Cloud Shell에서 TLS 인증서 및 비공개 키를 만들어 Istio ILB Gateway에 의한 TLS 종료를 허용합니다.

    openssl req -x509 -nodes -newkey rsa:2048 -days 365 \
        -keyout privkey.pem -out cert.pem -subj "/CN=grpc.example.com"
    
  2. Kubernetes 보안 비밀을 만들어 TLS 인증서와 비공개 키를 저장합니다.

    kubectl -n istio-system create secret tls istio-ilbgateway-certs \
        --key privkey.pem --cert cert.pem \
        --dry-run -o yaml | kubectl apply -f -
    

샘플 애플리케이션 설치

다음 단계는 샘플 gRPC 서비스의 컨테이너 이미지를 작성하여 GKE 클러스터에 배포하는 것입니다. 샘플 gRPC 서비스는 gRPC 클라이언트라는 클라이언트 구성요소와 gRPC 서버라는 서버 구성요소로 이루어집니다.

  1. Cloud Shell에서 default 네임스페이스에 자동 Istio 사이드카 삽입을 사용 설정합니다.

    kubectl label namespace default istio-injection=enabled
    
  2. Cloud Build를 사용하여 gRPC 서버에 대한 컨테이너 이미지를 만듭니다.

    gcloud builds submit server -t gcr.io/$GOOGLE_CLOUD_PROJECT/grpc-greeter-go-server
    
  3. gRPC 서버에 사용할 Kubernetes 배포 및 서비스 객체를 만듭니다.

    envsubst < manifests/greeter-k8s.template.yaml | kubectl apply -f -
    
  4. ClusterIP Kubernetes 서비스가 생성되고 Pod가 실행 중임을 확인합니다.

    kubectl get services,pods
    

    결과는 다음과 비슷하게 표시됩니다.

    NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)    AGE
    service/greeter      ClusterIP   10.0.18.67   <none>        8080/TCP   11s
    service/kubernetes   ClusterIP   10.0.16.1    <none>        443/TCP    1h
    NAME READY STATUS RESTARTS AGE pod/greeter-844cffd75-7hpcb 2/2 Running 0 56s pod/greeter-844cffd75-ffccl 2/2 Running 0 56s pod/greeter-844cffd75-zww6h 2/2 Running 0 56s

    Pod는 READY 열에 2/2를 표시합니다. 이러한 결과는 각 Pod에 대하여 gRPC 서버 컨테이너와 Envoy 사이드카 컨테이너가 모두 실행 중임을 의미합니다.

  5. gRPC 서버의 Istio 게이트웨이, 가상 서비스, 대상 규칙을 만듭니다.

    kubectl apply -f manifests/greeter-istio-ilbgateway.yaml \
        -f manifests/greeter-istio-virtualservice.yaml \
        -f manifests/greeter-istio-destinationrule.yaml
    
  6. 세 객체가 모두 성공적으로 만들어졌음을 확인합니다.

    kubectl get gateway,virtualservice,destinationrule
    

    결과는 다음과 비슷하게 표시됩니다.

    NAME                                  AGE
    gateway.networking.istio.io/greeter   1m
    NAME GATEWAYS HOSTS AGE virtualservice.networking.istio.io/greeter [greeter] [*] 1m
    NAME HOST AGE destinationrule.networking.istio.io/greeter greeter 1m

내부 연결 확인

내부 TCP/UDP 부하 분산은 지역적이므로 동일한 영역이나 리전의 VM에서 연결을 테스트할 수 있습니다.

  1. Cloud Shell에서 Cloud Build를 사용하여 gRPC 클라이언트의 컨테이너 이미지를 만듭니다.

    gcloud builds submit client \
        -t gcr.io/$GOOGLE_CLOUD_PROJECT/grpc-greeter-go-client
    
  2. GKE 클러스터와 동일한 영역 또는 리전에서 Compute Engine 인스턴스를 만듭니다.

    gcloud compute instances create grpc-istio-ilb-tutorial-client-vm \
        --scopes https://www.googleapis.com/auth/devstorage.read_only \
        --image-project cos-cloud \
        --image-family cos-stable
    

    Container Registry에서 이미지를 다운로드하려면 devstorage.read_only 범위가 필요합니다.

  3. Istio ILB Gateway IP 주소를 ilb-ip.txt라는 파일에 저장합니다.

    kubectl -n istio-system get services istio-ilbgateway \
        -o jsonpath='{.status.loadBalancer.ingress[0].ip}' > ilb-ip.txt
    
  4. 자체 서명된 TLS 인증서와 Istio ILB Gateway IP 주소가 포함된 파일을 VM으로 복사합니다.

    gcloud compute scp cert.pem ilb-ip.txt grpc-istio-ilb-tutorial-client-vm:~
    
    
  5. SSH를 사용하여 VM에 연결합니다.

    gcloud compute ssh grpc-istio-ilb-tutorial-client-vm
    
  6. VM에서 Compute Engine 메타데이터 서버에 프로젝트 ID를 쿼리하고 이를 환경 변수 GOOGLE_CLOUD_PROJECT에 저장합니다.

    GOOGLE_CLOUD_PROJECT=$(curl -sH "Metadata-Flavor: Google" \
        http://metadata.google.internal/computeMetadata/v1/project/project-id)
    

    Cloud Shell에서는 환경 변수 GOOGLE_CLOUD_PROJECT가 기본적으로 설정되지만 VM에서는 그렇지 않습니다.

  7. docker 명령어와 함께 사용할 Container Registry 사용자 인증 정보를 가져옵니다.

    docker-credential-gcr configure-docker
    
  8. gRPC 클라이언트 컨테이너 이미지를 실행합니다.

    docker run --rm -v $(pwd)/cert.pem:/data/cert.pem \
        --add-host grpc.example.com:$(cat ilb-ip.txt) \
        gcr.io/$GOOGLE_CLOUD_PROJECT/grpc-greeter-go-client \
        --address=grpc.example.com:443
    

    결과는 다음과 비슷하게 표시됩니다.

    2019/03/27 15:12:53 Hello world from greeter-844cffd75-ffccl
    2019/03/27 15:12:53 Hello world from greeter-844cffd75-zww6h
    2019/03/27 15:12:53 Hello world from greeter-844cffd75-7hpcb
    2019/03/27 15:12:53 Hello world from greeter-844cffd75-ffccl
    2019/03/27 15:12:53 Hello world from greeter-844cffd75-zww6h
    2019/03/27 15:12:53 Hello world from greeter-844cffd75-7hpcb
    2019/03/27 15:12:53 Hello world from greeter-844cffd75-ffccl
    2019/03/27 15:12:53 Hello world from greeter-844cffd75-zww6h
    2019/03/27 15:12:53 Hello world from greeter-844cffd75-7hpcb

    이 결과는 gRPC 단항 요청이 Istio 대상 규칙의 loadBalancer 구성(이 경우 ROUND_ROBIN)에 따라 gRPC 서버 pod(greeter-*)에 의해 처리됨을 보여줍니다.

  9. SSH 세션을 종료합니다.

    exit
    

소스 코드 검사

부하 분산 구성을 더 잘 이해하려면 샘플 애플리케이션의 소스 코드를 살펴보세요.

예를 들어 gRPC 클라이언트가 출력한 메시지를 이해하려면 서버와 클라이언트의 Go 소스 코드를 살펴봅니다. gRPC 서버는 요청을 처리할 때 호스트 이름을 찾아서 hostname이라는 응답 헤더로 추가합니다. 서버가 Kubernetes Pod에서 실행 중이므로 호스트 이름은 Pod의 이름입니다.

hostname, err := os.Hostname()
if err != nil {
	log.Printf("Unable to get hostname %v", err)
}
if hostname != "" {
	grpc.SendHeader(ctx, metadata.Pairs("hostname", hostname))
}

gRPC 클라이언트가 서버로부터 응답을 받으면 hostname 헤더를 가져와서 Console에 출력합니다.

if len(header["hostname"]) > 0 {
	hostname = header["hostname"][0]
}
log.Printf("%s from %s", r.Message, hostname)

gRPC 클라이언트에 의해 Console에 출력된 Kubernetes Pod 이름을 보려면 gRPC 서버의 istio 구성을 확인하세요. DestinationRule 객체가 ROUND_ROBINloadBalancer 알고리즘으로 지정합니다. 이 알고리즘은 수신 요청이 Kubernetes 배포의 Pod 간을 순환하는 이유입니다.

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: greeter
spec:
  host: greeter
  trafficPolicy:
    loadBalancer:
      simple: ROUND_ROBIN
    tls:
      mode: ISTIO_MUTUAL

문제해결

이 가이드에서 문제가 발생하면 다음 문서를 검토하시기 바랍니다.

삭제

이 가이드에서 사용한 리소스 비용이 Google Cloud 계정에 청구되지 않도록 하려면 다음 안내를 따르세요.

  1. Cloud Shell에서 GKE 클러스터를 삭제합니다.

    gcloud container clusters delete grpc-istio-ilb-tutorial --quiet --async
    
  2. Container Registry의 이미지를 삭제합니다.

    gcloud container images delete gcr.io/$GOOGLE_CLOUD_PROJECT/grpc-greeter-go-client \
        --force-delete-tags --quiet
    gcloud container images delete gcr.io/$GOOGLE_CLOUD_PROJECT/grpc-greeter-go-server \
        --force-delete-tags --quiet
    
  3. Compute Engine 인스턴스를 삭제합니다.

    gcloud compute instances delete grpc-istio-ilb-tutorial-client-vm --quiet
    

다음 단계