1. 程式人生 > >K8s + Istio 安裝和配置例子: Bookinfo

K8s + Istio 安裝和配置例子: Bookinfo

說明:本文的K8S的版本是 v1.8.2, ISTIO v0.2.12。

 

服務網格簡介

服務網格(Service Mesh)是為解決微服務的通訊和治理而出現的一種架構模式。

服務網格將服務間通訊以及與此相關的管理控制功能從業務程式中下移到一個基礎設施層,從而徹底隔離了業務邏輯和服務通訊兩個關注點。採用服務網格後,應用開發者只需要關注並實現應用業務邏輯。服務之間的通訊,包括服務發現,通訊的可靠性,通訊的安全性,服務路由等由服務網格層進行處理,並對應用程式透明。

讓我們來回顧一下微服務架構的發展過程。在出現服務網格之前,我們在微服務應用程式程序內處理服務通訊邏輯,包括服務發現,熔斷,重試,超時等邏輯,如下圖所示: 

 
通過對這部分負責服務通訊的邏輯進行抽象和歸納,可以形成一個程式碼庫供應用程式呼叫。但應用程式還是需要處理和各種語言程式碼庫的呼叫細節,並且各種程式碼庫互不相容,導致對應用程式使用的語言和程式碼框架有較大限制。

如果我們更進一步,將這部分邏輯從應用程式程序中抽取出來,作為一個單獨的程序進行部署,並將其作為服務間的通訊代理,如下圖所示: 
 
因為通訊代理程序和應用程序一起部署,因此形象地把這種部署方式稱為“sidecar”(三輪摩托的挎鬥)。 
 
應用間的所有流量都需要經過代理,由於代理以sidecar方式和應用部署在同一臺主機上,應用和代理之間的通訊被認為是可靠的。然後由代理來負責找到目的服務並負責通訊的可靠性和安全等問題。

當服務大量部署時,隨著服務部署的sidecar代理之間的連線形成了一個如下圖所示的網格,被稱之為Service Mesh(服務網格),從而得出如下的服務網格定義。

服務網格是一個基礎設施層,用於處理服務間通訊。雲原生應用有著複雜的服務拓撲,服務網格保證請求可以在這些拓撲中可靠地穿梭。在實際應用當中,服務網格通常是由一系列輕量級的網路代理組成的,它們與應用程式部署在一起,但應用程式不需要知道它們的存在。

William Morgan WHAT’S A SERVICE MESH? AND WHY DO I NEED ONE?_ _

瞭解了服務網格的基本概念,下一步介紹一下

Istio。Istio是來自Google,IBM和Lyft的一個Service Mesh(服務網格)開源專案,是Google繼Kubernetes之後的又一大作,Istio架構先進,設計合理,剛一宣佈就獲得了Linkerd,nginmesh等其他Service Mesh專案的合作以及Red hat/Pivotal/Weaveworks/Tigera/Datawire等的積極響應。 

可以設想,在不久的將來,微服務的標準基礎設施將是採用kubernetes進行服務部署和叢集管理,採用Istio處理服務通訊和治理,兩者相輔相成,缺一不可。

安裝Kubernetes

Istio是微服務通訊和治理的基礎設施層,本身並不負責服務的部署和叢集管理,因此需要和Kubernetes等服務編排工具協同工作。

Istio在架構設計上支援各種服務部署平臺,包括kubernetes,cloud foundry,Mesos等,但Istio作為Google親兒子,對自家兄弟Kubernetes的支援肯定是首先考慮的。目前版本的0.2版本的手冊中也只有Kubernetes整合的安裝說明,其它部署平臺和Istio的整合將在後續版本中支援。

從Istio控制面Pilot的架構圖可以看到各種部署平臺可以通過外掛方式整合到Istio中,為Istio提供服務註冊和發現功能。

kubernetes叢集的部署較為複雜,Rancher提供了kubernetes部署模板,通過一鍵式安裝,可以大大簡化kubernetes叢集的安裝部署過程。

