ASM支持在不需要修改应用代码的情况下可以实现同可用区优先路由(简称同AZ路由)的能力,本文以入口网关访问httpbin应用为例,介绍如何使用同AZ路由。
前提条件
- 已创建ASM实例。具体操作,请参见创建ASM实例。
- 已创建ACK集群。具体操作,请参见创建Kubernetes托管版集群。
- 添加集群到ASM实例。具体操作,请参见添加集群到ASM实例。
- ACK集群中的节点存在至少两个可用区,本示例中分别使用cn-hongkong-b和cn-hongkong-c, 您可以在容器服务管理控制台查看集群节点所对应的ECS所在地域及可用区。更多信息,请参见地域和可用区。
- 本示例中sleep应用部署在可用区cn-hongkong-b,helloworld-v1应用部署到可用区cn-hongkong-b中,helloworld-v2应用部署到可用区cn-hongkong-c中,您可以根据实际集群所使用的可用区进行替换。
背景信息
同AZ路由是指在客户端对一个目标服务的访问过程中,可以根据这个客户端所在的地域、可用区拓扑信息,优先路由到与该客户端在同一个节点或者可用区的目标服务上的路由行为。同AZ路由本身属于负载均衡策略的范畴,它能够使流量尽可能的在同一个可用区内流转,以保证服务间的调用延迟最低。
步骤一:部署示例应用
- 使用以下内容,创建sleep.yaml。
说明 如下示例中,sleep应用部署到可用区cn-hongkong-b,您可以根据实际集群所使用的可用区进行替换。
# Copyright Istio Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##################################################################################################
# Sleep service
##################################################################################################
apiVersion: v1
kind: ServiceAccount
metadata:
name: sleep
---
apiVersion: v1
kind: Service
metadata:
name: sleep
labels:
app: sleep
service: sleep
spec:
ports:
- port: 80
name: http
selector:
app: sleep
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: sleep
spec:
replicas: 1
selector:
matchLabels:
app: sleep
template:
metadata:
labels:
app: sleep
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: failure-domain.beta.kubernetes.io/zone
operator: In
values:
- 'cn-hongkong-b'
terminationGracePeriodSeconds: 0
serviceAccountName: sleep
containers:
- name: sleep
image: curlimages/curl
command: ["/bin/sleep", "3650d"]
imagePullPolicy: IfNotPresent
volumeMounts:
- mountPath: /etc/sleep/tls
name: secret-volume
volumes:
- name: secret-volume
secret:
secretName: sleep-secret
optional: true
- 执行以下命令,在集群中部署sleep应用。
kubectl apply -f sleep.yaml
- 使用以下内容,创建helloworld.yaml。
说明 如下示例中,helloworld-v1应用部署到可用区cn-hongkong-b中,helloworld-v2应用部署到可用区cn-hongkong-c中,您可以根据实际集群所使用的可用区进行替换。
apiVersion: v1
kind: Service
metadata:
name: helloworld
labels:
app: helloworld
service: helloworld
spec:
ports:
- port: 5000
name: http
selector:
app: helloworld
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: helloworld-v1
labels:
app: helloworld
version: v1
spec:
replicas: 1
selector:
matchLabels:
app: helloworld
version: v1
template:
metadata:
labels:
app: helloworld
version: v1
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: failure-domain.beta.kubernetes.io/zone
operator: In
values:
- 'cn-hongkong-b'
containers:
- name: helloworld
image: docker.io/istio/examples-helloworld-v1
resources:
requests:
cpu: "100m"
imagePullPolicy: IfNotPresent #Always
ports:
- containerPort: 5000
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: helloworld-v2
labels:
app: helloworld
version: v2
spec:
replicas: 1
selector:
matchLabels:
app: helloworld
version: v2
template:
metadata:
labels:
app: helloworld
version: v2
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: failure-domain.beta.kubernetes.io/zone
operator: In
values:
- 'cn-hongkong-c'
containers:
- name: helloworld
image: docker.io/istio/examples-helloworld-v2
resources:
requests:
cpu: "100m"
imagePullPolicy: IfNotPresent #Always
ports:
- containerPort: 5000
- 执行以下命令,在集群中部署helloworld应用。
kubectl apply -f helloworld.yaml
- 执行以下命令,查询目标服务的注册信息。
kubectl exec "$(kubectl get pod -l app=sleep -o jsonpath='{.items[0].metadata.name}')" -c sleep -- curl localhost:15000/clusters | grep helloworld
预期输出:
outbound|5000||helloworld.default.svc.cluster.local::172.28.32.49:5000::region::cn-hongkong
outbound|5000||helloworld.default.svc.cluster.local::172.28.32.49:5000::zone::cn-hongkong-b
outbound|5000||helloworld.default.svc.cluster.local::172.28.32.49:5000::sub_zone::
outbound|5000||helloworld.default.svc.cluster.local::172.28.32.49:5000::canary::false
outbound|5000||helloworld.default.svc.cluster.local::172.28.32.49:5000::priority::0
.......
outbound|5000||helloworld.default.svc.cluster.local::172.28.33.155:5000::region::cn-hongkong
outbound|5000||helloworld.default.svc.cluster.local::172.28.33.155:5000::zone::cn-hongkong-c
outbound|5000||helloworld.default.svc.cluster.local::172.28.33.155:5000::sub_zone::
outbound|5000||helloworld.default.svc.cluster.local::172.28.33.155:5000::canary::false
outbound|5000||helloworld.default.svc.cluster.local::172.28.33.155:5000::priority::0
由预期输出得到,两个helloworld应用的优先级相同,均为
priority::0
。说明当sleep客户端调用helloworld服务时,两个helloworld应用的路由策略相同。
步骤二:设置同AZ优先路由
通过应用目标规则来解决优先级问题,为服务
helloworld.default.svc.cluster.local
启用同AZ路由的能力。
说明 您可以调整连续5xx错误(consecutive5xxErrors
)、间隔时间(interval
)以及最小的移除时间长度(baseEjectionTime
)参数,实现同可用区路由的优先调用。本示例将在第一个请求失败时触发故障转移。
- 使用以下内容,创建helloworld-failover.yaml。
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
name: helloworld-failover
namespace: default
spec:
host: helloworld.default.svc.cluster.local
trafficPolicy:
connectionPool:
http:
maxRequestsPerConnection: 1
loadBalancer:
localityLbSetting:
enabled: true
simple: ROUND_ROBIN
outlierDetection:
baseEjectionTime: 1m
consecutive5xxErrors: 1
interval: 1s
- 执行以下命令,查看工作负载的优先级。
kubectl exec "$(kubectl get pod -l app=sleep -o jsonpath='{.items[0].metadata.name}')" -c sleep -- curl localhost:15000/clusters | grep helloworld
预期输出:
outbound|5000||helloworld.default.svc.cluster.local::172.28.32.49:5000::region::cn-hongkong
outbound|5000||helloworld.default.svc.cluster.local::172.28.32.49:5000::zone::cn-hongkong-b
outbound|5000||helloworld.default.svc.cluster.local::172.28.32.49:5000::sub_zone::
outbound|5000||helloworld.default.svc.cluster.local::172.28.32.49:5000::canary::false
outbound|5000||helloworld.default.svc.cluster.local::172.28.32.49:5000::priority::0
.......
outbound|5000||helloworld.default.svc.cluster.local::172.28.33.155:5000::region::cn-hongkong
outbound|5000||helloworld.default.svc.cluster.local::172.28.33.155:5000::zone::cn-hongkong-c
outbound|5000||helloworld.default.svc.cluster.local::172.28.33.155:5000::sub_zone::
outbound|5000||helloworld.default.svc.cluster.local::172.28.33.155:5000::canary::false
outbound|5000||helloworld.default.svc.cluster.local::172.28.33.155:5000::priority::1
由预期输出得到,两个helloworld应用的优先级不同,分别为
priority::0
和
priority::1
。 说明当sleep客户端调用helloworld服务时,同AZ路由的策略生效。
步骤三:验证同AZ优先路由
从位于可用区cn-hongkong-b中的客户端sleep应用发送请求,调用helloworld服务。启用同AZ优先路由之后, 所有流量都应指向同在可用区cn-hongkong-b中的helloworld-v1应用。其中,sleep应用部署在可用区cn-hongkong-b,
helloworld-v1应用部署到可用区cn-hongkong-b中,helloworld-v2应用部署到可用区cn-hongkong-c中。
- 重复执行以下命令,访问helloworld服务。
kubectl exec -c sleep "$(kubectl get pod -l app=sleep -o jsonpath='{.items[0].metadata.name}')" -- curl -sSL helloworld:5000/hello
预期输出:
Hello version: v1, instance: helloworld-v1-6f88967849-sq2h2
由预期输出得到,返回结果始终为helloworld-v1服务。
- 缩容helloworld-v1服务。
- 执行以下命令,缩容helloworld-v1服务到0个Pod,模拟该服务不可用状态。
kubectl scale deploy helloworld-v1 --replicas=0
- 等待几秒后,重复执行以下命令,访问helloworld服务。
kubectl exec -c sleep "$(kubectl get pod -l app=sleep -o jsonpath='{.items[0].metadata.name}')" -- curl -sSL helloworld:5000/hello
预期输出:
Hello version: v2, instance: helloworld-v2-75db5f978d-s7v4k
由预期输出得到,当本可用区的helloworld-v1服务不可用时,会自动切换到另一可用区cn-hongkong-c中的helloworld-v2服务。
- 扩容helloworld-v1服务。
- 执行以下命令,扩容helloworld-v1服务到1个Pod,恢复可用区cn-hongkong-b中的helloworld-v1应用服务。
kubectl scale deploy helloworld-v1 --replicas=1
- 等待几秒后,重复执行以下命令,访问helloworld服务。
kubectl exec -c sleep "$(kubectl get pod -l app=sleep -o jsonpath='{.items[0].metadata.name}')" -- curl -sSL helloworld:5000/hello
预期输出:
Hello version: v1, instance: helloworld-v1-6f88967849-sq2h2
由预期输出得到,返回结果始终为helloworld-v1服务。