☰
Armeria Kubernetes 混沌测试实战:用 Chaos Mesh 验证 SharedInformer 的 WebSocket 恢复能力
2026/10/13 9:54:12 网站建设 项目流程

【免费下载链接】armeria

Your go-to microservice framework for any situation, from the creator of Netty et al. You can build any type of microservice leveraging your favorite technologies, including gRPC, Thrift, Kotlin, Retrofit, Reactive Streams, Spring Boot and Dropwizard.

项目地址:https://gitcode.com/gh_mirrors/ar/armeria
点击查看免费下载

本文介绍 Armeria 仓库中 it/kubernetes-chaos-tests 模块的完整用法:它基于 Chaos Mesh 对 Kubernetes Client 的 SharedInformer 发起网络延迟、丢包、重复三类自动化混沌测试。读完后,你将掌握 minikube + Chaos Mesh 环境的搭建、checker/control 两个测试应用的运行原理、三种网络故障场景的配置,以及 SharedInformer 底层 WebSocket 通信在故障恢复上的源码级实现证据。

模块背景:从 fabric8 混沌测试 Fork 而来

该模块 fork 自 fabric8io/kubernetes-client 的 chaos-tests,并修改为运行在 Armeria 提供的 Kubernetes Client 之上(即 ArmeriaHttpClient 这条实现路径)。模块的定位很明确:

  • 测试对象:Kubernetes Client 的SharedInformer,它底层依赖 WebSocket(watch 长连接)与 API Server 保持资源变更通知;
  • 测试手段:Chaos Mesh 的NetworkChaos实验,对 informer 所在的 Pod 施加网络故障;
  • 验收目标:故障持续约 12 分钟后,informer 能够自行恢复,最终追平计数器目标值 N,两个 Pod 都进入Succeeded状态。

README 中还明确说明,这套测试的价值是保证“没有回归”,并且可以作为其他 HTTP client 实现(如 OkHttp、JDK HttpClient 等)接入 Armeria Kubernetes Client 时的验证门槛。

在构建体系上,该模块通过 settings.gradle 中的includeWithFlags ':it:kubernetes-chaos-tests', 'java11', 'relocate'注册,要求 Java 11 及以上,且会对依赖做 relocate 处理以避免类路径冲突。

环境搭建:minikube + Chaos Mesh

启动 minikube

按照 README 的说明,先以 docker 驱动启动一个 8GB 内存、3 CPU 的 minikube:

minikube start --driver=docker --memory 8192 --cpus 3

安装 Chaos Mesh

curl -sSL https://mirrors.chaos-mesh.org/v2.6.0/install.sh | bash

然后等待chaos-mesh命名空间下的所有 Pod 就绪:

kubectl wait --for=condition=Ready pods -n chaos-mesh --all --timeout=600s

这里需要注意:README 中注明了“如果本地没有安装 ChaosMesh,测试不会执行”。这一行为由 ChaosMeshAvailableCondition 实现——它通过@EnabledIf注解挂载在 ChaosIT 上,检查chaos-mesh命名空间是否存在且状态为Active:

static boolean isRunning() { try (KubernetesClient client = new KubernetesClientBuilder().build()) { assertThat(client.getHttpClient().getClass().getSimpleName()).isEqualTo("ArmeriaHttpClient"); final Namespace namespace = client.namespaces().withName("chaos-mesh").get(); return "Active".equals(namespace.getStatus().getPhase()); } catch (Exception cause) { logger.trace("Chaos Mesh is not running in Kubernetes", cause); return false; } }

一个细节值得注意:该方法同时还断言了 HTTP client 实现类名必须是ArmeriaHttpClient。也就是说,整个混沌测试本身就运行在被测的 Armeria 客户端之上——客户端构建失败或 SPI 未生效时,测试会在条件检查阶段直接暴露出来。

测试双角色:Control 与 Checker

README 的术语表给出了两个核心角色:

  • checker:运行 SharedInformer,持续接收共享资源(ConfigMap)的变更通知;
  • control:按时序对共享资源(同一个 ConfigMap)做出变更。

Control:递增计数器的“写端”

ControlCommand 是一个基于 picocli 的命令行应用,支持以下参数:

参数默认值说明
--num10达到该数值后正常退出
--namespacedefaultConfigMap 所在的命名空间
--labelkeychaos用于匹配的标签键
--labelvaluetest用于匹配的标签值
--delay1000每次递增之间的延迟(毫秒)

