当前位置:首页 > 技术分享 > 正文

ISTIO从A到Y

Istio是一款开源服务网格,允许您连接、保护、控制和观察应用程序的服务。我们将了解如何安装Istio,以及如何使用它来保护和监控我们的服务。当你开始使用Kubernetes时,你很快就会意识到管理服务之间的通信并不简单。一旦流量通过Ingress,你唯一能做的事情就是检查Pod日志,这既不实用也不...

Istio是一款开源服务网格,允许您连接、保护、控制和观察应用程序的服务。我们将了解如何安装Istio,以及如何使用它来保护和监控我们的服务。当你开始使用Kubernetes时,你很快就会意识到管理服......

Istio是一款开源服务网格,允许您连接、保护、控制和观察应用程序的服务。我们将了解如何安装Istio,以及如何使用它来保护和监控我们的服务。

当你开始使用Kubernetes时,你很快就会意识到管理服务之间的通信并不简单。一旦流量通过Ingress,你唯一能做的事情就是检查Pod日志,这既不实用也不高效。

这就是服务网格诞生的原因。它们允许你管理服务之间的通信,安全和监控交换,以及控制流量。本页面的目标是向你介绍Istio,一个开源服务网格。

让我们一起探索Istio,如何安装它,以及如何在喝一杯好咖啡的同时使用它。

但在我们开始之前,让我们先解释一下什么是服务网格。

什么是服务网格?

服务网格是一个基础设施层,允许你管理应用程序服务之间的通信。

简而言之:服务网格集成到由多个子应用程序(例如微服务)组成的应用程序的基础设施中,以添加功能。

在之前的一篇文章中,我介绍了Consul,它可以作为基于Envoy的sidecar的服务网格使用。

在上面的示例中,每个程序都需要访问另一个程序,UI访问身份验证和后端,后端访问存储和排队服务,最后:消费者访问排队服务。

有了这种方案,就会出现一些问题:

如何允许/拒绝两个服务之间的交换?

如何保护服务之间的交换?

可观测性如何?

例如,如果我们希望UI访问后端但不访问存储,我们该怎么做?或者如果我们希望后端访问存储但不访问UI?

一种可能性是使用NetworkPolicies,如果我们的CNI支持,但这不允许管理第7层交互(HTTP、gRPC等)除了Cilium。具体来说,我无法限制对特定路由(/api、/point/v3/ping)或请求类型(GET、POST等)的访问。

这就是像Istio(或Consul)这样的服务网格发挥作用的地方。

服务网格使用代理来拦截服务之间的请求。它们充当中间人,通过在通信上添加一层控制来实现。

这有点像Web应用程序的WAF(Web应用程序防火墙)。每个应用程序将拥有自己的“路由器”,它将把传入和传出的请求重定向到目标服务的代理。

因此,以下是带有服务网格的图表:

每次应用程序尝试与另一个服务通信时,代理都会拦截请求并将其重定向到目标服务的代理。

为什么使用服务网格?

当你只有2-3个应用程序时,服务网格可能看起来没有必要。但一旦你开始拥有多个服务、多个团队、多个集群,服务网格就会很快变得实用。

你可以通过信任服务的身份而不是IP地址或DNS名称(它们很容易被欺骗)来允许不同的服务以安全和受控的方式相互通信。

那Istio呢?

Istio可用作Kubernetes集群中的服务网格。事实上,它满足了上面提到的需求:保护交换、控制流量和监控交换。

它是一个完全开源的项目,于2022年9月30日加入CNCF(云原生计算基金会),并于2023年7月12日成为孵化项目。

我们将在本文后面有机会更多地讨论Istio的架构、组件和功能。

我的实验室环境

对于这个实验室,我使用了一个具有3个节点(1个主节点,2个工作节点)的Kubernetes集群,并使用Talos和Flannel作为CNI(通常,我更喜欢Cilium,但我与Istio的某个功能存在不兼容性,我将在后面讨论)。

以下是使用talhelper为我的集群配置的配置。请随时查看我关于Talos的文章,以获取有关其安装的更多信息。

Talhelper配置

---clusterName:istio-clustertalosVersion:::|--op:addpath:/cluster/discovery/enabledvalue:false-op:replacepath:/machine/network/kubespanvalue:enabled:false-op:addpath:/machine/kubelet/extraArgsvalue:rotate-server-certificates:true-op:addpath:/machine/filesvalue:-content:|[metrics]address="0.0.0.0:11234"path:/var/cri//:createnodes:-hostname:controlplaneipAddress:192.168.128.27controlPlane:truearch:amd64installDisk:/dev/sda-hostname:worker-1ipAddress:192.168.128.28controlPlane:falsearch:amd64installDisk:/dev/sda-hostname:worker-2ipAddress:192.168.128.30controlPlane:falsearch:amd64installDisk:/dev/sdacontrolPlane:schematic:customization:systemExtensions:officialExtensions:-siderolabs/qemu-guest-agent-siderolabs/iscsi-toolsworker:schematic:customization:systemExtensions:officialExtensions:-siderolabs/qemu-guest-agent-siderolabs/iscsi-tools

