Istio入門:佈署應用程式與混亂工程實驗

在深入研究混亂工程實驗之前,我們需要先佈署應用程式,並將其整合到Istio服務網格中。這次的應用程式會比之前的範例稍微複雜一些,因為我們需要加入一些Istio的設定,以便進行網路相關的混亂實驗。

如果您已經熟悉Istio,可以跳過一些基礎說明。但如果您對虛擬服務(Virtual Services)、閘道器(Gateways)等Istio資源不太瞭解,我強烈建議您參考一些線上資源或書籍,例如我自己撰寫的線上教學課程,以更深入地理解這些概念。

佈署示範應用程式

首先,我們需要佈署示範應用程式。這個應用程式與之前的章節類別似,但這次我們不會使用nginx Ingress,而是使用Istio Gateway。

cd go-demo-8
git pull
kubectl create namespace go-demo-8
kubectl label namespace go-demo-8 istio-injection=enabled
kubectl --namespace go-demo-8 apply --filename k8s/health/app/
kubectl --namespace go-demo-8 rollout status deployment go-demo-8

這些指令會建立一個名為go-demo-8的名稱空間,並啟用Istio的自動注入功能。如此一來,Istio會自動在每個Pod中加入一個proxy sidecar容器,用於管理網路流量。

檢查一下Pod的狀態:

kubectl --namespace go-demo-8 get pods

您會看到每個Pod都執行了兩個容器,其中一個是應用程式容器,另一個是由Istio注入的proxy sidecar容器。

設定Istio資源

接下來,我們需要建立一些Istio資源,讓Istio能夠管理應用程式的網路流量。

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: go-demo-8
spec:
  hosts:
  - go-demo-8
  http:
  - route:
  - destination:
    host: go-demo-8
    subset: primary
    port:
      number: 80

---

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: go-demo-8
spec:
  host: go-demo-8
  subsets:
  - name: primary
    labels:
      release: primary

這段YAML定義了一個虛擬服務和一個目標規則。虛擬服務go-demo-8會將內部流量導向我們的應用程式。目標規則go-demo-8則定義了主要子集primary,它會將所有請求轉發到帶有標籤release: primarygo-demo-8 Pod。

套用這些設定:

kubectl --namespace go-demo-8 apply --filename k8s/network/istio.yaml

佈署Repeater應用程式

為了進行網路混亂實驗,我們還需要佈署一個名為repeater的輔助應用程式。這個應用程式非常簡單,它的功能是將接收到的請求轉發到指定的地址。透過這個應用程式,我們可以模擬真實世界的網路環境,並觀察當網路出現問題時,應用程式會如何反應。

kubectl --namespace go-demo-8 apply -f k8s/network/repeater/

這個指令會佈署repeater應用程式及其相關的Istio資源。repeater的佈署包含Deployment、HorizontalPodAutoscaler、VirtualService、DestinationRule、Gateway和Service等資源。

Mermaid 圖表 - 應用程式架構

  graph LR
    client[client]
    go-demo-8[go-demo-8]
    go-demo-8-db[go-demo-8-db]
    repeater[repeater]
    client --> repeater
    repeater --> go-demo-8
    go-demo-8 --> go-demo-8-db
    subgraph Istio Service Mesh
        go-demo-8
        go-demo-8-db
        repeater
    end

內容解密: 此圖表展示了clientrepeatergo-demo-8go-demo-8-db之間的關係,以及它們如何在Istio服務網格中運作。client的請求會先送到repeater,然後repeater將請求轉發到go-demo-8服務,最後go-demo-8服務與資料函式庫go-demo-8-db互動。

現在我們已經佈署了示範應用程式和repeater,並設定好了Istio。接下來,我們就可以開始進行混亂工程實驗了。