其核心流程(run()方法,ControlCommand.java#L82-L141)为:

  1. 构建一个名为chaos-test、标签为chaos=test、数据为{"counter": "0"}的 ConfigMap;
  2. 若该 ConfigMap 已存在则先删除,再重新创建;
  3. 循环中每隔delay毫秒读取一次当前值,将其+1后createOrReplace()写回,直到达到--num指定的目标值 N;
  4. 达到 N 后额外等待一个delay,然后删除 ConfigMap(这次删除正是用来触发 checker 的onDelete,宣告实验成功)。

Checker:监听 ConfigMap 的“读端”

CheckerCommand 支持--num、--namespace、--labelkey、--labelvalue四个参数(默认值与上表一致)。它在 CheckerCommand.java#L140-L150 注册 informer:

SharedIndexInformer<ConfigMap> configMapInformer = client.configMaps().inNamespace(namespace) .withLabel(labelKey, labelValue).inform(handler); configMapInformer.start(); try { latch.await(30, TimeUnit.MINUTES); } catch (InterruptedException e) { throw new RuntimeException(e); }

它注册了一个ResourceEventHandler<ConfigMap>,对事件顺序有严格的正确性校验(CheckerCommand.java#L90-L133):

  • onAdd:首个事件必须是计数器为 0 的初始 ConfigMap,否则直接抛异常;
  • onUpdate:计数器必须严格递增(compareAndSet(currentValue - 1, currentValue));若收到的值比当前值大但不是紧接的下一个值,则视为“乱序但兼容”,记录日志并同步;若收到更小的值则判定为乱序错误;
  • onDelete:只有当计数器恰好等于 N 时才允许 latch 释放、以退出码 0 结束;否则视为实验失败。

这套校验逻辑是测试的判定核心:它验证的不只是“最终收到通知”,而是“在长达 20 分钟、中途叠加 12 分钟网络故障的前提下,SharedInformer 的通知序列仍然完整且有序”。

测试主流程:20 分钟计数 + 12 分钟网络故障

ChaosIT 是整个测试的编排者。README 中描述的“主要流程”与源码一一对应:

README 描述源码依据
Checker 作为 Pod 启动,对 ConfigMap 运行 SharedInformerrun(CHECKER, checkerImage)+ CheckerCommand
Control 作为 Pod 启动,持续递增计数器run(CONTROL, controlImage)+ ControlCommand
Checker 等待数值达到 NTOTAL_COUNT通过JAVA_ARGS注入为--num
针对 Checker 启动混沌实验,12 分钟network-delay.yaml 中duration: '12m'
实验结束后 informer 恢复,两个 Pod 最终Succeededawait()轮询断言两个 Pod 的phase == "Succeeded",上限 30 分钟

几个关键常量与配置(ChaosIT.java#L75-L93):

private static final String GROUP = "fabric8-chaos-tests"; private static final int TOTAL_COUNT = 1 * 60 * 20; // 1 count each second, for 20 minutes
  • 计数目标 N = 1200:Control 每秒递增一次,全程约 20 分钟;其中 ChaosMesh 实验占 12 分钟;
  • 测试方法级超时为 30 分钟(@Timeout(value = 30, unit = TimeUnit.MINUTES)),比预期运行时长多出 10 分钟余量;
  • 三个环境变量可覆盖默认值:
环境变量默认值用途
CHECKER_IMAGEchaos-test-checker:latestchecker Pod 使用的镜像
CONTROL_IMAGEchaos-test-control:latestcontrol Pod 使用的镜像
CHAOS_TESTnetwork-delay.yaml要加载的 Chaos Mesh 实验场景文件

编排细节:清理、启动、故障注入、等待

@BeforeEach(ChaosIT.java#L84-L94)在启动 Pod 前会先清理残留的chaos-testConfigMap 和带有group=fabric8-chaos-tests标签的旧 Pod,避免上一轮实验的脏数据干扰判定。

Pod 通过serverSideApply创建(ChaosIT.java#L144-L168),每个 Pod 的要点是:

  • 标签app=<name>、group=fabric8-chaos-tests——Chaos Mesh 的 selector 正是通过app: checker定位故障目标;
  • imagePullPolicy: Never——镜像必须在集群内预先构建好(见下文 k8sBuild);
  • JAVA_ARGS环境变量注入--num 1200 --namespace <ns>;
  • restartPolicy: Never——任何一次进程异常退出都会直接表现为 Pod 失败,保证测试不会“悄悄重试通过”;
  • 分别绑定chaos-test-checker-sa/chaos-test-control-sa服务账号。

启动顺序上,测试先启动 checker 并等待其进入Running;再启动 control,并轮询 checker 日志直到出现Update received, and it's in the correct order, counter: 1——这一步确认 informer 的初始 list+watch 已经建立、事件流正常之后,才加载混沌实验:

try (InputStream is = this.getClass().getResourceAsStream(chaosTest)) { client.load(is).inNamespace(client.getNamespace()).serverSideApply(); }

最后用 Awaitility 每 10 秒轮询一次(上限 30 分钟),断言两个 Pod 的phase都变为Succeeded。@AfterEach则会把 checker 与 control 的完整日志打印到测试输出,并删除本轮所有 Pod,方便失败时排查事件序列。

三种网络故障场景

模块内置了三个 Chaos MeshNetworkChaos场景,均作用于app: checker标签的 Pod、持续 12 分钟(duration: '12m')。默认执行的是第一个,其余两个可通过CHAOS_TEST环境变量切换。

1. 网络延迟(network-delay.yaml)

network-delay.yaml:

apiVersion: chaos-mesh.org/v1alpha1 kind: NetworkChaos metadata: name: network-delay spec: action: delay mode: all selector: labelSelectors: 'app': 'checker' delay: latency: '60s' duration: '12m'

对 checker 的所有流量注入 60 秒固定延迟,模拟高延迟网络下 watch 连接的心跳/重连压力。

2. 网络丢包(network-loss.yaml)

network-loss.yaml:

spec: action: loss mode: all selector: labelSelectors: 'app': 'checker' loss: loss: '100' correlation: '50' duration: '12m'

loss: '100'表示 100% 丢包,correlation: '50'表示 50% 的丢包事件相互关联(成簇出现),模拟整段链路中断。

3. 报文重复(network-duplicate.yaml)

network-duplicate.yaml:

spec: action: duplicate mode: all selector: labelSelectors: 'app': 'checker' duplicate: duplicate: '100' correlation: '50' duration: '12m'

100% 的报文会被复制一份(50% 关联),模拟重复送达。这个场景对 informer 尤其有挑战性——watch 收到重复资源更新后,客户端需要正确地去重而不是把计数器“加乱”。Checker 中的顺序校验(compareAndSet)正是针对这类异常设计的判定器。

基础设施清单:ServiceAccount 与 RBAC

@LoadKubernetesManifests({ "checker-infra.yaml", "control-infra.yaml" })会在测试开始前加载两份基础设施清单,分别位于 checker-infra.yaml 和 control-infra.yaml。两份结构相同,各包含:

  • ServiceAccount:chaos-test-checker-sa/chaos-test-control-sa,与 Pod 绑定使用;
  • Role:configMaps-checker-reader/configMaps-control-reader,授予configmaps资源的get、list、watch、patch、update、create、delete、post权限——watch 权限正是 SharedInformer 建立长连接所必需的;
  • RoleBinding:将 Role 绑定到对应 ServiceAccount。

构建镜像与执行测试

README 给出的最终执行命令是:

eval $(minikube -p minikube docker-env) ./gradlew :it:kubernetes-chaos-tests:k8sBuild :it:kubernetes-chaos-tests:test

eval $(minikube docker-env)的作用是把当前 shell 的 docker 命令重定向到 minikube 内部 docker daemon,这样构建出的镜像直接进入集群可访问的镜像存储,与 Pod 中imagePullPolicy: Never的要求配套。

镜像构建逻辑在 build.gradle 中,使用 JKube 插件:

def imageNameAndMainClasses= [ 'chaos-test-checker:latest': 'com.linecorp.armeria.kubernetes.it.CheckerCommand', 'chaos-test-control:latest': 'com.linecorp.armeria.kubernetes.it.ControlCommand' ] kubernetes { images { imageNameAndMainClasses.each { imageName, mainClass -> image { name = imageName build { from = 'quay.io/jkube/jkube-java:0.0.19' assembly { targetDir = '/deployments' ... } env { JAVA_LIB_DIR = '/deployments/*' JAVA_MAIN_CLASS = mainClass } } } } } } tasks.k8sBuild.dependsOn(tasks.copyDependencies)

要点:

  • 两个镜像共用基础镜像quay.io/jkube/jkube-java:0.0.19,以 jar 与运行时依赖拷贝到/deployments目录,通过JAVA_MAIN_CLASS环境变量区分入口类;
  • copyDependencies任务把模块 jar 与runtimeClasspath全部拷贝进build/dependencies,供镜像 assembly 使用;
  • 模块依赖了:kubernetes子项目(即 Armeria 的 Kubernetes Client 实现)以及picocli(命令行解析)、kubernetes-junit-jupiter(@KubernetesTest支持)。

底层原理:被测试的 WebSocket 长连接到底长什么样

README 的结论是:“我们在测试 SharedInformer 底层 WebSocket 通信的弹性,保证出问题时它能正确恢复。”结合 kubernetes 子项目 的源码,可以把这条链路看得更具体:

  1. 客户端入口:ArmeriaHttpClientFactory 是 fabric8HttpClient.Factory的 Armeria 实现,其 Javadoc 明确它是 “Armeria based implementation of StandardHttpClient”。checker/control 内部new KubernetesClientBuilder().build()时,SPI 机制会选到这个工厂;ChaosMeshAvailableCondition 中的类名断言也正是这一点的运行时验证。
  2. WebSocket 通道:watch 长连接走 ArmeriaHttpClient#buildWebSocketDirect,最终落到ArmeriaWebSocketClient。ArmeriaWebSocket 同时实现了 fabric8 的WebSocket接口和 Reactive Streams 的Subscriber<WebSocketFrame>,它逐帧处理 TEXT/BINARY 消息并回调 listener,遇到 PING/PONG 主动request()补流,收到 CLOSE 帧则回调onClose——informer 在 watch 断开后依赖 fabric8 客户端基于这些回调进行重连与重新 list,这正是混沌测试验证的“恢复”行为。
  3. 背压与流控:send(ByteBuffer)中用pending计数与writer.whenConsumed()跟踪未确认字节,queueSize()暴露给调用方——在 60 秒延迟场景下,这套机制决定了 watch 期间发送行为的稳定性。

从源码结构看,Armeria 客户端的 HTTP 请求与 WebSocket 分别复用 Armeria 的WebClient与WebSocketClient连接池,混沌实验对 checker Pod 注入的故障会同时冲击这两条路径,而测试判定最终收敛到“事件序列完整有序”这一个可观测结果上。

测试判定与失败排查

综合上述机制,一次成功运行的完整判据是:

  1. checker 日志先出现Counter is 0(初始 onAdd 校验通过);
  2. 实验期间日志持续出现Update received, and it's in the correct order, counter: <n>(若出现乱序日志NOT in the correct order but compatible仍算通过,但数值倒退会直接抛异常);
  3. 12 分钟故障窗口后,控制端达到 1200 并删除 ConfigMap,checker 收到onDelete后打印Experiment should successfully end并以退出码 0 结束;
  4. 两个 Pod 的 phase 均为Succeeded,测试方法在 30 分钟超时前结束。

如果测试失败,@AfterEach会无条件打印 checker 与 control 的完整 Pod 日志(ChaosIT.java#L96-L108),排查时应重点看 checker 侧Update received in an incorrect order与 control 侧Counter incremented的对应关系,判断是事件丢失、乱序还是恢复超时。

小结

it/kubernetes-chaos-tests模块用一套 20 分钟的计数实验,把“SharedInformer 在 12 分钟网络延迟/全丢包/报文重复下能否自愈”这件难以口头论证的事变成了可重复执行的自动化测试。它 fork 自 fabric8 的 chaos-tests 并保持与上游代码高度一致(源码注释中明确要求“尽量保留原始代码以便合并上游变更”),同时通过ArmeriaHttpClientFactorySPI 与ChaosMeshAvailableCondition的类名断言,把 Armeria Kubernetes Client 的 WebSocket 实现(ArmeriaWebSocket、ArmeriaHttpClient)放在了真实故障环境里做回归验证。对于计划接入其他 HTTP client 实现或评估 watch 连接弹性的开发者,这套“checker/control + NetworkChaos”的测试范式本身也具有很好的参考价值。

【免费下载链接】armeria

Your go-to microservice framework for any situation, from the creator of Netty et al. You can build any type of microservice leveraging your favorite technologies, including gRPC, Thrift, Kotlin, Retrofit, Reactive Streams, Spring Boot and Dropwizard.

项目地址:https://gitcode.com/gh_mirrors/ar/armeria
点击查看免费下载

相关推荐

上一篇:5步让Apple Silicon Mac免费运行Windows软件:Whisky完整上手指南
下一篇:LibrePhotos:基于 Django 与 ONNX Runtime 的自托管智能照片管理服务完全指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询