教學:用情境測試 AKS 區域韌性

在這個 Azure Chaos Studio Scenario 教學中,使用 Chaos Studio Workspaces 測試一個 Azure Kubernetes Service (AKS) 範例應用程式對區域故障的韌性。 將應用程式部署到具區域備援的測試叢集,並針對其節點基礎架構執行 Compute Zone Down。 首次執行會公開一個釘選於單一區域的前端。 修正莢艙位置,重播情境,並比較應用程式可用性與情境報告。

這個教學是很好的第一手示範,並且重用了 AKS 快速入門軟體裡的 AKS 商店示範 範例應用程式,所以沒有容器登錄檔或建置步驟。 請預留約 1 小時:包括建立叢集,以及兩次情境執行,每次約 5 分鐘。

Important

Chaos Studio 的工作空間與場景目前已公開預覽。 Microsoft 提供此預覽版「現況」與「可用狀態」,不受服務等級協議或有限保固保護。 Microsoft 以盡力而為的方式提供預覽版的客戶支援。 這個預覽並非為製作用途設計。 欲了解更多資訊,請參閱以下文章:

在這個教學中,你會學到如何:

  • 建立一個 AKS 叢集,節點跨越三個可用區域。
  • 部署 AKS 商店示範範例應用程式,並將其前端釘選在一個區域進行確定性示範。
  • 啟動一個瀏覽器監控器,實時追蹤商店、目標節點和 pod 位置。
  • 建立一個範圍限定於叢集基礎結構資源群組的工作區。
  • 執行計算區域關閉情境,觀察應用程式失敗。
  • 使用每個區域皆須遵守的硬性放置合約修正部署,確認後再重新執行情境。
  • 比較兩個情境報告。

本教學著重於可實際運作的示範。 關於每個步驟背後的概念、AKS 為你管理的基礎設施中斷的警示,以及如何解讀真實工作負載的結果,請參考 Chaos Studio 的 AKS 工作負載韌性測試。

先決條件

  • Azure 訂用帳戶。 如果您沒有 Azure 帳戶,請在開始之前建立 免費帳戶 。
  • Azure CLI、kubectl、 、 kubelogin和 Python 3(僅標準函式庫,無需安裝套件)。 Azure Cloud Shell 已經預裝了這四個。 如果您在本機上作業,請分別使用 az aks install-cli 和 kubelogin 安裝 kubectl。
  • 在您的訂用帳戶中註冊的 Microsoft.Chaos 資源提供者。 關於註冊說明,請參閱 Workspaces 快速入門前置條件。

建立區域冗餘的 AKS 叢集

區域失效測試只有針對一個設計成能存活的叢集才有意義,因此建立一個分散在三個可用區域、分布三個節點的叢集。 此範例使用東美國2號公路;任何有 可用區域 的區域都可以。

  1. 建立資源群組及叢集:

    az group create --name chaos-demo-rg --location eastus2
    
    az aks create \
      --resource-group chaos-demo-rg \
      --name chaos-demo-aks \
      --node-count 3 \
      --zones 1 2 3 \
      --generate-ssh-keys
    

    叢集建立只需幾分鐘。

  2. 在一個全新的 Cloud Shell 會話中,kubectl還沒連接到任何叢集。 在執行任何kubectl指令前,先設定訂閱、取得憑證,並將 kubeconfig 轉換成 Microsoft Entra 認證:

    az account set --subscription <SUBSCRIPTION_ID>
    az aks get-credentials --resource-group chaos-demo-rg --name chaos-demo-aks
    kubelogin convert-kubeconfig -l azurecli
    

    請將您的訂閱 ID 替換為 <SUBSCRIPTION_ID>。 如果該訂用帳戶已是您目前作用中的訂用帳戶,請跳過 az account set。 即使在全新 Cloud Shell 會話中,這個kubelogin步驟也是必須的——如果沒有這個步驟,對 Entra 認證叢集的第一個kubectl指令就會因認證錯誤而失敗。

  3. 確認節點跨越三個區域:

    kubectl get nodes -L topology.kubernetes.io/zone
    

    欄位 ZONE 顯示每個區域中的一個節點,例如 eastus2-1、 eastus2-2和 eastus2-3。 區域名稱後面的區域編號是你在劇情設定中後續鎖定的目標。

部署範例應用程式