我们还需要一个指标服务器才能让Istio的HPA(水平Pod自动扩缩器)正常工作。对此,我已经部署了以下清单,分别为kubelet部署指标服务器和证书批准者。

kubectlapply-f

一如既往,务必小心直接在网上应用的清单,务必在应用之前仔细阅读它们。

安装Istioctl

Istioctl是管理Istio的命令行工具。它允许您部署、验证组件的状态、通过清单注入Sidecar,以及执行更多操作。

安装Istioctl的最简单方法是使用Istio提供的脚本(它会下载最新版本),或者直接从Istio版本页面中检索二进制文件。

curl-L|sh-

当然,尽量避免下载脚本并在不阅读的情况下运行它们。另一个解决方案是下载二进制文件本身。

NixOS包也可用于安装Istioctl。

现在,我们已具备在Kubernetes集群上安装Istio所需的一切内容。

IstioProfile

配置文件在将Istio安装到我们的集群之前,选择一个配置文件非常重要。配置文件是一个预定义的Istio配置,它将确定要安装的组件、默认配置和已启用的功能。

Istio有几个版本,每个版本都有其自己的特点:

$istioctlprofilelistIstioconfigurationprofiles:ambientdefaultdemoemptyminimalopenshiftopenshift-ambientpreviewremotestable

若要查看配置文件,可以使用命令istioctlprofiledump。

例如,可以使用以下命令查看默认配置文件:

istioctlprofiledumpdefault

以下是它的价值:

apiVersion:/v1alpha1kind:IstioOperatorspec:components:base:enabled:trueegressGateways:-enabled:falsename:istio-egressgatewayingressGateways:-enabled:truename:istio-ingressgatewaypilot:enabled:truehub:/istioprofile:defaulttag:1.22.1values:defaultRevision:""gateways:istio-egressgateway:{}istio-ingressgateway:{}global:configValidation:trueistioNamespace:istio-system

若要比较两个配置文件,可以使用命令istioctlprofilediff。

若要比较默认配置文件和demo配置文件,可以使用以下命令:

istioctlprofilediffdefaultdemo

这将显示两个配置文件之间的差异:

apiVersion:/v1alpha1kind:IstioOperatormetadata:creationTimestamp:nullnamespace:istio-systemspec:components:base:enabled:trueegressGateways:--enabled:false+-enabled:truename:istio-egressgatewayingressGateways:-enabled:truename:istio-ingressgatewaypilot:enabled:truehub:/istioprofile:defaulttag:1.22.1values:defaultRevision:""gateways:istio-egressgateway:{}istio-ingressgateway:{}global:configValidation:trueistioNamespace:istio-system+profile:demo

可以自定义一个配置文件,命令为,修改文件,添加或移除组件。

在这个练习中,我们主要使用demo配置文件,这是一个已启用所有稳定Istio功能的完整配置文件。

安装Istio

要在Kubernetes集群上安装Istio,我们将使用istioctlinstall命令,后跟配置文件或一个配置文件。

要使用配置文件:

istioctlinstall--setprofile=demo

用法:使用配置文件:

所选配置文件将生成安装Istio和根据配置文件规范配置Istio所需的清单文件(当然也可以使用Helm,但此方法似乎并非推荐的最佳方法)。

嗯,是的。然后Istio将通过创建一个istio-system命名空间并部署必要的组件安装在Kubernetes集群上。

但是,它还没有生效,并且尚未将代理注入到Pods中。为此,我们需要启用所需命名空间中的Istio自动注入。我们稍后会讨论这个问题。

我们的测试应用程序:Bookinfo

我们将使用Istio提供的“Bookinfo”测试应用程序。它是一个由多个微服务组成的应用程序,将成为测试此服务网格的良好示例。

$kubectlapply-ndefault-f

Bookinfo应用程序现在已部署到我们的Kubernetes集群中。我们可以使用命令kubectlget-all-ndefault(krew插件)检查已部署的内容。

$kubectlget-all-ndefaultNAMENAMESPACEAGEserviceaccount/bookinfo-detailsdefault4m15sserviceaccount/bookinfo-productpagedefault4m9sserviceaccount/bookinfo-ratingsdefault4m14sserviceaccount/bookinfo-reviewsdefault4m12sservice/detailsdefault4m15sservice/kubernetesdefault15hservice/productpagedefault4m10sservice/ratingsdefault4m14sservice///////reviews-v3default4m11s

现在让我们尝试访问Bookinfo应用程序。为此,我们将使用端口转发从本地机器访问它。

kubectlport-forwardsvc/productpage9080:9080-ndefault

接下来,打开浏览器并访问URLhttp://localhost:9080/productpage。

