流量管理最佳实践
本节提供具体的部署或配置指南,以避免网络或流量管理问题。
为服务设置默认路由
虽然 Istio 的默认行为方便地将来自任何源的流量发送到目标服务的各个版本,而无需设置任何规则,但从一开始就为每个服务创建一个具有默认路由的 VirtualService
,通常被认为是 Istio 中的最佳实践。
即使您最初只有一个版本的服务,一旦您决定部署第二个版本,您就需要在启动新版本 之前 设置路由规则,以防止它立即以不受控制的方式接收流量。
依赖 Istio 的默认轮询路由时,另一个潜在问题是由于 Istio 目的地规则评估算法的细微差别。在路由请求时,Envoy 首先会评估虚拟服务中的路由规则,以确定是否将请求路由到特定子集。如果是,则它才会激活与该子集相对应的任何目的地规则策略。因此,只有在**显式**将流量路由到相应的子集时,Istio 才会应用您为特定子集定义的策略。
例如,以下目的地规则是为reviews服务定义的唯一配置,也就是说,在相应的VirtualService
定义中没有路由规则。
apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
name: reviews
spec:
host: reviews
subsets:
- name: v1
labels:
version: v1
trafficPolicy:
connectionPool:
tcp:
maxConnections: 100
即使 Istio 的默认轮询路由偶尔会调用“v1”实例,也许如果“v1”是唯一运行的版本,则始终会调用“v1”,但上述流量策略永远不会被调用。
您可以通过以下两种方式之一修复上述示例。您可以将流量策略在DestinationRule
中上移一级,使其适用于任何版本。
apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
name: reviews
spec:
host: reviews
trafficPolicy:
connectionPool:
tcp:
maxConnections: 100
subsets:
- name: v1
labels:
version: v1
或者,更重要的是,在VirtualService
定义中为该服务定义一个正确的路由规则。例如,为“reviews:v1”添加一个简单的路由规则。
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: reviews
spec:
hosts:
- reviews
http:
- route:
- destination:
host: reviews
subset: v1
控制跨命名空间的配置共享
您可以在一个命名空间中定义虚拟服务、目的地规则或服务条目,然后在其他命名空间中重复使用它们,前提是它们被导出到这些命名空间。默认情况下,Istio 将所有流量管理资源导出到所有命名空间,但您可以使用exportTo
字段覆盖可见性。例如,只有来自同一命名空间中工作负载的请求会受到以下虚拟服务的影响。
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: myservice
spec:
hosts:
- myservice.com
exportTo:
- "."
http:
- route:
- destination:
host: myservice
设置特定命名空间中目的地规则的可见性并不保证该规则会被使用。将目的地规则导出到其他命名空间使您可以在这些命名空间中使用它,但要实际在请求期间应用,该命名空间还需要位于目的地规则查找路径上。
- 客户端命名空间
- 服务命名空间
- 已配置的
meshconfig.rootNamespace
命名空间(默认情况下为istio-system
)
例如,请考虑以下目的地规则。
apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
name: myservice
spec:
host: myservice.default.svc.cluster.local
trafficPolicy:
connectionPool:
tcp:
maxConnections: 100
假设您在命名空间ns1
中创建了此目的地规则。
如果您从ns1
中的客户端向myservice
服务发送请求,则目的地规则将被应用,因为它位于查找路径上的第一个命名空间,即客户端命名空间。
如果您现在从不同的命名空间发送请求,例如ns2
,则客户端不再与目的地规则ns1
位于同一个命名空间。由于相应的服务myservice.default.svc.cluster.local
也不在ns1
中,而是在default
命名空间中,因此在查找路径的第二个命名空间,即服务命名空间中也找不到目的地规则。
即使myservice
服务被导出到所有命名空间,因此在ns2
中可见,并且目的地规则也被导出到所有命名空间,包括ns2
,它也不会在来自ns2
的请求期间被应用,因为它不在查找路径上的任何命名空间中。
您可以通过在与相应服务相同的命名空间(在本例中为default
)中创建目的地规则来避免此问题。然后它将被应用于来自任何命名空间的客户端的请求。您也可以将目的地规则移至istio-system
命名空间,这是查找路径上的第三个命名空间,尽管除非目的地规则确实是适用于所有命名空间的全局配置,否则不建议这样做,并且这将需要管理员权限。
Istio 使用此受限制的目的地规则查找路径有两个原因。
- 防止定义可能覆盖完全无关命名空间中服务的行为的目的地规则。
- 如果存在多个相同主机的目的地规则,则具有清晰的查找顺序。
将大型虚拟服务和目标规则拆分为多个资源
在无法在一个VirtualService
或DestinationRule
资源中为特定主机定义完整的一组路由规则或策略的情况下,最好在多个资源中逐步指定主机的配置。如果控制平面绑定到网关,则它会合并这些目的地规则并合并这些虚拟服务。
考虑一个绑定到入口网关的VirtualService
,它公开使用基于路径的委托到多个实现服务的应用程序主机,例如这样。
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: myapp
spec:
hosts:
- myapp.com
gateways:
- myapp-gateway
http:
- match:
- uri:
prefix: /service1
route:
- destination:
host: service1.default.svc.cluster.local
- match:
- uri:
prefix: /service2
route:
- destination:
host: service2.default.svc.cluster.local
- match:
...
这种配置的缺点是,任何基础微服务的其他配置(例如路由规则)也需要包含在此单个配置文件中,而不是包含在与单个服务团队关联的、可能由它们拥有的单独资源中。有关详细信息,请参阅路由规则对入口网关请求没有影响。
为了避免这个问题,最好将myapp.com
的配置分解成多个VirtualService
片段,每个后端服务一个。例如。
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: myapp-service1
spec:
hosts:
- myapp.com
gateways:
- myapp-gateway
http:
- match:
- uri:
prefix: /service1
route:
- destination:
host: service1.default.svc.cluster.local
---
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: myapp-service2
spec:
hosts:
- myapp.com
gateways:
- myapp-gateway
http:
- match:
- uri:
prefix: /service2
route:
- destination:
host: service2.default.svc.cluster.local
---
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: myapp-...
当为现有主机应用第二个及后续VirtualService
时,istiod
会将额外的路由规则合并到主机的现有配置中。但是,使用此功能时必须仔细考虑一些注意事项。
- 虽然任何给定源
VirtualService
中规则的评估顺序将被保留,但跨资源的顺序是未定义的。换句话说,没有跨片段配置的规则评估的保证顺序,因此只有在跨片段的规则之间没有冲突规则或顺序依赖关系时,它才具有可预测的行为。 - 片段中应该只有一个“catch-all”规则(即没有
match
字段的规则)。所有这些“catch-all”规则都将被移至合并配置列表的末尾,但由于它们捕获所有请求,因此第一个应用的规则将实质上覆盖并禁用任何其他规则。 - 只有将
VirtualService
绑定到网关时,才能以这种方式对其进行碎片化。侧边车不支持主机合并。
DestinationRule
也可以使用类似的合并语义和限制进行碎片化。
- 对于同一主机的多个目的地规则,应该只对给定子集有一个定义。如果有多个具有相同名称的子集,则使用第一个定义,并且任何后续重复项将被丢弃。不支持子集内容的合并。
- 对于同一主机,应该只有一个顶级
trafficPolicy
。当在多个目的地规则中定义顶级流量策略时,将使用第一个处理的策略。任何后续顶级trafficPolicy
配置将被丢弃。 - 与虚拟服务合并不同,目的地规则合并适用于侧边车和网关。
在重新配置服务路由时避免 503 错误
在设置路由规则以将流量定向到服务的特定版本(子集)时,必须注意确保子集在路由中使用之前可用。否则,在重新配置期间,对该服务的调用可能会返回 503 错误。
使用单个kubectl
调用创建定义相应子集的VirtualServices
和DestinationRules
(例如kubectl apply -f myVirtualServiceAndDestinationRule.yaml
)是不够的,因为资源是以最终一致的方式传播(从配置服务器,即 Kubernetes API 服务器)到 istiod 实例。如果使用子集的VirtualService
在定义子集的DestinationRule
之前到达,则由 istiod 生成的 Envoy 配置将引用不存在的上游池。这会导致 HTTP 503 错误,直到所有配置对象都可用于 istiod。
为了确保在配置具有子集的路由时,服务将具有零停机时间,请按照下面描述的“make-before-break”流程进行操作。
在添加新的子集时。
首先更新
DestinationRules
以添加一个新的子集,然后再更新任何使用它的VirtualServices
。使用kubectl
或任何平台特定的工具应用该规则。等待几秒钟,使
DestinationRule
配置传播到 Envoy 侧边车。更新
VirtualService
以引用新添加的子集。
在删除子集时。
在从
DestinationRule
中删除子集之前,更新VirtualServices
以删除对该子集的任何引用。等待几秒钟,使
VirtualService
配置传播到 Envoy 侧边车。更新
DestinationRule
以删除未使用的子集。