本文的測試環境為兩臺虛機組成的叢集,作業系統是Ubuntu 16.04.3 LTS。兩臺虛機的地址分別為: 
Rancher Server: 10.12.25.60 
工作節點: 10.12.25.116

通過Rancher安裝Kubernetes叢集的簡要步驟如下:

在server和工作節點上安裝docker

因為k8s並不支援最新版本的docker,因此需根據該頁面安裝指定版本的docker 
http://rancher.com/docs/rancher/v1.6/en/hosts/ ,目前是1.12版本。

curl https://releases.rancher.com/install-docker/1.12.sh | sh
  • 1

啟動Rancher server

sudo docker run -d --restart=always -p 8080:8080 rancher/server
  • 1

登入Rancher管理介面,建立k8s叢集

Rancher 管理介面的預設埠為8080,在瀏覽器中開啟該介面,通過選單Default->Manage Environment->Add Environment新增一個kubernetes叢集。這裡需要輸入名稱kubernetes,描述,然後選擇kubernetes template,點選create,建立Kubernetes環境。

點選選單切換到kubernetes Environment,然後點選右上方的Add a host,新增一臺host到kubernetes叢集中。注意新增到叢集中的host上必須先安裝好符合要求的docker版本。

然後根據Rancher頁面上的提示在host上執行指令碼啟動Rancher agent,以將host加入ranch cluster。注意指令碼中包含了rancher server的地址,在host上必須可以ping通該地址。

host加入cluster後Rancher會在host上pull kubernetes的images並啟動kubernetes相關服務,根據安裝環境所在網路情況不同需要等待幾分鐘到幾十分鐘不等。

安裝並配置kubectl

待Rancher介面提示kubernetes建立成功後,安裝kubernetes命令列工具kubectl

curl -LO https://storage.googleapis.com/kubernetes-release/release/v1.7.4/bin/linux/amd64/kubectl

chmod +x ./kubectl

sudo mv ./kubectl /usr/local/bin/kubectl
  • 1
  • 2
  • 3
  • 4
  • 5

登入Rancher管理介面, 將 All Environments->kubernetes->KUBERNETES->CLI create config 的內容拷貝到~/.kube/config 中,以配置Kubectl和kubernetes server的連線資訊。

說一遍:在進行下面的ISTIO安裝前,要確保k8s叢集中已安裝了kube-DNS, kube-proxy 外掛!

 

安裝Istio

Istio 安裝在這裡:https://istio.io/docs/setup/kubernetes/quick-start.html

Istio bookinfo 官網地址在這:https://istio.io/docs/guides/bookinfo.html

 

Istio提供了安裝指令碼,該指令碼會根據作業系統下載相應的Istio安裝包並解壓到當前目錄。

 curl -L https://git.io/getLatestIstio | sh -
  • 1

根據指令碼的提示將Istio命令列所在路徑加入到系統PATH環境變數中

export PATH="$PATH:/home/ubuntu/istio-0.2.12/bin"
  • 1

在kubernetes叢集中部署Istio控制面服務

注:如果你的安裝網路不支援LoadBalancer的話,則把istio-auth.yaml 和 istio.yaml裡面的

istio-ingress service的 型別 從 loadBalancer 方式改成 NodePort 方式,如下所示。

 

apiVersion: v1
kind: Service
metadata:
  name: istio-ingress
  namespace: istio-system
  labels:
    istio: ingress
spec:
  type: NodePort
  ports:
  - port: 80
    nodePort: 32000
    name: http
  - port: 443
    name: https
  selector:
    istio: ingresstype: NodePort
  ports:
  - port: 80
    nodePort: 32000
    name: http
  - port: 443
    name: https
  selector:
    istio: ingress


如果不啟用Istio envoy TLS認證的話,執行下面的命令

 

kubectl apply -f istio-0.2.12/install/kubernetes/istio.yaml
  • 1

反之,如果開啟Istio envoy TLS認證的話,執行下面的命令

 

kubectl apply -f istio-0.2.12/install/kubernetes/istio-auth.yaml
  • 1
  •  

 

