minikube start -p istio --kubernetes-version='v1.19.0' --vm-driver='virtualbox' --memory=4096 [istio] minikube v1.17.1 on Darwin 11.3 Kubernetes 1.20.2 is now available. If you would like to upgrade, specify: --kubernetes-version=v1.20.2 minikube 1.19.0 is available! Download it: https://github.com/kubernetes/minikube/releases/tag/v1.19.0 To disable this notice, run: 'minikube config set WantUpdateNotification false'
复制代码✨ 基于已有的 profile 并使用 virtualbox 驱动❗ 对于既有的 minikube 集群,我们无法改变它的内存大小如果需要的话,请先将该集群删除掉Starting control plane node istio in cluster istio Restarting existing virtualbox VM for "istio" ... Preparing Kubernetes v1.19.0 on Docker 19.03.12 ... Verifying Kubernetes components... Enabled addons: storage-provisioner, default-storageclass Done! kubectl is now configured to use "istio" cluster and "" namespace by default
复制代码Kubernetes 集群运行起来之后,我们就可以下载istioctl CLI 工具来安装 Istio 到集群中了在本例中,我们会从版本发布页面下载 Istio 1.9.4istioctl工具安装完成之后,我们就可以将 Istio 部署到集群之中了Istio 自带了不同的profiles,但是就开始学习 Istio 而言,demo profile 是最合适的istioctl install --set profile=demo -yDetected that your cluster does not support third party JWT authentication. Falling back to less secure first party JWT. See https://istio.io/docs/ops/best-practices/security/#configure-third-party-service-account-tokens for details.<small>✔ Istio core installed</small><small>✔ Istiod installed</small><small>✔ Egress gateways installed</small><small>✔ Ingress gateways installed</small><small>✔ Addons installed</small><small>✔ Installation complete</small>我们要一直等到istio-system命名空间中的所有 Pod 均处于 running 状态kubectl get pods -n istio-systemNAME READY STATUS RESTARTS AGEgrafana-b54bb57b9-fj6qk 1/1 Running 2 171distio-egressgateway-68587b7b8b-m5b58 1/1 Running 2 171distio-ingressgateway-55bdff67f-jrhpk 1/1 Running 2 171distio-tracing-9dd6c4f7c-9gcx9 1/1 Running 3 171distiod-76bf8475c-xphgd 1/1 Running 2 171dkiali-d45468dc4-4nbl4 1/1 Running 2 171dprometheus-74d44d84db-86hdr 2/2 Running 4 171d
复制代码为了发挥 Istio 的所有功能,网格中的 Pod 必须运行一个 Istio sidecar 代理我们有两种方式将 Istio sidecar 注入到 Pod 中:使用istioctl命令手动注入或者在将 Pod 部署到配置好的命名空间时自动注入为了简单起见,我们通过执行如下命令,为default命名空间配置默认的自动化 sidecar 注入:kubectl label namespace default istio-injection=enablednamespace/default labeled现在,Istio 已经安装到了 Kubernetes 集群中,并且为在default命名空间使用做好了准备在下面的章节中,我们将会看到如何“Istio 化”应用并部署一个这样的应用应用概览应用是由两个服务组成的,分别是 book service 和 rating serviceBook service 返回一本图书的信息及其评分Rating service 返回给定图书的评分我们有 rating service 的两个版本:v1 会为所有的图书返回一个固定的评分(也就是 1),而 v2 会返回一个随机的评分值部署因为已经启用了 sidecar 注入,我们不需要对 Kubernetes 部署文件做任何变更接下来,我们将这三个服务部署到“Istio 化”的命名空间中举例来说,_book service_的部署文件如下所示:---apiVersion: v1kind: Servicemetadata: labels: app.kubernetes.io/name: book-service app.kubernetes.io/version: v1.0.0 name: book-servicespec: ports: - name: http port: 8080 targetPort: 8080 selector: app.kubernetes.io/name: book-service app.kubernetes.io/version: v1.0.0 type: LoadBalancer---apiVersion: apps/v1kind: Deploymentmetadata: labels: app.kubernetes.io/name: book-service app.kubernetes.io/version: v1.0.0 name: book-servicespec: replicas: 1 selector: matchLabels: app.kubernetes.io/name: book-service app.kubernetes.io/version: v1.0.0 template: metadata: labels: app.kubernetes.io/name: book-service app.kubernetes.io/version: v1.0.0 spec: containers: - env: - name: KUBERNETES_NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace image: quay.io/lordofthejars/book-service:v1.0.0 imagePullPolicy: Always name: book-service ports: - containerPort: 8080 name: http protocol: TCP
复制代码我们可以看到,在文件中既没有 Istio 相关的内容,也没有 sidecar 容器的配置Istio 功能的注入默认会自动进行我们把应用部署到 Kubernetes 集群中:kubectl apply -f rating-service/src/main/kubernetes/service.yml -n defaultkubectl apply -f rating-service/src/main/kubernetes/deployment-v1.yml -n defaultkubectl apply -f rating-service/src/main/kubernetes/deployment-v2.yml -n defaultkubectl apply -f book-service/src/main/kubernetes/deployment.yml -n default
复制代码几秒钟之后,应用就会启动起来了为了进行校验,我们运行如下的命令并观察 Pod 所拥有的容器数量:kubectl get pods -n defaultNAME READY STATUS RESTARTS AGEbook-service-5cc59cdcfd-5qhb2 2/2 Running 0 79mrating-service-v1-64b67cd8d-5bfpf 2/2 Running 0 63mrating-service-v2-66b55746d-f4hpl 2/2 Running 0 63m
复制代码注意,每个 Pod 都包含了两个正在运行的容器,其中一个是服务本身,另外一个是 Istio 代理如果描述这个 Pod 的话,我们会发现:kubectl describe pod rating-service-v2-66b55746d-f4hplName: rating-service-v2-66b55746d-f4hplNamespace: default…Containers: rating-service: Container ID: docker://cda8d72194ee37e146df7bf0a6b23a184b5bfdb36fed00d2cc105daf6f0d6e85 Image: quay.io/lordofthejars/rating-service:v2.0.0… istio-proxy: Container ID: docker://7f4a9c1f425ea3a06ccba58c74b2c9c3c72e58f1d805f86aace3d914781e0372 Image: docker.io/istio/proxyv2:1.6.13
复制代码因为我们使用了 Minikube 并且 Kubernetes 服务是 LoadBalancer 类型,所以要访问应用需要 Minikube 的 IP 和服务端口为了找到这些值,可以执行如下命令:minikube IP -p istio192.168.99.116kubectl get services -n defaultNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGEbook-service LoadBalancer 10.106.237.42 <pending> 8080:31304/TCP 111mkubernetes ClusterIP 10.96.0.1 <none> 443/TCP 132mrating LoadBalancer 10.109.106.128 <pending> 8080:31216/TCP 95m
复制代码接下来,我们可以对服务执行 curl 命令:curl 192.168.99.116:31304/book/1{"bookId":1,"name":"Book 1","rating":1}curl 192.168.99.116:31304/book/1{"bookId":1,"name":"Book 1","rating":3}curl 192.168.99.116:31304/book/1{"bookId":1,"name":"Book 1","rating":1}curl 192.168.99.116:31304/book/1{"bookId":1,"name":"Book 1","rating":3}
复制代码从输出我们可以看到评分值的变化,也就是对于同一个图书的 id,评分值会在 1 和 3 之间变化默认情况下,Istio 会使用 round-robin 方式平衡对服务的调用在本例中,请求会在rating:v1(返回固定的评分值 1)和rating:v2(在启动的时候进行随机的评分计算,在本例中,会对 ID 为 1 的图书返回 3)之间进行平衡应用现在已经部署好了,并且实现了“Istio 化”,但是到目前为止还没有启用任何的微服务特性我们首先来创建一些 Istio 资源,以便于在 Istio 代理容器上启用和配置微服务特性Istio 微服务特性服务发现Kubernetes Service_实现了服务发现的理念它提供了一种方式将一组_Kubernetes Pod(作为一个整体)赋予一个稳定的虚拟 IP 和 DNS 名Pod 在访问其他 Pod 的时候,可以使用_Kubernetes Service_名作为主机名这只能允许我们实现基本的服务发现策略,但是我们可能会需要更高级的发现/部署策略,比如金丝雀发布、灰度发布或者镜像流量(shadowing traffic),此时 Kubernetes Service 就爱莫能助了Istio 能够让我们很容易地控制服务之间的网络流量,这是通过两个概念来实现的,即DestinationRule和VirtualServiceDestinationRule定义了在路由发生之后如何为网络流量提供服务的策略在 destination rule 中我们可以配置的内容如下所示:网络流量策略负载均衡策略连接池设置mTLS回弹性使用标签(label)指定服务的子集(subset),这些子集会在VirtualService中用到我们创建一个名为destination-rule-v1-v2.yml的文件来注册两个子集,其中一个用于_rating service v1_,另外一个用于_rating service v2_:apiVersion: networking.istio.io/v1alpha3kind: DestinationRulemetadata: name: ratingspec: host: rating subsets: - labels: app.kubernetes.io/version: v1.0.0 name: version-v1 - labels: app.kubernetes.io/version: v2.0.0 name: version-v2
复制代码在这里,我们将host字段设置为 rating,因为这是在_Kubernetes Service_中定义的 DNS 名随后,在subsets部分,我们以labels集的形式定义了多个子集,并将它们分组到一个“虚拟的”name例如,在前面的例子中,我们定义了两个组,其中一个组用于 rating service 的 version 1,另外一个组用于 version 2kubectl apply -f src/main/kubernetes/destination-rule-v1-v2.yml -n defaultdestinationrule.networking.istio.io/rating created
复制代码VirtualService能够让我们配置请求该如何路由至 Istio 服务网格的服务中借助 virtual service,实现像 A/B 测试、蓝/绿部署、金丝雀发布或灰度发布这样的策略就会变得非常简单我们创建一个名为virtual-service-v1.yml的文件以发送所有的流量到 v1:apiVersion: networking.istio.io/v1alpha3kind: VirtualServicemetadata: name: ratingspec: hosts: - rating http: - route: - destination: host: rating subset: version-v1 weight: 100
复制代码在前面的文件中,我们配置所有到达 rating 主机的请求都会被发送到 version-v1 子集所属的 Pod 中我们需要记住,子集是在DestinationRule文件中创建的kubectl apply -f src/main/kubernetes/virtual-service-v1.yml -n defaultvirtualservice.networking.istio.io/rating created
复制代码现在,我们可以再次向服务执行一些curl命令,但是在输出方面最大的差异在于所有的请求都发送到了_rating v1_中curl 192.168.99.116:31304/book/1{"bookId":1,"name":"Book 1","rating":1}curl 192.168.99.116:31304/book/1{"bookId":1,"name":"Book 1","rating":1}curl 192.168.99.116:31304/book/1{"bookId":1,"name":"Book 1","rating":1}
复制代码显然,我们可以创建另外一个 virtual service 文件,使其指向 rating v2:apiVersion: networking.istio.io/v1alpha3kind: VirtualServicemetadata: name: ratingspec: hosts: - rating http: - route: - destination: host: rating subset: version-v2 weight: 100
复制代码kubectl apply -f src/main/kubernetes/virtual-service-v2.yml -n defaultvirtualservice.networking.istio.io/rating configured
复制代码这样,所有的流量会发送至_rating_ v2:curl 192.168.99.116:31304/book/1{"bookId":1,"name":"Book 1","rating":3}curl 192.168.99.116:31304/book/1{"bookId":1,"name":"Book 1","rating":3}
复制代码现在,rating字段没有被设置为 1,这是因为所有的请求都被 version 2 处理了通过修改 virtual service 的weight字段,我们就能实现金丝雀发布apiVersion: networking.istio.io/v1alpha3kind: VirtualServicemetadata: name: ratingspec: hosts: - rating http: - route: - destination: host: rating subset: version-v1 weight: 75 - destination: host: rating subset: version-v2 weight: 25
复制代码kubectl apply -f src/main/kubernetes/virtual-service-v1-v2-75-25.yml -n defaultvirtualservice.networking.istio.io/rating configured
复制代码现在,我们对应用执行一些curl命令:curl 192.168.99.116:31304/book/1{"bookId":1,"name":"Book 1","rating":1}curl 192.168.99.116:31304/book/1{"bookId":1,"name":"Book 1","rating":1}curl 192.168.99.116:31304/book/1{"bookId":1,"name":"Book 1","rating":1}curl 192.168.99.116:31304/book/1{"bookId":1,"name":"Book 1","rating":3}
复制代码rating v1 的访问次数要比 rating v2 更多,这遵循了在weight字段中设置的占比现在,我们移除 virtual service 资源,使其回到默认的行为(也就是 round-robin 策略):kubectl delete -f src/main/kubernetes/virtual-service-v1-v2-75-25.yml -n defaultvirtualservice.networking.istio.io "rating" deleted
复制代码回弹性在微服务架构中,我们在开发时要始终考虑到可能出现的故障,在与其他的服务进行通信时更是如此在单体应用中,我们的应用会作为一个整体,要么全部处于可用状态,要么全部处于宕机状态,但是在微服务架构中,情况却并非如此,因为有些服务是可用的,而另外一些则可能已经宕机了回弹性(或称为应用回弹性)是指一个应用/服务能够对面临的问题作出反应的能力,在出现问题的时候,依然能够提供尽可能最好的结果接下来我们看一下 Istio 如何帮助我们实现回弹性策略,以及如何配置它们故障rating service 实现了一个特殊的端点,当它被访问后会导致服务开始返回 503 HTTP 错误码执行如下的命令(将 Pod 名替换为你自己的),使服务 rating v2 在访问的时候开始出现故障::kubectl get pods -n defaultNAME READY STATUS RESTARTS AGEbook-service-5cc59cdcfd-5qhb2 2/2 Running 4 47hrating-service-v1-64b67cd8d-5bfpf 2/2 Running 4 47hrating-service-v2-66b55746d-f4hpl 2/2 Running 4 47hkubectl exec -ti rating-service-v2-66b55746d-f4hpl -c rating-service -n default curl localhost:8080/rate/misbehaveRatings endpoint returns 503 error.
复制代码重试目前,Istio 配置为没有 virtual service,这意味着它会在两个版本之间平衡请求我们发送一些请求并校验 rating v2 会失败:curl 192.168.99.116:31304/book/1{"bookId":1,"name":"Book 1","rating":1}curl 192.168.99.116:31304/book/1curl 192.168.99.116:31304/book/1{"bookId":1,"name":"Book 1","rating":1}
复制代码其中有个请求没有产生响应,这是因为_rating v2_没有返回合法的响应,而是产生了错误Istio 支持重试,这是通过在VirtualService资源中进行配置实现的创建名为virutal-service-retry.yml的文件,其内容如下所示:apiVersion: networking.istio.io/v1alpha3kind: VirtualServicemetadata: name: ratingspec: hosts: - rating http: - route: - destination: host: rating retries: attempts: 2 perTryTimeout: 5s retryOn: 5xx
复制代码按照配置,如果 rating service(不管哪个版本)返回5XX HTTP 错误码的话,会自动进行两次重试kubectl apply -f src/main/kubernetes/virtua-service-retry.yml -n defaultvirtualservice.networking.istio.io/rating created
复制代码接下来,我们发送一些请求并检查输出:curl 192.168.99.116:31304/book/1{"bookId":1,"name":"Book 1","rating":1}curl 192.168.99.116:31304/book/1{"bookId":1,"name":"Book 1","rating":1}curl 192.168.99.116:31304/book/1{"bookId":1,"name":"Book 1","rating":1}
复制代码现在,我们可以看到,所有的请求都是由 rating v1 响应的原因很简单,当对 rating service 的请求发送至 v1,会得到一个合法的响应但是,如果请求被发送到 v2 的时候,会出现错误并且会自动执行重试因为调用是在两个服务之间进行负载均衡的,所以重试请求会被发送到 v1,从而产生一个合法的响应基于这样的原因,上述的所有请求都会返回来自 v1 的响应断路器对于处理网络故障或偶尔出现的错误来说,自动重试是一个很好的方式,但是如果多个并发用户向一个具有自动重试功能的故障系统发送请求时,会发生什么呢?我们通过使用Siege(一个 HTTP 负载测试工具)模拟这个场景,但首先,我们使用 kubectl 命令来探查一下 rating v2 的日志:kubectl get pods -n defaultNAME READY STATUS RESTARTS AGEbook-service-5cc59cdcfd-5qhb2 2/2 Running 4 47hrating-service-v1-64b67cd8d-5bfpf 2/2 Running 4 47hrating-service-v2-66b55746d-f4hpl 2/2 Running 4 47hkubectl logs rating-service-v2-66b55746d-f4hpl -c rating-service -n default…Request 31Request 32Request 33Request 34
复制代码这些日志行展示了该服务所处理的请求数目前,该服务处理了 34 个请求为了模拟四个并发用户,并且每个用户发送十个请求到应用上,我们可以执行如下的 siege 命令:siege -r 10 -c 4 -v -d 1 192.168.99.116:31304/book/1HTTP/1.1 200 0.04 secs: 39 bytes ==> GET /book/1HTTP/1.1 200 0.03 secs: 39 bytes ==> GET /book/1Transactions: 40 hitsAvailability: 100.00 %Elapsed time: 0.51 secsData transferred: 0.00 MBResponse time: 0.05 secsTransaction rate: 78.43 trans/secThroughput: 0.00 MB/secConcurrency: 3.80Successful transactions: 40Failed transactions: 0Longest transaction: 0.13Shortest transaction: 0.01
复制代码当然,这里没有错误发送给调用者,这是因为有自动重试机制,但是我们再次探测一下 rating v2 的日志:kubectl logs rating-service-v2-66b55746d-f4hpl -c rating-service -n default…Request 56Request 57Request 58Request 59
复制代码尽管 rating v2 不能产生一个合法的响应,但是服务依然被访问了 25 次,这会对应用产生很大的影响,因为:如果服务已经处于过载状态的话,发送更多的请求对它的恢复来讲并不是一个好主意也许,最好的方式是将实例放到一个隔离区中如果服务此时恰好因为某个缺陷出现了故障,那么重试并不会改善这种情况对于每次重试,都会建立一个 socket、分配一些文件描述符(file descriptor),还要通过网络发送一些数据包,但最终得到的却是故障这个过程会影响在同一个节点中其他服务(CPU、内存、文件描述符等)或者网络(增加无用的流量、延迟等)为了解决这个问题,我们需要有一种方式能够在出现重复执行失败的时候,让调用能够自动地快速失败断路器(circuit breaker)设计模式和舱壁(bulkhead)模式是这个问题的解决方案前者提供了在遇到并发错误的时候,快速失败的策略,而后者则能限制并发执行的数量现在,创建一个名为destination-rule-circuit-breaker.yml的文件,内容如下所示:apiVersion: networking.istio.io/v1alpha3kind: DestinationRulemetadata: name: ratingspec: host: rating subsets: - labels: version: v1 name: version-v1 - labels: version: v2 name: version-v2 trafficPolicy: connectionPool: http: http1MaxPendingRequests: 3 maxRequestsPerConnection: 3 tcp: maxConnections: 3 outlierDetection: baseEjectionTime: 3m consecutive5xxErrors: 1 interval: 1s maxEjectionPercent: 100
复制代码我们要注意的第一件事情就是DestinationRule配置了断路器除了配置断路器之外,子集也需要指定对并发连接的限制是在connectionPool字段中实现的要配置断路器,我们需要使用outlierDetection就本例而言,如果在一秒钟的时间窗口中发生了一次错误,断路器将会打开,使服务暂时跳闸(trip)三分钟在这个时间之后,断路器会处于半开状态,这意味着会执行真实的逻辑如果再次失败的话,断路器会保持打开的状态,否则的话,它将会关闭kubectl apply -f src/main/kubernetes/destination-rule-circuit-breaker.ymldestinationrule.networking.istio.io/rating configured
复制代码我们已经在 Istio 中配置完了断路器模式,接下来,我们再次执行siege命令并探查_rating v2_ v2 的日志siege -r 10 -c 4 -v -d 1 192.168.99.116:31304/book/1HTTP/1.1 200 0.04 secs: 39 bytes ==> GET /book/1HTTP/1.1 200 0.03 secs: 39 bytes ==> GET /book/1Transactions: 40 hitsAvailability: 100.00 %
复制代码再次探查日志注意,在前面的运行中,我们已经到了 Request 59kubectl logs rating-service-v2-66b55746d-f4hpl -c rating-service -n default…Request 56Request 57Request 58Request 59Request 60
复制代码_Rating v2 _只接收到了一个请求,因为在第一次处理请求的时候返回了错误,断路器就会打开,因此不会有更多的请求发送到 rating v2 上现在,我们已经看到了如何使用 Istio 实现回弹性在这里,我们并没有在服务中实现相关的逻辑,将其与业务逻辑混在一起,而是让 sidecar 容器实现了这些逻辑最后,执行如下的命令,让 rating v2 服务回到之前的状态kubectl exec -ti rating-service-v2-66b55746d-f4hpl -c rating-service curl localhost:8080/rate/behaveBack to normal
复制代码认证在实现微服务架构的时候,我们可能会发现的一个问题就是如何保护内部服务之间的通信我们是不是要使用 mTLS?是不是要对请求进行认证?是否要对请求进行鉴权?所有这些问题的答案都是肯定的接下来,我们将会一步一步地看一下 Istio 是如何帮助我们实现这些功能的认证Istio 会自动将代理和工作负载之间的所有网络流量升级为 mTLS,这个过程不需要修改任何的服务代码与此同时,作为开发人员,我们会使用 HTTP 协议实现服务当服务被“Istio 化”的时候,服务之间的通信会采用 HTTPSIstio 会负责管理证书,担任证书颁发机构并撤销/更新证书要校验 mTLS 是否已启用,我们可以使用 istioctl 工具执行如下的命令:
istioctl experimental authz check book-service-5cc59cdcfd-5qhb2 -aLISTENER[FilterChain] HTTP ROUTE ALPN mTLS (MODE) AuthZ (RULES)...virtualInbound[5] inbound|8080|http|book-service.default.svc.cluster.local istio,istio-http/1.0,istio-http/1.1,istio-h2 noneSDS: default yes (PERMISSIVE) no (none)…
复制代码book-service 托管在了 8080 端口,并且以 permissive 策略配置了 mTLS授权接下来,我们看一下如何使用 JSON Web Token(JWT)格式启用 Istio 的终端用户认证我们要做的第一件事情是应用一个RequestAuthentication资源这个策略能够确保如果Authorization头信息包含 JWT token 的话,它必须是合法的、没有过期的、由正确的用户颁发的并且没有被篡改apiVersion: "security.istio.io/v1beta1"kind: "RequestAuthentication"metadata: name: "bookjwt" namespace: defaultspec: selector: matchLabels: app.kubernetes.io/name: book-service jwtRules: - issuer: "testing@secure.istio.io" jwksUri: "https://gist.githubusercontent.com/lordofthejars/7dad589384612d7a6e18398ac0f10065/raw/ea0f8e7b729fb1df25d4dc60bf17dee409aad204/jwks.json"
复制代码其中的关键字段包括:issuer:token 的合法颁发者如果所提供的 token 没有在iss JWT 字段指定该颁发者,那么这个 token 就是非法的jwksUri:jwks文件的 URL,它指定了公钥注册的地址,用来校验 token 的签名kubectl apply -f src/main/kubernetes/request-authentication-jwt.yml -n defaultrequestauthentication.security.istio.io/bookjwt created
复制代码我们现在使用一个非法的 token 来运行curl命令:curl 192.168.99.116:31304/book/1 -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IkRIRmJwb0lVcXJZOHQyenBBMnFYZkNtcjVWTzVaRXI0UnpIVV8tZW52dlEiLCJ0eXAiOiJKV1QifQ.eyJleHAiOjQ2ODU5ODk3MDAsImZvbyI6ImJhciIsImlhdCI6MTUzMjM4OTcwMCwiaXNzIjoidGVzdGluZ0BzZWN1cmUuaXN0aW8uaW8iLCJzdWIiOiJ0ZXN0aW5nQHNlY3VyZS5pc3Rpby5pbyJ9.CfNnxWP2tcnR9q0vxyxweaF3ovQYHYZl82hAUsn21bwQd9zP7c-LS9qd_vpdLG4Tn1A15NxfCjp5f7QNBUo-KC9PJqYpgGbaXhaGx7bEdFWjcwv3nZzvc7M__ZpaCERdwU7igUmJqYGBYQ51vr2njU9ZimyKkfDe3axcyiBZde7G6dabliUosJvvKOPcKIWPccCgefSj_GNfwIip3-SsFdlR7BtbVUcqR-yv-XOxJ3Uc1MI0tz3uMiiZcyPV7sNCU4KRnemRIMHVOfuvHsU60_GhGbiSFzgPTAa9WTltbnarTbxudb_YEOx12JiwYToeX0DCPb43W1tzIBxgm8NxUU"Jwt verification fails
复制代码因为 token 是非法的,所以请求会被拒绝,并返回 HTTP/1.1 401 Unauthorized 状态码使用合法的 token 重复前面的请求:curl 192.168.99.116:31304/book/1 -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IkRIRmJwb0lVcXJZOHQyenBBMnFYZkNtcjVWTzVaRXI0UnpIVV8tZW52dlEiLCJ0eXAiOiJKV1QifQ.eyJleHAiOjQ2ODU5ODk3MDAsImZvbyI6ImJhciIsImlhdCI6MTUzMjM4OTcwMCwiaXNzIjoidGVzdGluZ0BzZWN1cmUuaXN0aW8uaW8iLCJzdWIiOiJ0ZXN0aW5nQHNlY3VyZS5pc3Rpby5pbyJ9.CfNnxWP2tcnR9q0vxyxweaF3ovQYHYZl82hAUsn21bwQd9zP7c-LS9qd_vpdLG4Tn1A15NxfCjp5f7QNBUo-KC9PJqYpgGbaXhaGx7bEdFWjcwv3nZzvc7M__ZpaCERdwU7igUmJqYGBYQ51vr2njU9ZimyKkfDe3axcyiBZde7G6dabliUosJvvKOPcKIWPccCgefSj_GNfwIip3-SsFdlR7BtbVUcqR-yv-XOxJ3Uc1MI0tz3uMiiZcyPV7sNCU4KRnemRIMHVOfuvHsU60_GhGbiSFzgPTAa9WTltbnarTbxudb_YEOx12JiwYToeX0DCPb43W1tzIBxgm8NxUg"{"bookId":1,"name":"Book 1","rating":3}
复制代码现在,我们可以看到一个合法的响应了,因为此时 token 是正确的到目前为止,我们只是认证了请求(只需要一个合法的 token),其实 Istio 还支持基于角色访问控制(role-based access control,RBAC)模型的授权我们接下来创建一个AuthorizationPolicy策略,只允许具有合法 JSON Web Token 并且 claim role 设置为 customer 的请求创建名为authorization-policy-jwt.yml的文件:apiVersion: security.istio.io/v1beta1kind: AuthorizationPolicymetadata: name: require-jwt namespace: defaultspec: selector: matchLabels: app.kubernetes.io/name: book-service action: ALLOW rules: - from: - source: requestPrincipals: ["testing@secure.istio.io/testing@secure.istio.io"] when: - key: request.auth.claims[role] values: ["customer"]
复制代码kubectl apply -f src/main/kubernetes/authorization-policy-jwt.ymlauthorizationpolicy.security.istio.io/require-jwt created
复制代码然后执行和上面一样的 curl 命令:curl 192.168.99.116:31304/book/1 -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IkRIRmJwb0lVcXJZOHQyenBBMnFYZkNtcjVWTzVaRXI0UnpIVV8tZW52dlEiLCJ0eXAiOiJKV1QifQ.eyJleHAiOjQ2ODU5ODk3MDAsImZvbyI6ImJhciIsImlhdCI6MTUzMjM4OTcwMCwiaXNzIjoidGVzdGluZ0BzZWN1cmUuaXN0aW8uaW8iLCJzdWIiOiJ0ZXN0aW5nQHNlY3VyZS5pc3Rpby5pbyJ9.CfNnxWP2tcnR9q0vxyxweaF3ovQYHYZl82hAUsn21bwQd9zP7c-LS9qd_vpdLG4Tn1A15NxfCjp5f7QNBUo-KC9PJqYpgGbaXhaGx7bEdFWjcwv3nZzvc7M__ZpaCERdwU7igUmJqYGBYQ51vr2njU9ZimyKkfDe3axcyiBZde7G6dabliUosJvvKOPcKIWPccCgefSj_GNfwIip3-SsFdlR7BtbVUcqR-yv-XOxJ3Uc1MI0tz3uMiiZcyPV7sNCU4KRnemRIMHVOfuvHsU60_GhGbiSFzgPTAa9WTltbnarTbxudb_YEOx12JiwYToeX0DCPb43W1tzIBxgm8NxUg"RBAC: access denied
复制代码这一次的响应显然不一样了尽管 token 是合法的,但是访问被拒绝了,这是因为 token 中并没有值为 customer 的 claim role然后,我们使用如下的 token:curl 192.168.99.116:31304/book/1 -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IkRIRmJwb0lVcXJZOHQyenBBMnFYZkNtcjVWTzVaRXI0UnpIVV8tZW52dlEiLCJ0eXAiOiJKV1QifQ.eyJleHAiOjI1NDkwNTY4ODgsImlhdCI6MTU0OTA1Njg4OSwiaXNzIjoidGVzdGluZ0BzZWN1cmUuaXN0aW8uaW8iLCJyb2xlIjoiY3VzdG9tZXIiLCJzdWIiOiJ0ZXN0aW5nQHNlY3VyZS5pc3Rpby5pbyJ9.VM9VOHD2NwDjQ6k7tszB3helfAn5wcldxe950BveiFVg43pp7x5MWTjMtWQRmQc7iYul19PXsmGnSSOiQQobxdn2UnhHJeKeccCdX5YVgX68tR0R9xv_wxeYQWquH3roxHh2Xr2SU3gdt6s7gxKHrW7Zc4Z9bT-fnz3ijRUiyrs-HQN7DBc356eiZy2wS7O539lx3mr-pjM9PQtcDCDOGsnmwq1YdKw9o2VgbesfiHDDjJQlNv40wnsfpq2q4BgSmdsofAGwSNKWtqUE6kU7K2hvV2FvgwjzcB19bbRYMWxRG0gHyqgFy-uM5tsC6Cib-gPAIWxCdXDmLEiqIdjM3w"{"bookId":1,"name":"Book 1","rating":3}现在,我们看到了一个合法的响应,因为此时 token 是正确的并且包含了一个合法的 role 值可观察性Istio 自带了四个组件以适应可观察性的需求:Prometheus:用于监控Grafana:用于可视化Jaeger + Zipkin:用于跟踪Kiali:用于为应用提供一个全局的概览我们可以在istio-system命名空间中看到所有的 Pod:kubectl get pods -n istio-systemNAME READY STATUS RESTARTS AGEgrafana-b54bb57b9-k5qbm 1/1 Running 0 178mistio-egressgateway-68587b7b8b-vdr67 1/1 Running 0 178mistio-ingressgateway-55bdff67f-hlnqw 1/1 Running 0 178mistio-tracing-9dd6c4f7c-44xhk 1/1 Running 0 178mistiod-76bf8475c-xphgd 1/1 Running 7 177dkiali-d45468dc4-fl8j4 1/1 Running 0 178mprometheus-74d44d84db-zmkd7 2/2 Running 0 178m
复制代码监控Istio 集成了Prometheus,用于发送与网络流量和服务相关的各种信息除此之外,它还提供了一个Grafana实例来可视化所有收集到的数据要访问 Grafana,我们可以使用 port-forward 命令来将 Pod 暴露出来:kubectl port-forward -n istio-system grafana-b54bb57b9-k5qbm 3000:3000Forwarding from 127.0.0.1:3000 -> 3000Forwarding from [::1]:3000 -> 3000
复制代码打开浏览器并导航至locahost:3000以访问 Grafana 的仪表盘Kiali是另外一个在 Istio 中运行的工具,它能够管理 Istio 并观察服务网格参数,比如服务是如何连接的、它们是如何执行的以及 Istio 资源是如何注册的要访问 Kiali,我们可以使用 port-forward 命令来将 Pod 暴露出来:kubectl port-forward -n istio-system kiali-d45468dc4-fl8j4 20001:20001Forwarding from 127.0.0.1:20001 -> 20001Forwarding from [::1]:20001 -> 20001
复制代码打开浏览器,访问 Istio 仪表盘,然后导航至 locahost:20001跟踪跟踪用来可视化一个程序的流程和数据进展Istio 会拦截所有的请求/响应,并将它们发送至Jaeger在这里,我们可以不用 port-forward 命令,而是使用istioctl来暴露端口并自动打开页面istioctl dashboard jaeger结论开发和实现微服务架构要比开发单体应用更具挑战性我们相信,微服务特性能够促使你在应用基础设施方面正确地开发服务Istio 在一个 sidecar 容器中实现了一些微服务特性,使得它们能够跨所有的服务进行重用,独立于应用所使用的编程语言除此之外,Istio 方式能够让我们在不重新部署服务的前提下改变服务的行为如果你计划开发微服务并将它们部署到 Kubernetes 中,那么 Istio 是一个切实可行的方案,因为它能够与 Kubernetes 无缝集成本文中所用到的源码可以在 GitHub 的仓库中找到,本系列第一篇文章的源码也可以在 GitHub 的仓库中找到作者简介:Alex Soto 是红帽公司的开发者体验总监他对 Java 领域、软件自动化充满热情,他相信开源软件模式Soto 是Manning的《Testing Java Microservices》和O’Reilly的《Quarkus Cookbook》两本书的共同作者,他还是多个开源项目的贡献者自 2017 年以来,他一直是 Java Champion,是国际演讲者和 Salle URL 大学的教师你可以在 Twitter 上关注他(Alex Soto ⚛️),随时了解 Kubernetes 和 Java 领域的动态原文链接:Implementing Microservicilites with Istio(图片来源网络,侵删)
0 评论