Istio:提升服务网格安全性、弹性与实现零停机更新
1. 验证访问令牌与恢复请求
- 再次发起请求,使用以下命令:
curl -k https://minikube.me/product-composite/1 -H "Authorization: Bearer $ACCESS_TOKEN" -i
该请求应失败,并返回 HTTP 响应状态码 401(未授权)和错误消息 “Jwt issuer is not configured”。由于 Istio 传播更改(发行者的新名称)需要几秒钟时间,可能需要重复执行该命令几次,直到请求失败,这证明 Istio 正在验证访问令牌。
- 将发行者的名称恢复为
http://auth-server
,使用命令:
kubectl edit RequestAuthentication product-composite-request-authentication
- 验证请求是否再次正常工作。先等待几秒钟让更改传播,然后运行命令:
curl -k https://minikube.me/product-composite/1 -H "Authorization: Bearer $ACCESS_TOKEN"
2. 使用相互认证(mTLS)保护内部通信
2.1 mTLS 简介
使用相互认证时,服务通过出示证书证明其身份,客户端也通过出示客户端证书向服务证明其身份,相比仅证明服务身份的普通 TLS/HTTPS,提供了更高的安全性。但设置和维护相互认证(如为客户端提供新证书和轮换过期证书)很复杂,因此很少使用。而 Istio 能完全自动化服务网格内部通信的相互认证证书的供应和轮换。
2.2 为何使用 mTLS
如果 Kubernetes 集群内的 Pod 被攻破,攻击者可以监听其他 Pod 之间的流量。若内部通信以明文发送,攻击者很容易获取敏感信息。使用相互认证可以防止攻击者窃听内部网络流量,减少此类入侵造成的损害。
2.3 配置 mTLS
要启用 Istio 管理的相互认证,需要在服务器端使用
PeerAuthentication
策略,在客户端使用
DestinationRule
进行配置。
-
PeerAuthentication
策略配置在通用 Helm 图表模板
_istio_base.yaml
中,示例如下:
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: default
spec:
mtls:
mode: PERMISSIVE
该策略配置为允许 mTLS 和普通 HTTP 请求(使用
PERMISSIVE
模式),使 Kubernetes 能够使用普通 HTTP 调用存活和就绪探针。
-
DestinationRule
清单的核心部分(要求 mTLS)如下:
trafficPolicy:
tls:
mode: ISTIO_MUTUAL
2.4 验证 mTLS 保护
验证内部通信是否受 mTLS 保护的步骤如下:
1. 确保在之前观察服务网格部分启动的负载测试仍在运行,并报告 200(OK)。
2. 在 Web 浏览器中访问 Kiali 图表(
https://kiali.minikube.me
)。
3. 点击 “Display” 按钮,启用 “Security” 标签。图表将在所有受 Istio 自动相互认证保护的通信链路上显示锁图标。
3. 确保服务网格的弹性
3.1 Istio 弹性机制概述
Istio 提供了类似于 Spring 框架的机制,如超时、重试和一种称为异常检测的断路器,用于处理服务网格中的临时故障。但对 RabbitMQ、MySQL 和 MongoDB 的调用不由 Istio 代理处理,若需要使用 TLS 保护,需手动配置。
在处理临时故障时,使用语言原生机制还是委托给 Istio 这样的服务网格,存在不同情况。在很多情况下,将处理错误的逻辑(如断路器的回退替代方案)与微服务的其他业务逻辑放在一起很重要,将处理临时故障的逻辑放在源代码中也更便于使用 JUnit 和测试容器进行测试。但如果微服务部署后发现无法处理生产中偶尔出现的临时故障,使用 Istio 添加超时或重试机制会很方便,而无需等待微服务的新版本发布。
3.2 通过注入故障测试弹性
-
故障注入配置
:可以使用虚拟服务
kubernetes/resilience-tests/product-virtual-service-with-faults.yml注入故障,示例如下:
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: product
spec:
hosts:
- product
http:
- route:
- destination:
host: product
fault:
abort:
httpStatus: 500
percentage:
value: 20
该定义表示发送到产品服务的请求中有 20% 将以 HTTP 状态码 500(内部服务器错误)中止。
-
测试步骤
:
1. 确保在观察服务网格部分启动的使用
siege
的负载测试正在运行。
2. 使用以下命令应用故障注入:
kubectl apply -f kubernetes/resilience-tests/product-virtual-service-with-faults.yml
-
监控
siege负载测试工具的输出。所有请求仍应成功(返回状态 200(OK)),但部分请求(20%)会多花一秒完成,这表明产品组合微服务中的重试机制已启动。 - 使用以下命令移除故障注入,结束测试:
kubectl delete -f kubernetes/resilience-tests/product-virtual-service-with-faults.yml
3.3 通过注入延迟测试弹性
-
延迟注入配置
:可以使用虚拟服务
kubernetes/resilience-tests/product-virtual-service-with-delay.yml注入延迟,示例如下:
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: product
spec:
hosts:
- product
http:
- route:
- destination:
host: product
fault:
delay:
fixedDelay: 3s
percent: 100
该定义表示发送到产品服务的所有请求将延迟 3 秒。产品组合服务发送到产品服务的请求配置为 2 秒后超时,断路器配置为连续 3 次请求失败时打开,打开后将快速失败(立即抛出异常,不尝试调用底层服务),产品组合微服务的业务逻辑将捕获此异常并应用回退逻辑。
-
测试步骤
:
1. 在运行
siege
的终端窗口中按
Ctrl + C
停止负载测试。
2. 使用以下命令在产品服务中创建临时延迟:
kubectl apply -f kubernetes/resilience-tests/product-virtual-service-with-delay.yml
- 获取访问令牌:
ACCESS_TOKEN=$(curl https://writer:secret-writer@minikube.me/oauth2/token -d grant_type=client_credentials -d scope="product:read product:write" -ks | jq .access_token -r)
echo ACCESS_TOKEN=$ACCESS_TOKEN
- 连续发送六个请求:
for i in {1..6}; do time curl -k https://minikube.me/product-composite/1 -H "Authorization: Bearer $ACCESS_TOKEN"; done
预期结果如下:
- 前三次失败调用后,断路器打开。
- 后三次调用,断路器应用快速失败逻辑。
- 后三次调用返回回退响应。
5. 使用以下命令移除临时延迟,模拟延迟问题已解决:
kubectl delete -f kubernetes/resilience-tests/product-virtual-service-with-delay.yml
-
使用步骤 4 中的
for循环命令发送新请求,验证是否再次返回正确答案且无延迟。
4. 实现零停机更新
4.1 零停机更新背景
随着越来越多的自治微服务相互独立更新,能够无停机部署更新变得至关重要。Kubernetes 可以执行滚动升级实现无停机,但无法在将所有用户路由到新版本之前测试新版本。而 Istio 可以部署新版本,但最初将所有用户路由到现有版本,之后使用细粒度路由机制控制用户如何路由到新旧版本。
4.2 升级策略
- 金丝雀部署 :除一组选定的测试用户被路由到新版本外,所有用户都被路由到旧版本。当测试用户批准新版本后,可以使用蓝绿部署将普通用户路由到新版本。
- 蓝绿部署 :传统的蓝绿部署是将所有用户切换到蓝色或绿色版本(一个是新版本,另一个是旧版本)。如果切换到新版本时出现问题,很容易切换回旧版本。使用 Istio 可以逐步将用户转移到新版本,例如从 20% 的用户开始,然后慢慢增加百分比。如果新版本中发现致命错误,随时可以将所有用户快速路由回旧版本。
4.3 部署场景
- 部署微服务的 v1 和 v2 版本,配置路由将所有请求发送到微服务的 v1 版本。
- 允许测试组进行金丝雀测试,验证微服务的新 v2 版本。为简化测试,仅部署核心微服务(产品、推荐和评论微服务)的新版本。
- 使用蓝绿部署开始将普通用户转移到新版本,最初是一小部分用户,随着时间推移,越来越多的用户被路由到新版本,直到最终所有用户都被路由到新版本。如果在新 v2 版本中检测到致命错误,可以快速切换回 v1 版本。
4.4 源代码更改
要同时运行微服务的多个版本,Deployment 对象及其对应的 Pod 必须有不同的名称,例如
product-v1
和
product-v2
,但每个微服务只能有一个 Kubernetes Service 对象,所有到特定微服务的流量始终通过同一个 Service 对象,无论请求最终将路由到哪个版本的 Pod。使用 Istio 的
VirtualService
和
DestinationRule
对象配置金丝雀测试和蓝绿部署的实际路由规则,
prod-env
Helm 图表中的
values.yaml
文件用于指定生产环境中每个微服务将使用的版本。
4.5 虚拟服务和目标规则
要在微服务的两个版本之间拆分流量,需要在发送方的虚拟服务中指定两个版本之间的权重分布,虚拟服务将流量分散到两个子集(
old
和
new
)。接收方的
DestinationRule
定义了
new
和
old
子集的具体含义,它使用标签来确定哪些 Pod 运行微服务的旧版本和新版本。为支持金丝雀测试,虚拟服务中需要一个路由规则,始终将金丝雀测试人员路由到新子集,假设金丝雀测试人员的请求包含名为
X-group
且值为
test
的 HTTP 头。
通用 Helm 图表中添加了一个模板
_istio_vs_green_blue_deploy.yaml
用于创建一组可以在微服务的两个版本之间拆分流量的虚拟服务,示例如下:
{{- define "common.istio_vs_green_blue_deploy" -}}
{{- range $name := .Values.virtualServices }}
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: {{ $name }}
spec:
hosts:
- {{ $name }}
http:
- match:
- headers:
X-group:
exact: test
route:
- destination:
host: {{ $name }}
subset: new
- route:
- destination:
host: {{ $name }}
subset: old
weight: 100
总结
本文介绍了 Istio 在服务网格中的多个重要功能,包括验证访问令牌、使用 mTLS 保护内部通信、确保服务网格的弹性以及实现零停机更新。通过这些功能,Istio 可以提高服务网格的安全性、可靠性和可管理性,帮助开发者更好地应对微服务架构中的各种挑战。
流程图
graph LR
A[开始] --> B[验证访问令牌]
B --> C[使用 mTLS 保护内部通信]
C --> D[确保服务网格弹性]
D --> E[实现零停机更新]
E --> F[结束]
表格
| 功能 | 描述 |
|---|---|
| 验证访问令牌 | 通过再次发起请求并验证失败信息,证明 Istio 验证访问令牌 |
| mTLS 保护内部通信 | 服务和客户端通过证书相互认证,Istio 自动化证书管理 |
| 服务网格弹性 | 通过注入故障和延迟测试重试和断路器机制 |
| 零停机更新 | 使用金丝雀部署和蓝绿部署策略,控制用户路由到新旧版本 |
5. 详细解析虚拟服务和目标规则
5.1 虚拟服务路由配置详解
在前面提到的虚拟服务模板
_istio_vs_green_blue_deploy.yaml
中,其核心作用是在微服务的不同版本间分配流量。下面详细解析其配置:
{{- define "common.istio_vs_green_blue_deploy" -}}
{{- range $name := .Values.virtualServices }}
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: {{ $name }}
spec:
hosts:
- {{ $name }}
http:
- match:
- headers:
X-group:
exact: test
route:
- destination:
host: {{ $name }}
subset: new
- route:
- destination:
host: {{ $name }}
subset: old
weight: 100
-
hosts字段 :指定了该虚拟服务所适用的主机名,这里使用{{ $name }}动态获取服务名称,意味着该虚拟服务会对指定名称的服务生效。 -
http部分 :包含了路由规则。-
第一个
match规则 :通过匹配 HTTP 头X-group的值为test,将符合条件的请求(即来自金丝雀测试人员的请求)路由到new子集,也就是新版本的微服务。 -
第二个
route规则 :将其他请求(即非金丝雀测试人员的请求)路由到old子集,权重为 100,表示目前所有这类请求都会被发送到旧版本的微服务。
-
第一个
5.2 目标规则的作用与配置
目标规则
DestinationRule
用于定义如何处理路由到不同子集的流量。它通过标签来区分不同版本的 Pod。示例如下:
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
name: product
spec:
host: product
subsets:
- name: old
labels:
version: v1
- name: new
labels:
version: v2
-
host字段 :指定了目标服务的名称。 -
subsets部分 :定义了不同的子集。-
old子集:通过labels中的version: v1标签,将带有该标签的 Pod 识别为旧版本的微服务实例。 -
new子集:同理,通过labels中的version: v2标签,将带有该标签的 Pod 识别为新版本的微服务实例。
-
6. 部署与服务配置
6.1 Deployment 对象配置
为了同时运行微服务的不同版本,Deployment 对象需要有不同的名称。以产品微服务为例,
product-v1
和
product-v2
的 Deployment 配置示例如下:
apiVersion: apps/v1
kind: Deployment
metadata:
name: product-v1
spec:
replicas: 3
selector:
matchLabels:
app: product
version: v1
template:
metadata:
labels:
app: product
version: v1
spec:
containers:
- name: product
image: product-image:v1
ports:
- containerPort: 8080
apiVersion: apps/v1
kind: Deployment
metadata:
name: product-v2
spec:
replicas: 3
selector:
matchLabels:
app: product
version: v2
template:
metadata:
labels:
app: product
version: v2
spec:
containers:
- name: product
image: product-image:v2
ports:
- containerPort: 8080
这里通过
labels
中的
version
字段区分不同版本的 Pod,同时
replicas
字段指定了每个版本的副本数量。
6.2 Service 对象配置
每个微服务只能有一个 Kubernetes Service 对象,它是流量进入微服务的统一入口。示例如下:
apiVersion: v1
kind: Service
metadata:
name: product
spec:
selector:
app: product
ports:
- protocol: TCP
port: 80
targetPort: 8080
selector
字段通过
app: product
标签选择所有属于产品微服务的 Pod,无论其版本是 v1 还是 v2。
7. 在 prod - env Helm 图表中整合配置
7.1 values.yaml 文件配置
prod-env
Helm 图表中的
values.yaml
文件用于指定生产环境中每个微服务将使用的版本。示例如下:
virtualServices:
- product
- recommendation
- review
product:
version: v2
recommendation:
version: v2
review:
version: v2
这里指定了产品、推荐和评论微服务在生产环境中使用的版本为 v2。
7.2 配置整合流程
以下是配置整合的流程图:
graph LR
A[values.yaml 配置版本] --> B[生成 VirtualService]
B --> C[生成 DestinationRule]
C --> D[创建 Deployment]
D --> E[创建 Service]
E --> F[流量根据规则路由]
通过
values.yaml
文件的配置,驱动生成相应的虚拟服务、目标规则,然后创建不同版本的 Deployment 和统一的 Service,最终实现流量根据规则在不同版本的微服务之间进行路由。
8. 总结与最佳实践
8.1 关键功能总结
- 安全性 :通过验证访问令牌和使用 mTLS 保护内部通信,提高了服务网格的安全性,防止攻击者窃听和非法访问。
- 弹性 :利用注入故障和延迟的方式,测试服务网格的重试和断路器机制,确保在面对临时故障时服务的稳定性。
- 零停机更新 :借助金丝雀部署和蓝绿部署策略,实现微服务的无停机更新,降低了升级风险。
8.2 最佳实践建议
- 安全性方面 :定期更新证书,确保 mTLS 配置的有效性;合理配置 JWT 发行者,防止访问令牌验证出现问题。
- 弹性方面 :在开发阶段就对重试和断路器机制进行充分测试,确保在生产环境中能够有效应对故障;根据服务的实际情况调整故障注入的比例和延迟时间,以更准确地模拟真实场景。
- 零停机更新方面 :在进行金丝雀测试时,选择合适的测试用户群体,确保能够全面发现新版本的问题;在蓝绿部署过程中,逐步增加新版本的流量比例,同时密切监控服务的运行状态。
表格总结
| 配置类型 | 关键配置项 | 作用 |
|---|---|---|
| VirtualService |
hosts
、
http
路由规则
| 在微服务不同版本间分配流量,支持金丝雀测试 |
| DestinationRule |
host
、
subsets
| 识别不同版本的微服务实例 |
| Deployment |
name
、
labels
| 创建不同版本的微服务 Pod |
| Service |
selector
| 作为流量进入微服务的统一入口 |
| values.yaml |
virtualServices
、各微服务版本
| 指定生产环境中微服务的版本 |
超级会员免费看
29

被折叠的 条评论
为什么被折叠?



