SpringBoot、Kubernetes和Istio微服務網格演示原始碼

banq發表於2019-02-25

如果單純使用kubernetes的pod部署Spring微服務,K8s的負載平衡以及代理設定和你微服務應用之間不是非常的智慧銜接,。無論如何,部署新的應用程式版本pod需要更加軟化的方法。以下是典型的需求:
  • 智慧調撥流量,在部署新的應用程式版本容器時,您經常需要以某種比例(即金絲雀Canary測試)分割新容器和當前生產之間的流量
  • 藍綠部署,在部署新的應用程式版本pod時,假設您需要藍色/綠色部署
  • 在部署新的應用程式版本pod時,讓我們說只有HTTP cookie識別的一些使用者可以測試新版本

所有這些問題都可以透過名為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
public class ControllerV1 {

    @GetMapping(path = "/service")
    public String getResult() {
        return "Hello I'm V1!";
    }
}


下面是這個版本的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
public class ControllerV2 {

    @GetMapping(path = "/service")
    public String getResult() {
        return "Hello i'm V2!";
    }
}


下面是這個版本的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 "docker.io/istio/proxy_init:1.0.5" 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 "service-v1:0.0.1-SNAPSHOT" 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 "docker.io/istio/proxyv2:1.0.5" already present on machine
  Normal  Created         8m33s  kubelet, minikube  Created container
  Normal  Started         8m33s  kubelet, minikube  Started container


Istio形成服務網格
好的,部署了兩個版本的Spring Boot MVC應用程式。現在我打賭你對像VirtualServiceDestinationRule這樣的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:
  - "*"
  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


這真的很容易。作為最後一步,我們需要公開服務閘道器,即Istio-Ingress閘道器 ,它從服務網格外部獲取流量並將其轉發到該閘道器

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: service-gateway
spec:
  selector:
    istio: ingressgateway # use istio default controller
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
    - "*"


在這個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: |
      {"apiVersion":"networking.istio.io/v1alpha3","kind":"DestinationRule","metadata":{"annotations":{},"name":"mvc-service","namespace":"default"},"spec":{"host":"mvc-service","subsets":[{"labels":{"version":"v1"},"name":"v1"},{"labels":{"version":"v2"},"name":"v2"}]}}
  creationTimestamp: null
  name: mvc-service
  namespace: default
  resourceVersion: "5722"
spec:
  host: mvc-service
  subsets:
  - labels:
      version: v1
    name: v1
  - labels:
      version: v2
    name: v2
---

然後是virtualservices:

istioctl get virtualservices -o short
VIRTUAL-SERVICE NAME   GATEWAYS          HOSTS         HTTP     TCP      NAMESPACE   AGE
mvc-service                              mvc-service       1        0      default     11d
service-gateway        service-gateway   *                 1        0      default     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://192.168.99.110:31380/service
Hello I'm V1!tomask79:spring-kubernetes-istio tomask79$ curl http://192.168.99.110:31380/service
Hello I'm V1!tomask79:spring-kubernetes-istio tomask79$ curl http://192.168.99.110:31380/service
Hello I'm V1!tomask79:spring-kubernetes-istio tomask79$ curl http://192.168.99.110:31380/service
Hello i'm V2!tomask79:spring-kubernetes-istio tomask79$ curl http://192.168.99.110:31380/service
Hello I'm V1!tomask79:spring-kubernetes-istio tomask79$ curl http://192.168.99.110:31380/service
Hello I'm V1!tomask79:spring-kubernetes-istio tomask79$ curl http://192.168.99.110:31380/service
Hello I'm V1!tomask79:spring-kubernetes-istio tomask79$ curl http://192.168.99.110:31380/service
Hello I'm V1!tomask79:spring-kubernetes-istio tomask79$ curl http://192.168.99.110:31380/service
Hello i'm V2!tomask79:spring-kubernetes-istio tomask79$ curl http://192.168.99.110:31380/service
Hello I'm V1!tomask79:spring-kubernetes-istio tomask79$ 


使用Istio進行藍/綠部署
現在讓我們說我們的應用程式的V2版本足夠穩定,我們可以將100%的流量路由到它。要使用Istio實現這一點,我們將更改VirtualService中的規則:
將istio-gateway.yaml檔案中的VirtualService修改為:

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: service-gateway
spec:
  hosts:
  - "*"
  gateways:
  - service-gateway
  http:
  - match:
    - uri:
        exact: /service
    route:
    - destination:
        host: mvc-service
        subset: v2



重新部署:

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://192.168.99.110:31380/service
Hello i'm V2!tomask79:spring-kubernetes-istio tomask79$ curl http://192.168.99.110:31380/service
Hello i'm V2!tomask79:spring-kubernetes-istio tomask79$ curl http://192.168.99.110:31380/service
Hello i'm V2!tomask79:spring-kubernetes-istio tomask79$ curl http://192.168.99.110:31380/service
Hello i'm V2!tomask79:spring-kubernetes-istio tomask79$ curl http://192.168.99.110:31380/service
Hello i'm V2!tomask79:spring-kubernetes-istio tomask79$ curl http://192.168.99.110:31380/service
Hello i'm V2!tomask79:spring-kubernetes-istio tomask79$ curl http://192.168.99.110:31380/service
Hello i'm V2!tomask79:spring-kubernetes-istio tomask79$ curl http://192.168.99.110:31380/service
Hello i'm V2!tomask79:spring-kubernetes-istio tomask79$ curl http://192.168.99.110:31380/service
Hello i'm V2!tomask79:spring-kubernetes-istio tomask79$ curl http://192.168.99.110:31380/service
Hello i'm V2!tomask79:spring-kubernetes-istio tomask79$ 


總結
Istio看起來對我來說是超級強大的工具。但它的學習曲線有點長。此外,他們還在配置模型之間進行了重大更改。無論如何,他們支援粘性會話甚至websockets。例如,對於我在EmbedIT中使用的系統,這是兩個“必須擁有”的東西。像istio這樣的另一個類似工具是Linkerd。你也可以看一下:)

本文原始碼

相關文章