Bookinfo应用程序中有4个微服务:

productpage:应用程序的前端服务。它调用details和reviews服务来显示页面内容。

details:包含书籍详细信息的服务。它不调用任何其他服务。

版本1:没有评分。

版本2:带有黑色星星的评分。

版本3:带有红色星星的评分。

那么,如何管理“reviews”版本的分配呢?它是通过Kubernetes服务(使用ClusterIP类型服务)完成的,该服务将以“轮询”模式将请求重定向到“reviews”服务的Pod。

现在,我们将启用Istio边车的注入。这可以通过3种方式完成:

通过在部署创建的Pod中注入标签:

kubectlpatchdeployment-ndefaultproductpage-v1-p'{"spec":{"template":{"metadata":{"labels":{"/inject":"true"}}}}}'

通过在部署之前修补清单文件(使用istioctlkube-inject,它将在清单文件中添加边车):

wget|kubectlapply-ndefault-f-

通过在命名空间中启用自动注入并重新部署Pod:

kubectllabelnamespacedefaultistio-injection=enabledkubectlrolloutrestartdeployment-ndefaultdetails-v1productpage-v1ratings-v1reviews-v1reviews-v2reviews-v3

无论选择哪种选项,Istio边车都将被注入到Bookinfo应用程序的Pod中。您可以使用命令kubectlgetpods-ndefault验证边车是否已注入。

$kubectlgetpodsNAMEREADYSTATUSRESTARTSAGEdetails-v1-64b7b7dd99-ctqd42/2Running0118sproductpage-v1-6bc7f5c4c6-tsxdt2/2Running0114sratings-v1-c54575675-cq8bv2/2Running0118sreviews-v1-76bf7c9d86-zbvts2/2Running0117sreviews-v2-bb7869c75-n7rb52/2Running0116sreviews-v3-5f978f677b-2bqw52/2Running0116s

每个Pod现在都有一个Istio边车âµ,它将拦截传入和传出的请求。

要获取有关此方面的更多信息,可以使用istioctlanalyze和istioctlproxy-status命令,它们将分别检查Istio配置是否正确以及代理是否处于活动状态。

$istioctlanalyze✔Novalidationissuesfoundwhenanalyzingnamespace:default.$istioctlproxy-statusNAME7968449684477777

但在我们继续之前,让我们配备一些工具来帮助我们使用Istio。

我们的可观测性套件

Istio对那些拒绝充分装备自己的人来说是相当不宽容的。因此,我们将发现一些工具来帮助我们了解集群中发生了什么。

Kiali

此工具对于可视化pod之间的交换至关重要,它直接与Istio交互以检索代理数据。它将是我们验证应用程序正常运行的主要工具。

它能够:

可视化服务及其之间的交换。

验证/修改我们的Istio配置。

获取服务指标。

它是一款真正的Istio万能工具。

要安装Kiali,我们可以使用Istio存储库中提供的清单。部署后,我们可以使用端口转发访问KialiWeb界面。

kubectlapply-f

注意:istioctldashboardkiali只是对Kiali服务进行端口转发,您也可以使用kubectlport-forwardsvc/kiali20001:20001-nistio-system。

让我们生成一些流量来查看Kiali可以向我们展示什么。

kubectlport-forward-ndefaultsvc/productpage9080:9080/dev/nullwatch-n1curl-shttp://localhost:9080/productpage-I

在“流量图(TrafficGraph)”部分,我们可以看到服务之间的交换(“ratings”服务丢失了,但这很正常)。

此页面可能是您最常用来调试应用程序的页面。在本文的其余部分,我将广泛引用它。

JaegerZipkin

Zipkin和Jaeger是跟踪工具,允许您跟踪请求通过各个服务的路径。它们允许您查看每个服务的响应时间、错误和交互延迟。

这类似于OpenTelemetry(我还没有测试过),对于了解应用程序的性能以及查看哪个服务是瓶颈非常有用。

要安装Jaeger,我们可以使用Istio存储库中提供的清单。部署后,我们可以使用端口转发访问JaegerWeb界面。

kubectlapply-f

从Jaeger,我可以看到我的请求的跟踪,并查看每个服务的响应时间。

让我们获取“productpage”服务的跟踪(Bookinfo应用程序的入口点),并查看跟踪的详细信息:

当然,我可以查看每个服务的详细信息,并查看每个服务的响应时间。

简而言之,Jaeger是一个非常有用的工具,可以查看请求的详细信息,并获得有关应用程序性能的更多信息。

Kiali更侧重于“概述”,而Jaeger更侧重于“细节”。

Bookinfo应用程序的架构图:

一个典型的用例是查找失败请求的跟踪,以了解原因。因此,我可以查找失败请求的跟踪,并查看哪个服务返回了错误。

在这种情况下,前端只是返回了404错误,我们将在后面看到更有趣的案例。

PrometheusGrafana