總結:這篇文章詳細介紹瞭如何在Istio服務網格中佈署應用程式,並準備進行混亂工程實驗。我們佈署了示範應用程式go-demo-8和輔助應用程式repeater,並設定了必要的Istio資源,例如虛擬服務和目標規則。透過這些準備工作,我們可以更有效地進行網路混亂實驗,並觀察應用程式在不同網路狀況下的行為。

模擬網路中斷:應用程式容錯測試

網路問題是現代應用程式常見的挑戰。本文將探討如何模擬網路請求中斷,並觀察應用程式在這種情況下的行為。我們將使用 Istio 服務網格和 Chaos Toolkit 來模擬 50% 的請求中斷,並驗證應用程式的容錯能力。

架構說明

我們的測試環境包含一個名為 go-demo-8 的應用程式、其關聯的資料函式庫,以及一個新的應用程式 repeaterrepeater 的作用是將請求轉發到指定的地址。我們將透過向 repeater 傳送請求,並觀察 go-demo-8 的回應來進行測試。

  graph LR
    client[client]
    database[database]
    go-demo-8[go-demo-8]
    repeater[repeater]
    client --> repeater
    repeater --> go-demo-8
    go-demo-8 --> database

Chaos Toolkit 實驗設計

我們使用 Chaos Toolkit 定義了一個實驗,用於模擬網路請求中斷。實驗的定義如下:

version: 1.0.0
title: "模擬網路請求中斷"
description: 測試應用程式在網路請求中斷時的容錯能力
tags:
  - k8s
  - istio
  - http
configuration:
  ingress_host:
    type: env
    key: INGRESS_HOST
steady-state-hypothesis:
  title: 應用程式正常執行
  probes:
    - type: probe
      name: app-responds-to-requests
      tolerance: 200
      provider:
        type: http
        timeout: 5
        verify_tls: false
        url: http://${ingress_host}?addr=http://go-demo-8
        headers:
          Host: repeater.acme.com
    - type: probe
      tolerance: 200
      ref: app-responds-to-requests
    - type: probe
      tolerance: 200
      ref: app-responds-to-requests
    - type: probe
      tolerance: 200
      ref: app-responds-to-requests
    - type: probe
      tolerance: 200
      ref: app-responds-to-requests
method:
  - type: action
    name: abort-failure
    provider:
      type: python
      module: chaosistio.fault.actions
      func: add_abort_fault
    arguments:
      virtual_service_name: go-demo-8
      http_status: 500
      routes:
        - destination:
            host: go-demo-8
            subset: primary
      percentage: 50
      version: networking.istio.io/v1alpha3
      ns: go-demo-8
pauses:
  after: 1

內容解密:

這個實驗定義了一個名為 abort-failure 的動作,使用 chaosistio.fault.actions 模組中的 add_abort_fault 函式。該動作會向 go-demo-8 虛擬服務注入 HTTP 500 錯誤,中斷 50% 的請求。實驗還定義了一個穩定狀態假設,透過向 repeater 傳送五次請求並檢查回應碼是否為 200 來驗證應用程式的健康狀態。

實驗預測

由於我們中斷了 50% 的請求,預計並非所有五次請求都能成功傳回 200 狀態碼。這將觸發應用程式的重試或超時機制。透過觀察應用程式的行為,我們可以評估其在網路中斷情況下的容錯能力。

chaostoolkit-istio 模組探索

為了執行 Istio 相關的混沌實驗,我們需要安裝 chaostoolkit-istio 模組。

pip install -U chaostoolkit-istio

透過 chaos discover 命令,我們可以探索該模組提供的功能。

chaos discover chaostoolkit-istio
cat discovery.json

discovery.json 檔案包含了 chaostoolkit-istio 模組提供的各種功能,例如注入故障、延遲等。

這個實驗模擬了真實世界中常見的網路中斷場景,幫助我們評估應用程式的容錯能力。透過調整中斷百分比和觀察應用程式的行為,我們可以更好地理解應用程式在不同網路條件下的效能表現,並針對性地進行最佳化。