AKS 商店示範是一個小型零售店面,擁有網頁前端、產品服務、訂單服務和 RabbitMQ 佇列。 它的容器映像檔是公開的,所以你可以用一個指令部署。

  1. 部署應用程式:

    kubectl apply -f https://raw.githubusercontent.com/Azure-Samples/aks-store-demo/2.2.0/aks-store-quickstart.yaml
    

    資訊清單會使用單一複本部署每個元件。 叢集是區域冗餘的,但應用程式本身不是。 這個教學揭露並修正了這個韌性差距。

  2. 等前端取得公開 IP 位址:

    kubectl get service store-front --watch
    

    當 EXTERNAL-IP 值從 <pending> 變更為公用 IP 位址時,按下 Ctrl+C 以停止監看。

  3. 在瀏覽器中開啟 http://<EXTERNAL-IP>,並確認商店已載入。 保持這個分頁開啟。 這是在測試期間用來查看應用程式健康狀況的次要檢視畫面——你稍後啟動的監視器才是主要的。

想了解應用程式本身是如何建置與部署的,請參閱 AKS 教學系列。

將前端釘選在單一區域進行確定性示範

注意

將單一副本固定在單一區域,是專為此示範安排的教學設定,並非正式環境中的建議作法。 生產部署絕不應限制單一副本只在單一區域——這會剝奪叢集所提供的冗餘。 這個教學課程是刻意設計的,讓第一次執行的失敗可靠且可重複,而不是取決於排程器挑選的區域。

沒有刻意的釘選,排程器可以將前端唯一的複本放置在任何區域,且簡單的重新排程可以迅速發生,影響很容易被忽略。 將複製品釘在已知區域,讓目標可預測,且每次執行示範時都能觀察到失敗。

  1. 找到該艙目前運行的節點 store-front ,並讀取該節點的區域標籤:

    STORE_NODE="$(kubectl get pods -l app=store-front -o jsonpath='{.items[0].spec.nodeName}')"
    PIN_ZONE="$(kubectl get node "$STORE_NODE" -o jsonpath="{.metadata.labels['topology\.kubernetes\.io/zone']}")"
    echo "$PIN_ZONE"
    

    PIN_ZONE 是完整區域標籤,例如 eastus2-1。 保持這個 Shell 工作階段開啟 - 您會將這個值重複用於監視器,稍後也會用於情境組態 (其中只需填入數字,也就是最後一個連字號後面的部分 - 例如 eastus2-1 中的 1)。

  2. 對 store-front 部署進行修補,要求排程到該區域,並新增註釋,將該修補標記為僅供示範的模式:

    kubectl patch deployment store-front --patch "$(cat <<EOF
    {
      "metadata": {
        "annotations": {
          "chaos-demo.aks-zone-down-demo/deliberate-anti-pattern": "Pins the single front-end replica to zone ${PIN_ZONE} so run 1 is deterministic. Removed as part of the fix later in this tutorial - do not carry this pin into a real deployment."
        }
      },
      "spec": {
        "template": {
          "spec": {
            "affinity": {
              "nodeAffinity": {
                "requiredDuringSchedulingIgnoredDuringExecution": {
                  "nodeSelectorTerms": [
                    {
                      "matchExpressions": [
                        {"key": "topology.kubernetes.io/zone", "operator": "In", "values": ["${PIN_ZONE}"]}
                      ]
                    }
                  ]
                }
              }
            }
          }
        }
      }
    }
    EOF
    )"
    
    kubectl rollout restart deployment/store-front
    kubectl rollout status deployment/store-front --timeout=300s
    
  3. 確認釘選已生效 - 複本應已回到 $PIN_ZONE 中的某個節點:

    STORE_NODE="$(kubectl get pods -l app=store-front -o jsonpath='{.items[0].spec.nodeName}')"
    STORE_ZONE="$(kubectl get node "$STORE_NODE" -o jsonpath="{.metadata.labels['topology\.kubernetes\.io/zone']}")"
    echo "Pod is in zone: $STORE_ZONE (expected $PIN_ZONE)"
    

    如果兩個值不一致,請重複上一個步驟——在兩者一致之前,第 1 次執行仍不具確定性。

下載並啟動示範監視器