关于指标的问题,Istio与Prometheus(它链接到Grafana以进行指标可视化)完美集成。通过使用Istio提供的清单文件,可以部署Prometheus和预先配置的Grafana仪表盘来显示指标。

kubectlapply-f

因此,我们有可视化仪表盘,以便查看:

Istio使用的资源。

服务之间的响应时间。

请求的状态(200、404等)。

服务使用的带宽。

现在我们准备好了,我们可以开始使用Istio了。

公开我们的应用程序

目前,流量只通过sidecar传输,Envoy没有执行任何操作(没有过滤、没有控制、没有安全)。我们将进行设置,以便Istio可以执行其工作。

我们将看到的第一个CRD(自定义资源定义)是VirtualService。VirtualService是一个Istio对象,允许您为Kubernetes服务配置路由规则。

VirtualService创建的路由将传播到所有sidecar,然后sidecar将根据定义的规则重定向流量。

例如,我将为Bookinfo应用程序的“details”服务创建第一个VirtualService。

kind:VirtualServiceapiVersion:/v1beta1metadata:name:details-vsnamespace:defaultspec:hosts:-details-enabled:truepilot:enabled:truehub:/istiotag:1.21.0values:defaultRevision:""gateways:istio-egressgateway:{}istio-ingressgateway:{}global:configValidation:trueistioNamespace:istio-systemprofile:a-cup-of-coffee

由于我没有负载均衡器,我将使用NodePort来访问网关:

kubectlpatchserviceistio-ingressgateway-nistio-system--type='json'-p='[{"op":"replace","path":"/spec/type","value":"NodePort"}]'exportINGRESS_PORT=$(kubectl-nistio-systemgetserviceistio-ingressgateway-ojsonpath='{.[?(@.name=="http2")].nodePort}')exportINGRESS_HOST=$(kubectlgetpo-listio=ingressgateway-nistio-system-ojsonpath='{.items[0].}')echo$INGRESS_HOST:$INGRESS_PORTuseistiodefaultcontrollerservers:-port:number:80name:httpprotocol:HTTPhosts:-"*"We:http2MaxRequests:3Maximumnumberofrequestsperconnection

有了这个配置,如果“productpage”服务在HTTP/2中收到超过3个请求,断路器将停止正在进行的请求并返回503错误(服务不可用)。因此,如果服务器收到太多请求,服务将不会崩溃,并将处理它可以处理的请求,而不会影响其他应用程序。

为了测试这一点,我们可以使用像fortio这样的基准测试工具向“productpage”发送大量请求。(我们稍后将有机会谈论fortio。)

如果我们一次启动一个访问:

$fortioload-c1-n50http://$INGRESS_HOST:$INGRESS_PORT/productpageConnectiontimehistogram(s):+/-0%0.134879target90%0.134879%0.134879Socketsused:1(forperfectkeepalive,wouldbe1)Uniform:false,Jitter:false,Catchupallowed:trueIPaddressesdistribution:192.168.128.30:30492:1Code200:50(100.0%)ResponseHeaderSizes:count50avg188+/-0min188max188sum9400ResponseBody/TotalSizes:+/-271min4480max5481sum269956Alldone50calls(plus0warmup)162.183msavg,6.2qps

100%的请求都成功了。现在,让我们用4个并发请求运行相同的测试:

$fortioload-c4-n50http://$INGRESS_HOST:$INGRESS_PORT/productpageConnectiontimehistogram(s):+/-0.02803%0.15target90%0.172626%0.17643Socketsused:7(forperfectkeepalive,wouldbe4)Uniform:false,Jitter:false,Catchupallowed:trueIPaddressesdistribution:192.168.128.30:30492:7Code200:46(92.0%)Code503:4(8.0%)ResponseHeaderSizes:+/-51min0max188sum8648ResponseBody/TotalSizes:+/-1421min247max5481sum250015Alldone50calls(plus0warmup)195.516msavg,7.0qps

我们可以看到,8%的请求返回了503错误,这是断路器停止这些请求以防止“productpage”服务过载。

Istio中的安全性

我们已经谈了很多关于流量管理和错误处理,但是安全性呢?Istio提供了广泛的功能来保护服务之间的交换,从证书管理到服务身份验证。

我建议我们深入研究这方面,看看Istio如何帮助我们保护我们的应用程序。

mTLS

我在本文开头简要提到了它,Istio默认支持mTLS(双向TLS)来保护服务之间的交换。

每次Envoy与新服务通信时,它都会请求Istiod获取证书以验证交换。因此,由于mTLS的性质,发送方和接收方都可以相互验证。

默认情况下,Istio中的mTLS以“宽松”模式启用。这意味着服务可以在HTTP或HTTPS中通信。

我们可以强制交换以“严格”模式进行,以便服务只能在HTTPS中通信。

