Istio支持使用一组发现API(xDS API)来动态配置Envoy Sidecar代理。也就是说,您无需使用Sidecar,就可以使用流量路由、mTLS认证等功能管理工作负载。本文介绍如何在gRPC服务中使用无代理服务网格功能。
前提条件
- 已创建ASM实例,且ASM实例为v1.12.4.2-gc5962641-aliyun或以上版本。具体操作,请参见创建ASM实例。
- 已添加集群到ASM实例。具体操作,请参见添加集群到ASM实例。
背景信息
无代理服务网格(Proxyless)功能不使用Proxy进行数据面通信,但仍然需要一个Agent来进行初始化和与控制面的通信。Agent具有以下功能:- Agent会获取并轮换数据平面通信中使用的证书。
- Agent作为一个xDS Proxy,会代表应用程序与Istiod进行连接和认证。
- Agent在启动时生成一个引导文件,与为Envoy生成引导文件的方式相同,用于告诉gRPC库如何连接到Istiod,在哪里可以找到数据面通信的证书,以及向控制面发送什么数据。
注意事项
当前Proxyless模式存在以下限制:
- 不支持PERMISSIVE(明文或双向TLS认证)。如果您需要在服务端上使用STRICT,您需要在客户端使用ISTIO_MUTUAL的明确mTLS配置。
- 在bootstrap或xDS配置生效之前,通过
grpc.Serve(listener)
或grpc.Dial("xds://...")
的请求可能会调用失败。您可以使用holdApplicationUntilProxyStarts来避免这个问题。 - gRPC中xDS API的实现可能与Envoy不兼容,存在功能缺失或配置不生效的情况。请检查Istio配置是真正适用于您的无代理的gRPC应用程序。更多信息,请参见xDS Features in gRPC。
Proxyless模式支持的功能
与Envoy相比,目前gRPC内的xDS API仅支持部分功能。以下为支持的功能:
功能归类 | 支持的功能 |
---|---|
服务发现 | gRPC服务可以发现在网格中注册的其他Pod和虚拟机。 |
目标规则 |
|
虚拟服务 |
|
对等身份认证 | 支持DISABLE和STRICT的mTLS模式。 |
使用无代理服务网格功能
本文以已支持Proxyless模式的gRPC应用Echo为例,在不使用Sidecar代理情况下,介绍Echo应用如何使用流量路由和mTLS认证功能。
部署示例应用
- 在echo-grpc应用的Pod中添加
inject.istio.io/templates:grpc-agent
注解。如果您的应用代码支持Proxyless模式,您需要在应用Pod中添加
inject.istio.io/templates:grpc-agen
t注解,用于开启Proxyless模式。同时需要为gRPC服务端添加proxy.istio.io/config: '{"holdApplicationUntilProxyStarts": true}'
注解(ASM已默认为您开启),以确保在gRPC服务端初始化之前,代理中的xDS代理和引导文件已经准备就绪。以下为echo-grpc应用添加注解的部分内容,关于echo-grpc应用的详细YAML内容,请参见grpc-echo。
template: metadata: annotations: inject.istio.io/templates: grpc-agent proxy.istio.io/config: '{"holdApplicationUntilProxyStarts": true}' labels: app: echo version: v1
- 获取集群KubeConfig并通过kubectl工具连接集群。
- 执行以下命令,创建echo-grpc命名空间。
kubectl create namespace echo-grpc
- 执行以下命令,为echo-grpc命名空间添加标签,从而注入Sidecar。Proxyless模式不会使用到Sidecar,但是需要使用Agent与控制面进行通信,因此仍然需要为命名空间注入Sidecar。
kubectl label namespace echo-grpc istio-injection=enabled
- 执行以下命令,部署echo-grpc应用。
kubectl -n echo-grpc apply -f https://alibabacloudservicemesh.oss-cn-beijing.aliyuncs.com/asm-grpc-proxyless/grpc-echo.yaml
- 验证应用是否部署成功。
场景示例一:使用流量路由功能
- 创建目标规则。使用目标规则为每个版本的工作负载创建一个子集。
- 创建虚拟服务。使用虚拟服务将80%的流量路由到v2版本,20%流量路由到v1版本。
- 执行以下命令,向echo-grpc应用发送10个请求。
grpcurl -plaintext -d '{"url": "xds:///echo.echo-grpc.svc.cluster.local:7070", "count": 10}' :17171 proto.EchoTestService/ForwardEcho | jq -r '.output | join("")' | grep ServiceVersion
预期输出:
[0 body] ServiveVersion=v2 [0 body] ServiveVersion=v1 [0 body] ServiveVersion=v2 [0 body] ServiveVersion=v2 [0 body] ServiveVersion=v2 [0 body] ServiveVersion=v2 [0 body] ServiveVersion=v1 [0 body] ServiveVersion=v2 [0 body] ServiveVersion=v2 [0 body] ServiveVersion=v2
可以看到,向echo-grpc应用发送10个请求,8个请求路由到v2版本,2个请求路由到v1版本。说明流量路由配置成功。
场景示例二:使用mTLS认证
使用mTLS认证,对服务之间的通信进行双向TLS加密。
- 创建目标规则。使用目标规则启用客户端的mTLS。
- 执行以下命令,访问echo-grpc应用。
grpcurl -plaintext -d '{"url": "xds:///echo.echo-grpc.svc.cluster.local:7070"}' :17171 proto.EchoTestService/ForwardEcho | jq -r '.output | join("")'
预期输出:
ERROR: Code: Unknown Message: 1/1 requests had errors; first error: rpc error: code = Unavaliable desc = all SubConna are in TransientFailure
由于服务端未启用mTLS认证,访问echo-grpc应用失败。
- 创建对等身份认证。使用对等身份认证启用服务端的mTLS。
- 执行以下命令,访问echo-grpc应用。
grpcurl -plaintext -d '{"url": "xds:///echo.echo-grpc.svc.cluster.local:7070"}' :17171 proto.EchoTestService/ForwardEcho | jq -r '.output | join("")'
预期输出:
[0] grpcecho.Echo(${xds:///echo.echo-grpc.svc.cluster.local:7070 map [] 0 <nil> 5s false }) [0 body] RequestHeader=x-request-id:0 [0 body] Host=echo.echo-grpc.svc.cluster.local [0 body] RequestHeader=:authorit:echo.echo-grpc.svc.cluster.local:7070 ........
可以看到,访问echo-grpc应用成功。
修改gRPC应用程序代码
如果您的gRPC服务不支持Proxyless模式,您需要修改gRPC的客户端和服务端序启用gRPC服务的xDS API功能,然后您才能在Proxyless模式下使用流量策略等功能。
重要 仅1.39.0及以上版本的gRPC应用支持通过修改应用程序的方式启用xDS API功能。
修改gRPC客户端
- 按照以下内容修改gRPC程序代码,使得应用程序可以在gRPC中注册xDS解析器和均衡器。以下内容需要被添加到主包或调用grpc.Dia的同一个包中。
import _ "google.golang.org/grpc/xds"
- 按照以下内容修改gRPC程序代码,使得当创建一个gRPC连接时,URL必须使用
xds:/// scheme
。conn, err := grpc.DialContext(ctx, "xds:///foo.ns.svc.cluster.local:7070")
- 按照以下内容修改gRPC程序代码,使得使用(m)TLS时,会向DialContext传递一个特殊的TransportCredentials选项。FallbackCreds表示允许Istiod不发送安全配置时成功。
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), )
修改gRPC服务端
- 按照以下内容修改gRPC程序代码,使用一个特殊的构造函数来创建GRPC Server。
import "google.golang.org/grpc/xds" ... server = xds.NewGRPCServer() RegisterFooServer(server, &fooServerImpl)
- 如果您的protoc生成的Go代码已经过期,你需要重新生成Go代码,以便与xDS服务器兼容。生成的RegisterFooServer函数应如下所示:
func RegisterFooServer(s grpc.ServiceRegistrar, srv FooServer) { s.RegisterService(&FooServer_ServiceDesc, srv) }
- 按照以下内容修改gRPC程序代码,启用安全支持。
creds, err := xds.NewServerCredentials(xdscreds.ServerOptions{FallbackCreds: insecure.NewCredentials()}) // handle err server = xds.NewGRPCServer(grpc.Creds(creds))