前言
在云原生环境中,Pod 是部署应用程序的基本单位,而 Service 将符合指定条件的 Pod 作为可通过网络访问的服务提供给服务调用者。在某些情况下,可能需要将某个 Pod 的流量从 Service 中切走,使其不再接收 Service 的请求。这可能是因为 Pod 出现故障需要保留现场人工排查。
目标
使能够灵活地将 Pod 的流量从 Service 中切走,同时确保在切走流量时,其他 Pod 仍然能够正常工作,并且对用户请求的影响最小化。
概述
- 基本概念和原理
在 Kubernetes 中,Service 通过 Label 选择器来将请求流量分发到对应的 Pod 上。因此,要切走某个 Pod 的流量,可以通过修改 Label 选择器来实现。我们可以为需要切走的 Pod Label 进行修改,使其 Service Label 选择器不再包含该 Pod 的 Label。
# Service 通过 Label 选择器绑定 Deployment
---
apiVersion: v1
kind: Service
metadata:
name: test
spec:
ports:
- name: port-xxxx
nodePort: xxxxx
port: xxxx
protocol: TCP
targetPort: xxxx
selector:
app: test
type: NodePort
# Deployment Label
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: test
spec:
selector:
matchLabels:
app: test # 与 Pod Label一致
template:
metadata:
labels:
app: test # 与 Service selector 一致
# Pod Label
---
apiVersion: v1
kind: Pod
metadata:
generateName: test-8495787574-
labels:
app: test
name: test-8495787574-rssvq
- 实现流程
(1)准备阶段:确定需要切走流量的 Pod,并修改成特定 Label,如原 Label:app=test,修改后 Label:app=test-offline。
(2)修改 Pod 的 Label 后,Deployment 会检测发现同 Label 实例与副本数实例不一致,通过扩容将 Pod 个数与副本数一致。
(3)验证流量切分:通过 Service 调用时,使用工具或监控系统验证流量是否成功切分,确保其他 Pod 能够正常接收请求和被切流的 Pod 不再接收请求。
(4)恢复流量:在需要恢复流量时,将切走流量的 Pod 的 Label 恢复为原始状态,Deployment 会检测发现同 Label 实例与副本数实例不一致,通过缩容将 Pod 个数与副本数一致。
(5)再次验证流量:确认流量是否成功恢复,确保切走流量的 Pod 重新接收请求。
针对于第4、5点,实际情况如排查完问题,没必要恢复该 Pod 流量,而是直接销毁该 Pod,因为第2点已将切流的 Pod 个数扩容了回来,所以无须保留切流的 Pod
具体实现步骤
- 准备阶段
- 确定需要切走流量的 Pod:确定需要切走流量的 Pod 的名称或标识。
- 修改 Pod Label:使用 K8S API 进行修改
public V1Pod patchPodLabel(String namespace, String name, String key, String value) throws ApiException {
CoreV1Api api = new CoreV1Api(client);
V1Patch v1Patch = new V1Patch(JSONUtil.toJsonStr(CollUtil.newArrayList(new PatchPodLabelsParam(key, value))));
return api.patchNamespacedPod(name, namespace, v1Patch, null, null, null, null);
}
@Getter
public static class PatchPodLabelsParam {
private final String op = "replace";
private final String path;
private final String value;
public PatchPodLabelsParam(String key, String value) {
this.path = StrFormatter.format("/metadata/labels/{}", key);
this.value = value;
}
}
@Test
public void patchPodLabel() throws ApiException {
Kube kube = Kube.init(dev_KubeConfig);
kube.patchPodLabel("default","test-8495787574-nz58d", "app", "test-offline");
}
- 验证流量切分
# 到 Pod 中执行命令安装 arthas
curl -O https://arthas.aliyun.com/arthas-boot.jar
java -jar arthas-boot.jar
# 通过 watch 监控方法是否被调用
watch 包名.类名 方法名 '{params,returnObj,throwExp}'
// 模拟通过 NodePort 进行调用
for (int i = 0; i < 100; i++) {
CompletableFuture.runAsync(() -> HttpUtil.get("http://测试接口"));
}
ThreadUtil.sleep(10000);
- 处理切流的 Pod
修改了 Label 的 Pod 则脱离了 Deployment 的管理,也无法通过 Deployment 来查看,该 Pod 则无法自动销毁,只能人工销毁。
总结和注意事项
本方案提供了一种将 Pod 的流量从 Service 中切走的方法。通过修改 Pod Label,可以灵活地控制流量的分发,确保在进行维护或升级时,对用户请求的影响最小化。在实际应用中,需要根据具体业务需求和环境进行适当的调整和测试,以确保方案的可行性和稳定性。