Istio 入門(三):體驗 Istio、微服務部署、可觀測性

本教程已加入 Istio 系列:https://istio.whuanle.cn

3,快速入門

在本章中,我們正式邁入學習 Istio 的第一步。因爲 Istio 的知識體系是較爲龐大的,因此我們可以先通過本章的入門教程快速瞭解如何使用 Istio 部署一套微服務,以及 Istio 核心功能的使用方法,瞭解 Istio 可以爲微服務解決什麼問題。

在本章中,我們將會學習到如何部署一套微服務、如何使用 Istio 暴露服務到集羣外,並且如何使用可觀測性組件監測流量和系統指標。

在後面的章節中,筆者會針對每個 Istio 組件做單獨講解,而在本章中,我們只需要大概瞭解使用方法即可。

書店微服務

本章教程示例使用的是 Istio 官方的一套微服務,這套微服務是一個在線書店,打開頁面之後會看到一個分類、書的信息以及書的評論,頁面的內容由不同的子服務提供。

1684807907059

書店微服務分爲四個單獨的微服務,在上圖中已經使用紅色方框畫出來了。這四個微服務分別是:

  • productpage: 彙集所有服務的數據,生成瀏覽頁面。
  • details:存儲了書籍的信息,如描述、作者、出版社等。
  • reviews:存儲了書籍相關的評論,但是不包含評分打星。
  • ratings:存儲評論中的評分打星。

在這個微服務中,Productpage 服務對外提供 Web 訪問頁面,而且其它的三個服務只能在集羣內部訪問。四個服務分別採用了不同的語言開發,Productpage 聚合其它三個服務的信息生成一個頁面。

在微服務設計中,我們不要每個子服務都暴露端口到集羣外部,應該通過一些應用集中數據後給外部顯示。我們可以使用 API 網關,代理子服務一部分接口,然後在 API 網關中實現基於客戶端或第三方調用的身份驗證。

productpage、details、ratings 都只有一個 v1 版本,而 reviews 有三個版本。

ratings 負責給出用戶打分的數據,例如一星、兩星。而 reviews 三個版本分別對 ratings 的數據做以下處理:

  • reviews v1:屏蔽星級,不顯示打分;
  • reviews v2:顯示星級,使用灰色星星表示,★★★★☆;
  • reviews v3:顯示星級,使用紅色星星表示,★★★★☆

8e06a36d6a43d9261c590daadedbfd44_noistio

【圖源 istio 官網

服務依賴圖如下所示:

image-20230507162407860

接下來我們將會使用 Kuubernetes Deployment 部署這些服務,這跟常規的 Kubernetes 部署並無差別。

預先準備

給這些示例服務創建一個命名空間。

kubectl create namespace bookinfo

給命名空間添加 Istio 的標籤,指示 Istio 在部署應用(只對 Pod 起效)的時候,自動注入 Envoy Sidecar Proxy 容器:

kubectl label namespace bookinfo istio-injection=enabled

開啓讓 Istio 注入 Sidecar 有很多種方式,其中一種是給命名空間設置下標籤,在此命名空間下部署的 Pod,會被自動注入 Sidecar 。

你可以從本系列教程的 git 倉庫中找到這些示例,文件位置:https://github.com/whuanle/istio_book/tree/main/3

倉庫拉取後打開 3 目錄,執行命令進行部署:

kubectl -n bookinfo apply -f details_deploy.yaml
kubectl -n bookinfo apply -f details_svc.yaml
kubectl -n bookinfo apply -f details_sa.yaml
kubectl -n bookinfo apply -f ratings_deploy.yaml
kubectl -n bookinfo apply -f ratings_svc.yaml
kubectl -n bookinfo apply -f ratings_sa.yaml
kubectl -n bookinfo apply -f reviews_v1_deploy.yaml
kubectl -n bookinfo apply -f reviews_v2_deploy.yaml
kubectl -n bookinfo apply -f reviews_v3_deploy.yaml
kubectl -n bookinfo apply -f reviews_svc.yaml
kubectl -n bookinfo apply -f reviews_sa.yaml
kubectl -n bookinfo apply -f productpage_deploy.yaml
kubectl -n bookinfo apply -f productpage_svc.yaml
kubectl -n bookinfo apply -f productpage_sa.yaml

或者你可以參考下面的四個小節,通過手動的方式部署應用,瞭解每一個應用是如何定義的。