模擬網路中斷:Istio 應用與混沌工程實踐

在建構現代分散式系統時,網路中斷是無可避免的挑戰。理解系統在網路故障下的行為至關重要。我將透過 Istio 和混沌工程工具 Chaos Toolkit,模擬網路中斷場景,並探討如何有效地驗證系統的穩固性。

實驗設計:模擬網路請求中斷

本次實驗目標是模擬應用程式對網路請求中斷的反應。我使用 chaos/network-rollback.yaml 檔案定義混沌實驗,其中包含探針 (Probe)、行動 (Action) 和回復 (Rollback) 設定。

# chaos/network-rollback.yaml
steady-state-hypothesis:
  title: The app is healthy
  probes:
    - name: app-responds-to-requests
      type: probe
      provider:
        type: http
        url: http://$INGRESS_HOST?addr=http://go-demo-8
        headers:
          Host: repeater.acme.com
        timeout: 5000
method:
  - type: action
    name: abort-failure
    provider:
      type: python
      module: chaosistio.fault.actions
      func: add_abort_fault
      arguments:
        virtual_service_name: go-demo-8
        routes:
          - destination:
              host: go-demo-8
              subset: primary
        abort_http_status: 500
        abort_percentage: 50
        version: networking.istio.io/v1alpha3
        ns: go-demo-8
rollbacks:
  - type: action
    name: remove-abort-failure
    provider:
      type: python
      func: remove_abort_fault
      module: chaosistio.fault.actions
      arguments:
        virtual_service_name: go-demo-8
        routes:
          - destination:
              host: go-demo-8
              subset: primary
        version: networking.istio.io/v1alpha3
        ns: go-demo-8

內容解密:

這個 YAML 檔案定義了一個混沌實驗,用於測試應用程式在網路中斷時的反應。steady-state-hypothesis 部分定義了系統的穩定狀態,透過 HTTP 探針 app-responds-to-requests 檢查應用程式是否正常回應請求。method 部分定義了實驗的行動,使用 chaosistio.fault.actions 模組的 add_abort_fault 函式向 Istio Virtual Service 中注入錯誤,模擬 50% 的請求中斷。rollbacks 部分定義了實驗後的回復操作,使用 remove_abort_fault 函式移除先前注入的錯誤,還原 Virtual Service 的原始設定。

執行實驗與結果分析

執行 chaos run chaos/network-rollback.yaml 命令後,Chaos Toolkit 會依據設定執行實驗。首先,它會執行探針確認應用程式處於穩定狀態。接著,注入網路中斷錯誤,再次執行探針觀察應用程式的反應。最後,執行回復操作移除注入的錯誤。

先前實驗中,注入錯誤後沒有回復機制,導致 Virtual Service 的設定永久改變。觀察到的現象是,大約 50% 的請求會傳回 fault filter abort 錯誤。這凸顯了回復機制的重要性,確保實驗不會對系統造成持續性影響。

加入回復機制後,實驗結束後 Virtual Service 會還原到原始狀態,所有請求都能正常處理。透過 kubectl describe virtualservice 命令可以確認 Virtual Service 的設定已還原。

流程圖:混沌實驗流程

  graph LR
    E[E]
    A[啟動實驗] --> B{穩定狀態檢查};
    B -- 穩定 --> C[注入網路錯誤];
    B -- 不穩定 --> D[實驗失敗];
    C --> E{穩定狀態檢查};
    E -- 穩定 --> F[回復操作];
    E -- 不穩定 --> G[實驗失敗];
    F --> H[實驗成功];
    G --> I[實驗失敗];

內容解密:

此流程圖描述了混沌實驗的執行流程。首先,實驗啟動後會檢查系統是否處於穩定狀態。如果系統穩定,則注入網路錯誤,並再次檢查系統穩定性。如果系統仍然穩定,則執行回復操作,實驗成功結束。如果任何一次穩定狀態檢查失敗,則實驗失敗。

