ETCD:gRPC代理

原文地址:gRPC proxy
gRPC代理是在gRPC层(L7)运行的无状态etcd反向代理。代理旨在减小核心etcd群集上的总处理负载。对于水平可伸缩性,它合并了监视和租约API请求。 为了保护集群免受滥用客户端的侵害,它会缓存关键范围请求。
gRPC代理支持多个etcd服务器端点。 代理启动时,它会随机选择一个etcd服务器端点来使用.该端点将处理全部请求,直到代理检测到端点故障为止。 若是gRPC代理检测到端点故障,它将切换到其余端点(若是有)以向其客户端隐藏故障。 未来可能会支持其余重试策略,例如加权轮询。git

可扩展的监视 API


gRPC代理将同一键或范围上的多个客户端监视程序(c-watcher)合并为链接到etcd服务器的单个监视程序(s-watcher)。 代理将全部事件从S-watcher广播到其c-watcher。
假设N个客户端监视相同的密钥,则一个gRPC代理能够将etcd服务器上的监视负载从N减小到1。用户能够部署多个gRPC代理来进一步分配服务器负载。
在如下示例中,三个客户端监视键A。gRPC代理将三个监视程序合并,从而建立一个附加到etcd服务器的监视程序。github

+-------------+
            | etcd 服务器 |
            +------+------+
                   ^ 监视 key A (s-watcher)
                   |
           +-------+-----+
           | gRPC 代理  | <-------+
           |             |         |
           ++-----+------+         |监视 key A (c-watcher)
监视 key A ^     ^ 监视 key A    |
(c-watcher) |     | (c-watcher)    |
    +-------+-+  ++--------+  +----+----+
    |  客户端 |  |  客户端 |  |  客户端 |
    |         |  |         |  |         |
    +---------+  +---------+  +---------+

局限性

为了有效地将多个客户端监视程序合并为一个监视程序,gRPC代理在可能的状况下将新的c-watcher合并为现有的s-watcher。 因为网络延迟或缓冲的未传递事件,此合并的s-watcher可能与etcd服务器不一样步。 若是未指定监视版本,则gRPC代理将不能保证c-watcher从最近的存储修订版本开始监视。 例如,若是客户端从具备修订版1000的etcd服务器监视,则该监视程序将从修订版1000开始。若是客户端从gRPC代理监视,则能够从修订版990开始监视。
相似的限制也适用于取消。 取消观察者后,etcd服务器的修订版可能大于取消响应修订版。
对于大多数用例,这两个限制不该引发问题。 未来,可能会有其余选项强制观察者绕过gRPC代理以得到更准确的修订响应。api

可扩展的租约 API


为了保持其租约有效,客户端必须至少向一个etcd服务器创建一个gRPC流,以发送按期的心跳信号。 若是etcd工做负载涉及大量租约活动分布在许多客户端上,则这些流可能会致使CPU使用率太高。 为了减小核心群集上的流总数,该代理支持租约流合并。
假设N个客户端正在更新租约,则单个gRPC代理将etcd服务器上的流负载从N减小到1。部署中可能具备其余gRPC代理,以进一步在多个代理之间分配流。
在如下示例中,三个客户端更新了三个独立的租约(L1,L2和L3)。 gRPC代理将三个客户端租约流(c-stream)合并为链接到etcd服务器的单个租约保持活动流(s-stream)。 代理将客户端租用心跳从c流转发到s流,而后将响应返回到相应的c流。缓存

+-------------+
          | etcd 服务器 |
          +------+------+
                 ^
                 | 心跳 L1, L2, L3
                 | (s-stream)
                 v
         +-------+-----+
         | gRPC 代理  +<-----------+
         +---+------+--+            | 心跳 L3
             ^      ^               | (c-stream)
心跳 L1 |      | 心跳 L2  |
(c-stream)   v      v (c-stream)    v
      +------+-+  +-+------+  +-----+--+
      | 客户端 |  | 客户端 |  | 客户端 |
      +--------+  +--------+  +--------+

客户保护滥用

gRPC代理在不违反一致性要求时会缓存请求的响应。 这能够保护etcd服务器免遭严密for循环中滥用客户端的侵害。安全

启动etcd gRPC代理


考虑一个etcd集群包括如下几个静态端点:服务器

名字 地址 主机名
infra0 10.0.1.10 infra0.example.com
infra1 10.0.1.11 infra1.example.com
infra2 10.0.1.12 infra2.example.com

经过如下命令使用静态节点启动gRPC代理:网络

$ etcd grpc-proxy start --endpoints=infra0.example.com,infra1.example.com,infra2.example.com --listen-addr=127.0.0.1:2379

etcd gRPC启动并监听端口2379.它将客户端请求转发到上面提供的三个端点之一。
经过代理发送请求:curl

$ ETCDCTL_API=3 etcdctl --endpoints=127.0.0.1:2379 put foo bar
OK
$ ETCDCTL_API=3 etcdctl --endpoints=127.0.0.1:2379 get foo
foo
bar

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


