跳至主内容

将 RabbitMQ 部署到 Kubernetes:涉及哪些内容?

·26 分钟阅读

随着时间的推移,我们看到在我们社区的 邮件列表Slack 频道上关于 Kubernetes 的查询数量飙升。

到 2024 年,大多数 Kubernetes 相关问题的答案是:使用 RabbitMQ 核心团队构建的 Kubernetes Operator。它包含了所有最佳实践,是强烈推荐的选项。

本文档介绍了在 Kubernetes 上自行部署 RabbitMQ 的基础知识:需要哪些 Kubernetes 资源,如何确保 RabbitMQ 节点使用持久化存储,如何处理敏感值的配置等等。

2024 年更新

提示

与其自行部署 RabbitMQ 到 Kubernetes,不如考虑使用 Kubernetes Operator,这是由 RabbitMQ 核心团队构建的。它整合了所有最佳实践,是我们强烈推荐的选择。

简介

在没有使用 Kubernetes Operator 的情况下,将 RabbitMQ 等有状态数据服务部署到 Kubernetes,就像在拼七巧板。

涉及多个部分

在本文中,我们将尝试涵盖这些关键部分,并提到一些虽然在技术上并非运行 RabbitMQ 所必需,但每个生产系统运维人员迟早都必须关心的额外步骤:

  • 如何使用 Prometheus 和 Grafana 设置集群监控
  • 如何部署 PerfTest 实例以对集群进行基本的性能和负载测试

本文绝非涵盖了在 Kubernetes 上部署 RabbitMQ 时可能涉及的所有方面;我们的目标是强调最重要的部分。针对具体部署和工作负载的决策,例如对 RabbitMQ 节点 Pod(容器)应用什么资源限制使用何种持久化存储、如何处理 TLS 证书/密钥对轮换、日志聚合以及升级,都是适合单独撰写博文的话题。请告诉我们您希望在后续文章中看到什么!

可执行示例

本文附带的文件可以在 DIY RabbitMQ on Kubernetes 示例仓库中找到。本文使用 Google Kubernetes Engine (GKE) 集群,但 Kubernetes 的概念是通用的。

要跟随示例操作,您需要:

本文假设读者熟悉 kubectl 使用基础,并且该工具已配置为可以与 GKE 集群协作

RabbitMQ Docker 镜像

我们建议使用 社区版 RabbitMQ Docker 镜像。该镜像由 Docker 社区维护,并使用最新版本的 RabbitMQ、Erlang 和 OpenSSL 构建。该镜像还有一个基于 RabbitMQ 发行候选版 (release candidate) 构建的变体,用于早期测试和采用。

现在,让我们从在 Kubernetes 上运行 RabbitMQ 集群的第一个构建块开始:选择一个部署命名空间。

Kubernetes 命名空间和权限 (RBAC)

每一组 Kubernetes 对象都属于一个 Kubernetes 命名空间。RabbitMQ 集群资源也不例外。

我们建议使用专用的命名空间,以便将 RabbitMQ 集群与可能部署在同一 Kubernetes 集群中的其他服务隔离开来。拥有专用命名空间在逻辑上是合理的,并且可以轻松地为集群节点授予恰到好处的权限。这是一种良好的安全实践。

RabbitMQ 的 Kubernetes 节点发现插件依赖 Kubernetes API 作为数据源。首次启动时,每个节点都会尝试使用 Kubernetes API 发现其对等节点并尝试加入它们。完成启动的节点会发出一个 Kubernetes 事件,以便在集群活动(事件)日志中更容易发现此类事件。

该插件需要对 Kubernetes 资源具有以下访问权限:

  • endpoints 资源的 get 访问权限
  • events 资源的 create 访问权限

指定一个 Role (角色)、Role Binding (角色绑定) 和 Service Account (服务账户) 来配置此访问权限。

可以在 rbac.yaml 示例文件中看到命名空间及其 RBAC 规则的示例。

如果参考示例,请使用以下命令创建命名空间和所需的 RBAC 规则。请注意,这会创建一个名为 test-rabbitmq 的命名空间。

kubectl apply -f namespace.yaml
kubectl apply -f rbac.yaml

下面的 kubectl 示例将使用 test-rabbitmq 命名空间。为方便起见,可以将此命名空间设置为默认值。