apiVersion:/v1beta1kind:PeerAuthenticationmetadata:name:default-mtlsnamespace:defaultspec:mtls:mode:STRICT
apiVersion:/v1beta1kind:PeerAuthenticationmetadata:name:default-mtlsnamespace:istio-systemspec:mtls:mode:STRICT

启用“严格”模式后,我们可以重新启动服务以使mTLS生效。

kubectlrolloutrestartdeployment-ndefaultdetails-v1productpage-v1ratings-v1reviews-v1reviews-v2reviews-v3

从Kiali中,我们可以看到mTLS已通过锁符号正确启用。

为了自己测试这一点,让我们使用NodePort将“productpage”服务暴露在集群外部(ClusterIP服务不足)。

kubectlpatchsvcproductpage-ndefault--type='json'-p='[{"op":"replace","path":"/spec/type","value":"NodePort"}]'PRODUCTPAGE_PORT=$(kubectlgetsvcproductpage-ndefault-ojsonpath='{.[0].nodePort}')PRODUCTPAGE_HOST=$(kubectlgetnodes-ojsonpath='{.items[0].[0].address}')echo$PRODUCTPAGE_HOST:$PRODUCTPAGE_PORT0)GET/productpageHTTP/1.1HTTP/1.1403Forbiddenserver:istio-envoyx-envoy-upstream-service-time:0*_ISSUER=('JWT_ISSUER')ex:1685505001header={'alg':'RS256'}payload={'iss':JWT_ISSUER,'sub':'admin','exp':JWT_EXPIRATION}private_key=open('','r').read()_TOKEN=('JWT_TOKEN')_key=open('','r').read()Providepathtoyourpublickeykey=(public_key,kty='RSA')print(key)
${'n':'rPbn21rfrOrjq5AZ4W6XMjfpUu0SMIAIY9zj6skWWRMEYJn4Jvj6v3olLgMd0JjJluPXxgBYalIL2Fv9mKnZIyFcaCWDkTKBj1xN9k4PN-g5pPSGtYEYHT-zfdBfH-8inea8c9XoQGwyqm7TEwmI4M43WsBoqsItBcB_rLTo8DLlRf0mzlbTeK-M0iEC8-Osfj2FV9vtHR_FdsWaLK5QN-c8aJZIAZQ_S81EvRzVYguJ2-3l05JNI0GGNdGwawvp4cXmvIlCGEuZ5fdNJTjd3pcEJqMR8Gzyd_kb32SiHDXvTdI48KHPo_EjUf_i1maufxJToqEBOPwjEdpg1D1BPQ','e':'AQAB','kty':'RSA'}

我们将生成的JSON提供给Istio,以便它可以验证对“productpage”的请求中的JWT。

apiVersion:/v1beta1kind:RequestAuthenticationmetadata:name:productpage-jwtnamespace:defaultspec:selector:matchLabels:app:productpagejwtRules:-forwardOriginalToken:trueissuer:qjoly@:|{"keys":[{"n":"rPbn21rfrOrjq5AZ4W6XMjfpUu0SMIAIY9zj6skWWRMEYJn4Jvj6v3olLgMd0JjJluPXxgBYalIL2Fv9mKnZIyFcaCWDkTKBj1xN9k4PN-g5pPSGtYEYHT-zfdBfH-8inea8c9XoQGwyqm7TEwmI4M43WsBoqsItBcB_rLTo8DLlRf0mzlbTeK-M0iEC8-Osfj2FV9vtHR_FdsWaLK5QN-c8aJZIAZQ_S81EvRzVYguJ2-3l05JNI0GGNdGwawvp4cXmvIlCGEuZ5fdNJTjd3pcEJqMR8Gzyd_kb32SiHDXvTdI48KHPo_EjUf_i1maufxJToqEBOPwjEdpg1D1BPQ","e":"AQAB","kty":"RSA"}]}

现在,我们可以应用一个ACL规则,仅当JWT由发行者“qjoly@”签名时,才允许对“productpage”的流量。

apiVersion:/v1beta1kind:AuthorizationPolicymetadata:name:productpage-jwtnamespace:defaultspec:selector:matchLabels:app:productpageaction:ALLOWrules:-when:-key:[iss]values:["qjoly@"]

现在一切都已到位,让我们删除在之前步骤中创建的ACL规则(允许对“productpage”的所有流量的规则):

在此配置中,我仅当JWT由发行者“qjoly@”签名时,才允许对“productpage”的流量。让我们尝试发出一个请求以验证这一点:

$curl$INGRESS_HOST:$INGRESS_PORT/productpage-s-IHTTP/1.1403Forbiddenserver:istio-envoydate:Sun,23Jun202406:13:26GMTx-envoy-upstream-service-time:1

现在,让我们使用有效的JWT进行测试(请记住,我从命令exportJWT_TOKEN=$()生成了令牌)。

