地域负载均衡
本文档介绍如何在 Kmesh 中使用 Istio 的地域负载均衡功能。
当前的 Kmesh 地域负载均衡处于 L4 级别,仅支持地域故障转移。
什么是地域负载均衡?
地域定义了网格中工作负载实例的地理位置。服务网格中的地域负载均衡通过根据服务实例的位置智能路由流量,有助于提高服务的可用性和性能。
我们强烈建议您首先阅读 https://istio.io/latest/docs/tasks/traffic-management/locality-load-balancing/ 以了解地域负载均衡的概念。
Kmesh 支持的模式和配置方法
目前,Istio 的 ambient 模式仅支持通过配置特定字段来指定固定的地域负载均衡策略。这包括两种模式:PreferClose 和 Local。
1. PreferClose
一种故障转移模式,使用 NETWORK、REGION、ZONE 和 SUBZONE 作为 routingPreference。
-
使用
spec.trafficDistribution
(k8s >= beta 1.31.0, istio >= 1.23.1)spec:
trafficDistribution: # spec.trafficDistribution
preferClose: true -
使用 annotation
metadata:
annotations:
networking.istio.io/traffic-distribution: PreferClose
2. Local
一种严格模式,仅匹配当前 NODE。
-
spec.internalTrafficPolicy: Local (k8s >= beta 1.24 或 >= 1.26)
spec:
internalTrafficPolicy: Local
实验测试
准备环境
- 参考在 kind 中开发
- 我们在集群中准备了三个节点
- istio >= 1.23.1
- k8s >= 1.31.0
- 确保 sidecar 注入已禁用:
kubectl label namespace default istio-injection-
- 所需镜像:
- docker.io/istio/examples-helloworld-v1
- curlimages/curl
kind create cluster --image=kindest/node:v1.31.0 --config=- <<EOF
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
name: ambient
nodes:
- role: control-plane
- role: worker
- role: worker
- role: worker
EOF
1. 为节点分配地域信息
kubectl label node ambient-worker topology.kubernetes.io/region=region
kubectl label node ambient-worker topology.kubernetes.io/zone=zone1
kubectl label node ambient-worker topology.kubernetes.io/subzone=subzone1
kubectl label node ambient-worker2 topology.kubernetes.io/region=region
kubectl label node ambient-worker2 topology.kubernetes.io/zone=zone1
kubectl label node ambient-worker2 topology.kubernetes.io/subzone=subzone2
kubectl label node ambient-worker3 topology.kubernetes.io/region=region
kubectl label node ambient-worker3 topology.kubernetes.io/zone=zone2
kubectl label node ambient-worker3 topology.kubernetes.io/subzone=subzone3
2. 启动测试服务器
-
创建
sample
命名空间kubectl create namespace sample
-
运行一个服务
kubectl apply -n sample -f - <<EOF
apiVersion: v1
kind: Service
metadata:
name: helloworld
labels:
app: helloworld
service: helloworld
spec:
ports:
- port: 5000
name: http
selector:
app: helloworld
trafficDistribution: PreferClose
EOF -
在 ambient-worker 上启动一个服务实例
kubectl apply -n sample -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: helloworld-region.zone1.subzone1
labels:
app: helloworld
version: region.zone1.subzone1
spec:
replicas: 1
selector:
matchLabels:
app: helloworld
version: region.zone1.subzone1
template:
metadata:
labels:
app: helloworld
version: region.zone1.subzone1
spec:
containers:
- name: helloworld
env:
- name: SERVICE_VERSION
value: region.zone1.subzone1
image: docker.io/istio/examples-helloworld-v1
resources:
requests:
cpu: "100m"
imagePullPolicy: IfNotPresent
ports:
- containerPort: 5000
nodeSelector:
kubernetes.io/hostname: ambient-worker
EOF -
在 ambient-worker2 上启动一个服务实例
kubectl apply -n sample -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: helloworld-region.zone1.subzone2
labels:
app: helloworld
version: region.zone1.subzone2
spec:
replicas: 1
selector:
matchLabels:
app: helloworld
version: region.zone1.subzone2
template:
metadata:
labels:
app: helloworld
version: region.zone1.subzone2
spec:
containers:
- name: helloworld
env:
- name: SERVICE_VERSION
value: region.zone1.subzone2
image: docker.io/istio/examples-helloworld-v1
resources:
requests:
cpu: "100m"
imagePullPolicy: IfNotPresent
ports:
- containerPort: 5000
nodeSelector:
kubernetes.io/hostname: ambient-worker2
EOF -
在 ambient-worker3 上启动一个服务实例
kubectl apply -n sample -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: helloworld-region.zone2.subzone3
labels:
app: helloworld
version: region.zone2.subzone3
spec:
replicas: 1
selector:
matchLabels:
app: helloworld
version: region.zone2.subzone3
template:
metadata:
labels:
app: helloworld
version: region.zone2.subzone3
spec:
containers:
- name: helloworld
env:
- name: SERVICE_VERSION
value: region.zone2.subzone3
image: docker.io/istio/examples-helloworld-v1
resources:
requests:
cpu: "100m"
imagePullPolicy: IfNotPresent
ports:
- containerPort: 5000
nodeSelector:
kubernetes.io/hostname: ambient-worker3
EOF
3. 在客户端上测试
-
在 ambient-worker 上启动测试客户端
kubectl apply -n sample -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: sleep
spec:
replicas: 1
selector:
matchLabels:
app: sleep
template:
metadata:
labels:
app: sleep
spec:
terminationGracePeriodSeconds: 0
containers:
- name: sleep
image: curlimages/curl
command: ["/bin/sleep", "infinity"]
imagePullPolicy: IfNotPresent
volumeMounts:
- mountPath: /etc/sleep/tls
name: secret-volume
volumes:
- name: secret-volume
secret:
secretName: sleep-secret
optional: true
nodeSelector:
kubernetes.io/hostname: ambient-worker
EOF -
测试访问
kubectl exec -n sample "$(kubectl get pod -n sample -l app=sleep -o jsonpath='{.items[0].metadata.name}')" -c sleep -- curl -sSL "http://helloworld:5000/hello"
输出来自当前与 ambient-worker 共置的 helloworld-region.zone1.subzone1:
Hello version: region.zone1.subzone1, instance: helloworld-region.zone1.subzone1-6d6fdfd856-9dhv8
-
删除 ambient-worker 上的服务并测试故障转移
kubectl delete deployment -n sample helloworld-region.zone1.subzone1
kubectl exec -n sample "$(kubectl get pod -n sample -l app=sleep -o jsonpath='{.items[0].metadata.name}')" -c sleep -- curl -sSL "http://helloworld:5000/hello"
输出为 helloworld-region.zone1.subzone2,流量已发生故障转移:
Hello version: region.zone1.subzone2, instance: helloworld-region.zone1.subzone2-948c95bdb-7p6zb
-
将 ambient-worker3 的地域标签更改为与 worker2 相同并测试
kubectl label node ambient-worker3 topology.kubernetes.io/zone=zone1 --overwrite
kubectl label node ambient-worker3 topology.kubernetes.io/subzone=subzone2 --overwrite删除 helloworld-region.zone2.subzone3 并重新应用开发 pod 如下,然后运行测试:
kubectl delete deployment -n sample helloworld-region.zone2.subzone3
kubectl apply -n sample -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: helloworld-region.zone1.subzone2-worker3
labels:
app: helloworld
version: region.zone1.subzone2-worker3
spec:
replicas: 1
selector:
matchLabels:
app: helloworld
version: region.zone1.subzone2-worker3
template:
metadata:
labels:
app: helloworld
version: region.zone1.subzone2-worker3
spec:
containers:
- name: helloworld
env:
- name: SERVICE_VERSION
value: region.zone1.subzone2-worker3
image: docker.io/istio/examples-helloworld-v1
resources:
requests:
cpu: "100m"
imagePullPolicy: IfNotPresent
ports:
- containerPort: 5000
nodeSelector:
kubernetes.io/hostname: ambient-worker3
EOF多次测试:
kubectl exec -n sample "$(kubectl get pod -n sample -l app=sleep -o jsonpath='{.items[0].metadata.name}')" -c sleep -- curl -sSL "http://helloworld:5000/hello"
输出随机显示 helloworld-region.zone1.subzone2 和 helloworld-region.zone1.subzone2-worker3:
Hello version: region.zone1.subzone2-worker3, instance: helloworld-region.zone1.subzone2-worker3-6d6fdfd856-6kd2s
Hello version: region.zone1.subzone2, instance: helloworld-region.zone1.subzone2-948c95bdb-7p6zb
Hello version: region.zone1.subzone2, instance: helloworld-region.zone1.subzone2-948c95bdb-7p6zb
Hello version: region.zone1.subzone2-worker3, instance: helloworld-region.zone1.subzone2-worker3-6d6fdfd856-6kd2s
Hello version: region.zone1.subzone2, instance: helloworld-region.zone1.subzone2-948c95bdb-7p6zb