架構圖:Istio 錯誤注入

  graph LR
    C[C]
    A[Client] --> B[Istio Gateway];
    B --> C{Virtual Service};
    C -- 正常 --> D[Application];
    C -- 錯誤注入 --> E[Fault Injection];
    E --> F[500 Error];

內容解密:

此架構圖展示了 Istio 如何注入錯誤。Client 的請求會先到達 Istio Gateway,然後根據 Virtual Service 的設定路由到應用程式。透過在 Virtual Service 中設定錯誤注入規則,可以將一部分請求導向 Fault Injection 模組,傳回指定的錯誤,例如 500 錯誤。

透過這次實驗,我驗證了應用程式在網路中斷下的行為,並學習瞭如何使用 Istio 和 Chaos Toolkit 進行混沌工程實踐。更重要的是,我體會到回復機制的重要性,它能確保實驗在受控環境下進行,避免對生產系統造成意外影響。混沌工程的價值在於幫助我們及早發現系統的弱點,提升系統的韌性,從而更好地應對真實世界的各種挑戰。

模擬網路延遲對應用程式造成的影響

在先前的章節中,我們探討瞭如何應對網路故障,更精確地說,我們模擬了網路故障的一種可能情況,並提出了一種解決方案來應對其產生的負面影響。但實際情況並非總是如此簡單。有時網路並不會完全中斷,請求也不會立即傳回 500 錯誤程式碼,而是會出現延遲。我們的應用程式可能需要等待數毫秒、數秒甚至更長時間才能收到回應。我們應該如何處理這種情況?

讓我們來看看如果我們在請求中引入延遲,或者更準確地說,在請求的回應中引入延遲,會發生什麼。以目前應用程式的狀態,它是否有能力處理這種情況?

為了模擬網路延遲,我們可以使用 chaos 命令搭配新的 YAML 設定檔。這個設定檔與之前的設定檔非常相似,主要區別在於我們將使用 delay 動作而不是 abort 動作。

apiVersion: litmuschaos.io/v1alpha1
kind: ChaosEngine
metadata:
  name: network-delay
spec:
  engineState: "active"
  chaosServiceAccount: litmus-admin
  experiments:
  - name: network-delay
    spec:
      probe:
      - name: app-responds-to-requests
        type: httpProbe
        httpProbe/inputs:
          url: "http://$INGRESS_HOST?addr=http://go-demo-8"
          method: GET
          criteria: "=="
          responseCode: "200"
      - name: app-responds-to-requests
        type: httpProbe
        httpProbe/inputs:
          url: "http://$INGRESS_HOST?addr=http://go-demo-8"
          method: GET
          criteria: "=="
          responseCode: "200"
      mode: Step
      steadyStateHypothesis:
        probes:
        - app-responds-to-requests
      experiment:
        duration: 1m
        chaosDuration: 30s
        fault:
          name: delay
          delay/inputs:
            percent: 100
            duration: "7s"
            corrlationID: ""
            hostname: repeater.acme.com
            port: "80"

內容解密:

這個 YAML 檔案定義了一個名為 network-delay 的混沌實驗。它會對 repeater.acme.com 的 80 連線埠的所有流量(percent: 100)引入 7 秒的延遲(duration: "7s")。實驗持續時間為 1 分鐘(duration: 1m),其中混沌注入持續 30 秒(chaosDuration: 30s)。實驗使用 HTTP 探針來驗證應用程式在延遲下的回應情況。

接下來,我們將執行這個實驗,並觀察應用程式的行為。

chaos run chaos/network-delay.yaml

執行此命令後,觀察實驗的輸出結果,特別注意探針的狀態。如果探針持續失敗,則表示應用程式無法處理引入的延遲。

在下一節中,我們將探討如何增強應用程式的彈性,使其能夠應對網路延遲。我們將使用 Istio 的功能來實作這一目標,而無需修改應用程式的程式碼。