$curl$INGRESS_HOST:$INGRESS_PORT/productpage--header"Authorization:Bearer$JWT_TOKEN"-s-IHTTP/1.1200OKserver:istio-envoydate:Sun,23Jun202406:16:08GMTx-envoy-upstream-service-time:17

以及使用无效的JWT:

$curl$INGRESS_HOST:$INGRESS_PORT/productpage--header"Authorization:Bearera-cup${JWT_TOKEN}of-coffee"-s-IHTTP/1.1401Unauthorizedwww-authenticate:Bearerrealm="",error="invalid_token"content-length:42content-type:text/plaindate:Sun,23Jun202406:18:14GMTserver:istio-envoyx-envoy-upstream-service-time:5
管理外部访问

可以要求Envoy管理外部服务(即不在Istio网格中的服务)。这对于在利用Istio的功能(重试、可观测性、带宽管理等)的同时管理与第三方服务的通信很有用。

为此,您可以使用ServiceEntry来声明外部服务。以下是一个示例:

apiVersion:/v1beta1kind:ServiceEntrymetadata:name:coffee-websitespec:hosts:-:MESH_EXTERNALports:-number:443name:httpsprotocol:TLSresolution:DNS

为了进行测试,我将声明一个pod,它将允许我们向外部服务发出请求。

apiVersion:v1kind:Podmetadata:name:debug-networkspec:containers:-name:debugimage:digitalocean/doks-debug:latestcommand:["sleep","infinity"]

我将通过此pod生成一些请求,以查看Istio如何处理对已注册服务的请求。

whiletrue;dokubectlexecpods/debug-network-cdebugexec--curl;done

我们可以看到Istio很好地识别了ServiceEntry。

现在,让我们尝试对未在ServiceEntry中声明的服务发出请求:

kubectlexecpods/debug-network-cdebugexec--curl

流量被发送到“PassThroughCluster”(这表明请求像经典pod一样正常处理(Envoy不处理请求))。

现在,让我们考虑我想限制集群的出站流量的情况。为此,我将修改Istio网格的设置,以阻止所有出站流量,除了在ServiceEntry中声明的流量。

=REGISTRY_ONLY

然后,如果我重试对的请求,我将收到错误:

$kubectlexecpods/debug-network-cdebugexec--curl*Recvfailure:Connectionresetbypeer*OpenSSLSSL_connect::44300000000--:--:----:--:----:--:--0*Closingconnection0curl:(35)Recvfailure:Connectionresetbypeercommandterminatedwithexitcode35

另一方面,如果我向发起请求,请求将成功发送:

$kubectlexecpods/debug-network-cdebugexec--curl
=ALLOW_ANY

请注意,REGISTRY_ONLY模式允许每个Pod中所有在ServiceEntry中声明的服务的流量。我还没有找到如何将出站流量限制到单个应用程序。例如,将“productpage”的出站流量限制为“”,并将“reviews”的出站流量限制为“”。

厌倦了Sidecar?

Istio提供了一个名为“Ambient”的功能,该功能允许不在每个Pod中部署Sidecar。在此模式下,Istio充当CNI(容器网络接口)并拦截来自Pod的传入和传出网络流量以应用安全规则。

此模式的主要目标是通过避免在每个Pod中部署Sidecar来减少资源消耗(CPU、内存),以及提高Istio网格的性能。我们将在专门的部分中查看这是否确实如此。

那么,您可能会问,“Ambient”模式如何提高Istio网格的性能?好吧,通过减少Sidecar的数量,从而减少每个请求的L7处理步骤数量。

要激活“Ambient”模式,我们需要使用ambient配置文件安装Istio,并添加GatewayAPI(一个官方的Kubernetes项目,旨在用新对象替换Ingress。如果您对该主题感兴趣,可以参考此处的文档)。

istioctlinstall--setprofile=/dev/null||\{kubectlkustomize"/kubernetes-sigs/gateway-api/config/crd/experimental?ref="|kubectlapply-f-;}

Ambient使用命名空间的新标签:/dataplane-mode=ambient。我们可以将其应用于“coffee”命名空间以激活“Ambient”模式,并删除istio-injection标签以禁用自动Sidecar注入。

/dataplane-mode=ambientkubectllabelnamespacedefaultistio-injection-kubectlrolloutrestartdeployment-ndefaultdetails-v1productpage-v1ratings-v1reviews-v1reviews-v2reviews-v3

现在,无需“istio-ingressgateway”服务来管理传入流量,我们可以直接使用Gateway对象。

为此,我们应用以下配置:

apiVersion:/v1beta1kind:Gatewaymetadata:name:bookinfo-gatewayspec:gatewayClassName:istiolisteners:-name:httpport:80protocol:HTTPallowedRoutes:namespaces:from:Same---apiVersion:/v1beta1kind:HTTPRoutemetadata:name:bookinfospec:parentRefs:-name:bookinfo-gatewayrules:-matches:-path:type:Exactvalue:/productpage-path:type:PathPrefixvalue:/static-path:type:Exactvalue:/login-path:type:Exactvalue:/logout-path:type:PathPrefixvalue:/api/v1/productsbackRefs:-name:productpageport:9080

