使用 Istio 为内部 gRPC 服务实现负载平衡

本教程将介绍如何使用 Istio,为在 Google Kubernetes Engine (GKE) 上运行的 gRPC 服务设置内部 TCP/UDP 负载平衡。此设置允许 VPC 网络中的其他资源使用专用的内部 (RFC 1918) IP 地址与 gRPC 服务进行通信,而 Istio 则负责处理运行 gRPC 服务的 Kubernetes Pod 之间的路由和负载平衡请求。

本教程假定您具备 gRPC 和 GKE 或 Kubernetes 的基础知识。

简介

Istio 为传入和离开服务网格的流量提供了管理网关。Istio 内部负载平衡器 (ILB) 网关将来自内部 VPC 网络中的源的入站流量路由到服务网格中的 Kubernetes Pod。在这种架构中,Google Cloud 内部 TCP/UDP 负载平衡机制在 GKE 集群中的节点之间执行第 4 层(传输层)负载平衡。Istio ILB 网关接收流量并执行第 7 层(应用层)负载平衡,利用在虚拟服务目标规则中定义的规则将流量分发给 Istio 服务网格中的服务。

本教程中使用的示例 gRPC 服务会返回一个响应标头,该标头中包含处理请求的 Kubernetes Pod 的名称。根据这些信息,您可以看到 Istio ILB 网关执行负载平衡,将客户端通过单个连接发出的请求分发给 GKE 集群中的多个 Kubernetes Pod。

目标

  • 使用 Istio 和 Istio ILB 网关创建 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)
    

    您必须具有 cluster-admin Kubernetes 集群角色中定义的权限才能安装 Istio。

  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 网关 (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 网关创建传输层安全协议 (TLS) 证书

  1. 在 Cloud Shell 中,创建 TLS 证书和私钥以允许 Istio ILB 网关终止 TLS:

    openssl req -x509 -nodes -newkey rsa:2048 -days 365 \
        -keyout privkey.pem -out cert.pem -subj "/CN=grpc.example.com"
    
  2. 创建用于存储 TLS 证书和私钥的 Kubernetes Secret:

    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 Deployment 和 Service 对象:

    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 Sidecar 容器都在运行。

  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 负载平衡是区域性的,因此您可以测试相同地区或区域中的虚拟机发起的连接。

  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 网关 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 网关 IP 地址的文件复制到虚拟机:

    gcloud compute scp cert.pem ilb-ip.txt grpc-istio-ilb-tutorial-client-vm:~
    
    
  5. 使用 SSH 连接到虚拟机:

    gcloud compute ssh grpc-istio-ilb-tutorial-client-vm
    
  6. 在虚拟机上,从 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,但在虚拟机中则不会进行这样的默认设置。

  7. 获取 Container Registry 凭据,以便配合 docker 命令使用:

    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 一元请求由 gRPC 服务器 Pod(名为 greeter-*)根据 Istio 目标规则内的 loadBalancer 配置(本例中为 ROUND_ROBIN)进行处理。

  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 标头并在控制台中显示此标头。

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

若要了解 gRPC 客户端在控制台中显示的 Kubernetes Pod 名称,请查看 gRPC 服务器的 Istio 配置。请注意,DestinationRule 对象将 ROUND_ROBIN 指定为 loadBalancer 算法。此算法是传入请求在 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
    

后续步骤