確認Istio控制面服務已成功部署。Kubernetes會建立一個istio-system namespace,將Istio相關服務部署在該namespace中。

確認Istio相關Service的部署狀態

kubectl get svc -n istio-system
  • 1
NAME            CLUSTER-IP      EXTERNAL-IP        PORT(S)                                                  AGE
istio-egress    10.43.192.74    <none>             80/TCP                                                   25s
istio-ingress   10.43.16.24     10.12.25.116,...   80:30984/TCP,443:30254/TCP                               25s
istio-mixer     10.43.215.250   <none>             9091/TCP,9093/TCP,9094/TCP,9102/TCP,9125/UDP,42422/TCP   26s
istio-pilot     10.43.211.140   <none>             8080/TCP,443/TCP                                         25s
  • 1
  • 2
  • 3
  • 4
  • 5

確認Istio相關Pod的部署狀態

kubectl get pods -n istio-system
  • 1
NAME                             READY     STATUS    RESTARTS   AGE
istio-ca-367485603-qvbfl         1/1       Running   0          2m
istio-egress-3571786535-gwbgk    1/1       Running   0          2m
istio-ingress-2270755287-phwvq   1/1       Running   0          2m
istio-mixer-1505455116-9hmcw     2/2       Running   0          2m
istio-pilot-2278433625-68l34     1/1       Running   0          2m
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

從上面的輸出可以看到,這裡部署的主要是Istio控制面的服務,而資料面的網路代理要如何部署呢? 
根據前面服務網格的架構介紹可以得知,網路代理是隨著應用程式以sidecar的方式部署的,在下面部署Bookinfo示例程式時會演示如何部署網路代理。

部署Bookinfo示例程式

在下載的Istio安裝包的samples目錄中包含了示例應用程式。

通過下面的命令部署Bookinfo應用

kubectl apply -f <(istioctl kube-inject -f istio-0.2.12/samples/bookinfo/kube/bookinfo.yaml)
  • 1

確認Bookinfo服務已經啟動

kubectl get services
  • 1
NAME          CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
details       10.43.175.204   <none>        9080/TCP   6m
kubernetes    10.43.0.1       <none>        443/TCP    5d
productpage   10.43.19.154    <none>        9080/TCP   6m
ratings       10.43.50.160    <none>        9080/TCP   6m
reviews       10.43.219.248   <none>        9080/TCP   6m
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

在瀏覽器中開啟應用程式頁面,地址為istio-ingress的External IP

http://10.12.25.116/productpage 

理解Istio Proxy實現原理

服務網格相對於sprint cloud等微服務程式碼庫的一大優勢是其對應用程式無侵入,在不修改應用程式程式碼的前提下對應用服務之間的通訊進行接管,Istio是如何做到這點的呢?下面通過示例程式的部署剖析其中的原理。

如果熟悉kubernetes的應用部署過程,我們知道Bookinfo應用程式的標準部署方式是這樣的:

kubectl apply -f istio-0.2.12/samples/bookinfo/kube/bookinfo.yaml
  • 1

但從上面的部署過程可以看到,kubectl apply命令的輸入並不是一個kubernetes yaml檔案,而是istioctl kube-inject -f istio-0.2.10/samples/bookinfo/kube/bookinfo.yaml命令的輸出。

這段命令在這裡起到了什麼作用呢?通過單獨執行該命令並將輸出儲存到檔案中,我們可以檢視istioctl kube-inject命令到底在背後搞了什麼小動作。

istioctl kube-inject -f istio-0.2.12/samples/bookinfo/kube/bookinfo.yaml >> bookinfo_with_sidecar.yaml
  • 1