# set the namespace to be the current (default) one
kubectl config set-context --current --namespace=test-rabbitmq
# verify
kubectl config view --minify | grep namespace:

或者,可以在下面演示的所有 kubectl 命令后附加 --namespace="test-rabbitmq"

使用 StatefulSet

RabbitMQ **要求**使用 StatefulSet 在 Kubernetes 上部署 RabbitMQ 集群。StatefulSet 可确保 RabbitMQ 节点按顺序依次部署。这避免了在部署多节点 RabbitMQ 集群时遇到潜在的节点发现竞争条件

使用 StatefulSet 而非 Deployment 还有其他同样重要的原因:固定的标识、简单的网络标识符、稳定的持久化存储以及执行有序滚动升级的能力。

StatefulSet 定义文件中包含许多细节,如挂载配置、挂载凭据、开放端口等,这些内容将在随后的章节中按主题进行解释。

最终的 StatefulSet 文件可以在 gke 目录下找到。

为集群和 CLI 工具创建服务

StatefulSet 定义可以引用一个服务,该服务为 StatefulSet 的 Pod 提供网络身份。这里,我们引用的是 v1.StatefulSet.Spec.serviceName 属性

这是 RabbitMQ 集群所必需的,正如 Kubernetes 文档中所述,必须在 StatefulSet 之前创建它。

RabbitMQ 使用端口 4369 进行节点发现,使用端口 25672 进行节点间通信。由于此服务在内部使用,无需向外暴露,因此我们创建一个 Headless Service (无头服务)。它可以在 headless-service.yaml 示例文件中找到。

如果参考示例,请运行以下命令为节点间和 CLI 工具流量创建 Headless Service。

kubectl apply -f rabbitmq-headless.yaml

现在可以在 test-rabbitmq 命名空间中观察到该服务。

kubectl get all
# => NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
# => service/rabbitmq-headless ClusterIP None <none> 4369/TCP 7s

为节点数据使用持久卷 (Persistent Volume)

为了使 RabbitMQ 节点在 Pod 重启后保留数据,节点的数据目录必须使用持久化存储。必须为每个 RabbitMQ Pod 挂载一个 Persistent Volume (持久卷)

如果使用临时卷来支持 RabbitMQ 节点,节点在重启时将丢失其标识和所有本地数据。这包括模式 (schema)持久化队列数据。在每次节点重启时同步所有这些数据将非常低效。如果在此期间发生法定人数 (quorum) 丢失,也会导致数据丢失。

在我们的 statefulset.yaml 示例中,我们创建了一个 Persistent Volume Claim (PVC) 来分配一个 Persistent Volume。

Persistent Volume 挂载在 /var/lib/rabbitmq/mnesia。此路径用于 RABBITMQ_MNESIA_BASE 位置:即节点所有持久化数据的基目录。

有关 RabbitMQ 默认文件路径的描述,可以在 RabbitMQ 文档中找到。

如果需要,可以使用 RABBITMQ_MNESIA_BASE 变量更改节点的数据目录基路径。请确保在更新后的路径上挂载持久卷。

RabbitMQ 节点和 CLI 工具使用一个名为 Erlang Cookie 的共享密钥进行相互身份验证。Cookie 值是一个长度不超过 255 个字符的字母数字字符串。该值必须在创建 RabbitMQ 集群之前生成,因为节点需要使用它来形成集群

使用社区版 Docker 镜像时,RabbitMQ 节点期望 Cookie 位于 /var/lib/rabbitmq/.erlang.cookie。我们建议创建一个 Secret 并将其作为卷挂载到 Pod 的此路径上。

这在 statefulset.yaml 示例文件中进行了演示。

Secret 必须包含以下键/值对:

cookie: {value}

要创建 Cookie Secret,请运行:

echo -n "this secret value is JUST AN EXAMPLE. Replace it!" > cookie
kubectl create secret generic erlang-cookie --from-file=./cookie

这将创建一个带有一个 cookie 键的 Secret,键名取自文件名,值为文件内容。

管理员凭据

RabbitMQ 在首次启动时会播种一个具有已知凭据的默认用户。该用户的用户名和密码均为 guest

默认情况下,该用户只能从 localhost 连接。通过选择加入,可以解除此限制。这可能对测试有用,但非常不安全。相反,必须使用生成的凭据创建一个管理用户。

