gRPC 无代理服务网格
与 Envoy 相比,gRPC 中 xDS API 的当前实现在某些领域受到限制。以下功能应该可以使用,尽管这不是一个详尽的列表,并且其他功能可能具有部分功能:
-
基本服务发现。您的 gRPC 服务可以访问网格中注册的其他 pod 和虚拟机。 -
DestinationRule: -
子集:您的 gRPC 服务可以根据标签选择器将流量拆分到不同的实例组。 -
当前唯一loadBalancer支持的 Istio 是ROUND_ROBIN,consistentHash将在 Istio 的未来版本中添加(它由 gRPC 支持)。 -
tls设置仅限于DISABLE或ISTIO_MUTUAL。其他模式将被视为DISABLE. -
VirtualService: -
标头匹配和 URI 格式匹配/ServiceName/RPCName。 -
覆盖目标主机和子集。 -
加权流量转移。 -
PeerAuthentication: -
仅支持DISABLE和STRICT。其他模式将被视为DISABLE. -
未来版本中可能会支持自动 mTLS。
gRPC 服务如何与 istiod 通信的示意图
尽管这不使用代理进行数据平面通信,但它仍然需要代理来初始化和与控制平面通信。首先,代理在启动时生成一个引导文件 ,就像它为 Envoy 生成引导程序一样。这告诉gRPC库如何连接istiod,它可以在哪里找到数据平面通信的证书,以及要发送到控制平面的元数据。接下来,代理充当xDS代理,istiod代表应用程序连接和验证。最后,代理获取并轮换数据平面流量中使用的证书。
03/ 更改应用程序代码
要在 gRPC 中启用 xDS 功能,您的应用程序必须进行一些必要的更改。您的 gRPC 版本至少应为1.39.0.
在客户端
以下副作用导入将在 gRPC 中注册 xDS 解析器和平衡器。它应该添加到您的 main包中或在同一个包中调用grpc.Dial.
import _ "google.golang.org/grpc/xds"
conn, err := grpc.DialContext(ctx, "xds:///foo.ns.svc.cluster.local:7070")
import "google.golang.org/grpc/credentials/xds"
...
creds, err := xds.NewClientCredentials(xds.ClientOptions{
FallbackCreds: insecure.NewCredentials()
})
// handle err
conn, err := grpc.DialContext(
ctx,
"xds:///foo.ns.svc.cluster.local:7070",
grpc.WithTransportCredentials(creds),
)
import "google.golang.org/grpc/xds"
...
server = xds.NewGRPCServer()
RegisterFooServer(server, &fooServerImpl)
如果您protoc生成的 Go 代码已过期,您可能需要重新生成它以与 xDS 服务器兼容。您生成RegisterFooServer的函数应如下所示:
func RegisterFooServer(s grpc.ServiceRegistrar, srv FooServer) {
s.RegisterService(&FooServer_ServiceDesc, srv)
}
最后,与客户端更改一样,我们必须启用安全支持:
creds, err := xds.NewServerCredentials(xdscreds.ServerOptions{FallbackCreds: insecure.NewCredentials()})
// handle err
server = xds.NewGRPCServer(grpc.Creds(creds))
04/ 在您的 Kubernetes 部署中
例子
在本指南中,您将部署echo一个已经支持服务器端和客户端无代理 gRPC 的应用程序。使用此应用程序,您可以尝试一些支持 mTLS 的流量策略。
先决条件
本指南要求在继续之前安装Istio (1.11+) 控制平面。
部署应用程序
创建一个启用注入的命名空间echo-grpc。接下来部署应用程序和服务的两个实例echo。
kubectl create namespace echo-grpc
kubectl label namespace echo-grpc istio-injection=enabled
kubectl -n echo-grpc apply -f samples/grpc-echo/grpc-echo.yaml
kubectl -n echo-grpc get pods
首先,端口转发17171到其中一个 Pod。此端口是非 xDS 支持的 gRPC 服务器,允许从端口转发的 Pod 发出请求。
kubectl -n echo-grpc port-forward $(kubectl -n echo-grpc get pods -l version=v1 -ojsonpath='{.items[0].metadata.name}') 17171 &
接下来,我们可以触发一批 5 个请求:
grpcurl -plaintext -d '{"url": "xds:///echo.echo-grpc.svc.cluster.local:7070", "count": 5}' :17171 proto.EchoTestService/ForwardEcho | jq -r '.output | join("")' | grep Hostname
您还可以对短名称使用类似 Kubernetes 的名称解析:
grpcurl -plaintext -d '{"url": "xds:///echo:7070"}' :17171 proto.EchoTestService/ForwardEcho | jq -r '.output | join
cat <<EOF | kubectl apply -f -
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: echo-versions
namespace: echo-grpc
spec:
host: echo.echo-grpc.svc.cluster.local
subsets:
- name: v1
labels:
version: v1
- name: v2
labels:
version: v2
EOF
使用上面定义的子集,您可以将 80% 的流量发送到特定版本:
cat <<EOF | kubectl apply -f -
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: echo-weights
namespace: echo-grpc
spec:
hosts:
- echo.echo-grpc.svc.cluster.local
http:
- route:
- destination:
host: echo.echo-grpc.svc.cluster.local
subset: v1
weight: 20
- destination:
host: echo.echo-grpc.svc.cluster.local
subset: v2
weight: 80
EOF
grpcurl -plaintext -d '{"url": "xds:///echo.echo-grpc.svc.cluster.local:7070", "count": 10}' :17171 proto.EchoTestService/ForwardEcho | jq -r '.output | join("")' | grep ServiceVersion
响应主要包含v2响应:
[0 body] ServiceVersion=v2
[1 body] ServiceVersion=v2
[2 body] ServiceVersion=v1
[3 body] ServiceVersion=v2
[4 body] ServiceVersion=v1
[5 body] ServiceVersion=v2
[6 body] ServiceVersion=v2
[7 body] ServiceVersion=v2
[8 body] ServiceVersion=v2
[9 body] ServiceVersion=v2
启用 mTLS
cat <<EOF | kubectl apply -f -
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: echo-mtls
namespace: echo-grpc
spec:
host: echo.echo-grpc.svc.cluster.local
trafficPolicy:
tls:
mode: ISTIO_MUTUAL
EOF
现在尝试调用尚未为 mTLS 配置的服务器将失败。
grpcurl -plaintext -d '{"url": "xds:///echo.echo-grpc.svc.cluster.local:7070"}' :17171 proto.EchoTestService/ForwardEcho | jq -r '.output | join("")'
Handling connection for 17171
ERROR:
Code: Unknown
Message: 1/1 requests had errors; first error: rpc error: code = Unavailable desc = all SubConns are in TransientFailure
cat <<EOF | kubectl apply -f -
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: echo-mtls
namespace: echo-grpc
spec:
mtls:
mode: STRICT
EOF
应用策略后,请求将开始成功。
grpcurl -plaintext -d '{"url": "xds:///echo.echo-grpc.svc.cluster.local:7070"}' :17171 proto.EchoTestService/ForwardEcho | jq -r '.output | join("")'
Handling connection for 17171
[0] grpcecho.Echo(&{xds:///echo.echo-grpc.svc.cluster.local:7070 map[] 0 5s false })
[0 body] x-request-id=0
[0 body] Host=echo.echo-grpc.svc.cluster.local:7070
[0 body] content-type=application/grpc
[0 body] user-agent=grpc-go/1.39.1
[0 body] StatusCode=200
[0 body] ServiceVersion=v1
[0 body] ServicePort=17070
[0 body] Cluster=
[0 body] IP=10.68.1.18
[0 body] IstioVersion=
[0 body] Echo=
[0 body] Hostname=echo-v1-7cf5b76586-z5p8l
-
不支持 Auto-mTLS,也不支持许可模式。STRICT相反,我们需要在服务器和客户端上进行显式 mTLS 配置 ISTIO_MUTUAL。在迁移到STRICT. -
grpc.Serve(listener)或grpc.Dial("xds:///...")在编写引导程序或 xDS 代理准备好之前调用可能会导致失败。holdApplicationUntilProxyStarts可以用来解决这个问题,或者应用程序可以更健壮地应对这些故障。 -
如果启用 xDS 的 gRPC 服务器使用 mTLS,那么您需要确保您的运行状况检查可以解决此问题。应该使用单独的端口,或者您的健康检查客户端需要一种方法来获取正确的客户端证书。 -
gRPC 中 xDS 的实现与 Envoy 不匹配。某些行为可能会有所不同,并且某些功能可能会丢失。gRPC的功能状态提供了更多详细信息。确保测试任何 Istio 配置实际上适用于您的无代理 gRPC 应用程序。
-
使用基于 Go 的负载测试应用程序 Fortio -
稍作修改,以支持 gRPC 的 XDS 功能 (PR) -
资源: -
具有 3 个节点的 GKE 1.20 集群e2-standard-16(每个节点 16 个 CPU + 64 GB 内存) -
Fortio 客户端和服务器应用程序:1.5 个 vCPU,1000 MiB 内存 -
Sidecar(istio-agent 和可能的 Envoy 代理):1 个 vCPU,512 MiB 内存 -
测试的工作负载类型: -
基线:没有使用 Envoy 代理或无代理 xDS 的常规 gRPC -
Envoy:标准 istio-agent + Envoy 代理 sidecar -
无代理:gRPCxds:///在客户端使用 xDS gRPC 服务器实现和解析器 -
PeerAuthentication通过和启用/禁用 mTLSDestinationRule
使用无代理 gRPC 解析器时延迟略有增加。与 Envoy 相比,这是一个巨大的改进,仍然允许高级流量管理功能和 mTLS。