K8S Pod 如何实现 Service 切流

2024-01-16  本文已影响0人  _晓__

前言

在云原生环境中,Pod 是部署应用程序的基本单位,而 Service 将符合指定条件的 Pod 作为可通过网络访问的服务提供给服务调用者。在某些情况下,可能需要将某个 Pod 的流量从 Service 中切走,使其不再接收 Service 的请求。这可能是因为 Pod 出现故障需要保留现场人工排查。

目标

使能够灵活地将 Pod 的流量从 Service 中切走,同时确保在切走流量时,其他 Pod 仍然能够正常工作,并且对用户请求的影响最小化。

概述

  1. 基本概念和原理
    在 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. 实现流程
    (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

具体实现步骤

  1. 准备阶段
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");
}
  1. 验证流量切分
# 到 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);
  1. 处理切流的 Pod
    修改了 Label 的 Pod 则脱离了 Deployment 的管理,也无法通过 Deployment 来查看,该 Pod 则无法自动销毁,只能人工销毁。

总结和注意事项

本方案提供了一种将 Pod 的流量从 Service 中切走的方法。通过修改 Pod Label,可以灵活地控制流量的分发,确保在进行维护或升级时,对用户请求的影响最小化。在实际应用中,需要根据具体业务需求和环境进行适当的调整和测试,以确保方案的可行性和稳定性。

上一篇 下一篇

猜你喜欢

热点阅读