details 應用

存儲了書籍信息的應用。

部署命令:

kubectl -n bookinfo apply -f details_deploy.yaml
kubectl -n bookinfo apply -f details_svc.yaml
kubectl -n bookinfo apply -f details_sa.yaml

使用 Deployment 部署 details 應用。

details_deploy.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: details-v1
  labels:
    app: details
    version: v1
spec:
  replicas: 1
  selector:
    matchLabels:
      app: details
      version: v1
  template:
    metadata:
      labels:
        app: details
        version: v1
    spec:
      serviceAccountName: bookinfo-details
      containers:
      - name: details
        image: docker.io/istio/examples-bookinfo-details-v1:1.17.0
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 9080
        securityContext:
          runAsUser: 1000

部署 details。

kubectl -n bookinfo apply -f details_deploy.yaml

爲 details 服務配置 Kubernetes Service 。

details_svc.yaml

apiVersion: v1
kind: Service
metadata:
  name: details
  labels:
    app: details
    service: details
spec:
  ports:
  - port: 9080
    name: http
  selector:
    app: details
kubectl -n bookinfo apply -f details_svc.yaml

接下來爲 details 服務創建一個 ServiceAccount。

Istio 爲服務之間的通信提供基於雙向 TLS 的認證,這是是通過給每個 ServiceAccount 創建一個證書實現的,可以使用 ServiceAccount 驗證對方的身份,不同的應用可以共享同一個 ServiceAccount,但是爲每個 Deployment 使用單獨的 ServiceAccount 可以更好地組織和管理安全配置。

details_sa.yaml

apiVersion: v1
kind: ServiceAccount
metadata:
  name: bookinfo-details
  labels:
    account: details
kubectl -n bookinfo apply -f details_sa.yaml

ratings 應用

提供每條評論的打星數據。

部署命令:

kubectl -n bookinfo apply -f ratings_deploy.yaml
kubectl -n bookinfo apply -f ratings_svc.yaml
kubectl -n bookinfo apply -f ratings_sa.yaml

ratings_deploy.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: ratings-v1
  labels:
    app: ratings
    version: v1
spec:
  replicas: 1
  selector:
    matchLabels:
      app: ratings
      version: v1
  template:
    metadata:
      labels:
        app: ratings
        version: v1
    spec:
      serviceAccountName: bookinfo-ratings
      containers:
      - name: ratings
        image: docker.io/istio/examples-bookinfo-ratings-v1:1.17.0
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 9080
        securityContext:
          runAsUser: 1000

ratings_svc.yaml

apiVersion: v1
kind: Service
metadata:
  name: ratings
  labels:
    app: ratings
    service: ratings
spec:
  ports:
  - port: 9080
    name: http
  selector:
    app: ratings

ratings_sa.yaml

apiVersion: v1
kind: ServiceAccount
metadata:
  name: bookinfo-ratings
  labels:
    account: ratings

reviews v1/v2/v3 應用

提供書籍的評論信息。

部署命令:

kubectl -n bookinfo apply -f reviews_v1_deploy.yaml
kubectl -n bookinfo apply -f reviews_v2_deploy.yaml
kubectl -n bookinfo apply -f reviews_v3_deploy.yaml
kubectl -n bookinfo apply -f reviews_svc.yaml
kubectl -n bookinfo apply -f reviews_sa.yaml

爲三個版本的 reviews 創建三個 Deployment。

reviews_v1_deploy.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: reviews-v1
  labels:
    app: reviews
    version: v1
spec:
  replicas: 1
  selector:
    matchLabels:
      app: reviews
      version: v1
  template:
    metadata:
      labels:
        app: reviews
        version: v1
    spec:
      serviceAccountName: bookinfo-reviews
      containers:
      - name: reviews
        image: docker.io/istio/examples-bookinfo-reviews-v1:1.17.0
        imagePullPolicy: IfNotPresent
        env:
        - name: LOG_DIR
          value: "/tmp/logs"
        ports:
        - containerPort: 9080
        volumeMounts:
        - name: tmp
          mountPath: /tmp
        - name: wlp-output
          mountPath: /opt/ibm/wlp/output
        securityContext:
          runAsUser: 1000
      volumes:
      - name: wlp-output
        emptyDir: {}
      - name: tmp
        emptyDir: {}

reviews_v2_deploy.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: reviews-v2
  labels:
    app: reviews
    version: v2