對比bookinfo_with_sidecar.yaml檔案和bookinfo.yaml,可以看到該命令在bookinfo.yaml的基礎上做了如下改動:

  • 為每個pod增加了一個代理container,該container用於處理應用container之間的通訊,包括服務發現,路由規則處理等。從下面的配置檔案中可以看到proxy container通過15001埠進行監聽,接收應用container的流量。

  • 為每個pod增加了一個init-container,該container用於配置iptable,將應用container的流量匯入到代理container中。

  #注入istio 網路代理
  image: docker.io/istio/proxy_debug:0.2.10
        imagePullPolicy: IfNotPresent
        name: istio-proxy
        resources: {}
        securityContext:
          privileged: true
          readOnlyRootFilesystem: false
          runAsUser: 1337
        volumeMounts:
        - mountPath: /etc/istio/proxy
          name: istio-envoy
        - mountPath: /etc/certs/
          name: istio-certs
          readOnly: true
      #使用init container修改iptable
      initContainers:
      - args:
        - -p
        - "15001"
        - -u
        - "1337"
        image: docker.io/istio/proxy_init:0.2.10
        imagePullPolicy: IfNotPresent
        name: istio-init
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

從上面的分析,我們可以看出Istio的kube-inject工具的用途即是將代理sidecar注入了Bookinfo的kubernetes yaml部署檔案中。通過該方式,不需要使用者手動修改kubernetes的部署檔案,即可在部署服務時將sidecar和應用一起部署。

通過命令檢視pod中部署的docker container,確認是否部署了Istio代理

kubectl get pods

NAME                              READY     STATUS    RESTARTS   AGE
details-v1-3688945616-8hv8x       2/2       Running   0          1d
productpage-v1-2055622944-cslw1   2/2       Running   0          1d
ratings-v1-233971408-8dcnp        2/2       Running   0          1d
reviews-v1-1360980140-474x6       2/2       Running   0          1d
reviews-v2-1193607610-cfhb5       2/2       Running   0          1d
reviews-v3-3340858212-b5c8k       2/2       Running   0          1d
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

檢視reviews pod的中的container,可以看到pod中除reviews container外還部署了一個istio-proxy container

kubectl get pod reviews-v3-3340858212-b5c8k -o jsonpath='{.spec.containers[*].name}'

reviews istio-proxy
  • 1
  • 2
  • 3

而應用container的流量是如何被匯入到istio-proxy中的呢?

原理是Istio proxy在埠15001進行監聽,pod中應用container的流量通過iptables規則被重定向到15001埠中。下面我們進入pod內部,通過相關命令來驗證這一點。

先通過命令列找到ratings-v1-233971408-8dcnp pod的PID,以用於檢視其network namespace內的iptables規則。

CONTAINER_ID=$(kubectl get po ratings-v1-233971408-8dcnp -o jsonpath='{.status.containerStatuses[0].containerID}' | cut -c 10-21)

PID=$(sudo docker inspect --format '{{ .State.Pid }}' $CONTAINER_ID)
  • 1
  • 2
  • 3

可以使用nsenter命令來進入pod的network namespace執行命令。 
使用netstat命令可以看到istio proxy代理的監聽埠15001

sudo nsenter -t ${PID} -n netstat -all | grep 15001

tcp        0      0 *:15001                 *:*                     LISTEN
  • 1
  • 2
  • 3

使用iptables命令可以檢視到下面的規則

sudo nsenter -t ${PID} -n iptables -t nat -L -n -v