僅在終端機中顯示的 kubectl 監看畫面在現場示範時反應太慢,也很容易被忽略。 店面本身的訊號並未在 Lockstep 模式中與叢集訊號一起移動。 這個教學使用來自 Chaos Studio 樣本庫的一個小型 Python 監控腳本,作為觀看跑步的主要方式。

  1. 下載監視器及修正驗證腳本:

    curl -O https://raw.githubusercontent.com/microsoft/chaos-studio/f9573c943694e88cf3aacbca38debe84fa91a62c/samples/aks-zone-down-demo/monitor.py
    curl -O https://raw.githubusercontent.com/microsoft/chaos-studio/f9573c943694e88cf3aacbca38debe84fa91a62c/samples/aks-zone-down-demo/verify-fix.sh
    chmod +x verify-fix.sh
    

    這些連結會固定到特定的 commit,因此無論範例後續如何變更,連結仍可正常運作。 在包含的提取要求合併後,本教學課程的後續修訂版本即可改為使用已加上標籤的發行版本。

  2. 啟動螢幕,指向店面的外部 IP 和您在上一節中釘選的區域:

    python3 monitor.py --storefront-url http://<EXTERNAL-IP> --target-zone "$PIN_ZONE"
    

    螢幕只用 Python 標準函式庫,所以沒其他東西要安裝。 預設是每 5 秒輪詢一次——可以用 --interval 環境 MONITOR_INTERVAL_SECONDS 變數配置。 該預設值的頻率已足以判定訊號的先後順序,而無須過度頻繁地輪詢 Kubernetes API 或店面。

  3. 在 Cloud Shell 中,選擇網頁預覽,並將埠號設為 8787,以便在瀏覽器分頁中開啟螢幕。如果你是本地跑,那就改開門http://localhost:8787吧。

    現在,監視頁面已成為這兩種執行的主要檢視畫面。 它顯示四個訊號:

    • Storefront HTTP 狀態 - 對店面發出的快取破壞要求,因此您看到的是即時的可連線/無法連線狀態,而不是快取成功。
    • 目標區域節點狀態 ——你的目標區域 Ready 節點是 還是 NotReady。
    • 前端艙體配置 ——哪些 store-front 艙體正在運行,以及每個艙體在哪個區域。
    • 轉換歷史 ——上面每個狀態變化的運行時間軸,附上時間戳記,讓你在運行結束後可以回顧序列,而不必依賴現場觀察到的。

    如果對 kubectl 或 Kubernetes API 的輪詢失敗,例如 API 伺服器暫時無法連線,或 kubeconfig 已過期,監視介面會顯示醒目的紅色橫幅並標明錯誤,而不會讓受影響的訊號卡在「檢查中」的預留位置上。 當橫幅顯示時,儀表板的其餘部分仍會顯示其最後已知正常狀態和歷史記錄。 將此橫幅訊息視為一個獨立的訊號:它表示監控程式已失去可視性,並不表示它所監控的對象是健康的。

建立一個範圍限定於基礎設施資源群組的工作區

AKS 將叢集的節點虛擬機縮放集置於獨立的 基礎設施 資源群組(預設名稱以 為 MC_ 開頭),而非包含叢集資源的資源群組。 將工作空間範圍設定為基礎設施資源群組,讓它能發現節點。 背景請參見《 為何一個工作區在你的 AKS 叢集範圍內找不到計算目標》。

  1. 查找基礎設施資源群組名稱:

    az aks show --resource-group chaos-demo-rg --name chaos-demo-aks --query nodeResourceGroup -o tsv
    
  2. 在 Azure 入口網站中,搜尋 Chaos Studio,選擇工作區,然後選擇建立。

  3. 在 基礎 標籤中,選擇 chaos-demo-rg 資源群組,命名工作區 chaos-demo-workspace,並選擇 支援區域。 工作區區域不需要和叢集區域相符。

  4. 在 範圍 標籤中,選擇 資源群組 作為範圍類型,然後從步驟 1 選擇基礎架構資源群組。

  5. 在 身份 標籤中,選擇 系統指派。

  6. 選擇檢閱 + 建立>建立,然後按一下前往資源。

    發現完成後,叢集的節點虛擬機尺度集(命名如 aks-nodepool1-12345678-vmss)會以發現資源的形式出現。

  7. 如果入口網站顯示橫幅,指出該身分識別在工作區範圍上缺少讀取權限,請選取 [在工作區範圍指派讀取者角色]。 要建立角色指派,你需要在基礎架構資源群組上擁有擁有者或使用者存取管理員權限。

您會為該身分識別指派該情境本身所需的角色;在下一節中,驗證會明確告訴您究竟缺少哪些項目。 想了解每個工作區建立步驟的完整流程,請參考 工作區快速入門。

執行情境,然後看應用程式失敗