管理用户凭据应存储在 Kubernetes Secret 中,并挂载到 RabbitMQ Pod 上。然后,可以将 RABBITMQ_DEFAULT_USERRABBITMQ_DEFAULT_PASS 环境变量设置为 Secret 的值。社区版 Docker 镜像将使用它们来覆盖默认用户凭据

参考示例:.

Secret 必须包含以下键/值对:

user: {username}
pass: {password}

要创建管理用户 Secret,请使用:

# this is merely an example, you are welcome to use a different username
echo -n "administrator" > user
# this is merely an example, you MUST use a different, generated password value!
echo -n "g3N3rAtED-Pa$$w0rd" > pass
kubectl create secret generic rabbitmq-admin --from-file=./user --from-file=./pass

这将创建一个带有两个键 userpass 的 Secret,键名取自文件名,值分别为对应的文件内容。

用户也可以使用 CLI 工具明确创建。请参阅 RabbitMQ 用户管理文档部分了解更多信息。

节点配置

配置 RabbitMQ 节点有多种方法。推荐的方法是使用配置文件。

配置文件可以表示为 ConfigMaps,并作为卷挂载到 RabbitMQ Pod 上。

要创建带有 RabbitMQ 配置的 ConfigMap,请应用我们的 最小化 configmap.yaml 示例

kubectl apply -f configmap.yaml

使用初始化容器 (Init Container)

自 Kubernetes 1.9.4 起,ConfigMaps 作为只读卷挂载到 Pod 上。这对 RabbitMQ 社区版 Docker 镜像来说是个问题:镜像可能会在容器启动时尝试更新配置文件。

因此,RabbitMQ 配置挂载的路径必须是可读写的。如果 Docker 镜像检测到只读文件,您将看到以下警告:

touch: cannot touch '/etc/rabbitmq/rabbitmq.conf': Permission denied

WARNING: '/etc/rabbitmq/rabbitmq.conf' is not writable, but environment variables have been provided which request that we write to it
We have copied it to '/tmp/rabbitmq.conf' so it can be amended to work around the problem, but it is recommended that the read-only
source file should be modified and the environment variables removed instead.

虽然 Docker 镜像确实解决了这个问题,但在 /tmp 中存储配置文件并不理想,我们建议将挂载路径设置为可读写。

与 Kubernetes 社区中的其他一些项目一样,我们使用 初始化容器 (init container) 来克服这个问题。

示例

rabbitmq 用户身份运行 Pod

Docker 镜像 以 uid 999 的 rabbitmq 用户身份运行,并写入 rabbitmq.conf 文件。因此,rabbitmq.conf 的文件权限必须允许这样做。可以在 StatefulSet 定义中添加 Pod Security Context (Pod 安全上下文) 来实现此目的。在安全上下文中,将 runAsUserrunAsGroupfsGroup 设置为 999。

请参阅 StatefulSet 定义文件中的 Security Context (安全上下文)

导入定义

RabbitMQ 节点可以导入从另一个 RabbitMQ 集群导出的定义。这也可以在节点启动时完成。

参考 RabbitMQ 文档,这可以通过以下步骤完成:

  1. 从您希望复制的 RabbitMQ 集群导出定义并保存文件。
  2. 创建一个 ConfigMap,键为文件名,值为文件内容(请参阅 rabbitmq.conf ConfigMap 示例)。
  3. 在 StatefulSet 定义中,将 ConfigMap 作为卷挂载到 RabbitMQ Pod 上。
  4. 使用 load_definitions = /path/to/definitions/file 更新 rabbitmq.conf ConfigMap。

就绪探针 (Readiness Probe)

Kubernetes 使用一种称为 就绪探针 (readiness probe) 的检查来确定 Pod 是否准备好处理客户端流量。这实际上是由系统运维人员定义的专门的健康检查

当使用有序 Pod 部署策略时——这也是 RabbitMQ 集群的推荐选项——探针会控制 Kubernetes 控制器何时认为当前部署的 Pod 已准备就绪并继续部署下一个。如果此检查选择不当,可能会导致集群节点滚动重启陷入死锁。

属于集群的 RabbitMQ 节点在启动时会尝试从其对等节点同步模式。如果在可配置的时间窗口(默认 5 分钟)内没有对等节点联机,节点将放弃并自动停止。在同步完成之前,节点不会将自己标记为完全启动。