(Mermaid 圖表)

  graph LR
    B[B]
    A[Client Request] --> B{Istio Ingress Gateway}
    B --> C[Envoy Proxy (Delay Injection)]
    C --> D[Application]
    D --> C
    C --> B
    B --> A

內容解密:

此流程圖展示了客戶端請求在引入延遲後的流程。請求首先到達 Istio Ingress Gateway,然後被轉發到 Envoy Proxy。Envoy Proxy 注入延遲後,再將請求轉發到應用程式。應用程式處理請求後,回應會經過 Envoy Proxy 和 Istio Ingress Gateway 傳回給客戶端。

延遲與中斷的網路環境下,應用程式如何應對?

在分散式系統中,網路問題是無可避免的。我時常在處理這類別問題時,發現網路延遲和中斷會對應用程式造成嚴重影響。因此,設計健壯的應用程式時,必須考慮如何應對這些網路異常。

這次,我們將探討更複雜的混沌實驗,結閤中斷和延遲兩種網路故障。我認為這種實驗更貼近真實世界的情況,因為網路故障發生時,延遲和中斷通常會同時出現。

實驗設計:同時模擬中斷和延遲

我們修改了先前的實驗定義,新增了延遲效果。新的實驗定義 chaos/network-delay.yaml 除了保留原有的請求中斷功能外,還加入了延遲機制。

# chaos/network-delay.yaml
title: "中斷與延遲並存下的應用程式反應"
description: 當請求遭遇中斷和延遲時,應用程式應能妥善處理重試和逾時
# ... 其他設定
timeout: 15
# ... 其他設定
- type: action
  name: delay
  provider:
    type: python
    module: chaosistio.fault.actions
    func: add_delay_fault
  arguments:
    # ... 其他設定
    fixed_delay: 15s
    percentage: 50
# ... 其他設定
- type: action
  name: remove-delay
  provider:
    type: python
    # ... 其他設定

此設定中,timeout 設定為 15 秒,fixed_delay 也設定為 15 秒,並套用於 50% 的請求。這表示當請求遇到延遲時,將會等待 15 秒,而我們的探測設定的逾時也是 15 秒。因此,我預期探測會因為延遲超過逾時時間而失敗。

實驗結果分析:應用程式對延遲的脆弱性

執行實驗後,結果顯示應用程式無法應對延遲。部分探測請求因等待時間過長而逾時,導致實驗失敗。

# 實驗輸出片段
[2020-03-13 23:45:50 ERROR] => failed: activity took too long to complete
[2020-03-13 23:45:50 CRITICAL] Steady state probe 'app-responds-to-requests' is not in the given tolerance so failing this experiment

從時間戳記可以看出,失敗的探測與前一次成功探測之間正好相差 15 秒,與設定的延遲時間一致。這證實了應用程式對延遲的脆弱性。

解決方案:調整 Virtual Service 設定

為了提升應用程式對延遲的容錯能力,我們調整了 Virtual Service 的設定,加入了重試機制和連線失敗重試。

# k8s/network/istio-delay.yaml
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
# ... 其他設定
http:
- route:
  - destination:
    # ... 其他設定
retries:
  attempts: 10
  perTryTimeout: 2s
  retryOn: 5xx,connect-failure
timeout: 10s

新的設定中,perTryTimeout 設定為 2 秒,retryOn 新增了 connect-failure,並設定了 timeout 為 10 秒。這表示每次重試的逾時時間為 2 秒,即使遇到 15 秒的延遲,Virtual Service 也只會等待 2 秒後重試,最多重試 10 次。

  graph LR
    2s[2s]
    B[B]
    success[success]
    timeout[timeout]
A[Client] --> B{Virtual Service};
B -- 2s timeout --> C[Retry];
C --> B;
B -- 2s timeout --> C;
C --> B;
B -- success --> D[Server];

