Istio 入門(七):出入口閘道器 - 負載均衡和熔斷等一系列功能

痴者工良發表於2023-10-26

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

5,出入口閘道器

Istio 可以管理叢集的出入口流量,當客戶端訪問叢集內的應用時, Istio 可以將經過 istio-ingressgateway 的流量實現負載均衡和熔斷等一系列功能。

可是,如果叢集內的一個應用要訪問 google.com ,那麼我們可以給內部所有請求了 google.com 的流量設定負載均衡嗎?答案是可以,Istio 提供了 istio-egressgateway 實現這種功能。因為 Pod 中的容器要訪問網路時,會被 Envoy 攔截,Envoy 可以很容易地分析這些請求,然後透過一系列手段影響著請求的行為。

在本章中,將會簡單說一下 istio-ingressgateway 和 istio-egressgateway。

istio-ingressgateway

入口閘道器指的是從外部經過 istio-ingressgateway 流入叢集的流量,需要建立 Gateway 繫結流量。

關於 istio-ingressgateway 經過前面幾章的學習,大家應該不陌生了。

istio-ingressgateway 由 Pod 和 Service 組成。 istio-ingressgateway 本身就是一個閘道器應用,你可以把它當作 Nginx、Apisix、Kong ,你可以從各種各種閘道器應用中找到與 istio-ingressgateway 類似的概念。

image-20230526194223740

image-20230526194154234

作為一個應用,它需要對外開放一些埠,只有當流量經過這些埠時, istio-ingressgateway 才會起作用。為了在 Kubernetes 中暴露埠, istio-ingressgateway 還有一個 Service 物件。

image-20230526194139225

有了 istio-ingressgateway 之後,我們可以透過 Istio Gateway 監控一些域名或IP,然後暴露叢集內部的服務 。

Gateway 的概念跟 Nginx 有很多相似之處。

比如從配置上看, Gateway 跟 Nginx 如果要監控某個入口流量,它們的配置如下:

Nginx:

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

Gateway:

  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
    - example.org
    - www.example.org

這些配置指定了 Gateway 和 Nginx 只監控哪些流量。

緊接著,監控到指定入口的流量之後,需要將流量轉發到叢集內的應用中。

Nginx 可以直接在同一個配置檔案裡面設定:

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

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

而 Gateway 需要使用 VirtualService 指定流量轉發到哪裡,並且 VirtualService 還可以進一步篩選入口地址。

spec:
  hosts:
  - "www.example.org"
  gateways:
  # 繫結 Gateway
  - mygateway
  http:
    route:
    - destination:
        host: bookinfo
        port:
          number: 9080

所以總結起來,Istio 的做法是 Gateway 監控入口流量,透過 VirtualService 設定流量進入的策略,並指向 Service。而 DestinationRule 則定義了流量流向 Pod 的策略

部署服務

下面我們將使用 httpbin 服務作為示例,如何一步步配置在外部訪問 httpbin 服務。

首先部署一個 httpbin 服務,這個 httpbin 服務很簡單,包含了 Service 和 Deployment 。

httpbin.yaml

apiVersion: v1
kind: ServiceAccount
metadata:
  name: httpbin
---
apiVersion: v1
kind: Service
metadata:
  name: httpbin
  labels:
    app: httpbin
    service: httpbin
spec:
  ports:
  - name: http
    port: 8000
    targetPort: 80
  selector:
    app: httpbin
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: httpbin
spec:
  replicas: 1
  selector:
    matchLabels:
      app: httpbin
      version: v1
  template:
    metadata:
      labels:
        app: httpbin
        version: v1
    spec:
      serviceAccountName: httpbin
      containers:
      - image: docker.io/kennethreitz/httpbin
        imagePullPolicy: IfNotPresent
        name: httpbin
        ports:
        - containerPort: 80
kubectl -n bookinfo apply -f httpbin.yaml

配置 Gateway

然後建立一個 Gateway ,指定監聽哪些入口流量。

httpbin_gw.yaml

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: httpbin-gateway
spec:
  selector:
    istio: ingressgateway # use Istio default gateway implementation
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
    - "httpbin.s1.whuanle.cn"
    - "*"

這一步為了大家能夠透過域名更加直觀地瞭解 Gateway,大家可以修改 httpbin.s1.whuanle.cn 替換為自己的域名。

然後在自己的電腦中開啟 C:\Windows\System32\drivers\etc\hosts 增加一條記錄 ,將 IP 指向自己的伺服器。

image-20230515193213923

kubectl -n bookinfo apply -f httpbin_gw.yaml

現在,我們已經讓 istio-ingressgateway 幫我們關注 httpbin.s1.whuanle.cn 這個地址,如果有人訪問了 httpbin.s1.whuanle.cn,那麼這個流量將會流入到 httpbin-gateway。

image-20230515190518804

接下來我們將要為 Gateway 配置服務地址,並配置外部允許訪問的地址字尾。

配置 VistualService:

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: httpbin
spec:
  hosts:
  - "*"
  gateways:
  - httpbin-gateway
  http:
  - match: 
    - uri:
        prefix: /status
    - uri:
        prefix: /delay 
    route:
    - destination:
        port:
          number: 8000
        host: httpbin

當 Gateway 和 VirtualService 埠只有一個時,不需要配置埠繫結。

kubectl -n bookinfo apply -f httpbin_vs.yaml

找到 istio-ingressgateway 對外暴露的埠。

kubectl get svc istio-ingressgateway  -n istio-system

1683287785674

httpbin 是一個 http 測試程式,我們可以透過使用 /status/{狀態碼} 獲取對應的 http 請求狀態。

例如:

image-20230505200437890

image-20230505200444999

image-20230515193314909image-20230515193301641

如果我們不希望這個服務被外界訪問到,我們可以先把 /status 刪除。

kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: httpbin
spec:
  hosts:
  - "*"
  gateways:
  - httpbin-gateway
  http:
  - match:
    - uri:
        prefix: /delay
    route:
    - destination:
        port:
          number: 8000
        host: httpbin
EOF

此時你將無法訪問 status 路徑。但是我們還可以訪問 /delay 路徑。

httpbin 的 /delay 路徑用於測試延遲 http 請求響應使用,/delay/{秒數} 可以指定伺服器在多久之後才會返回響應。

例如 http://192.168.3.150:32309/delay/5 將在 5 秒後響應。

httpbin 還有很多路由介面,我們可以透過 VirtualService 配置放通哪些路徑。

image-20230505201156220

如果需要全部放通,可以使用:

kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: httpbin
spec:
  hosts:
  - "*"
  gateways:
  - httpbin-gateway
  http:
  - match:
    - uri:
        prefix: /
    route:
    - destination:
        port:
          number: 8000
        host: httpbin
        subset: v1
EOF

子版本

第四章中進行版本路由實驗時使用到,可以將流量匯入到不同的版本之中。

kubectl -n bookinfo apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: httpbin
spec:
  host: httpbin
  subsets:
  - name: v1
    labels:
      version: v1
EOF

首先是使用 DestinationRule 指向一個 Service:

  host: httpbin

當然,我們也可以寫成

  host: httpbin.bookinfo.svc.cluster.local

透過 host 可以識別到對應的 Kubernetes Service,然後從 Service 對應的 Endpoints 中獲得所有 Pod 列表。

image-20230515194105645

1684151025350

透過 Endpoints 獲得所有 Pod 之後,檢視每個 Pod 的描述資訊。當有一個請求到達時,根據 DestinationRule 中的標籤選擇器,選擇合適的 Pod 進行訪問。

  - name: v1
    labels:
      version: v1

1684151265603

istio-egressgateway

istio-egressgateway 也是 Istio 中的一種元件,需要自行安裝。安裝 istio-egressgateway 命令:

helm install istio-egressgateway istio/gateway -n istio-system

在叢集中,如果 A 應用訪問的地址屬於叢集中的應用,那麼 Istio 可以給這些請求注入各種行為,實現負載均衡和熔斷等。

可是,如果叢集內部要訪問外部的一個服務時,需要配置訪問地址,如 aaa.com,我們應該如何實現負載均衡和熔斷這些功能呢?

image-20230515195151940

Istio ServiceEntry 是一種資源,允許將外部服務(即不在 Istio 服務網格中的服務)納入Istio服務網格。透過將外部服務新增到網格,可以使用 Istio 的流量管理和策略功能來控制與這些外部服務的互動。

以下是一個ServiceEntry示例,將外部HTTP服務 www.google.com新增到Istio服務網格:

apiVersion: networking.istio.io/v1alpha3  
kind: ServiceEntry  
metadata:  
  name: google
spec:  
  hosts:  
  - www.google.com  
  addresses:  
  - 192.168.1.1  
  ports:  
  - number: 80  
    name: http  
    protocol: HTTP  
  location: MESH_EXTERNAL  
  resolution: DNS  
  endpoints:  
  - address: "www.google.com"  
    ports:  
      http: 80  
    locality: "us-west1/zone1"  
  exportTo:  
  - "*"  

在此示例中,我們建立了一個名為httpbin-ext的ServiceEntry資源。指定的主機為httpbin.org,埠號為80,協議為HTTP。此外,我們將resolution設定為DNS,將location設定為MESH_EXTERNAL,表示該服務位於網格之外。

要將此ServiceEntry應用到叢集,請將其儲存到一個YAML檔案(例如:httpbin-ext.yaml),然後執行以下命令:

kubectl apply -f httpbin-ext.yaml  

現在,Istio 服務網格中的服務訪問 www.google.com 時仍受Istio策略的控制。例如,可以為此 ServiceEntry 建立 VirtualService 以應用流量管理規則,或者為其建立 DestinationRule 以配置負載均衡和連線池設定。

spec: 包含ServiceEntry的具體配置的物件。

  • hosts: 一個包含要匯入的外部服務的主機名(FQDN)的列表。例如:["httpbin.org"]
  • addresses: (可選)與外部服務關聯的虛擬IP地址的列表。例如:["192.168.1.1"]
  • ports: 一個描述外部服務使用的埠的列表。每個埠都有以下屬性:
    • number: 埠號,例如:80。
    • name: 埠的名稱,例如:http
    • protocol: 使用的協議,例如:HTTPTCPHTTPS等。
  • location: 服務的位置。可以是MESH_EXTERNAL(表示服務在網格外部)或MESH_INTERNAL(表示服務在網格內部,但不屬於任何已知服務)。
  • resolution: 用於確定服務例項地址的解析方法。可以是NONE(預設值,表示不解析地址),STATIC(表示使用addresses欄位中的IP地址),DNS(表示使用DNS解析主機名)或MESH_EXTERNAL
  • endpoints: (可選)外部服務的端點列表。每個端點都有以下屬性:
    • address: 端點的IP地址或主機名。
    • ports: 一個包含埠名稱和埠號的對映,例如:{"http": 8080}
    • labels: (可選)應用於端點的標籤。
    • locality: (可選)端點的地理位置,例如:us-west1/zone1
  • exportTo: (可選)一個包含名稱空間名稱的列表,指定可以訪問此ServiceEntry的名稱空間。可以使用星號(*)表示所有名稱空間。預設值為*
  • subjectAltNames: (可選)用於驗證伺服器證書主題替代名(SANs)的列表。

讀者可以從官方文件中瞭解更多:

https://istio.io/latest/zh/docs/tasks/traffic-management/egress/egress-control/

相關文章