如果单纯使用kubernetes的pod部署Spring微服务,K8s的负载平衡以及代理设置和你微服务应用之间不是非常的智能衔接,。无论如何,部署新的应用程序版本pod需要更加软化的方法。以下是典型的需求:
所有这些问题都可以通过名为 istio 的神奇工具来解决。
Istio安装
首先是在Istio安装之前,需要以至少4GB的内存启动 Minikube ,否则将无法启动 pilot ,阅读 stackoverflow讨论 ;第二个重要的是始终使用Istio Custom Resources Definitions开始安装:
$ kubectl apply -f <ISTIO_INSTALL_HOME> /install/kubernetes/helm/istio/templates/crds.yaml
这是安装后所需的输出:
kubernetes tomask79$ kubectl get pods -n istio-system NAME READY STATUS RESTARTS AGE grafana-59b8896965-6jcb8 1/1 Running 4 7d istio-citadel-856f994c58-jwdp2 1/1 Running 4 7d istio-cleanup-secrets-glmrz 0/1 Completed 0 7d istio-egressgateway-5649fcf57-5b885 1/1 Running 4 7d istio-galley-7665f65c9c-89wsz 1/1 Running 13 7d istio-grafana-post-install-z9v7z 0/1 Completed 0 7d istio-ingressgateway-6755b9bbf6-qrvq8 1/1 Running 4 7d istio-pilot-698959c67b-xpqnj 2/2 Running 11 7d istio-policy-6fcb6d655f-8mkf4 2/2 Running 18 7d istio-security-post-install-jn4sr 0/1 Completed 0 7d istio-sidecar-injector-768c79f7bf-p8xqr 1/1 Running 4 7d istio-telemetry-664d896cf5-zc8sr 2/2 Running 17 7d istio-tracing-6b994895fd-wpc2c 1/1 Running 7 7d prometheus-76b7745b64-hqnrq 1/1 Running 4 7d servicegraph-5c4485945b-jql54 1/1 Running 12 7d
演示示例
在向您展示Istio流量管理魔术之前,我们将介绍第一个非常简单的Spring Boot MVC应用程序,我们将在两个版本中部署到kubernetes。
版本1:
@RestController <b>public</b> <b>class</b> ControllerV1 { @GetMapping(path = <font>"/service"</font><font>) <b>public</b> String getResult() { <b>return</b> </font><font>"Hello I'm V1!"</font><font>; } } </font>
下面是这个版本的k8s的部署,标记mvc-service开始部分:
(mvc-1/istio/kubernetes-deploy/v1-deploy.yaml)
apiVersion: extensions/v1beta1 kind: Deployment metadata: name: mvc-service spec: replicas: 1 template: metadata: labels: app: mvc-service version: v1 spec: containers: - name: mvc-service image: service-v1:0.0.1-SNAPSHOT imagePullPolicy: IfNotPresent ports: - containerPort: 8080
版本2:
@RestController <b>public</b> <b>class</b> ControllerV2 { @GetMapping(path = <font>"/service"</font><font>) <b>public</b> String getResult() { <b>return</b> </font><font>"Hello i'm V2!"</font><font>; } } </font>
下面是这个版本的k8s的部署,标记mvc-service开始部分:
(mvc-2/istio/kubernetes-deploy/v2-deploy.yaml)
apiVersion: extensions/v1beta1 kind: Deployment metadata: name: mvc-service-v2 spec: replicas: 1 template: metadata: labels: app: mvc-service version: v2 spec: containers: - name: mvc-service image: service-v2:0.0.1-SNAPSHOT imagePullPolicy: IfNotPresent ports: - containerPort: 8080
我们在标有“mvc-service”字符串的pod中有两个版本的应用程序。因此kubernetes服务还应该使用标签'mvc-service'来定位pod后端:(mvc-1/istio/kubernetes-deploy/v1-deploy.yaml)
apiVersion: v1 kind: Service metadata: name: mvc-service labels: app: mvc-service spec: type: NodePort ports: - port: 8080 name: http selector: app: mvc-service
使用Istio支持部署到Kubernetes
好的,我们已经准备好了kubernetes清单,但还没有部署任何东西!为了能够使用 Istio流量管理, 您需要向您的pod 注入 sidecar代理 。如果没有istio边车,你就不会形成 服务网格 。你有两个选择:
我选择了第一个选项并将边车sidecar设置注入到这样的清单中:
istioctl kube-inject -f v1-deploy.yaml >> v1-deploy-istio.yaml
对于第二个版本(文件夹mvc-2 / istio / kubernetes-deploy)同样这么做:
istioctl kube-inject -f v2-deploy.yaml >> v2-deploy-istio.yaml
现在部署生成的kubernetes清单注入istio-sidecar:
kubectl apply -f v1-deploy-istio.yaml
然后部署第二个版本:
kubectl apply -f v2-deploy-istio.yaml
这是部署后所需的输出:
kubectl get pods NAME READY STATUS RESTARTS AGE mvc-service-76ffb4bc9f-sdrtn 2/2 Running 10 10d mvc-service-v2-59ff7d6886-v87jt 2/2 Running 10 10d
每个POD都有两个容器,因为它运行app容器和istio代理边车容器。要查看为pod启动的边车代理,只需键入:
kubernetes - deploy tomask79 $ kubectl describe pod mvc - service - 76 ffb4bc9f - sdrtn
检查事件:
Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal SandboxChanged 8m41s kubelet, minikube Pod sandbox changed, it will be killed and re-created. Normal Pulled 8m37s kubelet, minikube Container image <font>"docker.io/istio/proxy_init:1.0.5"</font><font> already present on machine Normal Created 8m35s kubelet, minikube Created container Normal Started 8m34s kubelet, minikube Started container Normal Pulled 8m34s kubelet, minikube Container image </font><font>"service-v1:0.0.1-SNAPSHOT"</font><font> already present on machine Normal Created 8m33s kubelet, minikube Created container Normal Started 8m33s kubelet, minikube Started container Normal Pulled 8m33s kubelet, minikube Container image </font><font>"docker.io/istio/proxyv2:1.0.5"</font><font> already present on machine Normal Created 8m33s kubelet, minikube Created container Normal Started 8m33s kubelet, minikube Started container </font>
Istio形成服务网格
好的,部署了两个版本的Spring Boot MVC应用程序。现在我打赌你对像 VirtualService , DestinationRule 这样的Istio对象感到困惑......什么时候使用它们,你还在Kubernetes服务吗?在 stackoverflow 有一个非常好的讨论,这对我来说非常有用。简而言之:
在我们的Spring MVC演示中,我们获得了名为mvc-service的kubernetes 服务。这将是DestinationRule 对象中的主机参数,因为这是提供目标服务的后端。现在我们的mvc-service提供了两个版本的Spring MVC应用程序v1和v2 ,它们将构成 服务网格 ,因此DestinationRule看起来像:
apiVersion: networking.istio.io/v1alpha3 kind: DestinationRule metadata: name: mvc-service spec: host: mvc-service subsets: - name: v1 labels: version: v1 - name: v2 labels: version: v2
在我们的演示中,通过kubectl部署它:
spring-kubernetes-istio tomask79$ kubectl apply -f istio-destionation-rule.yaml
现在VirtualService进入游戏:
VirtualService定义了一组要在主机被寻址时应用的流量路由规则。每个路由规则定义特定协议的流量的匹配标准。如果流量匹配,则将其发送到注册表中定义的命名目标服务(或其子集 /版本)。
换句话说,您首先部署K8s部署和服务。然后,通过Istio DestionRule定义微服务的网络,然后通过VirtualService设置HTTP路由规则。
用Istio进行金丝雀测试
因此,假设我们的演示SpringMVC应用程序的第二个版本(在服务网格中,子集v2)不够稳定,无法处理满负载,因此我们只将20%的流量路由到它。
apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: service-gateway spec: hosts: - <font>"*"</font><font> gateways: - service-gateway http: - match: - uri: exact: /service route: - destination: host: mvc-service subset: v1 weight: 80 - destination: host: mvc-service subset: v2 weight: 20 </font>
这真的很容易。作为最后一步,我们需要公开服务网关,即 Istio-Ingress网关 ,它从服务网格外部获取流量并将其转发到该 网关 。
apiVersion: networking.istio.io/v1alpha3 kind: Gateway metadata: name: service-gateway spec: selector: istio: ingressgateway # use istio <b>default</b> controller servers: - port: number: 80 name: http protocol: HTTP hosts: - <font>"*"</font><font> </font>
在这个repo中提到的网关和虚拟服务都在istio-gateway.yaml文件中,所以让我们部署它:
spring-kubernetes-istio tomask79$ kubectl apply -f istio-gateway.yaml
好的,这是测试之前所需的输出,服务网格在前:
istioctl get destinationrules -o yaml apiVersion: networking.istio.io/v1alpha3 kind: DestinationRule metadata: annotations: kubectl.kubernetes.io/last-applied-configuration: | {<font>"apiVersion"</font><font>:</font><font>"networking.istio.io/v1alpha3"</font><font>,</font><font>"kind"</font><font>:</font><font>"DestinationRule"</font><font>,</font><font>"metadata"</font><font>:{</font><font>"annotations"</font><font>:{},</font><font>"name"</font><font>:</font><font>"mvc-service"</font><font>,</font><font>"namespace"</font><font>:</font><font>"default"</font><font>},</font><font>"spec"</font><font>:{</font><font>"host"</font><font>:</font><font>"mvc-service"</font><font>,</font><font>"subsets"</font><font>:[{</font><font>"labels"</font><font>:{</font><font>"version"</font><font>:</font><font>"v1"</font><font>},</font><font>"name"</font><font>:</font><font>"v1"</font><font>},{</font><font>"labels"</font><font>:{</font><font>"version"</font><font>:</font><font>"v2"</font><font>},</font><font>"name"</font><font>:</font><font>"v2"</font><font>}]}} creationTimestamp: <b>null</b> name: mvc-service namespace: <b>default</b> resourceVersion: </font><font>"5722"</font><font> spec: host: mvc-service subsets: - labels: version: v1 name: v1 - labels: version: v2 name: v2 --- </font>
然后是virtualservices:
istioctl get virtualservices -o <b>short</b> VIRTUAL-SERVICE NAME GATEWAYS HOSTS #HTTP #TCP NAMESPACE AGE mvc-service mvc-service 1 0 <b>default</b> 11d service-gateway service-gateway * 1 0 <b>default</b> 11d
金丝雀测试
我们需要获取istio-ingress网关和端口的IP地址。
kubectl get service istio-ingressgateway -n istio-system NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE istio-ingressgateway LoadBalancer 10.111.15.19 <pending> 80:31380/TCP,443:31390/TCP,31400:31400/TCP,15011:32464/TCP,8060:30626/TCP,853:30365/TCP,15030:31121/TCP,15031:31359/TCP 13d
istio-ingress文档如是说:
如果设置了EXTERNAL-IP值,则您的环境具有可 用于入口网关的外部负载平衡器。如果EXTERNAL-IP值为<none>(或永久<pending>),则您的环境 不会为入口网关提供外部负载平衡器。在这种情况下,您可以 使用service s节点端口访问网关。
这意味着调用我们的金丝雀版本,我们将继续:
minikube ip 192.168.99.110
现在终于调用了后台,请注意文档中指出的nodeport端口:
10个点击中:2个进入V2版本,8个点击进入V1。
tomask79:spring-kubernetes-istio tomask79$ curl http:<font><i>//192.168.99.110:31380/service</i></font><font> Hello I'm V1!tomask79:spring-kubernetes-istio tomask79$ curl http:</font><font><i>//192.168.99.110:31380/service</i></font><font> Hello I'm V1!tomask79:spring-kubernetes-istio tomask79$ curl http:</font><font><i>//192.168.99.110:31380/service</i></font><font> Hello I'm V1!tomask79:spring-kubernetes-istio tomask79$ curl http:</font><font><i>//192.168.99.110:31380/service</i></font><font> Hello i'm V2!tomask79:spring-kubernetes-istio tomask79$ curl http:</font><font><i>//192.168.99.110:31380/service</i></font><font> Hello I'm V1!tomask79:spring-kubernetes-istio tomask79$ curl http:</font><font><i>//192.168.99.110:31380/service</i></font><font> Hello I'm V1!tomask79:spring-kubernetes-istio tomask79$ curl http:</font><font><i>//192.168.99.110:31380/service</i></font><font> Hello I'm V1!tomask79:spring-kubernetes-istio tomask79$ curl http:</font><font><i>//192.168.99.110:31380/service</i></font><font> Hello I'm V1!tomask79:spring-kubernetes-istio tomask79$ curl http:</font><font><i>//192.168.99.110:31380/service</i></font><font> Hello i'm V2!tomask79:spring-kubernetes-istio tomask79$ curl http:</font><font><i>//192.168.99.110:31380/service</i></font><font> Hello I'm V1!tomask79:spring-kubernetes-istio tomask79$ </font>
使用Istio进行蓝/绿部署
现在让我们说我们的应用程序的V2版本足够稳定,我们可以将100%的流量路由到它。要使用Istio实现这一点,我们将更改VirtualService中的规则:
将istio-gateway.yaml文件中的VirtualService修改为:
apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: service-gateway spec: hosts: - <font>"*"</font><font> gateways: - service-gateway http: - match: - uri: exact: /service route: - destination: host: mvc-service subset: v2 </font>
重新部署:
tomask79:spring-kubernetes-istio tomask79$ kubectl apply -f istio-gateway.yaml gateway.networking.istio.io/service-gateway unchanged virtualservice.networking.istio.io/service-gateway configured
测试蓝/绿部署
现在V2版本应该成为我们的生产版本并处理100%的流量:
tomask79:spring-kubernetes-istio tomask79$ curl http:<font><i>//192.168.99.110:31380/service</i></font><font> Hello i'm V2!tomask79:spring-kubernetes-istio tomask79$ curl http:</font><font><i>//192.168.99.110:31380/service</i></font><font> Hello i'm V2!tomask79:spring-kubernetes-istio tomask79$ curl http:</font><font><i>//192.168.99.110:31380/service</i></font><font> Hello i'm V2!tomask79:spring-kubernetes-istio tomask79$ curl http:</font><font><i>//192.168.99.110:31380/service</i></font><font> Hello i'm V2!tomask79:spring-kubernetes-istio tomask79$ curl http:</font><font><i>//192.168.99.110:31380/service</i></font><font> Hello i'm V2!tomask79:spring-kubernetes-istio tomask79$ curl http:</font><font><i>//192.168.99.110:31380/service</i></font><font> Hello i'm V2!tomask79:spring-kubernetes-istio tomask79$ curl http:</font><font><i>//192.168.99.110:31380/service</i></font><font> Hello i'm V2!tomask79:spring-kubernetes-istio tomask79$ curl http:</font><font><i>//192.168.99.110:31380/service</i></font><font> Hello i'm V2!tomask79:spring-kubernetes-istio tomask79$ curl http:</font><font><i>//192.168.99.110:31380/service</i></font><font> Hello i'm V2!tomask79:spring-kubernetes-istio tomask79$ curl http:</font><font><i>//192.168.99.110:31380/service</i></font><font> Hello i'm V2!tomask79:spring-kubernetes-istio tomask79$ </font>
总结
Istio看起来对我来说是超级强大的工具。但它的学习曲线有点长。此外,他们还在配置模型之间进行了 重大更改 。无论如何,他们支持粘性会话甚至websockets。例如,对于我在EmbedIT中使用的系统,这是两个“必须拥有”的东西。像istio这样的另一个类似工具是 Linkerd 。你也可以看一下:)
本文源码