大使(Ambassador)模式是一个专门的 Sidecar,它负责隐藏复杂性,并为访问 Pod 之外的服务提供一个统一的接口。在本章中,我们将看到Ambassador 模式如何作为代理,将主 Pod 从直接访问外部依赖关系中解脱出来。

问题描述

容器化服务并不是孤立存在的,很多时候必须访问其他服务,而这些服务可能很难以可靠的方式到达。访问其他服务的困难可能是由于动态和不断变化的地址、集群服务实例的负载均衡需求、不可靠的协议或困难的数据格式。理想情况下,容器应该是单一用途的,并且可以在不同的上下文中重复使用。但如果我们有一个容器,它提供一些业务功能,并以专门的方式消费外部服务,那么这个容器就会有不止一个责任。

消费外部服务可能需要一个特殊的服务发现库,而我们不想把它放在我们的容器中。或者我们可能希望通过使用不同种类的服务发现库和方法来交换不同种类的服务。这种将外侧世界中访问其他服务的逻辑进行抽象和隔离的技术就是这个大使模式的目标。

解决方案

为了演示这个模式,我们将使用一个应用程序的缓存。在开发环境中访问本地缓存可能是一个简单的配置,但在生产环境中,我们可能需要一个客户端配置来连接缓存的不同碎片。另一个例子是通过在注册表中查找并执行客户端服务发现。第三种情况是通过一个不可靠的协议(如 HTTP)来消费一个服务,所以为了保护我们的应用程序,我们必须使用断路逻辑,配置超时,按形式重试等等。

在所有这些情况下,我们都可以使用大使容器,它隐藏了访问外部服务的复杂性,并提供了一个简化的视图和通过 localhost 访问主应用容器。图 17-1 和 17-2 显示了大使 Pod 如何通过连接到在本地端口上监听的大使容器来解耦对键值存储的访问。在图 17-1 中,我们看到了如何将数据访问去掉到像 etcd 这样的完全分布式远程存储。
image.png
图 17-1 访问远程分布式缓存的大使

为了开发的目的,这个大使容器可以很容易地与本地运行的内存内键值存储(如 Memcached)交换(如图 17-2 所示)。
image.png
图 17-2 访问本地缓存的大使

:::tips 这种模式的好处与 Sidecar 模式类似 — 两者都允许保持容器的单一用途和可重用性。有了这样的模式,我们的应用容器可以专注于它的业务逻辑,并将消费外部服务的责任和细节委托给另一个专门的容器。这也允许创建专门的和可重用的大使容器,这些容器可以与其它应用容器组合。 :::

例 17-1 展示了一个与 REST 服务并行运行的大使。在返回它的响应之前,REST 服务通过将生成的数据发送到一个固定的 URL:http://localhost:9009,来记录这些数据。大使进程在这个端口上监听并处理数据。在这个例子中,它只是将数据打印出来到控制台,但是它
也可以做一些更复杂的事情,比如将数据转发到一个完整的日志基础设施。对于 REST 服务来说,日志数据发生了什么并不重要,你可以通过重新配置 Pod 轻松交换大使,而不需要接触主容器。

  1. # 例 17-1 大使容器处理日志输出
  2. ---
  3. apiVersion: v1
  4. kind: Pod
  5. metadata:
  6. name: random-generator
  7. labels:
  8. app: random-generator
  9. spec:
  10. containers:
  11. # 提供生成随机数的 REST 服务的主要应用容器
  12. - image: k8spatterns/random-generator:1.0
  13. name: main
  14. env:
  15. # 通过 localhost 与大使通信的连接 URL
  16. - name: LOG_URL
  17. value: http://localhost:9009
  18. ports:
  19. - containerPort: 8080
  20. protocol: TCP
  21. # 大使容器并行运行,并在 9009 端口上监听(该端口不暴露到 Pod 的外面)
  22. - image: k8spatterns/random-generator-log-ambassador
  23. name: ambassador

一些讨论

在更高的层次上,大使是一种 Sidecar 模式。大使和 Sidecar 之间的主要区别在于,大使并没有为主应用程序提供额外的功能。相反,它只是作为一个与外部世界的智能代理,这也是它名字的由来(这种模式有时也被称为代理模式)。

参考资料