ETCD 四 etcd 网关与 gRPC-Gateway

一、etcd gateway

1.什么是etcd gateway

etcd 网关是一个简单的TCP 代理,可将网络数据转发到 etcd 集群。网关是无状态且透明的,它既不会检查客户端请求,也不会干扰集群响应,支持多个 etcd 服务器实例,并采用简单的循环策略。

etcd 网关将请求路由到可用端点,并向客户端隐藏故障,使得客户端感知不到服务端的故障。后期可能会支持其他访问策略,例如加权轮询。

2.什么时候使用etcd gateway

我们使用客户端连接到 etcd 服务器时,每个访问 etcd 的应用程序必须知道所要访问的 etcd 集群实例的地址,即用来提供客户端服务的地址:ETCD_LISTEN_CLIENT_URLS。

如果将 etcd 集群重新配置,拥有不同的端点,那么每个应用程序还需要更新其端点列表。在大规模集群环境下,重新配置的操作既造成了重复又容易出错。

这些问题可以通过 etcd 网关来解决:使用 etcd 网关作为稳定的本地端点,对于客户端应用程序来说,不会感知到集群实例的变化。典型的 etcd 网关配置是使每台运行网关的计算机在本地地址上侦听,并且每个 etcd 应用程序都连接对应的本地网关,发生 etcd 集群实例的变更时,只需要网关更新其端点,而不需要更新每个客户端应用程序的代码实现。

3.什么时候不使用etcd gateway

不实用etcd 网关的场景:

  • 性能提升

etcd 网关不是为提高 etcd 集群性能设计的。它不提供缓存、watch 流合并或批量处理等功能。etcd 团队目前正在开发一种缓存代理,旨在提高集群的可伸缩性。

  • 运行在集群管理系统之上

高级集群管理系统比如 Kubernetes 原生支持服务发现。应用可以访问使用 DNS 名称 或者系统管理的虚拟IP地址来访问 etcd 集群。例如,kube-proxy 等价于 etcd 网关。

4.开启etcd 网关

NameAddress
etcd1192.168.70.100:12379
etcd2192.168.70.100:22379
etcd3192.168.70.100:32379
etcd gateway start --endpoints=http://127.0.0.1:12379,http://127.0.0.1:22379,http://127.0.0.1:32379 --listen-addr=192.168.70.100:23790

--endpoints:是以逗号分隔的、用于转发客户端连接的 etcd 服务器目标列表。默认值为127.0.0.1:2379,url不支持https,因为网关并不能决定 TLS。

--discovery-srv:使用DNS服务发现

--listen-addr :绑定的接口和端口,用于接受客户端请求,默认配置为127.0.0.1:23790

--retry-delay: 重试连接到失败的端点延迟时间,默认为 1m0s

--insecure-discovery :接受不安全或容易受到中间人攻击的 SRV 记录,默认为 false。

--trusted-ca-file: etcd群集客户端TLS CA文件的路径,用于验证从SRV发现返回的端点。请注意,它仅用于验证发现的端点,而不是创建用于数据传输的连接。网关从不终止TLS连接或代表其客户端创建TLS连接。

5.gRPC-Gateway

etcd v3 使用 gRPC作为其消息传递协议。etcd 项目包括一个基于 gRPC 的 Go 客户端和一个命令行实用程序 etcdctl,用于通过 gRPC 与 etcd 集群通信。

对于不支持 gRPC 的语言,etcd 提供了一个 JSON gRPC 网关。此网关提供 RESTful 代理,将 HTTP/JSON 请求转换为 gRPC 消息。

gRPC-gateway 不支持使用 TLS 公用名进行身份验证。

这里需要注意的是,在 HTTP 请求体中的 JSON 对象,其包含的 key 和 value 字段都被定义成了 byte 数组,因此必须在 JSON 对象中,使用 base64 编码对内容进行处理。

put

key:test1(base64编码:dGVzdDE=)

value:hello1(base64编码:aGVsbG8x)

range

watch

curl -N http://192.168.70.100:23790/v3/watch   -X POST -d '{"create_request": {"key":"dGVzdDE="} }' &

txn

1.发起事务,用以设置键值,compare 是断言列表,拥有多个联合的条件,这里的条件是当 createRevision 的值为 17时(我们在上面请求查询到该键值的创建版本为 17),表示符合条件,因此事务可以成功执行。

 

2.对比指定键值对版本的事务

compare 中 target 的枚举值为 VERSION。通过比较,发现键 dGVzdDE=对应的 version 确实是 13,因此执行查询结果,返回dGVzdDE=对应的正确值

 

6.gRPC-Gateway安全认证

HTTP 的方式访问 etcd 服务端,需要考虑安全的问题,gRPC-Gateway 中提供的 API 接口支持开启安全认证。通过 /v3/auth 接口设置认证,流程如下:

创建 root 用户

创建 root 角色

 

为 root 用户授予角色

 

开启权限

 

认证获取令牌

 

请求获取到 token 的值为 NGSVDxQigHImeBiv.57 ,接下来,设置请求的头部 Authorization 为刚刚获取到的身份验证令牌,以使用身份验证凭据来请求etcd


 

