Istio由浅入深
# Istio由浅入深
# Istio简介
# 什么是Istio?
在企业普遍都做云平台的时代背景下,云平台架构带来的诸多好处,但是,不可否认的是,采用云技术架构会对DevOps团队造成很大的压力。 开发人员也必须使用微服务来构建可移植性,同时各大运营商正在管理超大型混合和多云部署。而Istio使得您可以连接,安全,控制和监控这些服务。
从更高的维度来看,Istio有助于降低部署的复杂性,减轻开发团队的负担。Istio是一个完全开源的服务网格(service mesh),可以透明地分层到现有的分布式应用程序上。同时,它也是一个平台,通过其提供的API,可将其集成到任何日志平台,telemetry或策略系统中。
Istio的多样化功能集使您能够高效地运行分布式微服务架构,并提供统一的方式来保护,连接和监视微服务。
# 什么是服务网格(serivce mesh)?
当整体应用程序向分布式微服务架构过渡时,Istio解决了开发人员和运营商面临的挑战。
服务网格
术语用于描述组成此类应用程序的微服务网络及其之间的交互。随着服务网格的大小和复杂性的增长,它变得越来越难以理解和管理。它的要求可以包括发现,负载平衡,故障恢复,指标和监控。服务网格通常还具有更复杂的操作要求,例如A/B测试,金丝雀,网速限制,访问控制和端到端的身份验证。
Istio提供了整个服务网格上的行为洞察力和操作控制,从而提供了完整的解决方案来满足微服务应用程序的各种需求。
# 为什么使用Istio?
Istio可以轻松在已部署服务网络上创建带有负载平衡,服务到服务的身份验证,监控等功能,并且服务代码中的代码无需变动(或变动很少)。通过在整个环境中部署一个特殊的sidecar代理来拦截微服务之间的所有网络通信,然后使用其平台控制功能配置和管理Istio,包括:
- 自动为HTTP,gRPC,WebSocket和TCP流量负载平衡。
- 通过丰富的路由规则,重试,故障转移和故障注入对流量行为进行细粒度控制。
- 可插拔的策略层和配置API,支持访问控制,速率限制和配额。
- 集群内所有流量的自动度量,日志和跟踪,包括集群的入口和出口。
- 通过强大的基于身份的身份验证和授权,在集群中进行安全的服务之间通信。
Istio专为可扩展性而设计,可满足多种部署需求。
# 核心功能
Istio在服务网络中统一提供了许多关键功能:
# 流量管理
Istio规则配置和流量路由使您可以很容易的控制服务之间的流量和API调用的流量。Istio简化了诸如断路器,超时和重试之类的服务级别属性的配置,并使其轻而易举地设置重要任务,如A/B测试,金丝雀部署和基于百分比的流量拆分。
借助对流量的更好可见性和开箱即用的故障恢复功能,无论遇到什么情况,您都可以在问题引起故障之前及时发现问题,使得调用更加可靠,网络也更加强大。
# 安全
Istio的安全功能使开发人员可以将精力集中在应用程序级别。Istio提供基础安全通信通道,并大规模管理服务通信的身份验证,授权和加密。 借助Istio,默认情况下可以保护服务通信的安全,从而使您能够在各种协议和运行时之间一致地执行策略 - 所有这些操作几乎不需要更改应用程序。
虽然Istio是独立于平台的,但将其与Kubernetes(或基础架构)网络策略配合使用,则好处更大,包括能够在网络和应用程序层保护Pod到Pod或服务到服务的通信的能力。
# 可观察性(Observability)
Istio强大的跟踪,监控和日志记录功能使您可以深入了解服务网格部署。借助Istio的监视功能,您可以真正了解服务性能如何影响上游和下游事物,而其自定义dashboards(仪表盘)则可以提供对所有服务性能的可视性,并让您了解该性能如何影响您的其他的程序的。
Istio的Mixer组件负责策略控制和遥测(telemetry)采集。它提供了后端抽象和中介,使Istio的其余部分与各个基础架构后端的实现细节隔离开来,并为操作员提供了对网格和基础架构后端之间所有交互的精细控制。
所有这些功能使您可以更有效地设置,监控和执行服务上的SLO。当然,最重要的是您可以快速有效地检测和修复故障。
# 平台支持
Istio是独立于平台的,旨在在多种环境中运行,包括跨Cloud,本地,Kubernetes,Mesos等的环境。您可以在Kubernetes或Consul的Nomad上部署Istio。Istio当前支持:
- Kubernetes上部署Service
- 向Consul注册Services
- 在单个虚拟机上运行Services
# 集成和自定义
Istio的策略执行组件可以扩展和自定义,以与现有的ACL,日志记录,监视,配额,审核等集成。
# 架构
Istio服务网格在逻辑上分为数据平面和控制平面。
- 数据plane由部署为sidecar的一组智能代理(Envoy)组成。这些代理中介和控制微服务与Mixer、通用策略和遥测集线器之间的所有网络通信。
- 控制平面管理并将代理配置为路由流量。另外,控制平面配置Mixers来执行策略和采集遥测数据。
下图显示了组成每个平面的不同组件:
# Envoy
Istio使用Envoy代理的扩展版本。Envoy是使用C++
开发的高性能代理,可为服务网格中的所有服务调解所有入站和出站流量。Istio利用Envoy的许多内置功能,如:
- 动态服务发现
- 负载均衡
- TLS termination
- HTTP/2和gRPC代理
- 熔断(Circuit breakers)
- 健康检查
- 分阶段推出,按百分比分配流量
- 故障注入
- 丰富的指标
Sidecar代理模型还允许你将Istio功能添加到现有部署中,而无需重新构造或重写代码。你可以阅读更多关于为什么我们在设计目标中选择这种方法的信息。
# Mixer
Mixer是一个与平台无关的组件。Mixer跨服务网格实施访问控制和使用策略,并从Envoy代理和其他服务收集遥测数据。 代理提取请求级别属性,并将其发送到Mixer进行评估。您可以在我们的“Mixer配置”文档中找到有关此属性提取和策略评估的更多信息。
Mixer包含一个灵活的插件模型。该模型使Istio可以与各种主机环境和基础架构后端交互。因此,Istio从这些细节中抽象了Envoy代理和Istio管理的服务。
# Pilot
Pilot提供了Envoy sidecar的服务发现,智能路由的流量管理功能(例如 A/B测试,金丝雀等)和弹性(超时,重试,断路器等)。
Pilot将控制流量行为的高级路由规则转换为Envoy特定的配置,并在运行时将其传送到sidecar。Pilot提取了特定于平台的服务发现机制,并将它们合成为标准格式,任何符合Envoy数据平面API的Sidecar都可以使用。这种松散的耦合使得Istio可以在Kubernetes,Consul或Nomad等多种环境中运行,同时为流量管理保留相同的操作员界面。
# Citadel
Citadel通过内置的身份和凭据管理实现了强大的服务到服务和终端用户的身份验证。可以使用Citadel来升级服务网格中的未加密流量。使用Citadel,运营商可以基于服务身份而不是相对不稳定的3层或4层网络识别码来实施策略。从版本0.5开始,您可以使用Istio的授权功能来控制谁可以访问您的服务。
# Galley
Galley是Istio的配置验证,提取,处理和分发组件。它负责将其余Istio组件与从底层平台(例如Kubernetes)获取用户配置的细节隔离开来。
最大化透明度
:要采用Istio,要求操作员或开发人员进行尽可能少的工作,以从系统中获得真正的价值。为此,Istio可以自动将其自身插入服务之间的所有网络路径中。Istio使用Sidecar代理来捕获流量,并在可能的情况下自动对网络层进行编程,以通过这些代理路由流量,而无需更改已部署的应用程序代码。 在Kubernetes中,代理被注入到Pod中,并通过对iptables规则进行编程来捕获流量。 一旦注入了sidecar代理并且对流量路由进行了编程,Istio就可以调解所有流量。此原则也适用于性能。将Istio应用于部署时,运营商会发现所提供功能的资源成本增加最小。组件和API必须在设计时充分考虑性能和规模。可扩展性
:随着运营商和开发人员越来越依赖Istio提供的功能,系统也必须随着他们的需求而增长。在我们继续添加新功能的同时,最大的需求是扩展策略系统,与其他策略和控制源集成以及将有关网格行为的信号传送到的其他系统以进行分析。策略运行时支持插入其他服务的标准扩展机制。此外,它还允许扩展其词汇表,从而可以根据网格生成的新信号来实施策略。可移植性
:使用Istio的生态系统在许多方面都不同。Istio必须能在任何云端或本地环境中运行,只需最少的努力。将基于Istio的服务移植到新环境的任务必须很简单。使用Istio,您可以操作部署到多个环境中的单个服务。例如,可以在多个云上部署以实现冗余。策略统一性
:将策略应用于服务之间的API调用提供了对网格行为的大量控制。但是,将策略应用于不一定在API级别表达的资源也同样重要。例如,对ML训练任务消耗的CPU数量应用quota比对启动wrok的调用应用quota更有用。为此,Istio将策略系统作为具有其自己的API的独特服务来维护,而不是将策略系统烘焙到代理Sidecar中,从而使得服务根据需要直接与其集成。
# Istio的安装
# 使用 Istioctl 安装
# 1.拉取Istio的安装包
$ curl -O http://192.168.1.110/file/istio-1.9.5-linux-amd64.tar.gz
$ ls
istio-1.9.5-linux-amd64.tar.gz
$ tar -zxvf istio-1.9.5-linux-amd64.tar.gz
# 2.复制istioctl工具
$ cd istio-1.9.5/
$ cp bin/istioctl /usr/local/bin/
# 3.使用默认配置文件安装 Istio
最简单的选择是 使用以下命令安装default
Istio 配置文件 (opens new window):
$ istioctl install
# 4.安装不同的配置文件
通过在命令行上传递配置文件名称,可以将其他 Istio 配置文件安装到集群中。例如,以下命令可用于安装demo
配置文件:
$ kubectl create ns istio-system
$ istioctl install --set profile=demo -y
✔ Istio core installed
✔ Istiod installed
✔ Egress gateways installed
✔ Ingress gateways installed
✔ Installation complete
# 5.检查安装了什么
该istioctl
命令将IstioOperator
用于安装 Istio的CR保存在名为installed-state
. 而不是检查 Istio 安装的部署、pod、服务和其他资源,例如:
$ kubectl -n istio-system get deploy
NAME READY UP-TO-DATE AVAILABLE AGE
istio-egressgateway 1/1 1 1 25s
istio-ingressgateway 1/1 1 1 24s
istiod 1/1 1 1 20s
# 6.注入默认变量
将目录更改为 Istio 安装的根目录。
默认的 Istio 安装使用自动边车注入 (opens new window)。标记将托管应用程序的命名空间istio-injection=enabled
:
$ kubectl label namespace default istio-injection=enabled
# 7.卸载 Istio
要从集群中完全卸载 Istio,请运行以下命令:
$ istioctl x uninstall --purge
可选--purge
标志将删除所有 Istio 资源,包括可能与其他 Istio 控制平面共享的集群范围的资源。
# 测试一个实例
# Bookinfo
Bookinfo 应用程序分为四个独立的微服务:
productpage
. 该productpage
微服务调用details
和reviews
微服务来填充页面。details
. 该details
微服务包含图书信息。reviews
. 该reviews
微服务包含了书评。它还调用ratings
微服务。ratings
. 该ratings
微服务包含预定伴随书评排名信息。
reviews
微服务有 3 个版本:
- 版本 v1 不调用该
ratings
服务。 - 版本 v2 调用该
ratings
服务,并将每个评级显示为 1 到 5 颗黑星。 - 版本 v3 调用该
ratings
服务,并将每个评级显示为 1 到 5 颗红星。
该应用程序的端到端架构如下所示。
# 使用Istio部署应用程序🚀
使用 Istio 运行示例不需要更改应用程序本身。相反,您只需要在支持 Istio 的环境中配置和运行服务,并在每个服务旁边注入 Envoy sidecar。生成的部署将如下所示:
# 启动应用服务🚀
使用以下kubectl
命令部署您的应用程序:
$ kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml
# 查看所有服务🚀
[root@master istio-1.9.5]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
details ClusterIP 10.104.17.202 <none> 9080/TCP 21m
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 41m
productpage ClusterIP 10.110.235.179 <none> 9080/TCP 21m
ratings ClusterIP 10.104.53.51 <none> 9080/TCP 21m
reviews ClusterIP 10.101.21.63 <none> 9080/TCP 21m
[root@master istio-1.9.5]# kubectl get pods
NAME READY STATUS RESTARTS AGE
details-v1-79f774bdb9-gwhkf 2/2 Running 0 21m
productpage-v1-6b746f74dc-6dtg8 2/2 Running 0 21m
ratings-v1-b6994bb9-9h2wn 2/2 Running 0 21m
reviews-v1-545db77b95-6cmmv 2/2 Running 0 21m
reviews-v2-7bf8c9648f-m2k9d 2/2 Running 0 21m
reviews-v3-84779c7bbc-vd86b 2/2 Running 0 21m
# 通过Curl请求测试🚀
要确认 Bookinfo 应用程序正在运行,请通过curl
来自某个 pod的命令向其发送请求,例如来自ratings
:
[root@master istio-1.9.5]# kubectl exec "$(kubectl get pod -l app=ratings -o jsonpath='{.items[0].metadata.name}')" -c ratings -- curl -sS productpage:9080/productpage | grep -o "<title>.*</title>"
<title>Simple Bookstore App</title>
# 确定入口IP和端口🚀
现在 Bookinfo 服务已启动并运行,您需要使应用程序可从 Kubernetes 集群外部访问,例如从浏览器访问。一个Istio网关 (opens new window) 用于此目的。
为应用定义入口网关
[root@master istio-1.9.5]# kubectl apply -f samples/bookinfo/networking/bookinfo-gateway.yaml
gateway.networking.istio.io/bookinfo-gateway created
virtualservice.networking.istio.io/bookinfo created
确认网关已创建
[root@master istio-1.9.5]# kubectl get gateway
NAME AGE
bookinfo-gateway 61s
改为NodePort
此处我们并没有外部负载,所以要将svc修改成NodePort的方式。
[root@master istio-1.9.5]# kubectl get svc -n istio-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
istio-egressgateway ClusterIP 10.102.197.30 <none> 80/TCP,443/TCP,15443/TCP 89m
istio-ingressgateway LoadBalancer 10.109.71.35 <none> 15021:31964/TCP,80:31467/TCP,443:31845/TCP,31400:31077/TCP,15443:30757/TCP 89m
istiod ClusterIP 10.111.156.86 <none> 15010/TCP,15012/TCP,443/TCP,15014/TCP 90m
#访问masterIP+80端口对应暴露的31962
注意: 如果 EXTERNAL-IP 设置了该值,则要求您的环境具有可用于 Ingress 网关的外部负载均衡器。如果 EXTERNAL-IP 值是 <none>(或一直是 <pending> ),则说明可能您的环境不支持为 ingress 网关提供外部负载均衡器的功能。在这种情况下,您可以使用 Service 的 node port 方式访问网关。
使用 kubectl patch 更新 istio-ingressgateway 服务网关类型
[root@master istio-1.9.5]# kubectl patch service istio-ingressgateway -n istio-system -p '{"spec":{"type":"NodePort"}}'
设置入口端口
$ export INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="http2")].nodePort}')
$ export SECURE_INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="https")].nodePort}')
$ export TCP_INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="tcp")].nodePort}')
获取ingress ip地址
$ export INGRESS_HOST=$(kubectl get po -l istio=ingressgateway -n istio-system -o jsonpath='{.items[0].status.hostIP}')
# 应用默认目标规则🚀
在使用 Istio 控制 Bookinfo 版本路由之前,您需要在目标规则中 (opens new window)定义可用版本,称为子集。
运行以下命令为 Bookinfo 服务创建默认目标规则:
$ kubectl apply -f samples/bookinfo/networking/destination-rule-all.yaml
在
default
与demo
配置轮廓 (opens new window)具有自动相互TLS (opens new window)启用默认情况下。要强制实施双向 TLS,请使用samples/bookinfo/networking/destination-rule-all-mtls.yaml
.
# 清理🚀
当您完成 Bookinfo 示例的试验后,请按照以下说明卸载并清理它:
删除路由规则并终止应用程序 Pod
$ samples/bookinfo/platform/kube/cleanup.sh
确认关机
$ kubectl get virtualservices #-- there should be no virtual services
$ kubectl get destinationrules #-- there should be no destination rules
$ kubectl get gateway #-- there should be no gateway
$ kubectl get pods #-- the Bookinfo pods should be deleted
# 介绍Istio流量管理
# 首先是Istio如何在网格种引导流量的?
- 首先要知道所在的端点在哪里?
- 以及它们属于哪些服务?
- 填充自己的服务注册(就是内部的注册表,用来生成一些Envoy配置)连接到服务发现系统。
- 在Kubernetes中安装Istio,那么Istio会自动检测集群中的服务和端点。
# 在Kubernetes中如何使用istio?
1.通过Kubernetes安装istio之后对K8S使用CRD扩展, 也就是Kubernetes的 API扩展
,istio也能像其他 的resouces一样使用。
2.通过使用Kubrenetes API的扩展,可以使用资源有 虚拟服务、目的规则、网关、服务条目、边车。
# 在istio中能学到什么?
1.可以学习到流量的管理、入口出口流量管理。
请求路由
故障注入
交通转移
TCP流量转移
请求超时
熔断
镜像
局部负载均衡
入口的网关
出口的访问外部服务
# 什么是虚拟服务?
虚拟服务以及目标规则是Istio流量路由功能的关键构建块。虚拟服务允许您配置如何将请求的路由到Istio的服务网格中的服务。 建立在Istio和您的平台提供基本的连接和发现之上。每一个虚拟服务都包含了一组按顺序评估的路由规则,让Istio将每一个给定的虚拟服务请求匹配到网格中特定真实的目的地。
# 为什么要使用虚拟服务?
1.通过将客户端从实际实现它们的目标工作负载发送请求的位置强解耦来做到这一点。虚拟服务还提供一种丰富的方式来指定不同的流量路由规则,以将流量发送到这些工作负载。
2.为什么说这很有用? Envoy在所有服务实例之间使用循环负载平衡来分配流量,对此进行了改进,可以针对百分比进行特定的流量引导。
3.可以一个或者多个指定主机名指定流量行为。 在虚拟服务中,使用路由规则来告诉Envoy如何将虚拟服务的流量发送到合适的地方。
4.还可以将单个虚拟服务处理多个应用程序。 结合网关的流量规则,控制流量进出。
# 虚拟服务实例
以下的虚拟服务根据请求是否来自特定的用户,将请求路由到不同版本的服务。
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: reviews
spec:
hosts:
- reviews
http:
- match:
- headers:
end-user:
exact: jason
route:
- destination:
host: reviews
subset: v2
- route:
- destination:
host: reviews
subset: v3
# 主机字段
1.hosts字段
列出了虚拟服务的主机,这些路由规则适用的用户可以寻址目的或目的地。这是客户端在向 服务发送请求时使用的地址。
2.主机名可以是IP地址
,也可以是DNS名称
以及解析的。
hosts:
- reviews
# 路由规则
1.该http部分包含虚拟服务的路由规则,描述了将HTTP/1.1 HTTP2和gRPC流量
路由到hosts字段中指定的 目标的匹配条件和操作。
2.匹配条件(示例中的第一个路由有一个条件,因此以match字段开头。在这种情况下,您希望此路由适 用于来自用户‘jason’的所有请求,因此您使用headers、end-user和exact字段来选择适当的请求。)
- match:
- headers:
end-user:
exact: jason
# 目的地
路由部分destination字段
指定符合条件的流量实际目的地。 与虚拟主机不同,目标主机必须存在于istio服务注册表中真实目标,否则Envoy将不知道将流量发送到何处。
route:
- destintion:
host: reviews
subnet: v2
# 路由规则优先级
路由规则按照上到下的顺序进行评估,虚拟服务定义中的第一个规则被赋予最高优先级。
在这种情况下 ,您希望任何第一条路由规则不匹配的东西都转到第二条规则中指定默认的目的地。 因此,第二条规则 没有匹配条件,只是将流量定向到v3子集。
- route:
- destination:
host: reviews
subnet: v3
# 有关路由的更多信息
路由规则是将特定流量子集路由到特定目的地的强大工具。您可以对流量端口、标头字段、URL等设置匹 配条件
,例如:此虚拟服务允许用户将流量发送到两个单独的服务,评分和评论,就好像它们是更大的虚拟服务的一部分一样。虚拟服务规则根据请求的URL匹配流量,并将流量请求定向到合适的服务。
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: bookinfo
spec:
hosts:
- bookinfo.com
http:
- match:
- uri:
prefix: /reviews
route:
- destination:
host: reviews
- match:
- uri:
prefix: /ratings
route:
- destination:
host: ratings
除了使用匹配条件外,您还可以按"权重"百分比分配流量。这对于A/B测试和金丝雀发布很有用。
spec:
hosts:
- reviews
http:
- route
- destination:
host: reviews
subnet: v1
weight: 75
- destination:
host: reviews
subnet: v2
weight: 25
您还可以使用路由规则对流量执行一些操作,例如:
附加或删除标题
重写网址
为对该目的地的调用设置重试策略
# 目的地规则
1.目标规则是Istio流量路由功能的关键部分。可以将虚拟服务视为流量将路由给定目 的地的方式,目标规则在评估虚拟服务路由规则后应用,因此它们适用于流量的"真实"目标。 2.特别是,您使用目标规则来指定命名的服务子集,例如按版本对所有给定服务的实例进行分组。然后,您可以在虚拟服务的路由规则中使用这些服务子集来控制到不同服务实例的流量。 3.目标规则还允许您在调用整个目标服务或特定服务子集时自定义 Envoy 的流量策略,例如您首选的负 载平衡模型、TLS 安全模式或断路器设置。
# 负载平衡选项
默认情况下,Istio 使用循环负载均衡策略,实例池中的每个服务实例依次收到请求。Istio 还支持以下模型,您可以在目标规则中为对特定服务或服务子集的请求指定这些模型。
随机:
请求随机转发到池中的实例。加权:
根据特定百分比将请求转发到池中的实例。最少请求:
请求被转发到请求数量最少的实例。
# 目标规则示例
以下示例目标规则为my-svc目标服务配置了三个不同的子集,具有不同的负载平衡策略:
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: my-destination-rule
spec:
host: my-svc
trafficPolicy:
loadBalancer:
simple: RANDOM
subsets:
- name: v1
labels:
version: v1
- name: v2
labels:
version: v2
trafficPolicy:
loadBalancer:
simple: ROUND_ROBIN
- name: v3
labels:
version: v3
每个子集都是基于一个或多个定义的labels,在Kubernetes中,它们是附加到Pod等对象的键/值对。这些标签应用于Kubernetes服务的部署,metadata以识别不同的版本。
除了定义子集之外,此目标规则还具有针对此目标中所有子集的默认流量策略和仅针对该子集覆盖它的子集特定策略。在该字段上方定义的默认策略为和子集subsets 设置简单的随机负载均衡器。在 策略中, 在相应子集的字段中指定了循环负载均衡器。v1v3v2
# 网关
1.您使用网关来管理网格的入站和出站流量,让您指定要进入或离开网格的流量
。网关配置应用于在网格边缘运行的独立 Envoy 代理,而不是与服务工作负载一起运行的 Sidecar Envoy 代理。
2.与控制进入系统的流量的其他机制(例如 Kubernetes Ingress API)不同,Istio 网关让您可以使用 Istio 流量路由的全部功能和灵活性
。您可以这样做,因为 Istio 的网关资源只允许您配置 4-6 层负载平衡属性,例如要公开的端口、TLS 设置等。然后,您无需将应用层流量路由 (L7) 添加到同一 API 资 源,而是将常规 Istio虚拟服务绑定到网关。这使您可以像管理 Istio 网格中的任何其他数据平面流量 一样管理网关流量。
3.网关主要用于管理入口流量,但您也可以配置出口网关
。例如,出口网关允许您为离开网格的流量配置专用出口节点,让您限制哪些服务可以或应该访问外部网络,或者启用 对出口流量的安全控制 以增加网格的安全性。您还可以使用网关来配置纯内部代理。
4.Istio 提供了一些您可以使用的预配置网关代理部署 (istio-ingressgateway和istio-egressgateway)如果您使用我们的演示安装,则会部署两者,而仅使用我们的默认配置文件部署入口网关 。您可以将自 己的网关配置应用于这些部署,或者部署和配置自己的网关代理。
# 网关示例
以下示例显示了外部 HTTPS 入口流量的可能网关配置:
此网关配置允许 HTTPS 流量
从ext-host.example.com端口 443 进入网格
,但没有为流量指定任何路由。
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: ext-host-gwy
spec:
selector:
app: my-gateway-controller
servers:
- port:
number: 443
name: https
protocol: HTTPS
hosts:
- ext-host.example.com
tls:
mode: SIMPLE
credentialName: ext-host-cert
要指定路由并使网关按预期工作,您还必须将网关绑定到虚拟服务。您可以使用虚拟服务的gateways字 段执行此操作,如以下示例所示:
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: virtual-svc
spec:
hosts:
- ext-host.example.com
gateways:
- ext-host-gwy
然后,您可以使用外部流量的路由规则配置虚拟服务。
# 服务条目
您可以使用服务条目向Istio内部维护的服务注册表添加一个条目。添加服务条目之后,Envoy代理可以将流量发送到服务,就好像它是网格中的服务一样。配置服务条目允许您管理在网格之外允许的服务流量,包括以下任务:
重定向和转发外部目的地的流量
,例如从web使用的API,或到旧基础设施中的服务流量。为外部目标定义重试、超时和故障注入策略。
通过虚拟机添加网格中,在虚拟机中允许网格服务。
您无需希望网格服务使用的每个外部服务添加服务条目,默认情况下,Istio将Envoy代理设置为将请求传递给未知服务。但是,您不能使用Istio功能来控制到未来在网格中注册的目的地流量。
# 服务入口示例
以下示例网格外部服务条目将ext-svc.example.com 外部依赖项添加到 Istio 的服务注册表
:
您使用该hosts字段指定外部资源。您可以完全限定它或使用通配符前缀的域名。
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
name: svc-entry
spec:
hosts:
- ext-svc.example.com
ports:
- number: 443
name: https
protocol: HTTPS
location: MESH_EXTERNAL
resolution: DNS
您可以配置虚拟服务和目标规则,以更精细的方式控制服务条目的流量,就像为网格中的任何其他服务配置流量一样。例如,以下目标规则将流量路由配置为使用双向 TLS 来保护与 ext-svc.example.com我们 使用服务条目配置的外部服务的连接
:
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: ext-res-dr
spec:
host: ext-svc.example.com
trafficPolicy:
tls:
mode: MUTUAL
clientCertificate: /etc/certs/myclientcert.pem
privateKey: /etc/certs/client_private_key.pem
caCertificates: /etc/certs/rootcacerts.pem
# 边车
默认情况下,Istio将每个Envo代理配置为接受其相关工作负载的所有端口上的流量,并在转发流量时到 达网格中的每个工作负载。您可以使用sidecar配置执行以下操作:
微调Envoy代理接受的端口和协议集
限制Envoy代理可以访问的服务集
您可能希望在大型的应用程序像这样限制sidecar的可访问性,其中将每个代理配置为访问网格中的所有 其他服务可能会由于高内存的使用率而影响网格的性能。
您可以指定希望将 sidecar 配置应用于特定命名空间中的所有工作负载,或者使用 workloadSelector. 例如,以下 sidecar 配置将命名空间中的所有服务配置bookinfo为仅访问在同一命名空间和 Istio 控制平面中运行的服务(Istio 的出口和遥测功能需要):
apiVersion: networking.istio.io/v1alpha3
kind: Sidecar
metadata:
name: default
namespace: bookinfo
spec:
egress:
- hosts:
- "./*"
- "istio-system/*"
# 网络弹性和测试
除了帮助您引导网格周围的流量外,Istio 还提供了可选的故障恢复和故障注入功能,您可以在运行时动态配置这些功能。使用这些功能可以帮助您的应用程序可靠地运行,确保服务网格可以容忍故障节点并防止局部故障级联到其他节点。
# 超时
超时是 Envoy 代理应该等待来自给定服务的回复的时间量,确保服务不会无限期地等待回复,并且调用 在可预测的时间范围内成功或失败。
默认情况下,Istio 中禁用了 HTTP 请求的 Envoy 超时.
对于某些应用程序和服务,Istio 的默认超时可能不合适。例如,过长的超时可能会导致等待来自失败服务的回复的延迟过长,而过短的超时可能会导致调用在等待涉及多个服务的操作返回时不必要地失败。为了找到并使用您的最佳超时设置,Istio 允许您使用虚拟服务轻松地在每个服务的基础上动态调整超时,而无需编辑您的服务代码。下面是一个虚拟服务,它为调用 rating 服务的 v1 子集指定了 10 秒的超时时间:
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: ratings
spec:
hosts:
- ratings
http:
- route:
- destination:
host: ratings
subset: v1
timeout: 10s
# 重试
重试设置指定 Envoy 代理在初始调用失败时尝试连接服务的最大次数。
重试可以通过确保调用不会因为 临时性问题(例如临时过载的服务或网络)而永久失败,从而提高服务可用性和应用程序性能。重试间隔(25ms+)是可变的,由 Istio 自动确定,防止被调用的服务被请求淹没。HTTP 请求的默认重试行为是 在返回错误之前重试两次。
与超时一样,Istio 的默认重试行为可能不适合您的应用程序在延迟(失败的服务重试次数过多可能会减慢速度)或可用性方面的需求。也像超时一样,您可以在虚拟服务中基于每个服务调整重试设置,而无需接触您的服务代码。您还可以通过添加每次重试超时来进一步优化您的重试行为,指定您希望等待每次重试尝试成功连接到服务的时间量。以下示例在初始调用失败后配置最多 3 次重试以连接到此服务子集, 每次重试时间为 2 秒。
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: ratings
spec:
hosts:
- ratings
http:
- route:
- destination:
host: ratings
subset: v1
retries:
attempts: 3
perTryTimeout: 2s
# 熔断
熔断是 Istio 提供的另一种有用的机制,用于创建基于微服务的弹性应用程序。
在断路器中,您可以 设置对服务中各个主机的调用限制,例如并发连接数或对该主机的调用失败的次数。一旦达到该限制,断路器就会“跳闸”并停止与该主机的进一步连接。使用断路器模式可以实现快速故障,而不是客户端尝试连接到过载或故障主机。
由于断路器适用于负载平衡池中的“真实”网格目标,您可以在目标规则中配置断路器阈值 ,并将设置应 用于服务中的每个单独的主机。以下示例reviews将 v1 子集的服务工作负载的并发连接数限制为 100:
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: reviews
spec:
host: reviews
subsets:
- name: v1
labels:
version: v1
trafficPolicy:
connectionPool:
tcp:
maxConnections: 100
# 故障注入
配置好网络(包括故障恢复策略)后,您可以使用 Istio 的故障注入机制来测试整个应用程序的故障恢复能力。
故障注入是一种将错误引入系统以确保系统能够承受并从错误条件中恢复的测试方法。使用故障注入对于确保您的故障恢复策略不会不兼容或限制过多,可能会导致关键服务不可用特别有用。
与其他引入错误的机制(例如延迟数据包或在网络层杀死 pod)不同,Istio 允许您在应用程序层注入故障。这使您可以注入更多相关故障,例如 HTTP 错误代码,以获得更多相关结果。
您可以注入两种类型的故障,均使用 虚拟服务进行配置:
延迟:
延迟是计时失败。它们模仿增加的网络延迟或过载的上游服务。中止:
中止是崩溃失败。它们模仿上游服务中的故障。中止通常以 HTTP 错误代码或 TCP 连接失败的 形式出现。
例如,此虚拟服务对ratings服务的每 1000 个请求中的 1 个引入了 5 秒的延迟。
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: ratings
spec:
hosts:
- ratings
http:
- fault:
delay:
percentage:
value: 0.1
fixedDelay: 5s
route:
- destination:
host: ratings
subset: v1
# Sidecar注入
# 对工作负载的一些要求
支持的工作负载类型:
Job
DaemonSet
ReplicaSet
Pod
Deployment
等,对这些工作负载的要求如下:
- 要正确命名服务端口:
Service
对象中的Port
部分必须以 "协议名" 为前缀,目前支持的协议名包括http
,http2
,mongo
,redis
与grpc
;- istio 会命名来确定为端口提供什么样的服务,不符合命名规范的端口会被当做 TCP 服务器,其功能支持范围会大幅缩小。
- 工作负载的 Pod 必须有关联的 Service:
- 为满足服务发现的需要,所有 Pod 都必须有关联的服务;
- 官方建议为 Pod 模板加入两个标签:
app
与version
,分别标注应用名称与版本,虽仅是个建议,但 istio 很多默认策略会引用这两个标签,如果没有会引发很多不必要的麻烦。
# 手工注入
创建一个deployment做nginx的pod控制器,副本维持在2个。
[root@k8s-master-node1 ~]# cat deploymen.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
namespace: test
name: test
spec:
replicas: 2
selector:
matchLabels:
app: test
template:
metadata:
labels:
app: test
spec:
containers:
- image: nginx
name: nginx
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
[root@k8s-master-node1 ~]# kubectl apply -f deploymen.yaml
deployment.apps/test created
istioctl kube-inject 会将 istio 相关容器注入应用,"-o" 参数将注入结果生成 yaml 文件 ,方便观察使用此命令与 "kubectl apply -f" 的区别;
[root@k8s-master-node1 istio-work]# istioctl kube-inject -f deployment.yaml -o deployment-inject.yaml
新的 yaml 文件中多出了 "Sidecar" 容器, 并且出现了1个初始化容器 (initContainers) "istio-init" ,初始化容器即用来劫持应用通信到 "Sidecar" 容器的工具;
可直接 "kubectl apply -f" 生成的 yaml 文件,注入后 Nginx 应用 与 Istio-Proxy 共享网络命名空间
。
[root@k8s-master-node1 istio-work]# istioctl kube-inject -f /root/deploymen.yaml | kubectl apply -f - -n test
deployment.apps/test configured
[root@k8s-master-node1 istio-work]# kubectl get pods -n test
NAME READY STATUS RESTARTS AGE
test-67b6d4d78c-jzzml 2/2 Running 0 20s
test-67b6d4d78c-sfxk8 2/2 Running 0 20s
[root@k8s-master-node1 istio-work]# kubectl exec -it -n test test-67b6d4d78c-jzzml -c istio-proxy -- netstat -ntlp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:15021 0.0.0.0:* LISTEN 16/envoy
tcp 0 0 0.0.0.0:15021 0.0.0.0:* LISTEN 16/envoy
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:15090 0.0.0.0:* LISTEN 16/envoy
tcp 0 0 0.0.0.0:15090 0.0.0.0:* LISTEN 16/envoy
tcp 0 0 127.0.0.1:15000 0.0.0.0:* LISTEN 16/envoy
tcp 0 0 0.0.0.0:15001 0.0.0.0:* LISTEN 16/envoy
tcp 0 0 0.0.0.0:15001 0.0.0.0:* LISTEN 16/envoy
tcp 0 0 127.0.0.1:15004 0.0.0.0:* LISTEN 1/pilot-agent
tcp 0 0 0.0.0.0:15006 0.0.0.0:* LISTEN 16/envoy
tcp 0 0 0.0.0.0:15006 0.0.0.0:* LISTEN 16/envoy
tcp6 0 0 :::15020 :::* LISTEN 1/pilot-agent
tcp6 0 0 :::80 :::* LISTEN -
# 请求路由
# 环境准备
- 部署Bookinfo (opens new window)示例应用程序。
- 了解了流量管理的基础概念,以及专业语术。
注意:
Istio Bookinfo (opens new window)示例由四个独立的微服务组成,每个微服务都有多个版本。其中一个微服务的三个不同版本
reviews
已部署并同时运行。为了说明这导致的问题,请/productpage
在浏览器中访问 Bookinfo 应用程序并刷新几次。您会注意到,有时书评输出包含星级,有时则不包含。这是因为如果没有明确的默认服务版本来路由,Istio 会以循环方式将请求路由到所有可用版本。
# 应用虚拟服务
要仅路由到一个版本,您可以应用为微服务设置默认版本的虚拟服务。在这种情况下,虚拟服务会将所有流量路由到v1
每个微服务。
- 运行以下命令以应用虚拟服务:
$ kubectl apply -f samples/bookinfo/networking/virtual-service-all-v1.yaml
因为配置传播最终是一致的,所以等待几秒钟让虚拟服务生效。
- 使用以下命令显示定义的路由:
$ cat istio-1.9.5/samples/bookinfo/networking/virtual-service-all-v1.yaml
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: productpage
spec:
hosts:
- productpage
http:
- route:
- destination:
host: productpage
subset: v1
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: reviews
spec:
hosts:
- reviews
http:
- route:
- destination:
host: reviews
subset: v1
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: ratings
spec:
hosts:
- ratings
http:
- route:
- destination:
host: ratings
subset: v1
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: details
spec:
hosts:
- details
http:
- route:
- destination:
host: details
subset: v1
- 您还可以
subset
使用以下命令显示相应的定义:
$ kubectl get destinationrules -o yaml
您已将 Istio 配置为路由到v1
Bookinfo 微服务的版本,最重要的是reviews
服务版本 1。
# 测试新的路由配置
您可以通过再次刷新/productpage
Bookinfo 应用程序轻松测试新配置。
在浏览器中打开 Bookinfo 站点。URL 是
http://$GATEWAY_URL/productpage
,其中$GATEWAY_URL
是入口的外部 IP 地址,如Bookinfo (opens new window)文档中所述。请注意,无论您刷新多少次,页面的评论部分都不会显示评分星。这是因为您将 Istio 配置为将评论服务的所有流量路由到该版本
reviews:v1
,并且该版本的服务不访问星级评分服务。
您已成功完成此任务的第一部分:将流量路由到服务的一个版本
# 基于用户身份的路由
接下来,您将更改路由配置,以便将来自特定用户的所有流量路由到特定服务版本。在这种情况下,来自名为 Jason 的用户的所有流量都将路由到服务reviews:v2
。
该示例的启用是因为该productpage
服务将自定义end-user
标头添加到所有到评论服务的出站 HTTP 请求。
Istio 还支持在入口网关上基于强认证 JWT 的路由,有关更多详细信息,请参阅 基于 JWT 声明的路由 (opens new window)。
请记住,reviews:v2
是包含星级评分功能的版本。
- 运行以下命令以启用基于用户的路由:
$ kubectl apply -f samples/bookinfo/networking/virtual-service-reviews-test-v2.yaml
- 确认规则已创建:
创建规则允许用户jasone
[root@k8s-master-node1 networking]# cat virtual-service-reviews-test-v2.yaml
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: reviews
spec:
hosts:
- reviews
http:
- match:
- headers:
end-user:
exact: jason
route:
- destination:
host: reviews
subset: v2
- route:
- destination:
host: reviews
subset: v1
在
/productpage
Bookinfo 应用程序上,以 user 身份登录jason
。刷新浏览器。你看到了什么?星级评分显示在每条评论旁边。以其他用户身份登录(选择您想要的任何名称)。刷新浏览器。现在星星不见了。这是因为流量被路由到
reviews:v1
除了 Jason 之外的所有用户。您已成功配置 Istio 以根据用户身份路由流量。
# 清理
删除应用程序路由规则:
$ kubectl delete -f samples/bookinfo/networking/virtual-service-all-v1.yaml
# 故障注入
# 环境准备
- 部署Bookinfo (opens new window)示例应用程序,应用目标规则。
- 了解了流量管理的基础概念,以及专业语术。
通过执行 请求路由 (opens new window)任务或运行以下命令来应用应用程序版本路由:
$ kubectl apply -f samples/bookinfo/networking/virtual-service-all-v1.yaml $ kubectl apply -f samples/bookinfo/networking/virtual-service-reviews-test-v2.yaml
# 注入HTTP延迟错误
要测试 Bookinfo 应用程序微服务的弹性,请在userreviews:v2
和微服务之间注入 7s 延迟。此测试将发现一个有意引入 Bookinfo 应用程序的错误。ratings jason
请注意,该reviews:v2
服务对服务的调用有 10 秒的硬编码连接超时ratings
。即使您引入了 7 秒延迟,您仍然希望端到端流程继续进行而不会出现任何错误。
- 创建故障注入规则以延迟来自测试用户的流量
jason
。
$ kubectl apply -f samples/bookinfo/networking/virtual-service-ratings-test-delay.yaml
- 确认规则已创建:
创建规则通过jason用户登录
在微服务之间注入7s的延迟
参数详解:
fault: # 要应用于客户端HTTP流量的故障注入策略。 delay: # 延迟 percentage: # 百分率 value: 100.0 # 键值为百分之一百 fixedDelay: 7s # 固定时间为7s
[root@k8s-master-node1 networking]# cat virtual-service-ratings-test-delay.yaml
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: ratings
spec:
hosts:
- ratings
http:
- match:
- headers:
end-user:
exact: jason
fault:
delay:
percentage:
value: 100.0
fixedDelay: 7s
route:
- destination:
host: ratings
subset: v1
- route:
- destination:
host: ratings
subset: v1
等待几秒钟,让新规则传播到所有 pod。
# 测试延迟配置
在
/productpage
网页上,以 user 身份登录jason
。您希望 Bookinfo 主页在大约 7 秒内加载而不会出现错误。但是,有一个问题:Reviews 部分显示一条错误消息:
Sorry, product reviews are currently unavailable for this book.
查看网页响应时间:
- 在 Web 浏览器中打开开发者工具菜单。
- 打开网络选项卡
- 重新加载
/productpage
网页。您将看到页面实际加载大约需要 6 秒。
# 修复错误
您通常会通过以下方式解决问题:
- 增加服务超时或减少
productpage
服务超时reviews reviews ratings
- 停止和重启固定的微服务
- 确认
/productpage
网页返回其响应没有任何错误。
但是,您已经在reviews
服务的 v3 中运行了一个修复程序。该reviews:v3
服务将超时时间从 10s 减少reviews
到2.5s,以便与下游请求ratings
的超时时间兼容(小于) productpage
。
如果您按照流量转移 (opens new window)reviews:v3
任务中的描述 将所有流量迁移到,您可以尝试将延迟规则更改为小于 2.5s 的任意量,例如 2s,并确认端到端流程继续没有任何错误。
# 注入HTTP中止错误
测试微服务弹性的另一种方法是引入 HTTP 中止故障。ratings
在此任务中,您将为测试用户的微服务引入 HTTP 中止jason
。
在这种情况下,您希望页面立即加载并显示Ratings service is currently unavailable
消息。
- 创建一个故障注入规则,为用户发送 HTTP 中止
jason
:
$ kubectl apply -f samples/bookinfo/networking/virtual-service-ratings-test-abort.yaml
- 确认规则已创建:
创建规则通过jason用户流量
在微服务之间中止jason用户,httpstatus是500状态,百分率设置为100
参数详解:
fault: # 要应用于客户端HTTP流量的故障注入策略。 abort: # 中止策略 percentage: # 百分率 value: 100.0 # 键值为百分之一百 httpStatus: 500 # httpstatus设置为500报错
[root@k8s-master-node1 networking]# cat virtual-service-ratings-test-abort.yaml
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: ratings
spec:
hosts:
- ratings
http:
- match:
- headers:
end-user:
exact: jason
fault:
abort:
percentage:
value: 100.0
httpStatus: 500
route:
- destination:
host: ratings
subset: v1
- route:
- destination:
host: ratings
subset: v1
# 测试中止配置
在 上
/productpage
,以用户身份登录jason
。如果规则成功传播到所有 pod,页面会立即加载并显示
Ratings service is currently unavailable
消息。如果您从用户注销
jason
或在匿名窗口(或其他浏览器)中打开 Bookinfo 应用程序,您 将/productpage
看到除了. 因此,您不会看到任何错误消息。reviews:v1 ratings jason
# 清理
删除应用程序路由规则:
$ kubectl delete -f samples/bookinfo/networking/virtual-service-all-v1.yaml
# 流量转移
# 什么是流量转移?
此任务向您展示如何将流量从一个微服务版本转移到另一个版本。
一个常见的用例是将流量从旧版本的微服务逐渐迁移到新版本。在 Istio 中,您可以通过配置一系列路由规则来实现这一目标,这些规则将一定比例的流量从一个目的地重定向到另一个目的地。
在此任务中,您将使用将 50% 的流量发送到reviews:v1
和 50% 到reviews:v3
。然后,您将通过将 100% 的流量发送到 来完成迁移reviews:v3
。
# 环境准备
- 部署Bookinfo (opens new window)示例应用程序,应用目标规则。
- 了解了流量管理的基础概念,以及专业语术。
首先,运行此命令将所有流量路由到
v1
每个微服务的版本。$ kubectl apply -f samples/bookinfo/networking/virtual-service-all-v1.yaml
# 应用基于权重的路由
reviews:v1
使用reviews:v3
以下命令传输 50% 的流量:
$ kubectl apply -f samples/bookinfo/networking/virtual-service-reviews-50-v3.yaml
- 确认规则已被替换:
创建v1版本的权重为50
创建v3版本的权重为50
[root@k8s-master-node1 networking]# cat virtual-service-reviews-50-v3.yaml
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: reviews
spec:
hosts:
- reviews
http:
- route:
- destination:
host: reviews
subset: v1
weight: 50
- destination:
host: reviews
subset: v3
weight: 50
- 在您的浏览器中刷新
/productpage
,您现在大约有 50% 的时间会看到红色
的星级。这是因为v3
版本reviews
访问了星级服务,而v1
版本没有。
注意:
使用当前的 Envoy sidecar 实现,您可能需要
/productpage
多次刷新(可能 15 次或更多)才能看到正确的分布。您可以修改规则以将 90% 的流量路由到v3
更频繁地看到红星。
- 假设您确定
reviews:v3
微服务是稳定的,您可以reviews:v3
通过应用此虚拟服务将 100% 的流量路由到:
[root@k8s-master-node1 networking]# cat virtual-service-reviews-v3.yaml
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: reviews
spec:
hosts:
- reviews
http:
- route:
- destination:
host: reviews
subset: v3
现在,当您刷新时,/productpage
您总是会看到每条评论带有红色
星级的书评。
# 清理
删除应用程序路由规则:
$ kubectl delete -f samples/bookinfo/networking/virtual-service-all-v1.yaml
# TCP 流量转移
# 什么是TCP流量转移?
将 TCP 流量从一个微服务版本转移到另一个版本。
一个常见的用例是将 TCP 流量从旧版本的微服务逐渐迁移到新版本。在 Istio 中,您可以通过配置一系列路由规则来实现这一目标,这些规则将一定百分比的 TCP 流量从一个目的地重定向到另一个目的地。在此任务中,您将 100% 的 TCP 流量发送到tcp-echo:v1
. 然后,您将tcp-echo:v2
使用 Istio 的加权路由功能路由 20% 的 TCP 流量。
# 基础环境
- 首先,创建一个用于测试 TCP 流量转移的命名空间并将其标记为启用自动边车注入。
- 部署sleep (opens new window)示例应用程序以用作发送请求的测试源。
- 部署微服务的
v1
和v2
版本tcp-echo
。- 按照 确定入口 IP 和端口 (opens new window)
TCP_INGRESS_PORT
中 的说明定义INGRESS_HOST
环境变量。$ kubectl create namespace istio-io-tcp-traffic-shifting $ kubectl label namespace istio-io-tcp-traffic-shifting istio-injection=enabled $ kubectl apply -f samples/sleep/sleep.yaml -n istio-io-tcp-traffic-shifting $ kubectl apply -f samples/tcp-echo/tcp-echo-services.yaml -n istio-io-tcp-traffic-shifting
# 应用基于权重的TCP路由
- 将所有 TCP 流量路由到微服务的
v1
版本tcp-echo
。
[root@k8s-master-node1 samples]# kubectl apply -f samples/tcp-echo/tcp-echo-all-v1.yaml -n istio-io-tcp-traffic-shifting
[root@k8s-master-node1 samples]# cat tcp-echo/tcp-echo-all-v1.yaml
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: tcp-echo-gateway
spec:
selector:
istio: ingressgateway
servers:
- port:
number: 31400
name: tcp
protocol: TCP
hosts:
- "*"
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: tcp-echo-destination
spec:
host: tcp-echo
subsets:
- name: v1
labels:
version: v1
- name: v2
labels:
version: v2
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: tcp-echo
spec:
hosts:
- "*"
gateways:
- tcp-echo-gateway
tcp:
- match:
- port: 31400
route:
- destination:
host: tcp-echo
port: # 这里修改成使用端口的方式
number: 9000 # 端口号是9000
subset: v1 # 版本使用V1版本
tcp-echo
通过从客户端发送一些 TCP 流量来确认服务已启动并正在运行sleep
。
下面的
$INGRESS_HOST
变量是 ingress 的外部 IP 地址,$TCP_INGRESS_PORT
是ingress的端口$ export INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="http2")].nodePort}') $ export SECURE_INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="https")].nodePort}') $ export TCP_INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="tcp")].nodePort}') $ export INGRESS_HOST=worker-node-address
[root@k8s-master-node1 samples]# for i in {1..20}; do \
kubectl exec "$(kubectl get pod -l app=sleep -n istio-io-tcp-traffic-shifting -o jsonpath={.items..metadata.name})" \
-c sleep -n istio-io-tcp-traffic-shifting -- sh -c "(date; sleep 1) | nc $INGRESS_HOST $TCP_INGRESS_PORT"; \
done
one Mon Nov 12 23:24:57 UTC 2018
one Mon Nov 12 23:25:00 UTC 2018
one Mon Nov 12 23:25:02 UTC 2018
one Mon Nov 12 23:25:05 UTC 2018
one Mon Nov 12 23:25:07 UTC 2018
one Mon Nov 12 23:25:10 UTC 2018
one Mon Nov 12 23:25:12 UTC 2018
one Mon Nov 12 23:25:15 UTC 2018
one Mon Nov 12 23:25:17 UTC 2018
one Mon Nov 12 23:25:19 UTC 2018
...
您应该注意到所有时间戳的前缀都是one,这意味着所有流量都被路由到服务的v1
版本tcp-echo
。
tcp-echo:v1
使用tcp-echo:v2
以下命令传输 20% 的流量:
通过修改
weight的值
来确定流量的分布控制。
[root@k8s-master-node1 istio-1.9.5]# kubectl apply -f samples/tcp-echo/tcp-echo-20-v2.yaml
[root@k8s-master-node1 istio-1.9.5]# cat samples/tcp-echo/tcp-echo-20-v2.yaml
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: tcp-echo
spec:
hosts:
- "*"
gateways:
- tcp-echo-gateway
tcp:
- match:
- port: 31400
route:
- destination:
host: tcp-echo
port:
number: 9000
subset: v1
weight: 80
- destination:
host: tcp-echo
port:
number: 9000
subset: v2
weight: 20
向微服务发送更多 TCP 流量
tcp-echo
。您现在应该注意到大约 20% 的时间戳具有前缀2,这意味着 80% 的 TCP 流量被路由到服务
v1
版本tcp-echo
,而 20% 被路由到v2
.
[root@k8s-master-node1 istio-1.9.5]# for i in {1..20}; do \
kubectl exec "$(kubectl get pod -l app=sleep -n istio-io-tcp-traffic-shifting -o jsonpath={.items..metadata.name})" \
-c sleep -n istio-io-tcp-traffic-shifting -- sh -c "(date; sleep 1) | nc $INGRESS_HOST $TCP_INGRESS_PORT"; \
done
one Mon Nov 12 23:38:45 UTC 2018
two Mon Nov 12 23:38:47 UTC 2018
one Mon Nov 12 23:38:50 UTC 2018
one Mon Nov 12 23:38:52 UTC 2018
one Mon Nov 12 23:38:55 UTC 2018
two Mon Nov 12 23:38:57 UTC 2018
one Mon Nov 12 23:39:00 UTC 2018
one Mon Nov 12 23:39:02 UTC 2018
one Mon Nov 12 23:39:05 UTC 2018
one Mon Nov 12 23:39:07 UTC 2018
...
# 清理
删除sleep
示例、tcp-echo
应用程序和路由规则:
$ kubectl delete -f samples/tcp-echo/tcp-echo-all-v1.yaml -n istio-io-tcp-traffic-shifting
$ kubectl delete -f samples/tcp-echo/tcp-echo-services.yaml -n istio-io-tcp-traffic-shifting
$ kubectl delete -f samples/sleep/sleep.yaml -n istio-io-tcp-traffic-shifting
$ kubectl delete namespace istio-io-tcp-traffic-shifting
# 请求超时
# 环境准备
- 部署Bookinfo (opens new window)示例应用程序,应用目标规则。
- 了解了流量管理的基础概念,以及专业语术。
首先,运行此命令将所有流量路由到
v1
每个微服务的版本。$ kubectl apply -f samples/bookinfo/networking/virtual-service-all-v1.yaml
# 请求超时
可以使用路由规则的 (opens new window)timeout字段指定 HTTP 请求的超时时间。默认情况下,请求超时被禁用,但在此任务中,您将服务超时覆盖为 1 秒。但是,要查看其效果,您还可以在调用服务时人为地引入 2 秒延迟。
- 将请求路由到
reviews
服务的 v2,即调用ratings
服务的版本:
$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: reviews
spec:
hosts:
- reviews
http:
- route:
- destination:
host: reviews
subset: v2
EOF
- 对服务的调用添加 2 秒延迟
ratings
:使用http规则配置目的地版本为v1主机是ratings
配置百分比为100的2秒固定时间延迟
$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: ratings
spec:
hosts:
- ratings
http:
- fault:
delay:
percent: 100
fixedDelay: 2s
route:
- destination:
host: ratings
subset: v1
EOF
http://$GATEWAY_URL/productpage
在浏览器中打开 Bookinfo URL 。
您应该会看到 Bookinfo 应用程序正常工作(显示评级星),但是每当您刷新页面时都会有 2 秒的延迟。
- 现在为对服务的调用添加半秒请求超时
reviews
:
$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: reviews
spec:
hosts:
- reviews
http:
- route:
- destination:
host: reviews
subset: v2
timeout: 0.5s
EOF
- 刷新 Bookinfo 网页。
您现在应该看到它在大约 1 秒内返回,而不是 2 秒,并且评论不可用。
注意:
响应需要1秒的原因,即使超时配置为半秒,也是因为服务中有硬编码的重试,所以它在返回之前调用了两次
productpage
超时服务。reviews
# 清理
删除应用程序路由规则:
$ kubectl delete -f samples/bookinfo/networking/virtual-service-all-v1.yaml
# 熔断
# 熔断的方式
此任务向您展示如何为连接、请求和异常值检测配置断路。
断路是创建弹性微服务应用程序的重要模式。断路允许您编写限制故障、延迟峰值和网络特性的其他不良影响的影响的应用程序。
在此任务中,您将配置断路器规则,然后通过有意“跳闸”断路器来测试配置。
# 环境准备
启动httpbin (opens new window)示例。
如果您启用了自动边车注入 (opens new window),请部署该httpbin
服务:
否则,您必须在部署应用程序之前手动注入 sidecar httpbin
:
$ kubectl apply -f samples/httpbin/httpbin.yaml
$ kubectl apply -f <(istioctl kube-inject -f samples/httpbin/httpbin.yaml)
# 配置断路器
- 创建目标规则 (opens new window)以在调用服务时应用断路设置
httpbin
:配置交通政策的连接池和异常检测
配置连接池的tcp规则到目标主机的最大HTTP1/TCP连接数为 1
配置连接池的http规则对目标的最大挂起HTTP请求数为 1
配置连接池的http规则每个后端连接的最大请求数为 1
配置异常检测从连接池中弹出主机之前的5xx错误数为 1
配置异常检测弹射扫描分析之间的时间间隔为 1秒
配置异常检测最短弹射时间为 3分钟
配置异常检测最大弹射百分比为 100
如果您安装/配置了启用双向 TLS 身份验证的 Istio,则必须在应用之前添加 TLS 流量
mode: ISTIO_MUTUAL
策略DestinationRule
。否则请求将产生 503 错误,如此处 (opens new window)所述。
$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: httpbin
spec:
host: httpbin
trafficPolicy:
connectionPool:
tcp:
maxConnections: 1
http:
http1MaxPendingRequests: 1
maxRequestsPerConnection: 1
outlierDetection:
consecutive5xxErrors: 1
interval: 1s
baseEjectionTime: 3m
maxEjectionPercent: 100
EOF
- 验证目标规则是否已正确创建:
[root@k8s-master-node1 istio-1.9.5]# kubectl get destinationrules
NAME HOST AGE
details details 4h41m
httpbin httpbin 64m
# 添加客户端
创建客户端以将流量发送到httpbin
服务。该客户端是一个名为fortio (opens new window)的简单负载测试客户端。Fortio 允许您控制传出 HTTP 调用的连接数、并发性和延迟。您将使用此客户端“跳闸”您在DestinationRule
.
- 使用 Istio sidecar 代理注入客户端,以便网络交互由 Istio 管理。如果您启用了自动边车注入 (opens new window),请部署该
fortio
服务:
$ kubectl apply -f samples/httpbin/sample-client/fortio-deploy.yaml
否则,您必须在部署应用程序之前手动注入 sidecar fortio
:
$ kubectl apply -f <(istioctl kube-inject -f samples/httpbin/sample-client/fortio-deploy.yaml)
- 登录客户端pod,使用fortio工具调用
httpbin
. 传入curl
表示你只想打一个电话:
$ export FORTIO_POD=$(kubectl get pods -l app=fortio -o 'jsonpath={.items[0].metadata.name}')
$ kubectl exec "$FORTIO_POD" -c fortio -- /usr/bin/fortio curl -quiet http://httpbin:8000/get
HTTP/1.1 200 OK
server: envoy
date: Tue, 25 Feb 2020 20:25:52 GMT
content-type: application/json
content-length: 586
access-control-allow-origin: *
access-control-allow-credentials: true
x-envoy-upstream-service-time: 36
{
"args": {},
"headers": {
"Content-Length": "0",
"Host": "httpbin:8000",
"User-Agent": "fortio.org/fortio-1.3.1",
"X-B3-Parentspanid": "8fc453fb1dec2c22",
"X-B3-Sampled": "1",
"X-B3-Spanid": "071d7f06bc94943c",
"X-B3-Traceid": "86a929a0e76cda378fc453fb1dec2c22",
"X-Forwarded-Client-Cert": "By=spiffe://cluster.local/ns/default/sa/httpbin;Hash=68bbaedefe01ef4cb99e17358ff63e92d04a4ce831a35ab9a31d3c8e06adb038;Subject=\"\";URI=spiffe://cluster.local/ns/default/sa/default"
},
"origin": "127.0.0.1",
"url": "http://httpbin:8000/get"
}
# 使断路器跳闸
在DestinationRule
设置中,您指定了maxConnections: 1
和 http1MaxPendingRequests: 1
。这些规则表明,如果您超过一个连接并同时请求,则当 istio-proxy
打开更多请求和连接的电路时,您应该会看到一些失败。
- 使用两个并发连接 (
-c 2
) 调用服务并发送 20 个请求 (-n 20
):
[root@k8s-master-node1 istio-1.9.5]# kubectl exec "$FORTIO_POD" -c fortio -- /usr/bin/fortio load -c 2 -qps 0 -n 20 -loglevel Warning http://httpbin:8000/get
15:42:53 I logger.go:127> Log level is now 3 Warning (was 2 Info)
Fortio 1.17.1 running at 0 queries per second, 4->4 procs, for 20 calls: http://httpbin:8000/get
Starting at max qps with 2 thread(s) [gomax 4] for exactly 20 calls (10 per thread + 0)
15:42:53 W http_client.go:806> [0] Non ok http code 503 (HTTP/1.1 503)
15:42:53 W http_client.go:806> [0] Non ok http code 503 (HTTP/1.1 503)
15:42:53 W http_client.go:806> [1] Non ok http code 503 (HTTP/1.1 503)
15:42:53 W http_client.go:806> [0] Non ok http code 503 (HTTP/1.1 503)
15:42:53 W http_client.go:806> [1] Non ok http code 503 (HTTP/1.1 503)
15:42:53 W http_client.go:806> [1] Non ok http code 503 (HTTP/1.1 503)
Ended after 90.508623ms : 20 calls. qps=220.97
Aggregated Function Time : count 20 avg 0.0081634006 +/- 0.006652 min 0.000929244 max 0.027135759 sum 0.163268012
# range, mid point, percentile, count
>= 0.000929244 <= 0.001 , 0.000964622 , 5.00, 1
> 0.001 <= 0.002 , 0.0015 , 25.00, 4
> 0.002 <= 0.003 , 0.0025 , 30.00, 1
> 0.005 <= 0.006 , 0.0055 , 40.00, 2
> 0.006 <= 0.007 , 0.0065 , 50.00, 2
> 0.007 <= 0.008 , 0.0075 , 60.00, 2
> 0.008 <= 0.009 , 0.0085 , 65.00, 1
> 0.009 <= 0.01 , 0.0095 , 80.00, 3
> 0.014 <= 0.016 , 0.015 , 85.00, 1
> 0.018 <= 0.02 , 0.019 , 95.00, 2
> 0.025 <= 0.0271358 , 0.0260679 , 100.00, 1
# target 50% 0.007
# target 75% 0.00966667
# target 90% 0.019
# target 99% 0.0267086
# target 99.9% 0.027093
Sockets used: 8 (for perfect keepalive, would be 2)
Jitter: false
Code 200 : 14 (70.0 %)
Code 503 : 6 (30.0 %)
Response Header Sizes : count 20 avg 161.15 +/- 105.5 min 0 max 231 sum 3223
Response Body/Total Sizes : count 20 avg 649.25 +/- 267.3 min 241 max 825 sum 12985
All done 20 calls (plus 0 warmup) 8.163 ms avg, 221.0 qps
有趣的是,几乎所有请求都通过了!istio-proxy
确实留有余地。
Code 200 : 14 (70.0 %)
Code 503 : 6 (30.0 %)
- 将并发连接数增加到 3:
[root@k8s-master-node1 istio-1.9.5]# kubectl exec "$FORTIO_POD" -c fortio -- /usr/bin/fortio load -c 3 -qps 0 -n 20 -loglevel Warning http://httpbin:8000/get
15:44:49 I logger.go:127> Log level is now 3 Warning (was 2 Info)
Fortio 1.17.1 running at 0 queries per second, 4->4 procs, for 20 calls: http://httpbin:8000/get
Starting at max qps with 3 thread(s) [gomax 4] for exactly 20 calls (6 per thread + 2)
15:44:49 W http_client.go:806> [1] Non ok http code 503 (HTTP/1.1 503)
15:44:49 W http_client.go:806> [0] Non ok http code 503 (HTTP/1.1 503)
15:44:49 W http_client.go:806> [1] Non ok http code 503 (HTTP/1.1 503)
15:44:49 W http_client.go:806> [0] Non ok http code 503 (HTTP/1.1 503)
15:44:49 W http_client.go:806> [0] Non ok http code 503 (HTTP/1.1 503)
15:44:49 W http_client.go:806> [0] Non ok http code 503 (HTTP/1.1 503)
15:44:49 W http_client.go:806> [0] Non ok http code 503 (HTTP/1.1 503)
15:44:49 W http_client.go:806> [0] Non ok http code 503 (HTTP/1.1 503)
15:44:49 W http_client.go:806> [0] Non ok http code 503 (HTTP/1.1 503)
15:44:49 W http_client.go:806> [2] Non ok http code 503 (HTTP/1.1 503)
15:44:49 W http_client.go:806> [2] Non ok http code 503 (HTTP/1.1 503)
15:44:49 W http_client.go:806> [0] Non ok http code 503 (HTTP/1.1 503)
15:44:49 W http_client.go:806> [2] Non ok http code 503 (HTTP/1.1 503)
15:44:49 W http_client.go:806> [2] Non ok http code 503 (HTTP/1.1 503)
15:44:49 W http_client.go:806> [2] Non ok http code 503 (HTTP/1.1 503)
Ended after 32.522699ms : 20 calls. qps=614.96
Aggregated Function Time : count 20 avg 0.0029114787 +/- 0.003681 min 0.000447652 max 0.016532305 sum 0.058229575
# range, mid point, percentile, count
>= 0.000447652 <= 0.001 , 0.000723826 , 30.00, 6
> 0.001 <= 0.002 , 0.0015 , 60.00, 6
> 0.002 <= 0.003 , 0.0025 , 70.00, 2
> 0.004 <= 0.005 , 0.0045 , 90.00, 4
> 0.008 <= 0.009 , 0.0085 , 95.00, 1
> 0.016 <= 0.0165323 , 0.0162662 , 100.00, 1
# target 50% 0.00166667
# target 75% 0.00425
# target 90% 0.005
# target 99% 0.0164258
# target 99.9% 0.0165217
Sockets used: 16 (for perfect keepalive, would be 3)
Jitter: false
Code 200 : 5 (25.0 %)
Code 503 : 15 (75.0 %)
Response Header Sizes : count 20 avg 57.55 +/- 99.68 min 0 max 231 sum 1151
Response Body/Total Sizes : count 20 avg 386.8 +/- 252.5 min 241 max 825 sum 7736
All done 20 calls (plus 0 warmup) 2.911 ms avg, 615.0 qps
现在您开始看到预期的断路行为。只有 36.7% 的请求成功,其余的则被熔断机制困住:
Code 200 : 5 (25.0 %)
Code 503 : 15 (75.0 %)
查询
istio-proxy
统计信息以查看更多信息:您可以查看
21
该upstream_rq_pending_overflow
值,这意味着21
到目前为止的调用已被标记为断路。
[root@k8s-master-node1 istio-1.9.5]# kubectl exec "$FORTIO_POD" -c istio-proxy -- pilot-agent request GET stats | grep httpbin | grep pending
cluster.outbound|8000||httpbin.default.svc.cluster.local.circuit_breakers.default.remaining_pending: 1
cluster.outbound|8000||httpbin.default.svc.cluster.local.circuit_breakers.default.rq_pending_open: 0
cluster.outbound|8000||httpbin.default.svc.cluster.local.circuit_breakers.high.rq_pending_open: 0
cluster.outbound|8000||httpbin.default.svc.cluster.local.upstream_rq_pending_active: 0
cluster.outbound|8000||httpbin.default.svc.cluster.local.upstream_rq_pending_failure_eject: 0
cluster.outbound|8000||httpbin.default.svc.cluster.local.upstream_rq_pending_overflow: 40
cluster.outbound|8000||httpbin.default.svc.cluster.local.upstream_rq_pending_total: 41
# 清理
删除规则:
$ kubectl delete destinationrule httpbin
关闭httpbin (opens new window)服务和客户端:
$ kubectl delete deploy httpbin fortio-deploy
$ kubectl delete svc httpbin fortio
# 镜像
# 流量镜像
此任务演示了 Istio 的流量镜像功能。
流量镜像,也称为阴影,是一个强大的概念,它允许功能团队以尽可能小的风险将更改带入生产环境。镜像将实时流量的副本发送到镜像服务。镜像流量发生在主服务的关键请求路径的带外。
在此任务中,您将首先强制所有流量流向v1
测试服务。然后,您将应用规则将一部分流量镜像到v2
.
# 基础环境
首先部署两个启用了访问日志记录的httpbin服务版本: (opens new window)
httpbin-v1:
$ cat <<EOF | istioctl kube-inject -f - | kubectl create -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: httpbin-v1
spec:
replicas: 1
selector:
matchLabels:
app: httpbin
version: v1
template:
metadata:
labels:
app: httpbin
version: v1
spec:
containers:
- image: docker.io/kennethreitz/httpbin
imagePullPolicy: IfNotPresent
name: httpbin
command: ["gunicorn", "--access-logfile", "-", "-b", "0.0.0.0:80", "httpbin:app"]
ports:
- containerPort: 80
EOF
httpbin-v2:
$ cat <<EOF | istioctl kube-inject -f - | kubectl create -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: httpbin-v2
spec:
replicas: 1
selector:
matchLabels:
app: httpbin
version: v2
template:
metadata:
labels:
app: httpbin
version: v2
spec:
containers:
- image: docker.io/kennethreitz/httpbin
imagePullPolicy: IfNotPresent
name: httpbin
command: ["gunicorn", "--access-logfile", "-", "-b", "0.0.0.0:80", "httpbin:app"]
ports:
- containerPort: 80
EOF
httpbin Kubernetes 服务:
$ kubectl create -f - <<EOF
apiVersion: v1
kind: Service
metadata:
name: httpbin
labels:
app: httpbin
spec:
ports:
- name: http
port: 8000
targetPort: 80
selector:
app: httpbin
EOF
启动sleep
服务,以便您可以使用它curl
来提供负载:
睡眠服务:
$ cat <<EOF | istioctl kube-inject -f - | kubectl create -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: sleep
spec:
replicas: 1
selector:
matchLabels:
app: sleep
template:
metadata:
labels:
app: sleep
spec:
containers:
- name: sleep
image: curlimages/curl
command: ["/bin/sleep","3650d"]
imagePullPolicy: IfNotPresent
EOF
# 创建默认路由策略
默认情况下,Kubernetes 会在httpbin
服务的两个版本之间进行负载平衡。在此步骤中,您将更改该行为,以便所有流量都转到v1
.
- 创建默认路由规则以将所有流量路由到
v1
服务,现在所有流量都流向httpbin:v1
服务。:
$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: httpbin
spec:
hosts:
- httpbin
http:
- route:
- destination:
host: httpbin
subset: v1
weight: 100
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: httpbin
spec:
host: httpbin
subsets:
- name: v1
labels:
version: v1
- name: v2
labels:
version: v2
EOF
- 向服务发送一些流量:
$ export SLEEP_POD=$(kubectl get pod -l app=sleep -o jsonpath={.items..metadata.name})
$ kubectl exec "${SLEEP_POD}" -c sleep -- curl -sS http://httpbin:8000/headers
{
"headers": {
"Accept": "*/*",
"Content-Length": "0",
"Host": "httpbin:8000",
"User-Agent": "curl/7.35.0",
"X-B3-Parentspanid": "57784f8bff90ae0b",
"X-B3-Sampled": "1",
"X-B3-Spanid": "3289ae7257c3f159",
"X-B3-Traceid": "b56eebd279a76f0b57784f8bff90ae0b",
"X-Envoy-Attempt-Count": "1",
"X-Forwarded-Client-Cert": "By=spiffe://cluster.local/ns/default/sa/default;Hash=20afebed6da091c850264cc751b8c9306abac02993f80bdb76282237422bd098;Subject=\"\";URI=spiffe://cluster.local/ns/default/sa/default"
}
}
- 检查pod
v1
的日志v2
。httpbin
您应该看到以下的访问日志条目,v1
并且没有v2
:
$ export V1_POD=$(kubectl get pod -l app=httpbin,version=v1 -o jsonpath={.items..metadata.name})
$ kubectl logs "$V1_POD" -c httpbin
[2022-03-18 16:20:47 +0000] [1] [INFO] Starting gunicorn 19.9.0
[2022-03-18 16:20:47 +0000] [1] [INFO] Listening at: http://0.0.0.0:80 (1)
[2022-03-18 16:20:47 +0000] [1] [INFO] Using worker: sync
[2022-03-18 16:20:47 +0000] [9] [INFO] Booting worker with pid: 9
127.0.0.6 - - [18/Mar/2022:16:30:43 +0000] "GET /headers HTTP/1.1" 200 529 "-" "curl/7.81.0-DEV"
$ export V2_POD=$(kubectl get pod -l app=httpbin,version=v2 -o jsonpath={.items..metadata.name})
$ kubectl logs "$V2_POD" -c httpbin
[2022-03-18 16:20:53 +0000] [1] [INFO] Starting gunicorn 19.9.0
[2022-03-18 16:20:53 +0000] [1] [INFO] Listening at: http://0.0.0.0:80 (1)
[2022-03-18 16:20:53 +0000] [1] [INFO] Using worker: sync
[2022-03-18 16:20:53 +0000] [10] [INFO] Booting worker with pid: 10
# 将流量镜像到v2
更改路由规则以将流量镜像到 v2:
此路由规则将 100% 的流量发送到v1
. 最后一节指定您希望将 100% 的相同流量镜像(即,也发送)到 httpbin:v2
服务。
当流量被镜像时,请求被发送到镜像服务,其 Host/Authority 标头附加-shadow
. 例如,cluster-1
变成cluster-1-shadow
。
此外,重要的是要注意这些请求被镜像为“即发即弃”,这意味着响应被丢弃。您可以使用该value
字段下的mirrorPercentage
字段来镜像一部分流量,而不是镜像所有请求。如果没有这个字段,所有的流量都会被镜像。
$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: httpbin
spec:
hosts:
- httpbin
http:
- route:
- destination:
host: httpbin
subset: v1
weight: 100
mirror:
host: httpbin
subset: v2
mirrorPercentage:
value: 100.0
EOF
# 发送流量
通过发送流量再次进行测试,情况如下:
v1
现在,您应该会看到和的访问记录v2
中创建的访问日志v2
是实际将要访问的镜像请求v1
。
[root@k8s-master-node1 istio-work]# kubectl exec "${SLEEP_POD}" -c sleep -- curl -sS http://httpbin:8000/headers
{
"headers": {
"Accept": "*/*",
"Host": "httpbin:8000",
"User-Agent": "curl/7.81.0-DEV",
"X-B3-Parentspanid": "a422ba4aefe02440",
"X-B3-Sampled": "1",
"X-B3-Spanid": "acd7163f70c9aa89",
"X-B3-Traceid": "f7ca9fb1e0581d6ba422ba4aefe02440",
"X-Envoy-Attempt-Count": "1",
"X-Forwarded-Client-Cert": "By=spiffe://cluster.local/ns/default/sa/default;Hash=ab53dec650eb79f2aa9dc051ed27a2b4b698ee40ffd557cd885495105916a139;Subject=\"\";URI=spiffe://cluster.local/ns/default/sa/default"
}
}
[root@k8s-master-node1 istio-work]# kubectl logs "$V1_POD" -c httpbin
[2022-03-18 16:20:47 +0000] [1] [INFO] Starting gunicorn 19.9.0
[2022-03-18 16:20:47 +0000] [1] [INFO] Listening at: http://0.0.0.0:80 (1)
[2022-03-18 16:20:47 +0000] [1] [INFO] Using worker: sync
[2022-03-18 16:20:47 +0000] [9] [INFO] Booting worker with pid: 9
127.0.0.6 - - [18/Mar/2022:16:30:43 +0000] "GET /headers HTTP/1.1" 200 529 "-" "curl/7.81.0-DEV"
127.0.0.6 - - [18/Mar/2022:16:33:03 +0000] "GET /headers HTTP/1.1" 200 529 "-" "curl/7.81.0-DEV"
127.0.0.6 - - [18/Mar/2022:16:33:48 +0000] "GET /headers HTTP/1.1" 200 529 "-" "curl/7.81.0-DEV"
127.0.0.6 - - [18/Mar/2022:16:33:52 +0000] "GET /headers HTTP/1.1" 200 529 "-" "curl/7.81.0-DEV"
127.0.0.6 - - [18/Mar/2022:16:34:05 +0000] "GET /headers HTTP/1.1" 200 529 "-" "curl/7.81.0-DEV"
[root@k8s-master-node1 istio-work]# kubectl logs "$V2_POD" -c httpbin
[2022-03-18 16:20:53 +0000] [1] [INFO] Starting gunicorn 19.9.0
[2022-03-18 16:20:53 +0000] [1] [INFO] Listening at: http://0.0.0.0:80 (1)
[2022-03-18 16:20:53 +0000] [1] [INFO] Using worker: sync
[2022-03-18 16:20:53 +0000] [10] [INFO] Booting worker with pid: 10
127.0.0.6 - - [18/Mar/2022:16:33:03 +0000] "GET /headers HTTP/1.1" 200 569 "-" "curl/7.81.0-DEV"
127.0.0.6 - - [18/Mar/2022:16:33:48 +0000] "GET /headers HTTP/1.1" 200 569 "-" "curl/7.81.0-DEV"
127.0.0.6 - - [18/Mar/2022:16:33:52 +0000] "GET /headers HTTP/1.1" 200 569 "-" "curl/7.81.0-DEV"
127.0.0.6 - - [18/Mar/2022:16:34:05 +0000] "GET /headers HTTP/1.1" 200 569 "-" "curl/7.81.0-DEV"
# 清理
删除规则:
$ kubectl delete virtualservice httpbin
$ kubectl delete destinationrule httpbin
关闭httpbin (opens new window)服务和客户端:
$ kubectl delete deploy httpbin-v1 httpbin-v2 sleep
$ kubectl delete svc httpbin
# 入口网关
# 入口网关简要
除了支持 Kubernetes Ingress (opens new window),Istio 还提供了另一种配置模型,即 Istio Gateway (opens new window)。AGateway
提供了比 更广泛的自定义和灵活性Ingress
,并允许将监控和路由规则等 Istio 功能应用于进入集群的流量。
此任务描述了如何配置 Istio 以使用 Istio 在服务网格之外公开服务Gateway
。
# 环境准备
- 确保您的当前目录是该
istio
目录。 - 启动httpbin (opens new window)示例。
如果您启用了自动边车注入 (opens new window),请部署该httpbin
服务:
否则,您必须在部署应用程序之前手动注入 sidecar httpbin
:
$ kubectl apply -f samples/httpbin/httpbin.yaml
$ kubectl apply -f <(istioctl kube-inject -f samples/httpbin/httpbin.yaml)
# 确定入口IP和端口
执行以下命令来确定您的 Kubernetes 集群是否在支持外部负载均衡器的环境中运行(这里没有负载均衡器改为NodePort模式
):
[root@k8s-master-node1 ~]# kubectl get svc -n istio-system istio-ingressgateway
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
istio-ingressgateway NodePort 10.96.88.217 <none> 15021:28214/TCP,80:27007/TCP,443:9673/TCP,31400:3938/TCP,15443:41053/TCP 4d2h
如果EXTERNAL-IP
设置了该值,则您的环境具有可用于入口网关的外部负载均衡器。如果EXTERNAL-IP
值为<none>
或永久<pending>
,则您的环境不为入口网关提供外部负载均衡器。在这种情况下,您可以使用服务的节点端口 (opens new window)访问网关。
# 添加环境说明
如果您确定您的环境没有外部负载均衡器,请按照这些说明进行操作,因此您需要改用节点端口。
$ export INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="http2")].nodePort}')
$ export SECURE_INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="https")].nodePort}')
$ export TCP_INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="tcp")].nodePort}')
# 使用Istio网关配置入口
入口网关 (opens new window)描述了在网格边缘运行的负载均衡器,用于接收传入的 HTTP/TCP 连接。它配置暴露的端口、协议等,但与Kubernetes Ingress Resources (opens new window)不同,它不包括任何流量路由配置。入口流量的流量路由是使用 Istio 路由规则配置的,与内部服务请求的方式完全相同。
让我们看看如何Gateway
为 HTTP 流量配置端口 80。
- 创建一个 Istio
Gateway
:使用默认的ingressgateway网关
创建一个服务器规范列表,分别是主机和端口
配置http服务的80端口以及主机httpbin.example.com
kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: httpbin-gateway
spec:
selector:
istio: ingressgateway # use Istio default gateway implementation
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- "httpbin.example.com"
EOF
- 为通过以下方式进入的流量配置路由
Gateway
:配置虚拟服务允许主机httpbin.example.com
配追http的规则为两个uri的两个允许路径、分别是status和delay
配置目的地规则为主机是httpbin端口为8080的
网关列表指定只允许通过您的请求httpbin-gateway。所有其他外部请求都将被 404 响应拒绝。
$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: httpbin
spec:
hosts:
- "httpbin.example.com"
gateways:
- httpbin-gateway
http:
- match:
- uri:
prefix: /status
- uri:
prefix: /delay
route:
- destination:
port:
number: 8000
host: httpbin
EOF
注意:
网格中其他服务的内部请求不受这些规则的约束,而是默认使用循环路由。要将这些规则也应用于内部调用,您可以将特殊值添加
mesh
到gateways
. 由于服务的内部主机名可能与外部主机名不同(例如,httpbin.default.svc.cluster.local
),因此您还需要将其添加到hosts
列表中。
- 使用curl访问httpbin服务:
[root@k8s-master-node1 ~]# curl -s -I -HHost:httpbin.example.com "http://$INGRESS_HOST:$INGRESS_PORT/status/200"
HTTP/1.1 200 OK
server: istio-envoy
date: Sun, 20 Mar 2022 14:05:09 GMT
content-type: text/html; charset=utf-8
access-control-allow-origin: *
access-control-allow-credentials: true
content-length: 0
x-envoy-upstream-service-time: 24
请注意,您使用该-H
标志将Host HTTP 标头设置为“httpbin.example.com”。这是必需的,因为您的入口Gateway
配置为处理“httpbin.example.com”,但在您的测试环境中,您没有该主机的 DNS 绑定,只是将您的请求发送到入口 IP。
- 访问尚未明确公开的任何其他 URL。您应该会看到 HTTP 404 错误:
[root@k8s-master-node1 ~]# curl -s -I -HHost:httpbin.example.com "http://$INGRESS_HOST:$INGRESS_PORT/headers"
HTTP/1.1 404 Not Found
date: Sun, 20 Mar 2022 14:29:26 GMT
server: istio-envoy
transfer-encoding: chunked
# 使用浏览器访问入口服务
在浏览器中输入httpbin
服务 URL 将不起作用,因为您无法将Host标头传递给浏览器,就像使用curl
. 在现实世界的情况下,这不是问题,因为您正确配置了请求的主机并且 DNS 可解析。因此,您在 URL 中使用主机的域名,例如https://httpbin.example.com/status/200
.
要为简单的测试和演示解决此问题,请在 和配置*
中为主机使用通配符值。例如,如果您将入口配置更改为以下内容:Gateway VirtualService
kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: httpbin-gateway
spec:
selector:
istio: ingressgateway # use Istio default gateway implementation
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- "*"
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: httpbin
spec:
hosts:
- "*"
gateways:
- httpbin-gateway
http:
- match:
- uri:
prefix: /headers
route:
- destination:
port:
number: 8000
host: httpbin
EOF
在本机的hosts配置192.168.1.6:44526/headers httpbin.example.com
映射,然后通过浏览器访问。
# 清理
删除Gateway
andVirtualService
配置,关闭httpbin (opens new window)服务:
$ kubectl delete gateway httpbin-gateway
$ kubectl delete virtualservice httpbin
$ kubectl delete --ignore-not-found=true -f samples/httpbin/httpbin.yaml
# 安全网关
# 安全网关的作用
控制入口流量任务 (opens new window) 描述了如何配置入口网关以将 HTTP 服务公开给外部流量。此任务显示如何使用简单或双向 TLS 公开安全的 HTTPS 服务。
# 环境准备
- 确保您的当前目录是该
istio
目录。 - 启动httpbin (opens new window)示例。
如果您启用了自动边车注入 (opens new window),请部署该httpbin
服务:
否则,您必须在部署应用程序之前手动注入 sidecar httpbin
:
$ kubectl apply -f samples/httpbin/httpbin.yaml
$ kubectl apply -f <(istioctl kube-inject -f samples/httpbin/httpbin.yaml)
# 生成证书和密钥
对于此任务,您可以使用您喜欢的工具来生成证书和密钥。下面的命令使用 openssl (opens new window)
- 创建根证书和私钥来为您的服务签署证书:
$ openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -subj '/O=example Inc./CN=example.com' -keyout example.com.key -out example.com.crt
- 创建证书和私钥
httpbin.example.com
:
$ openssl req -out httpbin.example.com.csr -newkey rsa:2048 -nodes -keyout httpbin.example.com.key -subj "/CN=httpbin.example.com/O=httpbin organization"
$ openssl x509 -req -sha256 -days 365 -CA example.com.crt -CAkey example.com.key -set_serial 0 -in httpbin.example.com.csr -out httpbin.example.com.crt
# 为单个主机配置TLS入口网关
- 为入口网关创建一个密钥:
$ kubectl create -n istio-system secret tls httpbin-credential --key=httpbin.example.com.key --cert=httpbin.example.com.crt
- 使用端口 443的部分定义网关
servers:
,并指定credentialName
to be的值httpbin-credential
。这些值与密钥的名称相同。TLS 模式的值应为SIMPLE
.
$ cat <<EOF | kubectl apply -f -
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: mygateway
spec:
selector:
istio: ingressgateway # use istio default ingress gateway
servers:
- port:
number: 443
name: https
protocol: HTTPS
tls:
mode: SIMPLE
credentialName: httpbin-credential # must be the same as secret
hosts:
- httpbin.example.com
EOF
- 配置网关的入口流量路由。定义相应的虚拟服务。
$ cat <<EOF | kubectl apply -f -
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: httpbin
spec:
hosts:
- "httpbin.example.com"
gateways:
- mygateway
http:
- match:
- uri:
prefix: /status
- uri:
prefix: /delay
route:
- destination:
port:
number: 8000
host: httpbin
EOF
- 发送 HTTPS 请求以
httpbin
通过 HTTPS 访问服务,该httpbin
服务将返回 418 I'm a Teapot (opens new window)代码。
$ curl -v -HHost:httpbin.example.com --resolve "httpbin.example.com:$SECURE_INGRESS_PORT:$INGRESS_HOST" --cacert example.com.crt "https://httpbin.example.com:$SECURE_INGRESS_PORT/status/418"
* Added httpbin.example.com:7339:192.168.1.6 to DNS cache
* About to connect() to httpbin.example.com port 7339 (#0)
* Trying 192.168.1.6...
* Connected to httpbin.example.com (192.168.1.6) port 7339 (#0)
* Initializing NSS with certpath: sql:/etc/pki/nssdb
* CAfile: example.com.crt
CApath: none
* SSL connection using TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
* Server certificate:
* subject: O=httpbin organization,CN=httpbin.example.com
* start date: Mar 20 15:18:42 2022 GMT
* expire date: Mar 20 15:18:42 2023 GMT
* common name: httpbin.example.com
* issuer: CN=example.com,O=example Inc.
> GET /status/418 HTTP/1.1
> User-Agent: curl/7.29.0
> Accept: */*
> Host:httpbin.example.com
>
< HTTP/1.1 418 Unknown
< server: istio-envoy
< date: Sun, 20 Mar 2022 15:41:25 GMT
< x-more-info: http://tools.ietf.org/html/rfc2324
< access-control-allow-origin: *
< access-control-allow-credentials: true
< content-length: 135
< x-envoy-upstream-service-time: 23
<
-=[ teapot ]=-
_...._
.' _ _ `.
| ."` ^ `". _,
\_;`"---"`|//
| ;/
\_ _/
`"""`
* Connection #0 to host httpbin.example.com left intact
- 删除网关的密码并创建一个新密码以更改入口网关的凭据。
$ kubectl -n istio-system delete secret httpbin-credential
$ mkdir new_certificates
$ openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -subj '/O=example Inc./CN=example.com' -keyout new_certificates/example.com.key -out new_certificates/example.com.crt
$ openssl req -out new_certificates/httpbin.example.com.csr -newkey rsa:2048 -nodes -keyout new_certificates/httpbin.example.com.key -subj "/CN=httpbin.example.com/O=httpbin organization"
$ openssl x509 -req -sha256 -days 365 -CA new_certificates/example.com.crt -CAkey new_certificates/example.com.key -set_serial 0 -in new_certificates/httpbin.example.com.csr -out new_certificates/httpbin.example.com.crt
$ kubectl create -n istio-system secret tls httpbin-credential \
--key=new_certificates/httpbin.example.com.key \
--cert=new_certificates/httpbin.example.com.crt
- 使用新的证书链访问
httpbin
服务:curl
$ curl -v -HHost:httpbin.example.com --resolve "httpbin.example.com:$SECURE_INGRESS_PORT:$INGRESS_HOST" \
--cacert new_certificates/example.com.crt "https://httpbin.example.com:$SECURE_INGRESS_PORT/status/418"
...
HTTP/2 418
...
-=[ teapot ]=-
_...._
.' _ _ `.
| ."` ^ `". _,
\_;`"---"`|//
| ;/
\_ _/
`"""`
- 如果您尝试使用
httpbin
以前的证书链进行访问,则现在尝试失败。
$ curl -v -HHost:httpbin.example.com --resolve "httpbin.example.com:$SECURE_INGRESS_PORT:$INGRESS_HOST" \
--cacert example.com.crt "https://httpbin.example.com:$SECURE_INGRESS_PORT/status/418"
...
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (OUT), TLS alert, Server hello (2):
* curl: (35) error:04FFF06A:rsa routines:CRYPTO_internal:block type is not 01
# 为多个主机配置TLS入口网关
您可以为多个主机配置一个入口网关 httpbin.example.com
,helloworld-v1.example.com
例如。入口网关检索与特定credentialName
.
- 要恢复 的凭据
httpbin
,请删除其密钥并重新创建。
$ kubectl -n istio-system delete secret httpbin-credential
$ kubectl create -n istio-system secret tls httpbin-credential \
--key=httpbin.example.com.key \
--cert=httpbin.example.com.crt
- 启动
helloworld-v1
示例
$ cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Service
metadata:
name: helloworld-v1
labels:
app: helloworld-v1
spec:
ports:
- name: http
port: 5000
selector:
app: helloworld-v1
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: helloworld-v1
spec:
replicas: 1
selector:
matchLabels:
app: helloworld-v1
version: v1
template:
metadata:
labels:
app: helloworld-v1
version: v1
spec:
containers:
- name: helloworld
image: istio/examples-helloworld-v1
resources:
requests:
cpu: "100m"
imagePullPolicy: IfNotPresent #Always
ports:
- containerPort: 5000
EOF
- 生成证书和私钥
helloworld-v1.example.com
:
$ openssl req -out helloworld-v1.example.com.csr -newkey rsa:2048 -nodes -keyout helloworld-v1.example.com.key -subj "/CN=helloworld-v1.example.com/O=helloworld organization"
$ openssl x509 -req -sha256 -days 365 -CA example.com.crt -CAkey example.com.key -set_serial 1 -in helloworld-v1.example.com.csr -out helloworld-v1.example.com.crt
- 创建
helloworld-credential
密钥:
$ kubectl create -n istio-system secret tls helloworld-credential --key=helloworld-v1.example.com.key --cert=helloworld-v1.example.com.crt
- 为端口 443 定义一个具有两个服务器部分的网关。将
credentialName
每个端口上的值分别设置为httpbin-credential
和helloworld-credential
。- 将 TLS 模式设置为
SIMPLE
是单向TLS入口网关. - 将 TLS 模式设置为
MUTUAL
是双向TLS入口网关
- 将 TLS 模式设置为
$ cat <<EOF | kubectl apply -f -
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: mygateway
spec:
selector:
istio: ingressgateway # use istio default ingress gateway
servers:
- port:
number: 443
name: https-httpbin
protocol: HTTPS
tls:
mode: SIMPLE
credentialName: httpbin-credential
hosts:
- httpbin.example.com
- port:
number: 443
name: https-helloworld
protocol: HTTPS
tls:
mode: SIMPLE
credentialName: helloworld-credential
hosts:
- helloworld-v1.example.com
EOF
- 配置网关的流量路由。定义相应的虚拟服务。
$ cat <<EOF | kubectl apply -f -
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: helloworld-v1
spec:
hosts:
- helloworld-v1.example.com
gateways:
- mygateway
http:
- match:
- uri:
exact: /hello
route:
- destination:
host: helloworld-v1
port:
number: 5000
EOF
- 发送 HTTPS 请求到
helloworld-v1.example.com
:
$ curl -v -HHost:httpbin.example.com --resolve "httpbin.example.com:$SECURE_INGRESS_PORT:$INGRESS_HOST" \
> --cacert example.com.crt "https://httpbin.example.com:$SECURE_INGRESS_PORT/status/418"
* Added httpbin.example.com:7339:192.168.1.6 to DNS cache
* About to connect() to httpbin.example.com port 7339 (#0)
* Trying 192.168.1.6...
* Connected to httpbin.example.com (192.168.1.6) port 7339 (#0)
* Initializing NSS with certpath: sql:/etc/pki/nssdb
* CAfile: example.com.crt
CApath: none
* SSL connection using TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
* Server certificate:
* subject: O=httpbin organization,CN=httpbin.example.com
* start date: Mar 20 15:18:42 2022 GMT
* expire date: Mar 20 15:18:42 2023 GMT
* common name: httpbin.example.com
* issuer: CN=example.com,O=example Inc.
> GET /status/418 HTTP/1.1
> User-Agent: curl/7.29.0
> Accept: */*
> Host:httpbin.example.com
>
< HTTP/1.1 418 Unknown
< server: istio-envoy
< date: Mon, 21 Mar 2022 00:58:24 GMT
< x-more-info: http://tools.ietf.org/html/rfc2324
< access-control-allow-origin: *
< access-control-allow-credentials: true
< content-length: 135
< x-envoy-upstream-service-time: 2
<
-=[ teapot ]=-
_...._
.' _ _ `.
| ."` ^ `". _,
\_;`"---"`|//
| ;/
\_ _/
`"""`
* Connection #0 to host httpbin.example.com left intact
- 发送一个 HTTPS 请求
httpbin.example.com
并仍然得到一个茶壶作为回报:
$ curl -v -HHost:httpbin.example.com --resolve "httpbin.example.com:$SECURE_INGRESS_PORT:$INGRESS_HOST" \
> --cacert example.com.crt "https://httpbin.example.com:$SECURE_INGRESS_PORT/status/418"
* Added httpbin.example.com:7339:192.168.1.6 to DNS cache
* About to connect() to httpbin.example.com port 7339 (#0)
* Trying 192.168.1.6...
* Connected to httpbin.example.com (192.168.1.6) port 7339 (#0)
* Initializing NSS with certpath: sql:/etc/pki/nssdb
* CAfile: example.com.crt
CApath: none
* SSL connection using TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
* Server certificate:
* subject: O=httpbin organization,CN=httpbin.example.com
* start date: Mar 20 15:18:42 2022 GMT
* expire date: Mar 20 15:18:42 2023 GMT
* common name: httpbin.example.com
* issuer: CN=example.com,O=example Inc.
> GET /status/418 HTTP/1.1
> User-Agent: curl/7.29.0
> Accept: */*
> Host:httpbin.example.com
>
< HTTP/1.1 418 Unknown
< server: istio-envoy
< date: Mon, 21 Mar 2022 01:13:48 GMT
< x-more-info: http://tools.ietf.org/html/rfc2324
< access-control-allow-origin: *
< access-control-allow-credentials: true
< content-length: 135
< x-envoy-upstream-service-time: 3
<
-=[ teapot ]=-
_...._
.' _ _ `.
| ."` ^ `". _,
\_;`"---"`|//
| ;/
\_ _/
`"""`
* Connection #0 to host httpbin.example.com left intact
# 密钥格式
Istio 支持读取几种不同的 Secret 格式,以支持与各种工具的集成,例如cert-manager (opens new window):
- 一个 TLS 密钥,带有
tls.key
和tls.crt
,如上所述。对于双向 TLS,ca.crt
可以使用密钥。 - 带有键的通用 Secret
key
和cert
. 对于双向 TLS,cacert
可以使用密钥。 - 带有键的通用 Secret
key
和cert
. 对于双向 TLS,一个名为 的单独通用 Secret<secret>-cacert
带有一个cacert
密钥。例如,httpbin-credential
有key
和cert
,并且httpbin-credential-cacert
有cacert
。 cacert
键值可以是一个 CA 捆绑包,由串联的各个 CA 证书组成。
# 清理
删除网关配置、虚拟服务定义和机密:
$ kubectl delete gateway mygateway
$ kubectl delete virtualservice httpbin
$ kubectl delete --ignore-not-found=true -n istio-system secret httpbin-credential \
helloworld-credential
$ kubectl delete --ignore-not-found=true virtualservice helloworld-v1
删除证书和密钥:
$ rm -rf example.com.crt example.com.key httpbin.example.com.crt httpbin.example.com.key httpbin.example.com.csr helloworld-v1.example.com.crt helloworld-v1.example.com.key helloworld-v1.example.com.csr client.example.com.crt client.example.com.csr client.example.com.key ./new_certificates
关闭httpbin
和helloworld-v1
服务:
$ kubectl delete deployment --ignore-not-found=true httpbin helloworld-v1
$ kubectl delete service --ignore-not-found=true httpbin helloworld-v1
# 出口TLS发起
# 什么是TLS发起
用于配置 Istio 以受控方式访问外部服务。这个例子展示了如何配置 Istio 来执行ServiceEntry
(opens new window)TLS 发起 用于到外部服务的流量。当原始流量为 HTTP 时,Istio 将打开与外部服务的 HTTPS 连接。
考虑一个对外部站点执行 HTTP 调用的遗留应用程序。假设运行应用程序的组织收到一个新要求,要求所有外部流量都必须加密。使用 Istio,只需配置即可实现此要求,无需更改应用程序中的任何代码。应用程序可以发送未加密的 HTTP 请求,然后 Istio 将为应用程序加密它们。
从源发送未加密的 HTTP 请求并让 Istio 执行 TLS 升级的另一个好处是 Istio 可以生成更好的遥测数据并为未加密的请求提供更多的路由控制。
# 环境准备
启动将用作外部调用测试源的睡眠样本。 (opens new window)
如果您启用了自动边车注入 (opens new window),请部署sleep
应用程序:
$ kubectl apply -f samples/sleep/sleep.yaml
$ kubectl apply -f <(istioctl kube-inject -f samples/sleep/sleep.yaml)
请注意,您可以exec
和curl
来自的任何 pod 都将执行以下过程。
创建一个 shell 变量来保存用于向外部服务发送请求的源 pod 的名称。如果您使用了sleep (opens new window)示例,请运行:
$ export SOURCE_POD=$(kubectl get pod -l app=sleep -o jsonpath={.items..metadata.name})
# 配置对外部服务的访问
首先使用访问外部服务 (opens new window)任务edition.cnn.com
中显示的相同技术配置对外部服务的访问。但是,这一次使用单个来启用对服务的 HTTP 和 HTTPS 访问。ServiceEntry
- 创建一个
ServiceEntry
以启用对edition.cnn.com
:
$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
name: edition-cnn-com
spec:
hosts:
- edition.cnn.com
ports:
- number: 80
name: http-port
protocol: HTTP
- number: 443
name: https-port
protocol: HTTPS
resolution: DNS
EOF
- 向外部 HTTP 服务发出请求:
注意 curl 的标志
-L
,它指示curl遵循重定向。在这种情况下,服务器为 HTTP 请求返回了重定向响应(301 Moved Permanently (opens new window) ) 。重定向响应指示客户端发送一个额外的请求,这次使用 HTTPS,到. 对于第二个请求,服务器返回请求的内容和200 OK状态码。http://edition.cnn.com/politics``https://edition.cnn.com/politics
尽管curl命令透明地处理了重定向,但这里有两个问题。第一个问题是冗余请求,它使获取
http://edition.cnn.com/politics
. 第二个问题是 URL 的路径,在这种情况下是政治,是以明文形式发送的。如果有攻击者嗅探你的应用程序和 之间的通信edition.cnn.com
,攻击者就会知道edition.cnn.com
应用程序获取了哪些特定主题。出于隐私原因,您可能希望阻止此类披露。这两个问题都可以通过配置 Istio 来执行 TLS 发起来解决。
$ kubectl exec "${SOURCE_POD}" -c sleep -- curl -sSL -o /dev/null -D - http://edition.cnn.com/politics
HTTP/1.1 301 Moved Permanently
...
location: https://edition.cnn.com/politics
...
HTTP/2 200
...
# 出口流量的TLS发起
- 重新定义
ServiceEntry
上一节中的将 HTTP 请求重定向到端口 443 并添加一个DestinationRule
以执行 TLS 发起:配置服务入口的http重定向到443端口
配置交通策略针对80端口使用模式为SIMPLE的tls
$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
name: edition-cnn-com
spec:
hosts:
- edition.cnn.com
ports:
- number: 80
name: http-port
protocol: HTTP
targetPort: 443
- number: 443
name: https-port
protocol: HTTPS
resolution: DNS
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: edition-cnn-com
spec:
host: edition.cnn.com
trafficPolicy:
portLevelSettings:
- port:
number: 80
tls:
mode: SIMPLE # initiates HTTPS when accessing edition.cnn.com
EOF
以上DestinationRule
将为端口 80 上的 HTTP 请求执行 TLS 发起,ServiceEntry
然后将端口 80 上的请求重定向到目标端口 443。
- 向 发送 HTTP 请求
http://edition.cnn.com/politics
,如上一节所述:
$ kubectl exec "${SOURCE_POD}" -c sleep -- curl -sSL -o /dev/null -D - http://edition.cnn.com/politics
HTTP/1.1 200 OK
...
这次您收到200 OK作为第一个也是唯一的响应。Istio 为curl执行 TLS 发起,因此原始 HTTP 请求被转发edition.cnn.com
为 HTTPS。服务器直接返回内容,无需重定向。您消除了客户端和服务器之间的双重往返,并且请求使网格加密,而没有披露您的应用程序获取.edition.cnn.com
请注意,您使用了与上一节中相同的命令。对于以编程方式访问外部服务的应用程序,无需更改代码。您可以通过配置 Istio 获得 TLS 发起的好处,而无需更改一行代码。
- 请注意,使用 HTTPS 访问外部服务的应用程序继续像以前一样工作:
$ kubectl exec "${SOURCE_POD}" -c sleep -- curl -sSL -o /dev/null -D - https://edition.cnn.com/politics
HTTP/2 200
...
# 清理
删除您创建的 Istio 配置项、关闭睡眠 (opens new window)服务:
$ kubectl delete serviceentry edition-cnn-com
$ kubectl delete destinationrule edition-cnn-com
$ kubectl delete -f samples/sleep/sleep.yaml
# 出口网关
# 什么是出口网关
Istio 使用入口和出口网关 (opens new window) 来配置在服务网格边缘执行的负载均衡器。入口网关允许您定义所有传入流量流过的网格的入口点。出口网关是一个对称的概念;它定义了网格的出口点。出口网关允许您将 Istio 功能(例如监控和路由规则)应用于退出网格的流量。
考虑一个具有严格安全要求的组织,即所有离开服务网格的流量都必须流经一组专用节点。这些节点将在专用机器上运行,与集群中运行应用程序的其余节点分开。这些特殊节点将用于对出口流量执行策略,并且将比其他节点更彻底地监控。
另一个用例是应用程序节点没有公共 IP 的集群,因此在它们上运行的网内服务无法访问 Internet。定义一个出口网关,引导所有出口流量通过它,并将公共 IP 分配给出口网关节点,允许应用程序节点以受控方式访问外部服务。
# 基础环境
部署sleep (opens new window)示例应用程序以用作发送请求的测试源。如果您 启用了自动 Sidecar 注入 (opens new window) ,请运行以下命令来部署示例应用程序:
$ kubectl apply -f samples/sleep/sleep.yaml
$ kubectl apply -f <(istioctl kube-inject -f samples/sleep/sleep.yaml)
将SOURCE_POD
环境变量设置为源 pod 的名称:
$ export SOURCE_POD=$(kubectl get pod -l app=sleep -o jsonpath={.items..metadata.name})
# 部署Istio出口网关
- 检查是否部署了 Istio 出口网关:
$ kubectl get pod -l istio=egressgateway -n istio-system
如果没有返回 pod,请执行以下步骤部署 Istio 出口网关。
- 如果您使用
IstioOperator
CR 安装 Istio,请将以下字段添加到您的配置中:
spec:
components:
egressGateways:
- name: istio-egressgateway
enabled: true
否则,将等效设置添加到您的原始istioctl install
命令,例如:
$ istioctl install <flags-you-used-to-install-Istio> \
--set components.egressGateways[0].name=istio-egressgateway \
--set components.egressGateways[0].enabled=true
# HTTP流量的出口网关
首先创建一个ServiceEntry
允许直接流量到外部服务。
ServiceEntry
为定义一个edition.cnn.com
。配置主机为edition.cnn.com
配置主机发现模式为DNS
配置HTTP和HTTPS两个协议
DNS
必须在下面的服务条目中使用分辨率。如果分辨率为NONE
,网关将在无限循环中将流量定向到自身。这是因为网关接收到一个请求,其原始目标 IP 地址等于网关的服务 IP(因为请求是由 Sidecar 代理定向到网关的)。通过DNS
解析,网关执行 DNS 查询以获取外部服务的 IP 地址并将流量定向到该 IP 地址。
$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
name: cnn
spec:
hosts:
- edition.cnn.com
ports:
- number: 80
name: http-port
protocol: HTTP
- number: 443
name: https
protocol: HTTPS
resolution: DNS
EOF
通过向http://edition.cnn.com/politics (opens new window)
ServiceEntry
发送 HTTP 请求来验证您的应用是否正确。输出应与 TLS Origination for Egress Traffic (opens new window)示例中的相同,但没有 TLS 发起。
$ kubectl exec "$SOURCE_POD" -c sleep -- curl -sSL -o /dev/null -D - http://edition.cnn.com/politics
...
HTTP/1.1 301 Moved Permanently
...
location: https://edition.cnn.com/politics
...
HTTP/2 200
Content-Type: text/html; charset=utf-8
...
Gateway
为edition.cnn.com创建一个出口,端口 80,并为定向到出口网关的流量创建一个目标规则。配置出口网关为egressgateway
配置主机edition.cnn.com规范为允许80端口
创建路由规则主机使用 istio-egressgateway.istio-system.svc.cluster.local
路由规则下的子集使用cnn
要通过出口网关引导多个主机,您可以
*
在Gateway
. 应将 中的subset
字段重新用于其他主机。DestinationRule
$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: istio-egressgateway
spec:
selector:
istio: egressgateway
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- edition.cnn.com
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: egressgateway-for-cnn
spec:
host: istio-egressgateway.istio-system.svc.cluster.local
subsets:
- name: cnn
EOF
- 定义
VirtualService
以将流量从 sidecar 引导到 egress 网关,并从 egress 网关引导到外部服务:定义主机地址为edition.cnn.com
配置虚拟服务使用两个网关 istio-egressgateway和mesh
配置http规则分别使用两个网关都是允许80端口
配置mesh的路由规则允许主机 istio-egressgateway.istio-system.svc.cluster.local 、子集为cnn、端口是80 、权重分配为100
配置istio-egressgateway的路由规则允许主机为edition.cnn.com、端口是80、权重分配也是100
$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: direct-cnn-through-egress-gateway
spec:
hosts:
- edition.cnn.com
gateways:
- istio-egressgateway
- mesh
http:
- match:
- gateways:
- mesh
port: 80
route:
- destination:
host: istio-egressgateway.istio-system.svc.cluster.local
subset: cnn
port:
number: 80
weight: 100
- match:
- gateways:
- istio-egressgateway
port: 80
route:
- destination:
host: edition.cnn.com
port:
number: 80
weight: 100
EOF
- 将 HTTP 请求重新发送到http://edition.cnn.com/politics (opens new window)。
$ kubectl exec "$SOURCE_POD" -c sleep -- curl -sSL -o /dev/null -D - http://edition.cnn.com/politics
...
HTTP/1.1 301 Moved Permanently
...
location: https://edition.cnn.com/politics
...
HTTP/2 200
Content-Type: text/html; charset=utf-8
...
检查
istio-egressgateway
pod 的日志中是否有与我们的请求相对应的行。如果 Istio 部署在istio-system
命名空间中,打印日志的命令是:请注意,您仅将流量从端口 80 重定向到出口网关。到端口 443 的 HTTPS 流量直接转到edition.cnn.com。
[root@k8s-master-node1 ~]# kubectl logs -l istio=egressgateway -c istio-proxy -n istio-system | tail
2022-03-21T09:42:18.336211Z info xdsproxy connected to upstream XDS server: istiod.istio-system.svc:15012
2022-03-21T10:14:36.196020Z info xdsproxy connected to upstream XDS server: istiod.istio-system.svc:15012
2022-03-21T10:47:07.424114Z info xdsproxy connected to upstream XDS server: istiod.istio-system.svc:15012
2022-03-21T11:15:16.460832Z info xdsproxy connected to upstream XDS server: istiod.istio-system.svc:15012
2022-03-21T11:42:49.235363Z info xdsproxy connected to upstream XDS server: istiod.istio-system.svc:15012
[2022-03-21T11:52:20.947Z] "- - -" 0 UF,URX - - "-" 0 0 15 - "-" "-" "-" "-" "151.101.65.67:443" outbound|443||edition.cnn.com - 10.244.1.4:8443 10.244.1.20:33384 edition.cnn.com -
[2022-03-21T11:52:28.733Z] "- - -" 0 - - - "-" 910 1262165 45829 - "-" "-" "-" "-" "151.101.193.67:443" outbound|443||edition.cnn.com 10.244.1.4:49136 10.244.1.4:8443 10.244.1.20:33440 edition.cnn.com -
2022-03-21T12:15:16.256967Z info xdsproxy connected to upstream XDS server: istiod.istio-system.svc:15012
2022-03-21T12:45:27.104242Z info xdsproxy connected to upstream XDS server: istiod.istio-system.svc:15012
[2022-03-21T13:06:35.033Z] "GET /politics HTTP/2" 301 - via_upstream - "-" 0 0 356 356 "10.244.1.20" "curl/7.81.0-DEV" "82a2b1d1-70f0-9907-bfc3-1a86cfd91721" "edition.cnn.com" "151.101.129.67:80" outbound|80||edition.cnn.com 10.244.1.4:47896 10.244.1.4:8080 10.244.1.20:42962 - -
# 清理HTTP网关
在继续下一步之前删除以前的定义:
$ kubectl delete gateway istio-egressgateway
$ kubectl delete serviceentry cnn
$ kubectl delete virtualservice direct-cnn-through-egress-gateway
$ kubectl delete destinationrule egressgateway-for-cnn
# HTTPS流量的出口网关
在本节中,您将通过出口网关引导 HTTPS 流量(由应用程序发起的 TLS)。您需要TLS
在一个对应ServiceEntry
的、一个出口Gateway
和一个VirtualService
.
ServiceEntry
为定义一个edition.cnn.com
:配置服务入口的主机列表为 edition.cnn.com
配置ports规则为TLS模式以及443端口
配置主机发现模式为DNS
$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
name: cnn
spec:
hosts:
- edition.cnn.com
ports:
- number: 443
name: tls
protocol: TLS
resolution: DNS
EOF
- 通过向https://edition.cnn.com/politics (opens new window)
ServiceEntry
发送 HTTPS 请求来验证您的应用是否正确。
$ kubectl exec "$SOURCE_POD" -c sleep -- curl -sSL -o /dev/null -D - https://edition.cnn.com/politics
...
HTTP/2 200
Content-Type: text/html; charset=utf-8
...
Gateway
为edition.cnn.com创建一个出口、一个目标规则和一个虚拟服务,以引导流量通过出口网关并从出口网关到外部服务。创建服务网关和目标规则以及虚拟服务
配置Gateway使用egressgateway的出口网关,服务规则为允许443端口使用TLS
Gateway允许的主机为 edition.cnn.com tls的模式使用PASSTHROUGH
配置目标规则允许的主机列表istio-egressgateway.istio-system.svc.cluster.local 子集为cnn
配置虚拟服务允许主机列表为edition.cnn.com
配置虚拟服务使用出口网关istio-egressgateway和mesh
配置虚拟服务的mesh网关要匹配的SNI(服务器名称指示器)edition.cnn.com 允许443端口
配置虚拟服务的mesh网关目标规则允许主机列表istio-egressgateway.istio-system.svc.cluster.local并且允许443端口
配置虚拟服务的istio-egressgateway网关要匹配的SNI(服务器名称指示器)edition.cnn.com 允许443端口
配置虚拟服务的istio-egressgateway网关目标规则允许主机列表edition.cnn.com并且允许443端口、权重为100
要通过出口网关引导多个主机,您可以
*
在Gateway
. 应将 中的subset
字段重新用于其他主机。DestinationRule
$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: istio-egressgateway
spec:
selector:
istio: egressgateway
servers:
- port:
number: 443
name: tls
protocol: TLS
hosts:
- edition.cnn.com
tls:
mode: PASSTHROUGH
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: egressgateway-for-cnn
spec:
host: istio-egressgateway.istio-system.svc.cluster.local
subsets:
- name: cnn
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: direct-cnn-through-egress-gateway
spec:
hosts:
- edition.cnn.com
gateways:
- mesh
- istio-egressgateway
tls:
- match:
- gateways:
- mesh
port: 443
sniHosts:
- edition.cnn.com
route:
- destination:
host: istio-egressgateway.istio-system.svc.cluster.local
subset: cnn
port:
number: 443
- match:
- gateways:
- istio-egressgateway
port: 443
sniHosts:
- edition.cnn.com
route:
- destination:
host: edition.cnn.com
port:
number: 443
weight: 100
EOF
- 向https://edition.cnn.com/politics (opens new window)发送 HTTPS 请求。输出应该和以前一样。
$ kubectl exec "$SOURCE_POD" -c sleep -- curl -sSL -o /dev/null -D - https://edition.cnn.com/politics
...
HTTP/2 200
Content-Type: text/html; charset=utf-8
...
- 查看出口网关代理的日志。如果 Istio 部署在
istio-system
命名空间中,打印日志的命令是:
[root@k8s-master-node1 ~]# kubectl logs -l istio=egressgateway -n istio-system
[2022-03-21T11:52:28.733Z] "- - -" 0 - - - "-" 910 1262165 45829 - "-" "-" "-" "-" "151.101.193.67:443" outbound|443||edition.cnn.com 10.244.1.4:49136 10.244.1.4:8443 10.244.1.20:33440 edition.cnn.com -
2022-03-21T12:15:16.256967Z info xdsproxy connected to upstream XDS server: istiod.istio-system.svc:15012
2022-03-21T12:45:27.104242Z info xdsproxy connected to upstream XDS server: istiod.istio-system.svc:15012
[2022-03-21T13:06:35.033Z] "GET /politics HTTP/2" 301 - via_upstream - "-" 0 0 356 356 "10.244.1.20" "curl/7.81.0-DEV" "82a2b1d1-70f0-9907-bfc3-1a86cfd91721" "edition.cnn.com" "151.101.129.67:80" outbound|80||edition.cnn.com 10.244.1.4:47896 10.244.1.4:8080 10.244.1.20:42962 - -
2022-03-21T13:15:59.020699Z info xdsproxy connected to upstream XDS server: istiod.istio-system.svc:15012
2022-03-21T13:40:14.303559Z info ads XDS: Incremental Pushing:0 ConnectedEndpoints:2 Version:
2022-03-21T13:40:14.597921Z info cache generated new workload certificate latency=294.218007ms ttl=23h59m59.402089572s
2022-03-21T13:40:14.598006Z info ads SDS: PUSH for node:istio-egressgateway-7f4864f59c-q5cdv.istio-system resources:1 size:4.0kB resource:default
2022-03-21T13:47:00.985263Z info xdsproxy connected to upstream XDS server: istiod.istio-system.svc:15012
2022-03-21T14:18:49.713637Z info xdsproxy connected to upstream XDS server: istiod.istio-system.svc:15012
# 清理HTTPS网关
$ kubectl delete serviceentry cnn
$ kubectl delete gateway istio-egressgateway
$ kubectl delete virtualservice direct-cnn-through-egress-gateway
$ kubectl delete destinationrule egressgateway-for-cnn