成功部署了“gateway”Pod:

$kubectlgetpodsNAMEREADYSTATUSRESTARTSAGEbookinfo-gateway-istio-7c755f6876-t59dn1/1Running014sdetails-v1-cf74bb974-ph5dd1/1Running050sproductpage-v1-87d54dd59-nwxgd1/1Running049sratings-v1-7c4bbf97db-sq4751/1Running050sreviews-v1-5fd6d4f8f8-2r4dz1/1Running050sreviews-v2-6f9b55c5db-4fkwb1/1Running050sreviews-v3-7d99fd7978-nbbdr1/1Running049s

没有LoadBalancer,我将公开“bookinfo-gateway”服务作为NodePort以从外部访问应用程序。

$/service-type=NodePort--namespace=default$kubectlgetsvcNAMETYPECLUSTER-IPEXTERNAL-IPPORT(S):31677/TCP,80:31334/////TCP51s

以下命令用于查找网关服务的端口:

exportINGRESS_PORT=$(kubectlgetservicebookinfo-gateway-istio-ojsonpath='{.[?(@.name=="http")].nodePort}')exportINGRESS_HOST=$(kubectlgetnodes-ojsonpath='{.items[0].[?(@.type=="InternalIP")].address}')
$curl$INGRESS_HOST:$INGRESS_PORT/productpage-IHTTP/1.1200OKserver:istio-envoydate:Tue,25Jun202420:55:31GMTcontent-type:text/html;charset=utf-8content-length:4294vary:Cookiex-envoy-upstream-service-time:28

在这种情况下,流量仅在L4层进行管理(不再像sidecar那样在L7层进行管理)。这可以减少pod的负载并提高Istio网格的性能。但是,一些功能(如HTTP流量管理(重试、断路器等))会丢失。可以通过使用网关(或Istio术语中的“航路点”)来管理HTTP流量来缓解此问题。

我将在以后的文章中继续探讨“环境”模式,以澄清这些不确定性。

性能基准测试

最后,我将执行性能基准测试,以查看Istio如何影响Kubernetes集群的性能。为此,我们将比较三种不同场景下的两种通信方法(HTTP和TCP):

无Istio;

有Istio;

Istio在“环境”模式下。

为此,我将使用Fortio,它是由Istio开发的用于HTTP和TCP服务的基准测试工具。

我的集群中使用的CNI是Flannel(我在Cilium和IstioAmbient之间遇到了问题)。但是,我仍然使用Cilium执行了性能测试,以让您了解我的pod之间的带宽。

NetworkPerformanceTestSummary[cilium-test]:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------Scenario|Node|Test|Duration|Min|Mean|Max|P50|P90|P99|TransactionrateOP/s--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------pod-to-pod|same-node|TCP_RR|10s|27µs|75.32µs|8.115ms|69µs|108µs|211µs|13149.99pod-to-pod|same-node|UDP_RR|10s|29µs|81.06µs|23.993ms|67µs|113µs|308µs|12222.58pod-to-pod|same-node|TCP_CRR|10s|143µs|320.87µs|14.373ms|284µs|411µs|1.068ms|3106.70pod-to-pod|other-node|TCP_RR|10s|129µs|298.52µs|14.168ms|245µs|395µs|1.197ms|3340.77pod-to-pod|other-node|UDP_RR|10s|147µs|382.21µs|37.771ms|309µs|573µs|1.534ms|2609.31pod-to-pod|other-node|TCP_CRR|10s|440µs|1.21346ms|17.531ms|1.061ms|1.797ms|4.255ms|823.03---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------Scenario|Node|Test|Duration|ThroughputMb/s-------------------------------------------------------------------------------------pod-to-pod|same-node|TCP_STREAM|10s|610.56pod-to-pod|same-node|UDP_STREAM|10s|272.15pod-to-pod|other-node|TCP_STREAM|10s|1506.68pod-to-pod|other-node|UDP_STREAM|10s|209.39-------------------------------------------------------------------------------------

现在我们已经了解了最大可实现的性能,我们将开始我们的基准测试。

它将包含两个部分:

使用Fortio的HTTP部分来测试延迟;

使用Iperf的TCP部分来测试带宽。

小免责声明:我将获得的结果可能与您的结果不同。性能可能会因集群配置、集群负载、应用程序配置等而异。我将获得的结果不一定代表现实情况。它们只是为了让您了解Istio在给定用例下在Kubernetes集群中的性能。

如果您想阅读更全面的基准测试,我建议您参考这个Github仓库,它提供了非常完整和有趣的结果。

HTTP基准测试

如前所述,我将使用Fortio来测试服务的延迟。为此,我将在集群中部署一个Fortiopod,并向“bookinfo”应用程序的其中一个服务“details”发出请求。