代理支持经过写入用户定义的端点来注册其端点以进行发现。 这有两个目的。 首先,它容许客户端将其端点与一组代理端点同步,以实现高可用性。 其次,它是etcd gRPC命名的端点提供程序。
经过提供用户定义的前缀来注册代理:ide

$ etcd grpc-proxy start --endpoints=localhost:2379 \
  --listen-addr=127.0.0.1:23790 \
  --advertise-client-url=127.0.0.1:23790 \
  --resolver-prefix="___grpc_proxy_endpoint" \
  --resolver-ttl=60

$ etcd grpc-proxy start --endpoints=localhost:2379 \
  --listen-addr=127.0.0.1:23791 \
  --advertise-client-url=127.0.0.1:23791 \
  --resolver-prefix="___grpc_proxy_endpoint" \
  --resolver-ttl=60

代理将会列出成员列表中的全部成员:测试

ETCDCTL_API=3 etcdctl --endpoints=http://localhost:23790 member list --write-out table

+----+---------+--------------------------------+------------+-----------------+
| ID | STATUS  |              NAME              | PEER ADDRS |  CLIENT ADDRS   |
+----+---------+--------------------------------+------------+-----------------+
|  0 | started | Gyu-Hos-MBP.sfo.coreos.systems |            | 127.0.0.1:23791 |
|  0 | started | Gyu-Hos-MBP.sfo.coreos.systems |            | 127.0.0.1:23790 |
+----+---------+--------------------------------+------------+-----------------+

这使客户端能够经过Sync自动发现代理端点:

cli, err := clientv3.New(clientv3.Config{
    Endpoints: []string{"http://localhost:23790"},
})
if err != nil {
    log.Fatal(err)
}
defer cli.Close()

// fetch registered grpc-proxy endpoints
if err := cli.Sync(context.Background()); err != nil {
    log.Fatal(err)
}

注意,若是配置的代理没有解析程序前缀,

$ etcd grpc-proxy start --endpoints=localhost:2379 \
  --listen-addr=127.0.0.1:23792 \
  --advertise-client-url=127.0.0.1:23792

grpc-proxy的成员列表API返回其本身的advertise-client-url

ETCDCTL_API=3 etcdctl --endpoints=http://localhost:23792 member list --write-out table

+----+---------+--------------------------------+------------+-----------------+
| ID | STATUS  |              NAME              | PEER ADDRS |  CLIENT ADDRS   |
+----+---------+--------------------------------+------------+-----------------+
|  0 | started | Gyu-Hos-MBP.sfo.coreos.systems |            | 127.0.0.1:23792 |
+----+---------+--------------------------------+------------+-----------------+

命名空间


假设一个应用程序指望对整个键空间有彻底控制,可是etcd集群与其余应用程序共享。 为了使全部应用程序都不会相互干扰地运行,代理能够对etcd键空间进行分区,以便客户端能够访问完整的键空间。 当给代理提供标志--namespace时,全部进入代理的客户端请求都将转换为在键上具备用户定义的前缀。 对etcd集群的访问将在前缀下,而来自代理的响应将删除该前缀;对于客户端,显然根本没有前缀。
要为代理命名空间,请经过--namespace启动:

$ etcd grpc-proxy start --endpoints=localhost:2379 \
  --listen-addr=127.0.0.1:23790 \
  --namespace=my-prefix/

如今,对代理的访问在etcd集群上透明地加上前缀:

$ ETCDCTL_API=3 etcdctl --endpoints=localhost:23790 put my-key abc
# OK
$ ETCDCTL_API=3 etcdctl --endpoints=localhost:23790 get my-key
# my-key
# abc
$ ETCDCTL_API=3 etcdctl --endpoints=localhost:2379 get my-prefix/my-key
# my-prefix/my-key
# abc

TLS终端


使用来自安全etcd群集的TLS的gRPC代理终端为未加密的本地端点提供服务.
使用客户端https启动单个成员etcd集群尝试:

$ etcd --listen-client-urls https://localhost:2379 --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://localhost2379localhost: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 &

最后,经过在http上将密钥放入代理来测试TLS终端:

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

指标和健康


gRPC代理为--endpoints定义的etcd成员公开了/health和Prometheus/metrics端点。 另外一种方法是定义一个附加URL,该URL将使用--metrics-addr参数来响应/metrics/health端点。

$ etcd grpc-proxy start \
  --endpoints https://localhost:2379 \
  --metrics-addr https://0.0.0.0:4443 \
  --listen-addr 127.0.0.1:23790 \
  --key client.key \
  --key-file proxy-server.key \
  --cert client.crt \
  --cert-file proxy-server.crt \
  --cacert ca.pem \
  --trusted-ca-file proxy-ca.pem

已知问题

代理的主接口同时服务于HTTP2和HTTP/1.1。若是如上例所示,使用TLS设置了代理,则在监听接口上使用诸如cURL之类的客户端时,将要求在返回/metrics/health的请求上将协议显式设置为HTTP/1.1。经过使用--metrics-addr参数,辅助接口将没有此要求。

$ curl --cacert proxy-ca.pem --key proxy-client.key --cert proxy-client.crt https://127.0.0.1:23790/metrics --http1.1
相关文章
相关标签/搜索