上圖展示了 Virtual Service 的重試機制。當請求遇到延遲時,Virtual Service 會在 2 秒後重試,直到請求成功或達到最大重試次數。

透過這樣的設定,應用程式就能夠在一定程度上應對網路延遲,提升系統的穩定性。

內容解密:

以上程式碼片段展示瞭如何利用 Istio Virtual Service 的重試機制來應對網路延遲。perTryTimeout 設定了每次重試的逾時時間,retryOn 指定了觸發重試的條件,attempts 限制了最大重試次數,而 timeout 則設定了整個請求的逾時時間。這些引數的組合使用,可以有效提高應用程式在網路不穩定環境下的容錯能力。

模擬網路中斷與延遲對應用程式可用性的影響

在分散式系統中,網路問題是無可避免的。網路延遲和中斷會嚴重影回應用程式的可用性,因此瞭解系統在這些情況下的行為至關重要。我將透過一系列混沌工程實驗,模擬網路中斷和延遲,並觀察應用程式的反應。

十秒的逾時設定在實際應用中過高,但在簡化實驗的前提下,我們假設應用程式在十秒內必須做出回應,無論期間發生任何延遲或中斷。

首先,我們套用新的 Virtual Service 定義,並重新執行實驗。

kubectl --namespace go-demo-8 apply --filename k8s/network/istio-delay.yaml
chaos run chaos/network-delay.yaml

實驗結果顯示,應用程式成功透過了所有探測,即使在模擬網路延遲和中斷後,應用程式仍然能夠在可接受的時間內回應請求。部分請求的回應時間確實比平常長,但都在設定的逾時時間內完成。這表示我們的應用程式具有一定的容錯能力,能夠應對網路延遲和中斷。實驗結束後,系統自動回復到初始狀態。

模擬完全網路中斷

接下來,我們模擬更嚴重的網路中斷,也就是完全網路中斷。我們修改實驗設定,將中斷百分比設定為 100%。

cat chaos/network-abort-100.yaml

與先前實驗的差異在於:

diff chaos/network-rollback.yaml chaos/network-abort-100.yaml
51c51
< percentage: 50
---
> percentage: 100

我們將中斷百分比從 50% 提高到 100%。執行實驗:

chaos run chaos/network-abort-100.yaml

不出所料,探測失敗。應用程式持續嘗試傳送請求,但在設定的重試次數後仍未收到回應,導致實驗判斷應用程式狀態異常。

這種情況的解決方案通常不在 Kubernetes 或 Istio 層級,而是在應用程式層級。當網路完全中斷時,應用程式本身應該具備處理這種情況的能力。例如,前端應用程式在無法連線後端服務時,應顯示友善的提示訊息,引導使用者瀏覽其他可用的服務。微服務架構的優勢之一就是可以將故障影響範圍縮小。

模擬阻斷服務攻擊 (DoS)

另一種常見的網路問題是阻斷服務攻擊 (DoS)。駭客會透過大量請求癱瘓目標伺服器,使其無法正常服務。

  graph LR
    B[B]
A[攻擊者] --> B{目標伺服器}
A --> B
A --> B
A --> B
A --> B

內容解密: 上圖展示了 DoS 攻擊的基本原理,攻擊者傳送大量請求到目標伺服器,使其過載。

如何應對 DoS 攻擊是一個複雜的議題,涉及多個層面,包含網路架構、應用程式設計和安全策略等。在後續的文章中,我將探討如何利用 Kubernetes 和 Istio 構建更具韌性的應用程式,以抵禦 DoS 攻擊和其他網路威脅。

模擬DoS攻擊:應用程式在高負載下的反應

在高流量壓力下,應用程式或整個叢集都可能面臨極端負載。如果流量過大,基礎設施無法處理,系統當機並非聞所未聞,甚至可說是司空見慣。更精確地說,如果沒有採取一些預防措施,系統很可能會當機。