为了安装Fortio,我首先部署了FortioOPERATOR,然后再选择经典部署(KISS)。

apiVersion:v1kind:Servicemetadata:name:fortio-debugspec:ports:-port:8080name:http-debugselector:app:fortio-debug---apiVersion:apps/v1kind:Deploymentmetadata:name:fortio-debug-deploymentspec:replicas:1selector:matchLabels:app:fortio-debugtemplate:metadata:labels:app:fortio-debugspec:containers:-name:fortio-debugimage:fortio/fortio:latest_releaseimagePullPolicy:Alwaysports:-containerPort:8080

为了启动和配置测试,我只需对FortioPod进行端口转发:

kubectlport-forwardsvc/fortio-debug8080:8080

其余配置直接在FortioWeb界面上完成,地址为http://localhost:8080/fortio/。我选择执行延迟测试,每个测试有10个并发连接,每秒发出100个请求。当然,我确保测试始终在两个不同的节点之间进行。

以下是三种场景的测试结果:

无Istio

带IstioSidecar

带IstioAmbient

有趣的是,我们得到了截然不同的结果。在延迟方面,结果如下:

IstioAmbient:2.35毫秒延迟;

无Istio:2.8毫秒延迟;

IstioSidecar:39.3毫秒延迟。

我注意到,在IstioAmbient中存在连接错误,即使经过多次测试也无法解释。

令人惊叹的是,与没有Istio的集群相比,IstioAmbient能够降低延迟(我甚至多次检查结果以确保)。这表明“Ambient”模式是一种可行的解决方案,可以潜在地提高Kubernetes集群的性能。

TCP基准测试

对于此基准测试,我将使用Iperf测试服务之间的带宽。然后,我将部署一个IperfPod作为服务器,并使用一个“tcp-iperf-client”Pod向Iperf服务器发出请求。

IPerf清单.

客户端Iperf

apiVersion:v1kind:Podmetadata:name:tcp-iperf-clientspec:containers:-name:debugimage:digitalocean/doks-debug:latestcommand:["sleep","infinity"]

服务器Iperf

apiVersion:apps/v1kind:Deploymentmetadata:name:tcp-iperfnamespace:defaultspec:replicas:1selector:matchLabels:app:tcp-iperfversion:v1template:metadata:labels:app:tcp-iperfversion:v1spec:containers:-args:--s---port-"5201"image:mlabbe/iperfimagePullPolicy:IfNotPresentname:tcp-iperfports:-containerPort:5201name:tcp-appprotocol:TCP---apiVersion:v1kind:Servicemetadata:labels:app:tcp-iperfservice:tcp-iperfname:tcp-iperfnamespace:defaultspec:ports:-name:tcp-iperfport:5201protocol:TCPselector:app:tcp-iperftype:ClusterIP---apiVersion:/v1alpha3kind:VirtualServicemetadata:name:tcp-iperfspec:hosts:-"*"gateways:-tcp-iperftcp:-match:-port:5201route:-destination:host:tcp-iperfport:number:5201

我还将强制执行mTLS,以使其更接近实际使用场景。

WithIstioSidecariperf-ctcp-iperf--port5201------------------------------------------------------------Clientconnectingtotcp-iperf,TCPport5201TCPwindowsize:2.50MByte(default)------------------------------------------------------------[1](icwnd/mss/irtt=13/1398/34)[ID]IntervalTransferBandwidth[1]0.0000-10.1227/sec
#WithIstioAmbientiperf-ctcp-iperf--port5201------------------------------------------------------------Clientconnectingtotcp-iperf,TCPport5201TCPwindowsize:2.50MByte(default)------------------------------------------------------------[1](icwnd/mss/irtt=13/1398/50)[ID]IntervalTransferBandwidth[1]0.0000-10.0806/sec

与延迟不同,IstioAmbient在延迟方面表现最佳,而“无Istio”模式在带宽方面则优于其他两种模式。

无Istio:3.62Gbits/sec;

IstioAmbient:2.12Gbits/sec;

IstioSidecar:1.88Gbits/sec。

结论

Istio是一款功能强大且完整的产品,但也并非没有缺陷。很容易在Istio的配置中迷失方向,最终导致网格无法按预期工作(此外,日志并不总是很明确)。因此,在深入配置网格之前,了解Istio的概念非常重要。

尽管我花了很多时间学习Istio,但我仍然没有足够的信心用于生产环境,还有很多东西需要从这个解决方案中学习。我希望阅读本文对那些想要开始学习Istio的人有所帮助。

如果您想鼓励我写这类文章(并资助我失眠的夜晚),请随时在我的Kofi页面上进行少量捐赠,您也可以在下面的社交网络上给我一些支持:

Twitter

LinkedIn

在此之前,祝您度过美好的一天,并祝您在Istio之旅中好运!

最新文章