Chain PREROUTING (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination
   16   960 ISTIO_REDIRECT  all  --  *      *       0.0.0.0/0            0.0.0.0/0            /* istio/install-istio-prerouting */

Chain INPUT (policy ACCEPT 16 packets, 960 bytes)
 pkts bytes target     prot opt in     out     source               destination

Chain OUTPUT (policy ACCEPT 84838 packets, 7963K bytes)
 pkts bytes target     prot opt in     out     source               destination
 1969  118K ISTIO_OUTPUT  tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            /* istio/install-istio-output */

Chain POSTROUTING (policy ACCEPT 84838 packets, 7963K bytes)
 pkts bytes target     prot opt in     out     source               destination

Chain ISTIO_OUTPUT (1 references)
 pkts bytes target     prot opt in     out     source               destination
    0     0 ISTIO_REDIRECT  all  --  *      lo      0.0.0.0/0           !127.0.0.1            /* istio/redirect-implicit-loopback */
 1969  118K RETURN     all  --  *      *       0.0.0.0/0            0.0.0.0/0            owner UID match 1337 /* istio/bypass-envoy */
    0     0 RETURN     all  --  *      *       0.0.0.0/0            127.0.0.1            /* istio/bypass-explicit-loopback */
    0     0 ISTIO_REDIRECT  all  --  *      *       0.0.0.0/0            0.0.0.0/0            /* istio/redirect-default-outbound */

Chain ISTIO_REDIRECT (3 references)
 pkts bytes target     prot opt in     out     source               destination
   16   960 REDIRECT   tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            /* istio/redirect-to-envoy-port */ redir ports 15001
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

從pod所在network namespace的iptables規則中可以看到,pod的入口和出口流量分別通過PREROUTING和OUTPUT chain指向了自定義的ISTIO_REDIRECT chain,而ISTIO_REDIRECT chain中的規則將所有流量都重定向到了istio proxy正在監聽的15001埠中。從而實現了對應用透明的通訊代理。

測試路由規則

多次重新整理Bookinfo應用的productpage頁面,我們會發現該頁面中顯示的Book Reviews有時候有帶紅星的評價資訊,有時有帶黑星的評價資訊,有時只有文字評價資訊。 
這是因為Bookinfo應用程式部署了3個版本的Reviews服務,每個版本的返回結果不同,在沒有設定路由規則時,預設的路由會將請求隨機路由到每個版本的服務上,如下圖所示:

通過建立一條路由規則route-rule.yaml,將請求流量都引導到Reviews-v1服務上

apiVersion: config.istio.io/v1alpha2
kind: RouteRule
metadata:
  name: reviews-default
spec:
  destination:
    name: reviews
  precedence: 1
  route:
  - labels:
      version: v1
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

啟用該路由規則

istioctl create -f route-rule.yaml -n default
  • 1

再次開啟productpage頁面, 無論重新整理多少次,顯示的頁面將始終是v1版本的輸出,即不帶星的評價內容。 

刪除該路由規則

istioctl delete -f route_rule.yaml -n default
  • 1

繼續重新整理productpage頁面,將重新隨機出現三個版本的評價內容頁面。

分散式呼叫追蹤

首先修改安裝包中的 istio-0.2.10/install/kubernetes/addons/zipkin.yaml 部署檔案,增加Nodeport,以便能在kubernetes叢集外部訪問zipkin介面。

apiVersion: v1
kind: Service
metadata:
  name: zipkin
  namespace: istio-system
spec:
  ports:
  - name: http
    port: 9411
    nodePort: 30001
  selector:
    app: zipkin
  type: NodePort
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

部署zipkin服務。

kubectl apply -f istio-0.2.10/install/kubernetes/addons/zipkin.yaml
  • 1

在瀏覽器中開啟zipkin頁面,可以追蹤一個端到端呼叫經過了哪些服務,以及各個服務花費的時間等詳細資訊,如下圖所示: 
http://10.12.25.116:30001 

效能指標監控

首先修改安裝包中的 istio-0.2.10/install/kubernetes/addons/grafana.yaml 部署檔案,增加Nodeport,以便能在kubernetes叢集外部訪問grafana介面。

apiVersion: v1
kind: Service
metadata:
  name: grafana
  namespace: istio-system
spec:
  ports:
  - port: 3000
    protocol: TCP
    name: http
    nodePort: 30002
  selector:
    app: grafana
  type: NodePort
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

prometheus用於收集和儲存資訊指標,grafana用於將效能指標資訊進行視覺化呈現,需要同時部署prometheus和grafana服務。

kubectl apply -f istio-0.2.10/install/kubernetes/addons/prometheus.yaml

kubectl apply -f istio-0.2.10/install/kubernetes/addons/grafana.yaml
  • 1
  • 2
  • 3

首先在瀏覽器中開啟Bookinfo的頁面http://10.12.25.116/productpage,重新整理幾次,以製造一些效能指標資料。

然後開啟grafana頁面檢視效能指標http://10.12.25.116:30002/dashboard/db/istio-dashboard,如下圖所示: 

參考