計算區域關閉情境會透過在設定的持續時間內關閉目標區域中的虛擬機器擴展集執行個體,來模擬可用性區域失敗。 當動作持續時間結束時,實例會重新開始。

  1. 在工作區中,選取 情境,然後從情境庫中選取 計算區域停機。

  2. 設定情境。 對於可用區域,請輸入 $PIN_ZONE 中的數字(即最後一個連字號後面的部分,例如 eastus2-1 中的 1)。 將 [持續時間] 設為 [5 分鐘] - 這樣的時間足以讓店面、節點和 Pod 的訊號趨於穩定,又不必等太久。 這個 5 分鐘的設定是針對此特定示範而定的;其他情境類型則會根據其測試項目,各自有相應的持續時間指引。 例如,圍繞 DNS 快取行為設計的情境,所需持續時間必須長到足以超過該記錄的快取 TTL,而這個 TTL 可能遠遠超過 5 分鐘。 選取 [儲存組態]。

  3. 驗證會檢查工作區的受控識別是否能對目標資源執行情境所需的每一項動作。 若驗證報告缺少權限,請在情境設定頁面選擇 「修正權限 」,以授予該身份推薦的內建角色。 在這個情境中,這就是節點虛擬機器擴展集上的「虛擬機器參與者」角色。 若要自行指派角色,或使用最低權限自訂角色取代內建角色,請參閱 Chaos Studio 工作區中的權限與身份,並使用Chaos Studio Workspaces中的最低權限自訂角色。

    如果執行時仍缺少所需角色,執行仍會開始,但關機動作會因情境報告中的權限錯誤而失敗。

  4. 選擇 執行 並確認。

運行開始後可能需要幾分鐘才會自動關閉,所以如果螢幕上沒有立即變化,也不必擔心。 然後請觀看螢幕頁面:

  • 商店前端的 HTTP 訊號和目標節點的狀態不會同時改變。 應用程式層級的訊號才是使用者實際感受到的訊號,應將其視為主要訊號;節點和 Pod 訊號則是叢集內部的狀態記錄,之後才會跟上。 預期在節點顯示 NotReady 之前,店面會明顯顯示無法連線 - 該差距是正常的非同步訊號傳播,示範本身沒有問題。
  • 目標節點的狀態會變為 NotReady。
  • 因為前端被釘選在該區域,唯一的複本沒有其他地方可以執行。 店面會一直無法連線,直到 Kubernetes 能重新排程 Pod - 而在釘選就緒後,只有在目標節點傳回或您變更放置限制時才會發生。 不要只依賴這裡固定的停機時間數值;請查看監控器的狀態轉換記錄,確認在你這次執行中實際發生了什麼。

這個結果就是該項發現。 叢集具備區域備援能力,但應用程式的部署選擇使區域故障演變成一場在限制條件未解除前看不到明確終點的服務中斷。 監視器的轉換歷程記錄,準確記載了店面確切是在何時中斷,以及之後又是在何時恢復上線。

疑難排解:未顯示影響

如果監視器顯示店面在第 1 次執行期間保持可連線狀態,請先檢查下列項目,再判定該情境未成功:

  • 確認 PIN 碼已生效。 執行 kubectl get pods -l app=store-front -o wide,並檢查 pod 所在的節點是否在 $PIN_ZONE。 如果補丁不適用,排程器可能會把複本放到別處。 在中斷期間,單純的重新排程速度可能非常快,若未進行釘選,您可能會錯過它。
  • 確認監視器是否在監視正確的區域和網址。 使用叢集中的確切 --target-zone 值和 storefront URL 重新啟動 monitor.py。 過時或錯誤的數值顯示出誤導性的「健康」狀態。
  • 請查看情境報告中的 Skipped 行動。 如果關閉動作顯示為 Skipped 而非 Succeeded,則表示此次執行在目標區域中未找到任何相符的目標。 詳見 「解讀結果」。
  • 再等幾秒鐘。 關機動作在跑動開始後需要短暫時間才會生效。 監測器的狀態轉換記錄會在事件一旦發生後顯示確切的時間戳記。

修正部署問題並驗證其結果

