1.虚拟主机域名匹配
路由匹配时将请求报文中的host标头值与此处列表项进行匹配检测
1.1 方案设计
1.1.1 gateway设定
(1)gateway单独部署在一个namespace中,其他不同namespace的vs均绑定到这一个gateway上
(2)gateway使用HTTP协议
(3)gateway端口使用80
(4)gateway的限制域为’*'(即不限制)
1.1.2 VirtualService设定
(1)分别位于两个namespace中
(2)分别限制spec.hosts,通过限制域来进行域匹配
(3)spec.http.route.destination.host尽量使用FQDN(全限定域名)
(4)绑定方式使用namespace/gateway
,跨namespace绑定
1.1.3服务设定
(1)本实验采用同一个服务,请求服务接口返回该pod的子网IP,以此区分流量走向
(2)deployment的spec.replicas设置为1
(3)deployment+service与VirtualService对应部署在相同namespace
1.2 部署
1.2.1 部署服务
cat demo-java.yaml
apiVersion: v1
kind: Namespace
metadata:
name: demo-java
labels:
istio-injection: enabled
---
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
labels:
app: demo-java
name: demo-java
namespace: java-demo-devops
spec:
progressDeadlineSeconds: 600
replicas: 1
revisionHistoryLimit: 10
selector:
matchLabels:
app: demo-java
strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
type: RollingUpdate
template:
metadata:
creationTimestamp: null
labels:
app: demo-java
spec:
containers:
- image: 192.168.8.39:9090/demo/demo:SNAPSHOT-master-34
imagePullPolicy: IfNotPresent
name: java-demo
ports:
- containerPort: 8080
protocol: TCP
resources: {
}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
dnsPolicy: ClusterFirst
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {
}
terminationGracePeriodSeconds: 30
---
apiVersion: v1
kind: Service
metadata:
labels:
app: demo-java
version: v1
name: demo-java
namespace: java-demo-devops
spec:
ports:
- name: http
port: 8080
protocol: TCP
targetPort: 8080
selector:
app: demo-java
sessionAffinity: None
type: ClusterIP
---
apiVersion: v1
kind: Namespace
metadata:
name: demo
labels:
istio-injection: enabled
---
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
labels:
app: demo-java
name: demo-java
namespace: demo
spec:
progressDeadlineSeconds: 600
replicas: 1
revisionHistoryLimit: 10
selector:
matchLabels:
app: demo-java
strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
type: RollingUpdate
template:
metadata:
creationTimestamp: null
labels:
app: demo-java
spec:
containers:
- image: 192.168.8.39:9090/demo/demo:SNAPSHOT-master-34
imagePullPolicy: IfNotPresent
name: java-demo
ports:
- containerPort: 8080
protocol: TCP
resources: {
}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
dnsPolicy: ClusterFirst
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {
}
terminationGracePeriodSeconds: 30
---
apiVersion: v1
kind: Service
metadata:
labels:
app: demo-java
version: v1
name: demo-java
namespace: demo
spec:
ports:
- name: http
port: 8080
protocol: TCP
targetPort: 8080
selector:
app: demo-java
sessionAffinity: None
type: ClusterIP
kubectl apply -f demo-java.yaml
#查看
[root@master istio-demo]# kubectl get all -n demo
NAME READY STATUS RESTARTS AGE
pod/demo-java-6bbd4bd86f-hh74k 2/2 Running 0 21h
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/demo-java ClusterIP 10.1.79.232 <none> 8080/TCP 7d20h
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/demo-java 1/1 1 1 7d20h
NAME DESIRED CURRENT READY AGE
replicaset.apps/demo-java-6bbd4bd86f 1 1 1 7d20h
[root@master istio-demo]# kubectl get all -n java-demo-devops
NAME READY STATUS RESTARTS AGE
pod/demo-java-6bbd4bd86f-2cbtg 2/2 Running 0 20h
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/demo-java ClusterIP 10.1.100.157 <none> 8080/TCP 20h
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/demo-java 1/1 1 1 20h
NAME DESIRED CURRENT READY AGE
replicaset.apps/demo-java-6bbd4bd86f 1 1 1 20h
[root@master istio-demo]#
#查看pod的IP
[root@master istio-demo]# kubectl get endpoints -n demo
NAME ENDPOINTS AGE
demo-java 10.244.166.145:8080 7d20h
[root@master istio-demo]# kubectl get endpoints -n java-demo-devops
NAME ENDPOINTS AGE
demo-java 10.244.166.146:8080 20h
[root@master istio-demo]#
1.2.2 部署gateway
cat gateway-80.yaml
apiVersion: v1
kind: Namespace
metadata:
name: istio-gateway
---
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
name: gateway-80
namespace: istio-gateway
spec:
selector:
istio: ingressgateway
servers:
- hosts:
- '*'
port:
name: http
number: 80
protocol: HTTP
kubectl apply -f gateway-80.yaml
#查看
[root@master istio-demo]# kubectl get gateways.networking.istio.io -n istio-gateway
NAME AGE
gateway-80 28h
[root@master istio-demo]#
1.2.3 部署virtual service
cat vs-demo-java.yaml
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: demo-java
namespace: demo
spec:
hosts:
- 'test.wangjb.com' #限制访问域
gateways:
- istio-gateway/gateway-80
http:
- route:
- destination:
host: demo-java.demo.svc.cluster.local #FQDN
port:
number: 8080
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: demo-java
namespace: java-demo-devops
spec:
hosts:
- 'test2.wangjb.com' #限制访问域
gateways:
- istio-gateway/gateway-80
http:
- route:
- destination:
host: demo-java.java-demo-devops.svc.cluster.local #FQDN
port:
number: 8080
kubectl apply -f vs-demo-java.yaml
#查看
[root@master istio-demo]# kubectl get vs -n demo
NAME GATEWAYS HOSTS AGE
demo-java ["istio-gateway/gateway-80"] ["test.wangjb.com"] 22h
[root@master istio-demo]# kubectl get vs -n java-demo-devops
NAME GATEWAYS HOSTS AGE
demo-java ["istio-gateway/gateway-80"] ["test2.wangjb.com"] 21h
[root@master istio-demo]#
1.3 访问验证
1.3.1 配置域名
首先配置一下域名,因为我们是本地验证,所以就没必要去配置DNS了,直接改一下hosts文件即可
1.3.2 查看istio-ingressgateway映射端口
80端口是istio-ingressgateway默认映射的端口(当然我们也可以自己修改),查看一下映射
[root@master istio-demo]# kubectl get svc -n istio-system istio-ingressgateway
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
istio-ingressgateway LoadBalancer 10.1.39.203 <pending> 15021:30326/TCP,80:31080/TCP,443:31443/TCP,20001:32001/TCP,8800:32080/TCP 11d
获取80端口映射为31080
1.3.3 请求服务接口
1.3.3.1 IP请求
预料之中的404
1.3.3.2 test.wangjb.com
1.3.3.3 test2.wangjb.com
1.3.4 分析
从1.3.3的验证我们可以看到,预期与结果相吻合,我们再来看一下istio的路由策略,进一步验证我们的猜想
1.3.4.1 获取路由配置名称
因为我们的服务端口是8080,所以我们可以通过如下方式来获取路由配置名称
[root@master istio-demo]# istioctl -n istio-system pc listeners istio-ingressgateway-6fd6665c9c-nm22r --port 8080 -o json
[
{
"name": "0.0.0.0_8080",
"address": {
"socketAddress": {
"address": "0.0.0.0",
"portValue": 8080
}
},
"filterChains": [
{
"filters": [
{
"name": "envoy.filters.network.http_connection_manager",
"typedConfig": {
"@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager",
"statPrefix": "outbound_0.0.0.0_8080",
"rds": {
"configSource": {
"ads": {
},
"initialFetchTimeout": "0s",
"resourceApiVersion": "V3"
},
"routeConfigName": "http.8080"
},
"httpFilters": [
{
"name": "istio.metadata_exchange",
"typedConfig": {
"@type": "type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm",
"config": {
"vmConfig": {
"runtime": "envoy.wasm.runtime.null",
"code": {
"local": {
"inlineString": "envoy.wasm.metadata_exchange"
}
}
},
"configuration": {
"@type": "type.googleapis.com/envoy.tcp.metadataexchange.config.MetadataExchange"
}
}
}
},
{
"name": "istio.alpn",
"typedConfig": {
"@type": "type.googleapis.com/istio.envoy.config.filter.http.alpn.v2alpha1.FilterConfig",
"alpnOverride": [
{
"alpnOverride": [
"istio-http/1.0",
"istio",
"http/1.0"
]
},
{
"upstreamProtocol": "HTTP11",
"alpnOverride": [
"istio-http/1.1",
"istio",
"http/1.1"
]
},
{
"upstreamProtocol": "HTTP2",
"alpnOverride": [
"istio-h2",
"istio",
"h2"
]
}
]
}
},
{
"name": "envoy.filters.http.fault",
"typedConfig": {
"@type": "type.googleapis.com/envoy.extensions.filters.http.fault.v3.HTTPFault"
}
},
{
"name": "envoy.filters.http.cors",
"typedConfig": {
"@type": "type.googleapis.com/envoy.extensions.filters.http.cors.v3.Cors"
}
},
{
"name": "istio.stats",
"typedConfig": {
"@type": "type.googleapis.com/udpa.type.v1.TypedStruct",
"typeUrl": "type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm",
"value": {
"config": {
"configuration": {
"@type": "type.googleapis.com/google.protobuf.StringValue",
"value": "{\n \"debug\": \"false\",\n \"stat_prefix\": \"istio\",\n \"disable_host_header_fallback\": true\n}\n"
},
"root_id": "stats_outbound",
"vm_config": {
"code": {
"local": {
"inline_string": "envoy.wasm.stats"
}
},
"runtime": "envoy.wasm.runtime.null",
"vm_id": "stats_outbound"
}
}
}
}
},
{
"name": "envoy.filters.http.router",
"typedConfig": {
"@type": "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router"
}
}
],
"tracing": {
"clientSampling": {
"value": 100
},
"randomSampling": {
"value": 1
},
"overallSampling": {
"value": 100
},
"customTags": [
{
"tag": "istio.authorization.dry_run.allow_policy.name",
"metadata": {
"kind": {
"request": {
}
},
"metadataKey": {
"key": "envoy.filters.http.rbac",
"path": [
{
"key": "istio_dry_run_allow_shadow_effective_policy_id"
}
]
}
}
},
{
"tag": "istio.authorization.dry_run.allow_policy.result",
"metadata": {
"kind": {
"request": {
}
},
"metadataKey": {
"key": "envoy.filters.http.rbac",
"path": [
{
"key": "istio_dry_run_allow_shadow_engine_result"
}
]
}
}
},
{
"tag": "istio.authorization.dry_run.deny_policy.name",
"metadata": {
"kind": {
"request": {
}
},
"metadataKey": {
"key": "envoy.filters.http.rbac",
"path": [
{
"key": "istio_dry_run_deny_shadow_effective_policy_id"
}
]
}
}
},
{
"tag": "istio.authorization.dry_run.deny_policy.result",
"metadata": {
"kind": {
"request": {
}
},
"metadataKey": {
"key": "envoy.filters.http.rbac",
"path": [
{
"key": "istio_dry_run_deny_shadow_engine_result"
}
]
}
}
},
{
"tag": "istio.canonical_revision",
"literal": {
"value": "latest"
}
},
{
"tag": "istio.canonical_service",
"literal": {
"value": "istio-ingressgateway"
}
},
{
"tag": "istio.mesh_id",
"literal": {
"value": "cluster.local"
}
},
{
"tag": "istio.namespace",
"literal": {
"value": "istio-system"
}
}
]
},
"httpProtocolOptions": {
},
"serverName": "istio-envoy",
"streamIdleTimeout": "0s",
"useRemoteAddress": true,
"forwardClientCertDetails": "SANITIZE_SET",
"setCurrentClientCertDetails": {
"subject": true,
"cert": true,
"dns": true,
"uri": true
},
"upgradeConfigs": [
{
"upgradeType": "websocket"
}
],
"normalizePath": true,
"pathWithEscapedSlashesAction": "KEEP_UNCHANGED",
"requestIdExtension": {
"typedConfig": {
"@type": "type.googleapis.com/envoy.extensions.request_id.uuid.v3.UuidRequestIdConfig",
"useRequestIdForTraceSampling": true
}
}
}
}
]
}
],
"trafficDirection": "OUTBOUND"
}
]
可以从中得到"routeConfigName": “http.8080”
我们再根据http.8080
去获取路由信息
1.3.4.2 获取路由配置
[root@master istio-demo]# istioctl -n istio-system pc routes istio-ingressgateway-6fd6665c9c-nm22r --name http.8080 -o json
[
{
"name": "http.8080",
"virtualHosts": [
{
"name": "test.wangjb.com:80",
"domains": [
"test.wangjb.com"
],
"routes": [
{
"match": {
"prefix": "/"
},
"route": {
"cluster": "outbound|8080||demo-java.demo.svc.cluster.local",
"timeout": "0s",
"retryPolicy": {
"retryOn": "connect-failure,refused-stream,unavailable,cancelled,retriable-status-codes",
"numRetries": 2,
"retryHostPredicate": [
{
"name": "envoy.retry_host_predicates.previous_hosts",
"typedConfig": {
"@type": "type.googleapis.com/envoy.extensions.retry.host.previous_hosts.v3.PreviousHostsPredicate"
}
}
],
"hostSelectionRetryMaxAttempts": "5",
"retriableStatusCodes": [
503
]
},
"maxStreamDuration": {
"maxStreamDuration": "0s",
"grpcTimeoutHeaderMax": "0s"
}
},
"metadata": {
"filterMetadata": {
"istio": {
"config": "/apis/networking.istio.io/v1alpha3/namespaces/demo/virtual-service/demo-java"
}
}
},
"decorator": {
"operation": "demo-java.demo.svc.cluster.local:8080/*"
}
}
],
"includeRequestAttemptCount": true
},
{
"name": "test2.wangjb.com:80",
"domains": [
"test2.wangjb.com"
],
"routes": [
{
"match": {
"prefix": "/"
},
"route": {
"cluster": "outbound|8080||demo-java.java-demo-devops.svc.cluster.local",
"timeout": "0s",
"retryPolicy": {
"retryOn": "connect-failure,refused-stream,unavailable,cancelled,retriable-status-codes",
"numRetries": 2,
"retryHostPredicate": [
{
"name": "envoy.retry_host_predicates.previous_hosts",
"typedConfig": {
"@type": "type.googleapis.com/envoy.extensions.retry.host.previous_hosts.v3.PreviousHostsPredicate"
}
}
],
"hostSelectionRetryMaxAttempts": "5",
"retriableStatusCodes": [
503
]
},
"maxStreamDuration": {
"maxStreamDuration": "0s",
"grpcTimeoutHeaderMax": "0s"
}
},
"metadata": {
"filterMetadata": {
"istio": {
"config": "/apis/networking.istio.io/v1alpha3/namespaces/java-demo-devops/virtual-service/demo-java"
}
}
},
"decorator": {
"operation": "demo-java.java-demo-devops.svc.cluster.local:8080/*"
}
}
],
"includeRequestAttemptCount": true
}
],
"validateClusters": false,
"ignorePortInHostMatching": true
}
]
可以看到virtualHosts有两个配置,分别是test.wangjb.com:80
与test2.wangjb.com:80
,对应的domains分别为test.wangjb.com
和test2.wangjb.com
route对应关系为
test.wangjb.com:80 —— outbound|8080||demo-java.demo.svc.cluster.local
test2.wangjb.com:80 —— outbound|8080||demo-java.java-demo-devops.svc.cluster.local
知识补充:
istioctl pc(istioctl proxy-config):检索代理配置信息
例如,使用如下方式可以检索特定pod中的Envoy实例的集群配置信息。
$ istioctl proxy-config cluster [flags]
使用如下方式可以检索特定pod中的Envoy实例的bootstrap配置信息。
$ istioctl proxy-config bootstrap [flags]
使用如下方式可以检索特定pod中的Envoy实例的listener(监听器)配置信息。
$ istioctl proxy-config listener [flags]
使用如下方式可以检索特定pod中的Envoy实例的route(路由)配置信息。
$ istioctl proxy-config route [flags]
使用如下方式可以检索特定pod中的Envoy实例的endpoint (后端)配置信息。
$ istioctl proxy-config endpoints [flags]
2.http.match路由匹配
2.1 方案设计
2.1.1 gateway设定
(1)gateway单独部署在一个namespace中,其他不同namespace的vs均绑定到这一个gateway上
(2)gateway使用HTTP协议
(3)gateway端口使用80
(4)gateway的限制域为’*'(即不限制)
2.1.2 VirtualService设定
(1)分别位于两个namespace中
(2)spec.hosts为"*",即不限制访问host
(3)两个服务分别设置不同的.spec.http.match,服务A为.spec.http.match.uri={ {“prefix”: “/systemInfo01/”}},服务B为.spec.http.match.uri={ {“prefix”: “/systemInfo02/”}}
(4)spec.http.route.destination.host尽量使用FQDN(全限定域名)
(5)绑定方式使用namespace/gateway
,跨namespace绑定
2.1.3服务设定
(1)本实验采用同一个服务,请求服务接口返回该pod的子网IP,以此区分流量走向
(2)deployment的spec.replicas设置为1
(3)deployment+service与VirtualService对应部署在相同namespace
2.2 部署
2.2.1 部署服务
同1.2.1不变
2.2.2 部署gateway
同1.2.2不变
2.2.3 部署virtual service
cat vs-demo-java.yaml
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: demo-java
namespace: demo
spec:
hosts:
- '*'
gateways:
- istio-gateway/gateway-80
http:
- match:
- uri:
prefix: /systemInfo01/
route:
- destination:
host: demo-java.demo.svc.cluster.local
port:
number: 8080
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: demo-java
namespace: java-demo-devops
spec:
hosts:
- '*'
gateways:
- istio-gateway/gateway-80
http:
- match:
- uri:
prefix: /systemInfo02/
route:
- destination:
host: demo-java.java-demo-devops.svc.cluster.local
port:
number: 8080
kubectl apply -f vs-demo-java.yaml
#查看
[root@master istio-demo]# kubectl get vs -n demo
NAME GATEWAYS HOSTS AGE
demo-java ["istio-gateway/gateway-80"] ["*"] 69m
[root@master istio-demo]# kubectl get vs -n java-demo-devops
NAME GATEWAYS HOSTS AGE
demo-java ["istio-gateway/gateway-80"] ["*"] 72m
[root@master istio-demo]#
2.3 访问验证
2.3.1 请求服务接口
2.3.1.1 请求/systemInfo01/hostInfo
2.3.2.2 请求/systemInfo02/hostInfo
注意:
前缀匹配一定要写后边的”/“,比如前缀匹配应该写成”/systemInfo/“,而最好不要写成”/systemInfo“,因为当用第二种写法的时候,如果其他的前缀匹配中有前缀相似的uri时,会导致匹配出现问题,比如”/systemInfo“与”/systemInfo01“同时存在时,路由会优先匹配”/systemInfo“,而不去匹配”/systemInfo01“
2.3.2 分析
2.3.1 获取路由配置
[root@master istio-demo]# istioctl -n istio-system pc routes istio-ingressgateway-6fd6665c9c-nm22r --name http.8080 -o json
[
{
"name": "http.8080",
"virtualHosts": [
{
"name": "*:80",
"domains": [
"*"
],
"routes": [
{
"match": {
"prefix": "/systemInfo02/",
"caseSensitive": true
},
"route": {
"cluster": "outbound|8080||demo-java.java-demo-devops.svc.cluster.local",
"timeout": "0s",
"retryPolicy": {
"retryOn": "connect-failure,refused-stream,unavailable,cancelled,retriable-status-codes",
"numRetries": 2,
"retryHostPredicate": [
{
"name": "envoy.retry_host_predicates.previous_hosts",
"typedConfig": {
"@type": "type.googleapis.com/envoy.extensions.retry.host.previous_hosts.v3.PreviousHostsPredicate"
}
}
],
"hostSelectionRetryMaxAttempts": "5",
"retriableStatusCodes": [
503
]
},
"maxStreamDuration": {
"maxStreamDuration": "0s",
"grpcTimeoutHeaderMax": "0s"
}
},
"metadata": {
"filterMetadata": {
"istio": {
"config": "/apis/networking.istio.io/v1alpha3/namespaces/java-demo-devops/virtual-service/demo-java"
}
}
},
"decorator": {
"operation": "demo-java.java-demo-devops.svc.cluster.local:8080/systemInfo02/*"
}
},
{
"match": {
"prefix": "/systemInfo01",
"caseSensitive": true
},
"route": {
"cluster": "outbound|8080||demo-java.demo.svc.cluster.local",
"timeout": "0s",
"retryPolicy": {
"retryOn": "connect-failure,refused-stream,unavailable,cancelled,retriable-status-codes",
"numRetries": 2,
"retryHostPredicate": [
{
"name": "envoy.retry_host_predicates.previous_hosts",
"typedConfig": {
"@type": "type.googleapis.com/envoy.extensions.retry.host.previous_hosts.v3.PreviousHostsPredicate"
}
}
],
"hostSelectionRetryMaxAttempts": "5",
"retriableStatusCodes": [
503
]
},
"maxStreamDuration": {
"maxStreamDuration": "0s",
"grpcTimeoutHeaderMax": "0s"
}
},
"metadata": {
"filterMetadata": {
"istio": {
"config": "/apis/networking.istio.io/v1alpha3/namespaces/demo/virtual-service/demo-java"
}
}
},
"decorator": {
"operation": "demo-java.demo.svc.cluster.local:8080/systemInfo01*"
}
}
],
"includeRequestAttemptCount": true
}
],
"validateClusters": false,
"ignorePortInHostMatching": true
}
]
可以看到此处virtualHosts
中只有一个domains
,而在routes中根据不同的match到达不同的route
这种情况的应用可以用于来自同一domains的请求需要根据uri转发至不同service的情况,为了安全起见,这种情况也可以设置sepc.hosts的值为指定域。
3. Istio官方提醒
当为已存在的host创建第二个及更多的 VirtualService
时,istio-pilot
会将额外的路由规则合并到host现有配置中。但是,在使用此功能时,有一些注意事项。
- 尽管会保留任何给定源
VirtualService
中规则的评估顺序,但跨资源的顺序是不确定的。换句话说,无法保证片段配置中规则的评估顺序,因此,只有在片段规则之间没有冲突的规则或者顺序依赖性时,它才具有可预测的行为。 - 片段中应该只有一个“catch-all”规则(即与任何请求路径或 header 都匹配的规则)。所有这些“catch-all”规则将在合并配置中移至列表的末尾,但是由于它们捕获了所有请求,因此,首先应用的那个规则,实际上会覆盖并禁用其它的规则。
- 如果想将
VirtualService
绑定到网关,则只能以这种方式进行分段。sidecar不支持host合并。
也可以使用类似的合并语义和限制将 DestinationRule
分段。
- 在这里,它应该只是同一host的多个
DestinationRule
中任何给定子集的一种定义。如果有多个同名,则使用第一个定义,并丢弃随后的所有重复项。不支持子集内容的合并。 - 同一host只能有一个顶级的
trafficPolicy
。在多个DestinationRule
中定义了顶级trafficPolicy
时,将使用第一个策略。之后的所有顶级trafficPolicy
配置都将被丢弃。 - 与
VirtualService
合并不同,DestinationRule
合并在 sidecar 和 gateway 中均有效。