二、gRPC proxy

gRPC proxy 是在 gRPC 层(L7)运行的无状态 etcd 反向代理,旨在减少核心 etcd 集群上的总处理负载。gRPC proxy 合并了监视和 Lease API 请求,实现了水平可伸缩性。同时,为了保护集群免受滥用客户端的侵害,gRPC proxy 实现了键值对的读请求缓存。

gRPC代理支持多个etcd服务器端点。当代理启动时,它会随机选择一个etcd服务器端点来使用。该端点服务于所有请求,直到代理检测到端点故障。如果gRPC代理检测到端点故障,它会切换到另一个端点(如果可用),以对其客户端隐藏故障。将来可能会支持其他重试策略,例如加权循环。

gRPC proxy 通过对 etcd client 的封装,实现了与etcd连接,不同客户端请求复用同一个client,其目的是为了加少etcd server的负载。这个目的也决定了proxy的使用场景,即降低 etcd 负载。

1.gRPC proxy 基本应用

NameAddress
etcd1192.168.70.100:12379
etcd2192.168.70.100:22379
etcd3192.168.70.100:32379

使用etcd grpc-proxy start的命令开启 etcd 的 gRPC proxy 模式,包含上表中的静态成员:

etcd grpc-proxy start --endpoints=http://127.0.0.1:12379,http://127.0.0.1:22379,http://127.0.0.1:32379 --listen-addr=192.168.70.100:2379

etcd gRPC代理在端口2379上启动并侦听。它将客户端请求转发到上面提供的三个端点之一。

通过代理发送请求:

[root@localhost etcd-cluster]# ETCDCTL_API=3 etcdctl --endpoints=192.168.70.100:2379 put foo bar
OK
[root@localhost etcd-cluster]# ETCDCTL_API=3 etcdctl --endpoints=192.168.70.100:2379 get foo
foo
bar

2.客户端端点同步和名称解析

gRPC 代理是 gRPC 命名的提供者,支持在启动时通过写入相同的前缀端点名称进行注册。这样可以使客户端将其端点与具有一组相同前缀端点名的代理端点同步,进而实现高可用性。

通过提供用户定义的前缀来注册代理:

etcd grpc-proxy start --endpoints=127.0.0.1:12379 --listen-addr=192.168.70.100:23790 --advertise-client-url=192.168.70.100:23790 --resolver-prefix="___grpc_proxy_endpoint" --resolver-ttl=60

etcd grpc-proxy start --endpoints=127.0.0.1:12379 --listen-addr=192.168.70.100:23791 --advertise-client-url=192.168.70.100:23791 --resolver-prefix="___grpc_proxy_endpoint" --resolver-ttl=60

在上面的启动命令中,将需要加入的自定义端点--resolver-prefix设置为___grpc_proxy_endpoint。启动成功之后,我们来验证下,gRPC 代理在查询成员时是否列出其所有成员作为成员列表,执行如下的命令:

ETCDCTL_API=3 etcdctl --endpoints=192.168.70.100:23790 member list --write-out table

 同样地,客户端也可以通过 Sync 方法自动发现代理的端点,代码实现如下:

 

相应地,如果配置的代理没有配置前缀,gRPC 代理启动命令如下:

etcd grpc-proxy start --endpoints=127.0.0.1:12379 --listen-addr=192.168.70.100:23790 --advertise-client-url=192.168.70.100:23790

验证下 gRPC proxy 的成员列表 API 是不是只返回自己的advertise-client-url

结果如我们预期:当我们没有配置代理的前缀端点名时,获取其成员列表只会显示当前节点的信息,也不会包含其他的端点

3.可伸缩的 watch API

如果客户端监视同一键或某一范围内的键,gRPC 代理可以将这些客户端监视程序(c-watcher)合并为连接到 etcd 服务器的单个监视程序(s-watcher)。当 watch 事件发生时,代理将所有事件从 s-watcher 广播到其 c-watcher。

假设 N 个客户端监视相同的 key,则 gRPC 代理可以将 etcd 服务器上的监视负载从 N 减少到 1。用户可以部署多个 gRPC 代理,进一步分配服务器负载。

如下图所示,三个客户端监视键 A。gRPC 代理将三个监视程序合并,从而创建一个附加到 etcd 服务器的监视程序。

为了有效地将多个客户端监视程序合并为一个监视程序,gRPC 代理在可能的情况下将新的 c-watcher 合并为现有的 s-watcher。由于网络延迟或缓冲的未传递事件,合并的 s-watcher 可能与 etcd 服务器不同步。

如果没有指定监视版本,gRPC 代理将不能保证 c-watcher 从最近的存储修订版本开始监视。例如,如果客户端从修订版本为 1000 的 etcd 服务器监视,则该监视者将从修订版本 1000 开始。如果客户端从 gRPC 代理监视,则可能从修订版本 990 开始监视。

类似的限制也适用于取消。取消 watch 后,etcd 服务器的修订版可能大于取消响应修订版。

对于大多数情况,这两个限制一般不会引起问题,未来也可能会有其他选项强制观察者绕过 gRPC 代理以获得更准确的修订响应。