現在將刻意設定的單一區域釘選,取代為真正的叢集規模修正:三個複本,並硬性限制每個區域各分配一個。

  1. 移除您先前新增的區域圖釘:

    kubectl patch deployment store-front --type=merge --patch '{"spec":{"template":{"spec":{"affinity":null}}}}'
    
  2. 將前端擴展為三個複本,並新增拓撲分佈限制,要求每個區域有一個複本,而非僅是偏好設定:

    kubectl patch deployment store-front --patch '{"spec":{"replicas":3,"template":{"spec":{"topologySpreadConstraints":[{"maxSkew":1,"topologyKey":"topology.kubernetes.io/zone","whenUnsatisfiable":"DoNotSchedule","labelSelector":{"matchLabels":{"app":"store-front"}}}]}}}}'
    

    whenUnsatisfiable: DoNotSchedule 使每個區域各有一個副本的分散配置成為硬性要求。 不符合條件的複本會停留在 Pending,而不是落在已經有複本的區域。 這是一個刻意的取捨 - 它保證了此測試所依賴的區域覆蓋率,代價是如果某個區域暫時沒有空間,可能會導致複本保持在未排程狀態。 ScheduleAnyway 會讓排程器在壓力情況下略過這項限制,而這正是此修正要防止的失效模式。

  3. 在信任之前,先驗證這個修正方法。 執行驗證腳本。 它會等待推出完成,以免將舊版單一複本修訂版中過時的 Pod 納入計算,然後確認每個區域至少有一個 Readystore-front Pod:

    ./verify-fix.sh
    

    kubectl呼叫、API 請求或它回傳的 JSON 可能會暫時失敗——短暫逾時、連線中斷——但不代表修復本身失敗。 指令碼會重試這些暫時性失敗,直到自身逾時為止,而不是在第一次發生錯誤時就結束執行。 只有在等待逾時後,它才會以非零狀態結束,並顯示診斷訊息,指出哪個區域仍缺少一個就緒複本。 在它通過之前,不要繼續執行步驟 2。 測試通過的結果讓「每個區域一個複本」成為經過驗證的事實,而不是從 patch 命令沿用下來的假設。

再跑一次情境並做比較

  1. 在工作區中,使用相同的目標區域和相同的 5 分鐘持續時間,再次執行 Compute Zone Down 情境。

  2. 注意監視器。 目標節點仍然會 NotReady,並連帶將其 store-front 複本也一併下線,但店面會持續回應,由存活區域中的複本提供服務。 受測試的主張是在區域中斷期間維持可用性,這一點可由監控器的連續歷史紀錄加以證明——並不是指請求零丟失,也不是指監控器在整個過程中始終顯示不間斷的健康狀態。 在 Azure Load Balancer 收斂至剩餘健康複本的過程中,仍可能發生短暫的中斷;測試通過的執行結果會顯示短暫的收斂波動,而非持續整個中斷期間的停機。 收斂所需的時間因環境而異,所以不要依賴固定的時間限制——利用監視器的轉換歷史來區分短暫的閃爍和持續的斷線。

單一複本元件仍可能出現短暫的服務中斷。 如果 RabbitMQ 佇列所在的節點位於目標區域,訂單提交會在其 Pod 復原期間降級。 找出下一個最脆弱的元件,並判斷是否值得修復,這正是混沌測試旨在推動的循環。

比較各情境報告

  1. 在工作區中,選擇 執行歷史紀錄。 您現在有同一情境的兩次已完成執行。

  2. 選擇每一次執行,然後選擇 產生報告。 確認關機動作在兩者中皆顯示已成功狀態,這代表每次執行皆已找到並中斷目標區域中的執行個體。 若行動顯示 跳過,則該跑動未找到匹配目標。 常見的原因是範圍不包含基礎設施資源群組,或是目標區域內沒有節點。 欲了解更多資訊,請參閱 Chaos Studio 在 AKS 上的測試工作負載韌性。

  3. 請注意,兩份報告看起來一樣,儘管申請結果相反。 已成功表示中斷已傳遞 - 這並不代表應用程式維持良好狀態。 報告證明了中斷發生了什麼以及何時發生;監控器的轉換歷史是應用程式回應行為的證明。 將兩者結合,就是將一次運行轉化為證據的方法:報告會標示故障的時間戳,監控器則顯示修復前後的差異。

你可以下載這兩份報告,作為韌性審查前後對照的佐證。 詳情請參閱 情境報告。

清理資源

刪除資源群組以移除叢集、範例應用程式和工作區。 刪除叢集也會刪除其基礎設施資源群組。

az group delete --name chaos-demo-rg --yes --no-wait

回報問題與請求功能

Azure Chaos Studio 是公開開發的。 若要回報錯誤、請求功能,或詢問有關 Workspaces、Scenarios 或 Azure CLI 擴充功能的問題,請在 GitHub 的 Chaos Studio 倉庫開啟一個問題。 透過提交問題,您可以追蹤其進度並查看其他客戶的請求。

下一步