spec:
  replicas: 1
  selector:
    matchLabels:
      app: reviews
      version: v2
  template:
    metadata:
      labels:
        app: reviews
        version: v2
    spec:
      serviceAccountName: bookinfo-reviews
      containers:
      - name: reviews
        image: docker.io/istio/examples-bookinfo-reviews-v2:1.17.0
        imagePullPolicy: IfNotPresent
        env:
        - name: LOG_DIR
          value: "/tmp/logs"
        ports:
        - containerPort: 9080
        volumeMounts:
        - name: tmp
          mountPath: /tmp
        - name: wlp-output
          mountPath: /opt/ibm/wlp/output
        securityContext:
          runAsUser: 1000
      volumes:
      - name: wlp-output
        emptyDir: {}
      - name: tmp
        emptyDir: {}

reviews_v3_deploy.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: reviews-v3
  labels:
    app: reviews
    version: v3
spec:
  replicas: 1
  selector:
    matchLabels:
      app: reviews
      version: v3
  template:
    metadata:
      labels:
        app: reviews
        version: v3
    spec:
      serviceAccountName: bookinfo-reviews
      containers:
      - name: reviews
        image: docker.io/istio/examples-bookinfo-reviews-v3:1.17.0
        imagePullPolicy: IfNotPresent
        env:
        - name: LOG_DIR
          value: "/tmp/logs"
        ports:
        - containerPort: 9080
        volumeMounts:
        - name: tmp
          mountPath: /tmp
        - name: wlp-output
          mountPath: /opt/ibm/wlp/output
        securityContext:
          runAsUser: 1000
      volumes:
      - name: wlp-output
        emptyDir: {}
      - name: tmp
        emptyDir: {}

給三個 Deployment 創建一個 Service,三個相同應用的不同版本共有同一個 Service。

reviews_svc.yaml

apiVersion: v1
kind: Service
metadata:
  name: reviews
  labels:
    app: reviews
    service: reviews
spec:
  ports:
  - port: 9080
    name: http
  selector:
    app: reviews

reviews_sa.yaml

apiVersion: v1
kind: ServiceAccount
metadata:
  name: bookinfo-reviews
  labels:
    account: reviews

productpage 應用

頁面聚合服務,供用戶瀏覽書籍信息。

部署命令:

kubectl -n bookinfo apply -f productpage_deploy.yaml
kubectl -n bookinfo apply -f productpage_svc.yaml
kubectl -n bookinfo apply -f productpage_sa.yaml

productpage_deploy.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: productpage-v1
  labels:
    app: productpage
    version: v1
spec:
  replicas: 1
  selector:
    matchLabels:
      app: productpage
      version: v1
  template:
    metadata:
      labels:
        app: productpage
        version: v1
    spec:
      serviceAccountName: bookinfo-productpage
      containers:
      - name: productpage
        image: docker.io/istio/examples-bookinfo-productpage-v1:1.17.0
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 9080
        volumeMounts:
        - name: tmp
          mountPath: /tmp
        securityContext:
          runAsUser: 1000
      volumes:
      - name: tmp
        emptyDir: {}

productpage_svc.yaml

apiVersion: v1
kind: Service
metadata:
  name: productpage
  labels:
    app: productpage
    service: productpage
spec:
  ports:
  - port: 9080
    name: http
  selector:
    app: productpage

productpage_sa.yaml

apiVersion: v1
kind: ServiceAccount
metadata:
  name: bookinfo-productpage
  labels:
    account: productpage

檢查

執行命令完成後,查看 bookinfo 命名空間下的 Pod。

kubectl get pods -n bookinfo

image-20230503094126330

可以看到,每個 Pod 的 READY 屬性都是 2/2 ,這表示該 Pod 中有兩個容器,並且當前有兩個容器已經就緒。

如果我們查看其中一個 Pod 的組成結構,會發現有 Pod 被塞進了一個 istio-proxy 容器。

image-20230503094048232

如果 Kubernetes 中沒有安裝 Dashbooard ,那麼可以使用 kubectl -n bookinfo describe pod {Pod ID} 查看組成結構。

接着使用 kubectl -n bookinfo get svc 查看 Service,四個微服務都已經被註冊了 Service。

image-20230503094624702

然後我們訪問 productpage 對應的 CLUSTER-IP:

curl 10.233.37.130:9080

默認 Istio 不會開啓零信任雙向認證模式,因此在集羣內可以自己訪問應用。如果開啓了 mTLS 雙向認證模式,則只能在 Pod 中訪問應用。

可以看到返回了一堆 html,說明我們的部署是正常的。

臨時訪問

接着爲了查看頁面效果,我們在暫未使用 Istio-ingressgateway 之前,臨時創建一個 Service 暴露 productpage 的頁面。

productpage_tmpsvc.yaml

apiVersion: v1
kind: Service
metadata:
  name: productpagetmp
  labels:
    app: productpage
    service: productpage
spec:
  ports:
  - port: 9080
    name: http
  selector:
    app: productpage
  type: NodePort
kubectl -n bookinfo apply -f  productpage_tmpsvc.yaml

查看所有 Service:

root@k8smain:/data/learn/book# kubectl -n bookinfo get svc
NAME             TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
details          ClusterIP   10.233.63.247   <none>        9080/TCP         40m
productpage      ClusterIP   10.233.37.130   <none>        9080/TCP         23m
productpagetmp   NodePort    10.233.47.14    <none>        9080:30258/TCP   77s
ratings          ClusterIP   10.233.7.6      <none>        9080/TCP         36m
reviews          ClusterIP   10.233.58.219   <none>        9080/TCP         23m

然後在頁面中訪問 30258 端口(大家的端口不一樣,按自己的來)。

image-20230503100005259

接着打開 http://192.168.3.150:30258/productpage?u=normal

因爲當前使用 Service 綁定 Pod,因此會使用輪詢實現負載均衡,你可以多次刷新 http://192.168.3.150:30258/productpage?u=normal,會查到右側的評分星星有所變化。

Istio 默認情況下使用輪詢負載均衡的方法。

頁面右側評論顯示規則是 無星星 => 黑色星星 => 紅色星星。

productpagetmp

部署入口網關

什麼是 Gateway

終於來到體驗 Istio 的時刻了,在本小節中,我們將會爲 productpage 創建 Istio Gateway,對外提供網頁訪問。

在第二章中,我們已經部署了 istio-ingressgateway,這個組件起到了類似 nginx、apisix 的效果,對外提供端口訪問,然後將流量轉發到內部服務中。

但是 istio-ingressgateway 並不能直接轉發流量到 Pod,它還需要進行一些配置。我們要爲 productpage 創建一個站點,綁定對應的域名,這樣外部訪問 istio-ingressgateway 的端口時,istio-ingressgateway 才知道該將流量轉發給誰。在 Istio 中,定義這種綁定關係的資源叫 Gateway

後面的章節會解釋清楚,這裏大概瞭解即可。

image-20230523105731802

Gateway 類似 Nginx 需要創建一個反向代理時需要綁定的域名配置。

部署 Gateway

創建一個 Gateway,綁定域名入口。

ingress_gateway.yaml

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

hosts 表示對外開放的訪問路徑,你可以綁定域名、IP 等。這裏使用 * ,表示所有訪問都可以進入此網關。

kubectl -n bookinfo apply -f ingress_gateway.yaml

這一步就像 nginx 的監聽配置:

server {
    listen      80;
    server_name example.org www.example.org;
    #...
}

當我們創建 Istio Gateway 之後,istio-ingressgateway 會爲我們監控流量,檢測不同的域名或端口屬於哪個 Istio Gateway 。

image-20230507191326199

部署 VirtualService

什麼是 VirtualService

雖然創建了 Istio Gateway,但是我們還不能直接通過網關訪問到前面部署的微服務,我們還需要創建 Istio VirtualService 將 Istio Gateway 跟對應的 Kubernetes Service 綁定起來,然後流量才能正式流向 Pod。

image-20230523111048859

請一定要注意這裏,流量實際並不會經過 Service 中,但是 VirtualService 需要通過 Service 來發現 Pod。

這裏類似 nginx 配置反向代理,配置監聽之後,還需要指向將請求映射到哪個地址。

server {
    listen      80;
    server_name example.org www.example.org;
    #...
}

location /some/path/ {
    proxy_pass http://A:9080;
}

爲什麼不直接將 Gateway 跟 Service 綁定,而是中間加個 VirtualService 呢?有句話叫做,計算機領域中的問題,都可以通過增加一個層來解決。