4.可伸缩的 lease API

为了保持客户端申请租约的有效性,客户端至少建立一个 gRPC 连接到 etcd 服务器,以定期发送心跳信号。如果 etcd 工作负载涉及很多的客户端租约活动,这些流可能会导致 CPU 使用率过高。为了减少核心集群上的流总数,gRPC 代理支持将 lease 流合并

假设有 N 个客户端正在更新租约,则单个 gRPC 代理将 etcd 服务器上的流负载从 N 减少到 1。在部署的过程中,可能还有其他 gRPC 代理,进一步在多个代理之间分配流。

在下图示例中,三个客户端更新了三个独立的租约(L1、L2 和 L3)。gRPC 代理将三个客户端租约流(c-stream)合并为连接到 etcd 服务器的单个租约(s-stream),以保持活动流。代理将客户端租约的心跳从 c-stream 转发到 s-stream,然后将响应返回到相应的 c-stream。

 除此之外,gRPC 代理在满足一致性时会缓存请求的响应。该功能可以保护 etcd 服务器免遭恶意 for 循环中滥用客户端的攻击。

5.命名空间的实现

上面我们讲到 gRPC proxy 的端点可以通过配置前缀,自动发现。而当应用程序期望对整个键空间有完全控制,etcd 集群与其他应用程序共享的情况下,为了使所有应用程序都不会相互干扰地运行,代理可以对etcd 键空间进行分区,以便客户端大概率访问完整的键空间。

当给代理提供标志--namespace时,所有进入代理的客户端请求都将转换为在键上具有用户定义的前缀。普通的请求对 etcd 集群的访问将会在我们指定的前缀(即指定的 --namespace 的值)下,而来自代理的响应将删除该前缀;而这个操作对于客户端来说是透明的,根本察觉不到前缀。

下面我们给 gRPC proxy 命名,只需要启动时指定--namespace标识:

etcd grpc-proxy start --endpoints=127.0.0.1:12379 --listen-addr=192.168.70.100:23790 --namespace=myprefix/

此时对代理的访问会在 etcd 群集上自动地加上前缀,对于客户端来说没有感知。我们通过 etcdctl 客户端进行尝试:

上述三条命令,首先通过代理写入键值对,然后读取。为了验证结果,第三条命令通过 etcd 集群直接读取,不过需要加上代理的前缀,两种方式得到的结果完全一致。因此,使用 proxy 的命名空间即可实现 etcd 键空间分区,对于客户端来说非常便利。 

6.扩展功能

gRPC 代理的功能非常强大,除了上述提到的客户端端点同步、可伸缩 API、命名空间功能,还提供了指标与健康检查接口和 TLS 加密中止的扩展功能。

指标与健康检查接口

gRPC 代理为--endpoints定义的 etcd 成员公开了/health和 Prometheus 的/metrics接口。我们通过浏览器访问这两个接口:

http://192.168.70.100:23790/metrics

http://192.168.70.100:23790/health

 

通过代理访问/metrics端点的结果如上图所示 ,其实和普通的 etcd 集群实例没有什么区别,同样也会结合一些中间件进行统计和页面展示,如 Prometheus 和 Grafana 的组合。

除了使用默认的端点访问这两个接口,另一种方法是定义一个附加 URL,该 URL 将通过 --metrics-addr 标志来响应/metrics/health端点。命令如下所示 :

etcd grpc-proxy start \
  --endpoints http://localhost:12379 \
  --metrics-addr http://0.0.0.0:6633 \
  --listen-addr 127.0.0.1:23790 \

TLS 加密的代理

通过使用 gRPC 代理 etcd 集群的 TLS,可以给没有使用 HTTPS 加密方式的本地客户端提供服务,实现 etcd 集群的 TLS 加密中止,即未加密的客户端与 gRPC 代理通过 HTTP 方式通信,gRPC 代理与 etcd 集群通过 TLS 加密通信。下面我们进行实践:

etcd --listen-client-urls https://localhost:12379 --advertise-client-urls https://localhost:2379 --cert-file=peer.crt --key-file=peer.key --trusted-ca-file=ca.crt --client-cert-auth

确认客户端端口正在提供https:

# fails 
$ ETCDCTL_API=3 etcdctl --endpoints=http://localhost:2379 endpoint status 
# works 
$ ETCDCTL_API=3 etcdctl --endpoints=https://localhost:2379 --cert=client.crt --key=client.key --cacert=ca.crt endpoint status

显然第一种方式不能访问。

接下来通过使用客户端证书连接到 etcd 端点https://localhost:2379,并在 localhost:12379 上启动 gRPC 代理,命令如下:

etcd grpc-proxy start --endpoints=https://localhost:2379 --listen-addr localhost:12379 --cert client.crt --key client.key --cacert=ca.crt --insecure-skip-tls-verify

启动后,我们通过 gRPC 代理写入一个键值对测试:

$ ETCDCTL_API=3 etcdctl --endpoints=http://localhost:12379 put abc def
# OK

可以看到,使用 HTTP 的方式设置成功。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值