因此,如果就绪探针假设节点已完全启动并运行,那么使用此类探针进行 RabbitMQ 节点 Pod 的滚动重启将会死锁:探针将永远无法成功,因此永远不会继续部署下一个 Pod,而下一个 Pod 的启动是原 Pod 被部署机制视为就绪的前提。

因此,建议将非常基本的 RabbitMQ 健康检查用于就绪探针:

rabbitmq-diagnostics ping

虽然此检查不够全面,但它允许所有 Pod 在一定时间内启动并重新加入集群,即使是在 Pod 被逐一有序重启时也是如此。

这在 RabbitMQ 集群指南的专门章节中有所介绍:重启和健康检查(就绪探针)

StatefulSet 定义文件中的 readiness probe 部分演示了如何配置就绪探针。

存活探针 (Liveness Probe)

与上面描述的就绪探针类似,Kubernetes 允许使用另一种称为 存活探针 (liveness probe) 的健康检查来执行 Pod 健康检查。此检查确定 Pod 是否必须重启。

与所有健康检查一样,没有一种单一的解决方案可以推荐给所有部署。健康检查可能会产生误报,这意味着原本健康、可操作的节点可能会无缘无故地被重启、甚至被销毁重建,从而降低系统可用性。

此外,RabbitMQ 节点重启不一定能解决问题。例如,重启一个因磁盘空间不足而处于告警状态的节点是无济于事的。

所有这些都是说,存活探针的选择必须明智,并考虑到误报和“重启恢复”的可能性。存活探针也必须使用节点本地的健康检查,而不是集群范围的检查

RabbitMQ CLI 工具提供了一系列预定义的健康检查,它们在全面性、侵入性以及在不同场景(例如系统负载高时)下产生误报的可能性方面各不相同。这些检查是可组合的,可以结合使用。正确的存活探针选择是特定于系统的决策。如有疑问,请从更简单、更少侵入性且不太全面的选项开始,例如:

rabbitmq-diagnostics -q ping

以下检查可以是合理的存活探针候选方案:

rabbitmq-diagnostics -q check_port_connectivity
rabbitmq-diagnostics -q check_local_alarms

但请注意,对于被“暂停少数” (pause minority) 分区处理策略暂停的节点,这些检查将会失败。

StatefulSet 定义文件中的 liveness probe 部分演示了如何配置存活探针。

插件

RabbitMQ 支持插件。在 Kubernetes 上运行 RabbitMQ 时,某些插件是必不可少的,例如特定的 Kubernetes 节点发现实现。

rabbitmq_peer_discovery_k8s 插件是部署 Kubernetes 上 RabbitMQ 的必备插件。通常还会启用 rabbitmq_management 插件以获得基于浏览器的管理 UI 和 HTTP API,以及 rabbitmq_prometheus 以进行监控。

插件可以通过多种方式启用。我们建议将插件文件 enabled_plugins 挂载到节点配置目录 /etc/rabbitmq。可以使用 ConfigMap 来表示 enabled_plugins 文件的内容,然后将其作为卷挂载到 StatefulSet 定义中的每个 RabbitMQ 容器上。

在我们的 configmap.yaml 示例文件中,我们演示了如何填充 enabled_plugins 文件并将其挂载到 /etc/rabbitmq 目录下。

端口

StatefulSet 的最终考虑因素是需要在 RabbitMQ Pod 上开放的端口。RabbitMQ 支持的所有协议均基于 TCP,并且要求在 RabbitMQ 节点上打开协议端口。根据节点上启用的插件,所需端口列表可能会有所不同。

上述示例 enabled_plugins 文件启用了几个插件:rabbitmq_peer_discovery_k8s(强制)、rabbitmq_managementrabbitmq_prometheus。因此,服务必须开放几个端口,这些端口与核心服务器和启用的插件相关:

  • 5672:供 AMQP 0-9-1 和 AMQP 1.0 客户端使用
  • 15672:管理 UI 和 HTTP API
  • 15692:Prometheus 指标抓取端点

部署 StatefulSet

这些是 StatefulSet 文件中的关键组件。请查看该文件,如果参考示例,请部署 StatefulSet:

kubectl apply -f statefulset.yaml

这将开始启动一个 RabbitMQ 集群。要查看进度,请运行:

watch kubectl get all
# => NAME READY STATUS RESTARTS AGE
# => pod/rabbitmq-0 0/1 Pending 0 8s
# =>
# => NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
# => service/rabbitmq-headless ClusterIP None <none> 4369/TCP 61m
# =>
# => NAME READY AGE
# => statefulset.apps/rabbitmq 0/1 8s

为客户端连接创建服务

如果以上所有步骤都成功,您应该已经在 Kubernetes 上成功部署了一个正常运行的 RabbitMQ 集群!?然而,在 Kubernetes 上拥有一个 RabbitMQ 集群只有在客户端能够连接到它时才有用。

现在是时候创建一个服务,使集群能够被客户端连接访问了。

服务的类型取决于您的用例。Kubernetes API 参考对可用的服务类型做了很好的概述。

client-service.yaml 示例文件中,我们使用了 LoadBalancer 服务。这为我们提供了一个可用于访问 RabbitMQ 集群的外部 IP。

例如,通过访问 {external-ip}:15672 并登录,应该可以访问 RabbitMQ 管理 UI。客户端应用程序可以连接到诸如 {external-ip}:5672 (AMQP 0-9-1, AMQP 1.0) 或 {external-ip}:1883 (MQTT) 的端点。请参阅入门指南以了解如何使用 RabbitMQ。

如果参考示例,请运行:

kubectl apply -f client-service.yaml

来创建一个带有外部 IP 地址的 LoadBalancer 类型服务。要查找外部 IP 地址,请使用 kubectl get svc

kubectl get svc
# => NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
# => service/rabbitmq-client LoadBalancer 10.59.244.70 34.105.135.216 15672:30902/TCP,15692:30605/TCP,5672:31210/TCP 2m19s

资源使用和限制

容器资源管理是一个值得单独撰写的课题。容量规划建议完全取决于具体的工作负载、环境和系统。最佳值通常是通过对系统进行广泛的监控以及反复试验得出的。然而,在选择限制和资源分配设置时,请考虑一些 RabbitMQ 特有的事项。

使用最新的 Erlang 主版本

RabbitMQ 运行在 Erlang 运行时之上。最近的 Erlang/OTP 版本引入了一些改进,对于在 Kubernetes 上运行 RabbitMQ 的用户而言非常重要:

  • 在 Erlang 22 中,节点间通信的延迟和队头阻塞问题得到了显著降低。在早期版本中,链路拥塞通常会导致集群节点心跳出现误报。
  • 在 Erlang 23 中,运行时在计算要启动的默认调度程序数量时,会尊重容器 CPU 配额。这意味着节点将遵循 Kubernetes 管理的 CPU 资源限制。

在撰写本文时,RabbitMQ 的 Docker 社区版镜像附带了 Erlang 23。强烈建议自定义 Docker 镜像的用户也配置 Erlang 23。

CPU 资源使用

RabbitMQ 专为涉及多个队列且节点同时服务多个客户端的工作负载而设计。在没有任何显式配置的情况下,节点通常会使用所有允许的 CPU 核心。随着核心数量的增加,可能需要进行一些调整以减少 CPU 上下文切换

CPU 时间的使用方式可以通过运行时线程活动指标进行监控,这些指标也通过 RabbitMQ Prometheus 插件暴露。

如果 RabbitMQ Pod 在拥有大量相对空闲客户端的环境中徘徊在 CPU 资源配额附近并遇到节流 (throttling),负载通常可以通过适度的配置调整来降低。

内存限制

RabbitMQ 使用运行时内存高水位线 (high watermark) 的概念。默认情况下,节点会将检测到(可用)内存的 40% 作为水位线。当超过水位线时,整个集群的发布者将被阻塞,并启动更积极的向磁盘分页。水位线值起初看起来像是 Kubernetes 上的内存配额,但有一个重要区别:RabbitMQ 资源告警假设节点通常可以从此状态中恢复。例如,积压的消息最终会被消费掉。

Kubernetes 内存限制是由 OOM killer (内存溢出杀手) 强制执行的:预期不会发生恢复。这意味着 RabbitMQ 节点的高内存水位线必须低于节点容器上强加的内存限制。Kubernetes 部署应在建议范围内使用相对水位线值。

应使用 内存使用分解数据来确定节点上什么占用了最多的内存。