在模擬這種攻擊之前,讓我們先確認應用程式在處理大量請求時的運作是否正常。我們不會模擬數百萬個請求,因為那超出了本文的範圍。我們將執行一個簡化的DoS攻擊。

首先,我們將測試應用程式,看看如果持續傳送 50 個併發請求 20 秒會發生什麼事。我們將使用一個名為 Siege 的工具,它將作為容器在 Pod 中執行。

kubectl --namespace go-demo-8 run siege \
--image yokogawa/siege \
--generator run-pod/v1 \
-it --rm \
-- --concurrent 50 --time 20S "http://go-demo-8"

我們在 go-demo-8 名稱空間中建立了一個名為 siege 的 Pod。它根據 yokogawa/siege 映像檔。我們使用了 -it 引數(互動式終端),並使用 --rm,以便在 Pod 內的唯一容器程式終止後刪除 Pod。這些都平淡無奇。該命令的重點是我們傳遞給容器中主要程式的引數。--concurrent=50--time 20S 引數告訴 Siege 執行 50 個併發請求 20 秒。最後一個引數是 Siege 應傳送請求的地址。這次我們跳過 repeater,直接將請求傳送到 go-demo-8

可以將該命令和 Siege 視為一種非常簡單的效能測試方法。實際上,它甚至不是測試。我們傳送一系列併發請求(更精確地說是 50 個)持續 20 秒。我想在測試系統面對模擬 DoS 攻擊時的行為之前,先確認應用程式可以處理大量請求。

以下是輸出的相關部分:

...
Transactions: 1676 hits
Availability: 91.94 %
Elapsed time: 19.21 secs
Data transferred: 0.05 MB
Response time: 0.01 secs
Transaction rate: 87.25 trans/sec
Throughput: 0.00 MB/sec
Concurrency: 1.07
Successful transactions: 1676
Failed transactions: 147
Longest transaction: 0.08
Shortest transaction: 0.00
...

20 秒後,加上提取映像檔和執行它所需的時間,siege 結束了。在我的測試中,可以看到執行了 1676 次點選。其中大部分是成功的。在我的測試中,可用性為 91.94%。有一些失敗的交易,因為這不是一個穩健的解決方案。這是一個演示應用程式。一定會有一些失敗,但忽略這一點。重要的是,大多數請求都是成功的。在我的測試中,有 1676 個成功的交易和 147 個失敗的交易。

在繼續之前,讓我們快速瀏覽一下應用程式的程式碼。

不用擔心您是否精通 Go 語言。我們只是要觀察一些對模擬 DoS 攻擊有用的東西。

package main

import (
    ...
    "golang.org/x/time/rate"
    ...
)

...
var limiter = rate.NewLimiter(5, 10)
var limitReachedTime = time.Now().Add(time.Second * (-60))
var limitReached = false
...
func RunServer() {
    ...
    mux.HandleFunc("/limiter", LimiterServer)
    ...
}
...
func LimiterServer(w http.ResponseWriter, req *http.Request) {
    logPrintf("%s request to %s\n", req.Method, req.RequestURI)
    if limiter.Allow() == false {
        logPrintf("Limiter in action")
        http.Error(w, http.StatusText(500), http.StatusTooManyRequests)
        limitReached = true
        limitReachedTime = time.Now()
        return
    } else if time.Since(limitReachedTime).Seconds() < 15 {
        logPrintf("Cooling down after the limiter")
        http.Error(w, http.StatusText(500), http.StatusTooManyRequests)
        return
    }
    msg := fmt.Sprintf("Everything is OK\n")
    io.WriteString(w, msg)
}
...

內容解密:

我們將模擬 DoS 攻擊。為此,應用程式使用一個名為 rate 的 Go 函式庫。此外,我們將 limiter 變數設定為 NewLimiter(5, 10)。這表示它將應用程式限制為只有 5 個請求,突發為 10 個。沒有任何「真正的」應用程式會是這樣的。但是,我們使用它來模擬當請求數量超過應用程式可以處理的限制時會發生什麼。任何應用程式總是有極限的,我們只是強制這個極限非常低。

我們的應用程式應該自動向上和向下擴充套件。但是,如果請求數量突然急劇增加,可能會產生一些負面影響。應用程式可能無法快速擴充套件。

然後,我們有 LimiterServer 函式,它處理來自 /limiter 端點的請求。它檢查是否達到 5 個請求的限制,如果達到,則傳送 500 回應程式碼。此外,還有一個額外的邏輯,在達到限制後 15 秒內阻止所有其他請求。

總而言之,我們正在模擬應用程式達到其處理能力極限的情況。如果達到極限,它將在 15 秒內不可用。這是一個簡單的程式碼,模擬了當應用程式開始接收比它可以處理的更多的流量時會發生什麼。如果此應用程式的副本收到超過五個同時請求,它將在十五秒內不可用。這(大致)就是應用程式在 DoS 攻擊下請求會發生的情況。

我們將再次執行 siege,但這次是針對 /limiter 端點。

kubectl --namespace go-demo-8 run siege \
--image yokogawa/siege \
--generator run-pod/v1 \
-it --rm \
-- --concurrent 50 --time 20S "http://go-demo-8/limiter"

以下是輸出的相關部分:

...
Transactions: 1845 hits
Availability: 92.02 %
Elapsed time: 19.70 secs
Data transferred: 0.04 MB
Response time: 0.01 secs
Transaction rate: 93.65 trans/sec
Throughput: 0.00 MB/sec
Concurrency: 1.12
Successful transactions: 20
Failed transactions: 160
Longest transaction: 0.09
Shortest transaction: 0.00
...

我們可以看到,這次成功的交易數量只有 20 個。它不僅僅是您預期的 5 個成功的交易,因為我們有多個應用程式副本。確切的數字並不重要。重要的是,我們可以看到成功的交易數量比以前少得多。在我的測試中,只有 20 個。作為比較,第一次執行 siege 產生了 1676 個成功的交易。

(待續) 這一部分將探討如何在 Kubernetes 環境中模擬阻礙和破壞網路,以及如何應對這些情況。我將分享我使用 process provider 執行任意命令的經驗,這在 Chaos Toolkit 外掛無法滿足需求時非常有用。

利用 Process Provider 執行任意命令

Process provider 允許我們執行任何命令,例如指令碼、Shell 命令等,只要它是可執行的。這在 Chaos Toolkit 外掛無法滿足我們特定需求時非常有用。舉例來說,我們可以使用 kubectl 命令搭配一系列引數來模擬對應用程式的 DoS 攻擊。

# network-dos.yaml
apiVersion: spec.chaos-toolkit.org/v1alpha1
kind: Experiment
metadata:
  name: network-dos
spec:
  steady-state-hypothesis:
    probes:
      - name: app-responds-to-requests
        type: probe
        provider:
          type: http
          url: http://${INGRESS_HOST}/
  method:
    - type: action
      name: abort-failure
      provider:
        type: process
        path: kubectl
        arguments:
          - run
          - siege
          - --image=apexacme/siege
          - --restart=Never
          - --generator=run-pod/v1
          - -- -c50 -t20S ${INGRESS_HOST}/limiter
      pauses:
        - type: after
          duration: 5s

這個實驗中,我們使用 siege 工具傳送 50 個併發請求到 /limiter 端點,持續 20 秒。執行實驗後,我們可以觀察應用程式的反應。

實驗結果與日誌分析

執行實驗後,我們發現應用程式在高負載下當機,無法回應請求。實驗結果本身並不十分詳細,但 chaostoolkit.log 檔案提供了更詳細的資訊,例如 siege 的輸出,可以幫助我們診斷問題。