
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
一如既往,务必小心直接在网上应用的清单,务必在应用之前仔细阅读它们。
安装IstioctlIstioctl是管理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”服务丢失了,但这很正常)。
此页面可能是您最常用来调试应用程序的页面。在本文的其余部分,我将广泛引用它。
JaegerZipkinZipkin和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页面上进行少量捐赠,您也可以在下面的社交网络上给我一些支持:
在此之前,祝您度过美好的一天,并祝您在Istio之旅中好运!