磁盘使用

我们强烈建议超额配置提供给 RabbitMQ 容器的磁盘空间。磁盘空间耗尽的节点并不总能从此类事件中恢复。此类节点必须被停用并更换。

最后,考虑用于节点间通信的链路类型和 Kubernetes 网络选项。网络链路拥塞可能是限制系统吞吐量并影响其可用性的重要因素。

以下是一个非常简单的计算工作负载所需带宽(以位为单位)的公式:

# peak message rate * bits per message * 110% to account for metadata and protocol framing
PeakMessageRate * AverageMessagePayloadSizeInBytes * 8 * 1.1

因此,一个平均消息大小为 3 kiB,预期峰值消息速率为每秒 20K 条消息的工作负载,可能消耗高达:

3 kiB * 20000/second * 8 * 1.1 = 528 megabits/second

的带宽。

RabbitMQ 团队维护着一个用于节点间通信链路指标的 Grafana 仪表板

使用 rabbitmq-perf-test 对集群进行功能和负载测试

RabbitMQ 附带了一个负载模拟工具 PerfTest,它可以在集群外部运行,或使用公共 PerfTest Docker 镜像部署到 Kubernetes。以下是如何将该镜像部署到 Kubernetes 集群的示例:

kubectl run perf-test --image=pivotalrabbitmq/perf-test -- --uri amqp://{username}:{password}@{service}

这里,{username}{password} 是用户凭据,例如在 rabbitmq-admin Secret 中设置的凭据。{service} 是要连接的主机名。我们使用部署时解析为主机名的客户端服务的名称。

上面的 kubectl run 命令将启动一个 PerfTest Pod,可以在以下位置观察到:

kubectl get pods

对于正常运行的 RabbitMQ 集群,运行 kubectl logs -f {perf-test-pod-name}(其中 {perf-test-pod-name}kubectl get pods 报告的 Pod 名称),将产生类似于此的输出:

id: test-110102-976, time: 263.100s, sent: 21098 msg/s, received: 21425 msg/s, min/median/75th/95th/99th consumer latency: 1481452/1600817/1636996/1674410/1682972 ?s
id: test-110102-976, time: 264.100s, sent: 17314 msg/s, received: 17856 msg/s, min/median/75th/95th/99th consumer latency: 1509657/1600942/1636253/1695525/1718537 ?s
id: test-110102-976, time: 265.100s, sent: 18597 msg/s, received: 17707 msg/s, min/median/75th/95th/99th consumer latency: 1573151/1716519/1756060/1813985/1846490 ?s

要了解有关 PerfTest 及其设置、功能和输出的更多信息,请参阅 PerfTest 文档指南

PerfTest 不打算永久运行。要拆除 perf-test Pod,请使用:

kubectl delete pod perf-test

监控集群

监控是任何生产部署中至关重要的一部分。

RabbitMQ 带有内置的 Prometheus 支持。要启用它,请启用 rabbitmq_prometheus 插件。这可以通过如上所述将 rabbitmq_prometheus 添加到 enabled_plugins ConfigMap 中来实现。

Prometheus 抓取端口 15972 必须在 Pod 和客户端服务上同时开放。

节点和集群指标可以通过 Grafana 可视化

替代方案:用于 RabbitMQ 的 Kubernetes 集群操作员

正如本文所示,在 Kubernetes 上托管 RabbitMQ 等有状态数据服务涉及相当多的部分。这看起来可能是一项艰巨的任务。本文演示的这种 DIY 部署方式有几种替代方案。

VMware 的 RabbitMQ 团队开源了一个用于 RabbitMQ 的 Kubernetes Operator 模式实现。截至 2020 年 8 月,这是一个处于积极开发阶段的年轻项目。虽然它目前有局限性,但它是我们推荐的方案,优于本文演示的手动 DIY 设置。

请参阅 RabbitMQ Cluster Operator for Kubernetes 以了解更多信息。该项目在 GitHub 上以开源方式开发:rabbitmq/cluster-operator。试用一下,并告诉我们感觉如何。除了 GitHub,向 Operator 幕后团队提供反馈的两个绝佳场所是 RabbitMQ 邮件列表RabbitMQ 社区 Slack 中的 #kubernetes 频道

© . This site is unofficial and not affiliated with VMware.