VirtualService 的主要目標是爲服務提供穩定的入口地址,並通過配置一系列的路由規則來控制流量在網格內的行爲。

就以最簡單的路由區配來說,Kubernetes Service 是不支持路由規則的,而 Istio 可以通過指定路由後綴中;Service 不支持流量分析,負載均衡只有輪詢。而 Istio 利用 Service 來發現 Pod,然後直接將流量轉發到 Pod 中,可以實現各種功能。

VirtualService 可以用於實現以下功能:

請求路由:將請求路由到特定的服務或版本,例如將請求分發到不同版本的服務,以實現灰度發佈或金絲雀發佈。

請求重試:爲失敗的請求配置重試策略,以提高服務的可用性。

請求超時:設置請求的超時時間,以便在特定時間內沒有得到響應時中斷請求。

請求鏡像:將請求的副本發送到另一個服務,用於測試新版本的服務,而不影響實際的生產流量。

流量分割:將流量按照特定的比例分發到不同的服務或版本,以實現流量控制。

部署 VirtualService

productpage_vs.yaml

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: bookinfo
spec:
  hosts:
  - "*"
  gateways:
  - bookinfo-gateway
  http:
  - match:
    - uri:
        exact: /productpage
    - uri:
        prefix: /static
    - uri:
        exact: /login
    - uri:
        exact: /logout
    - uri:
        prefix: /api/v1/products
    route:
    - destination:
        host: productpage
        port:
          number: 9080
kubectl -n bookinfo apply -f productpage_vs.yaml

關於 VistualService 中各種配置的作用,在 4.1 章節中會有介紹。

這裏的 YAML 分爲兩大部分,第一部分是 http.match,表示暴露了哪些 API 地址,外部訪問時只能訪問到這些地址。

image-20230507193102866

可以通過 http.match 限制集羣外部訪問此地址時所能使用的 URL。

然後通過 http.route 綁定 Kubernetes Service ,通過 Service 中的服務發現,將流量轉發到對應的 Pod 中。

image-20230507193219882

host 這裏,由於 VirtualService 跟 Service/Pod 在同一個命名空間中,所以只需要配置 Service 的名稱即可,如果要跨命名空間訪問,則需要加上完整的命名空間名稱。

什麼是 DestinationRule

在本章中,會提前預告 DestinationRule,下一章纔會使用 DestinationRule,這裏我們知道還有 DestinationRule 這個東西即可。

Istio VistualService 中可以限制外部能夠訪問的路由地址,而 DestinationRule 則可以配置訪問的 Pod 策略。可以爲 Istio VistualService 綁定一個 Istio DestinationRule,通過 DestinationRule 我們還可以定義版本子集等,通過更加豐富的策略轉發流量。

image-20230507192751780

由於只暴露了五個地址,所以外部直接訪問 / ,是打不開頁面的。

檢查

爲了確保網關沒問題,我們需要執行 Istio 命令查看日誌:

istioctl analyze

然後我們查看爲 productpage 創建的網關。

root@k8smain:/data/learn/book# kubectl get gw -A
NAMESPACE   NAME               AGE
bookinfo    bookinfo-gateway   26m

Kubernetes 本身也有一個 Gateway,因此不能使用 kubectl get gateway 來獲取 Istio 的 Gateway,而是使用簡寫 gw

然後查看 VistualService。

root@k8smain:/data/learn/book# kubectl get vs -A
NAMESPACE   NAME       GATEWAYS               HOSTS   AGE
bookinfo    bookinfo   ["bookinfo-gateway"]   ["*"]   79m

在第二章中,我們通過 Helm 部署了 istio-ingressgateway,其訪問端口如下:

image-20230503122226493

在本節部署 bookinfo-gateway 的時候,我們使用了端口 80,因此不需要另外配置 ,直接通過 istio-ingressgateway 的 32309 端口訪問即可。

訪問時一定需要帶 /productpage ,因爲我們並沒有放通 /

image-20230503122808034

嘗試修改 Gateway 端口

如果需要更換端口,可以修改 istio-ingressgateway 的 Service,增加新的端口映射。

kubectl edit svc istio-ingressgateway -n istio-system  

1683088306346

然後修改前面的 ingress_gateway.yaml,將端口從 80 改成 666 。

1683088377651

即可通過 32666 端口訪問到此微服務。

示例:http://192.168.3.150:30666/productpage

image-20230503123418870

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章