Google Kubernetes Engine 的效能測試

如要從多個 GKE 用戶端測試 Google Kubernetes Engine (GKE) 工作負載的讀取和寫入效能,請使用 IOR 基準化工具。下列操作說明會示範如何自動設定用戶端,以及如何透過 Kubernetes Pod 之間無密碼的 SSH 使用 IOR 和 mpirun,測試匯總 I/O。

必要條件

  • 已佈建 Managed Lustre 執行個體。

  • 已設定本機 Docker 環境,並通過驗證,可將內容推送至 Google Artifact Registry 或 Container Registry (請參閱「驗證方法」)。

  • 確認聯播網的 mtu已設為 8896

建立 GKE 叢集

如要測試效能,您需要啟用 Managed Lustre CSI 驅動程式的 GKE 叢集。如要處理高效能儲存空間工作負載,請使用運算最佳化機器家族 (例如 c2c3) 和 TIER_1 網路,設定 GKE 節點集區。

執行下列指令,建立經過最佳化的標準 GKE 叢集,以進行效能測試:

gcloud container clusters create CLUSTER_NAME \
    --zone=ZONE \
    --machine-type=MACHINE_TYPE \
    --addons=LustreCsiDriver \
    --network-performance-configs=total-egress-bandwidth-tier=TIER_1 \
    --network=NETWORK \
    --num-nodes=NUM_NODES
  • ZONENETWORK 替換為特定部署值。叢集必須與 Managed Lustre 執行個體位於相同的虛擬私有雲網路。

  • 選擇MACHINE_TYPE。如要瞭解如何選擇機型以獲得最佳處理量,請參閱效能考量

  • 如果機型不支援 TIER_1 網路,請從指令中刪除 --network-performance-configs 行。

  • 指定 NUM_NODES。如要達到檔案系統的飽和狀態,叢集的總網路容量應比檔案系統的佈建輸送量高出約 20%。

    如果機器啟用 Tier 1 網路,單一節點可推送 25 Gbps 至 200 Gbps (約 3,000 至 25,000 MBps),視 VM 系列和 CPU 數量而定。標準執行個體的輸出通常會設限,每個 vCPU 的上限約為 2 Gbps。

    舉例來說,如果 Managed Lustre 執行個體容量可產生 100,000 MBps 的理論處理量,則需要 120,000 MBps (100,000 * 1.2) 的匯總用戶端輸出,才能達到飽和狀態:

    • 使用標準執行個體:如果每個節點的已發布輸出為 2,000 MBps,您應佈建至少 60 個節點 (120,000 / 2,000)。
    • 使用第 1 層網路:如果每個節點的已發布輸出為 10,000 MBps (約 80 Gbps),則應佈建至少 12 個節點 (120,000 / 10,000)。

建立 IOR Docker 映像檔

建構已安裝 OpenMPI 和 IOR 的容器映像檔。使用支援非同步 I/O (AIO) 的 IOR 進行編譯,提升效能。

  1. 在本機建立名為 Dockerfile 的檔案:

    FROM ubuntu:22.04
    
    # Prevent interactive prompts during installation
    ENV DEBIAN_FRONTEND=noninteractive
    
    # Install dependencies, SSH, and required Autotools packages
    RUN apt-get update && apt-get install -y \
      openssh-server \
      openmpi-bin \
      libopenmpi-dev \
      wget \
      git \
      make \
      gcc \
      g++ \
      automake \
      autoconf \
      libtool \
      pkg-config \
      libaio-dev \
      sudo \
      && rm -rf /var/lib/apt/lists/*
    
    # Build IOR from source (version 4.0.0) with Asynchronous I/O (AIO) support
    RUN git clone -b 4.0.0 https://github.com/hpc/ior /tmp/ior \
      && cd /tmp/ior \
      && ./bootstrap \
      && ./configure --disable-dependency-tracking --with-aio \
      && make -j"$(nproc)" \
      && make install \
      && rm -rf /tmp/ior
    
    # Configure SSH for OpenMPI passwordless communication
    RUN mkdir /var/run/sshd
    RUN echo 'root:root' | chpasswd
    RUN sed -i 's/^#PermitRootLogin.*/PermitRootLogin yes/' /etc/ssh/sshd_config
    RUN sed -i 's/^#PasswordAuthentication.*/PasswordAuthentication yes/' /etc/ssh/sshd_config
    
    # SSH login fix so user isn't kicked out after container initialization
    RUN sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd
    
    EXPOSE 22
    CMD ["/usr/sbin/sshd", "-D"]
    
  2. 建構這個映像檔,並推送至偏好的容器登錄服務。本文中的操作說明使用 Artifact Registry。

    export IMAGE_TAG="gcr.io/PROJECT_ID/lustre-ior-benchmark:latest"
    docker build -t $IMAGE_TAG .
    docker push $IMAGE_TAG
    

產生 MPI 的免密碼安全殼層金鑰

OpenMPI 需要使用無密碼 SSH 進行節點間通訊。建立 SSH 金鑰並儲存在 Kubernetes Secret 中。

  1. 產生 RSA 金鑰:

    ssh-keygen -t rsa -b 4096 -C "mpi-user" -N '' -f "./id_rsa"
    
  2. 建立 Kubernetes Secret:

    kubectl create secret generic mpi-ssh-secret \
      --from-file=id_rsa=./id_rsa \
      --from-file=id_rsa.pub=./id_rsa.pub \
      --from-file=authorized_keys=./id_rsa.pub
    

建立永久磁碟區和要求

使用靜態佈建,將 GKE Pod 連線至 Managed Lustre 執行個體。

  1. 建立名為 lustre-pv.yaml 的檔案。更改下列內容:

    • CAPACITY,以 GiB 為單位。
    • EXTENDED_LUSTRE_ID,格式為 PROJECT_ID/ZONE/INSTANCE_NAME。例如:project-123/us-west1-a/my-lustre-instance
    • LUSTRE_IP 替換為執行個體的掛接 IP 位址。
    • FS_NAME 替換為執行個體檔案系統名稱。

    您可以使用 gcloud lustre instances describe 指令擷取這些值。

    apiVersion: v1
    kind: PersistentVolume
    metadata:
      name: my-lustre-pv
    spec:
      storageClassName: ""
      claimRef:
        name: my-lustre-pvc
        namespace: default
      accessModes:
        - ReadWriteMany
      capacity:
        storage: CAPACITYGi   # retain `Gi` suffix
      persistentVolumeReclaimPolicy: Retain
      volumeMode: Filesystem
      csi:
        driver: lustre.csi.storage.gke.io
        volumeHandle: EXTENDED_LUSTRE_ID   # project-name/zone/instance-name
        volumeAttributes:
          ip: LUSTRE_IP
          filesystem: FS_NAME
    ---
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
      name: my-lustre-pvc
    spec:
      storageClassName: ""
      volumeName: my-lustre-pv
      accessModes:
        - ReadWriteMany
      resources:
        requests:
          storage: CAPACITYGi
    
  2. 套用資訊清單:

    kubectl apply -f lustre-pv.yaml
    

部署 MPI 工作站

如要在多個節點中擴展 IOR 工作,請部署StatefulSet,並使用基準圖片。

  1. 建立名為 mpi-workers.yaml 的檔案。指定 PROJECT_ID,並將 NUM_NODES 設為叢集中的節點數量。

    apiVersion: v1
    kind: Service
    metadata:
      name: mpi-workers
      labels:
        app: mpi-worker
    spec:
      clusterIP: None
      selector:
        app: mpi-worker
      ports:
      - port: 22
        name: ssh
    ---
    apiVersion: apps/v1
    kind: StatefulSet
    metadata:
      name: mpi-worker
    spec:
      serviceName: "mpi-workers"
      replicas: NUM_NODES
      selector:
        matchLabels:
          app: mpi-worker
      template:
        metadata:
          labels:
            app: mpi-worker
        spec:
          tolerations:
            - operator: "Exists"
          containers:
            - name: mpi-worker
              image: gcr.io/PROJECT_ID/lustre-ior-benchmark:latest
              command: ["/bin/sh", "-c"]
              args:
                - >-
                  mkdir -p /var/run/sshd &&
                  ssh-keygen -A &&
                  mkdir -p /root/.ssh &&
                  echo "Host *" > /root/.ssh/config &&
                  echo "    StrictHostKeyChecking no" >> /root/.ssh/config &&
                  echo "    UserKnownHostsFile=/dev/null" >> /root/.ssh/config &&
                  cp /mnt/mpi-ssh-keys/id_rsa /root/.ssh/id_rsa &&
                  cp /mnt/mpi-ssh-keys/id_rsa.pub /root/.ssh/id_rsa.pub &&
                  cp /mnt/mpi-ssh-keys/authorized_keys /root/.ssh/authorized_keys &&
                  chmod 700 /root/.ssh &&
                  chmod 600 /root/.ssh/* &&
                  exec /usr/sbin/sshd -D
              ports:
                - containerPort: 22
              volumeMounts:
                - name: lustre-mount
                  mountPath: /lustre
                - name: ssh-key-secret
                  mountPath: /mnt/mpi-ssh-keys
                  readOnly: true
          volumes:
            - name: lustre-mount
              persistentVolumeClaim:
                claimName: my-lustre-pvc
            - name: ssh-key-secret
              secret:
                secretName: mpi-ssh-secret
    
  2. 套用資訊清單:

    kubectl apply -f mpi-workers.yaml
    

執行 IOR 基準測試

從第一個 Pod (mpi-worker-0) 啟動基準測試,將其視為主要節點。

  1. 產生包含工作站內部 IP 位址的主機檔案,並複製到主要節點:

    kubectl get pods -l app=mpi-worker -o jsonpath='{range .items[*]}{.status.podIP}{"\n"}{end}' > hosts.txt
    kubectl cp hosts.txt mpi-worker-0:/root/hostfile
    
  2. 在頭部 Pod 中開啟 bash 工作階段:

    kubectl exec -it mpi-worker-0 -- /bin/bash
    
  3. 在 Pod 內建立測試目錄:

    mkdir -p /lustre/test
    
  4. 定義測試變數:

    export NUM_NODES="NUM_NODES"
    export PROCESSES_PER_NODE="PROCESSES_PER_NODE"
    export NUM_PROCESSES=$(( NUM_NODES * PROCESSES_PER_NODE ))
    

    其中:

    • NUM_NODES:參與測試的 worker Pod 總數。

    • PROCESSES_PER_NODE:要在每個容器上執行的 MPI 等級數量。建議您先將此值設為與用戶端電腦上的實體核心數 (或 vCPU 數的一半) 相符。對於高效能機器類型,將此值設為 816 之間,通常可產生最佳網路處理量。

  5. 執行基準測試指令:

    寫入總處理量

    這項指令會持續寫入 60 秒,以測試尖峰穩態總處理量。每個工作的檔案大小上限設為任意大的 50 TiB,讓工作持續寫入資料,直到 60 秒計時器到期為止。

    mpirun \
      --allow-run-as-root \
      --mca plm_rsh_no_tree_spawn 1 \
      --mca opal_set_max_sys_limits 1 \
      --mca plm_rsh_num_concurrent ${NUM_NODES} \
      --mca plm_rsh_args "-o StrictHostKeyChecking=no" \
      --npernode ${PROCESSES_PER_NODE} \
      --np ${NUM_PROCESSES} \
      --hostfile ~/hostfile \
      /usr/local/bin/ior \
      -a AIO \
      --posix.odirect \
      -F -g -v -w -k \
      -t 4m -b 50t \
      -D 60 \
      -O stoneWallingWearOut=1 \
      -O stoneWallingStatusFile=/lustre/test/ior-easy.stonewall \
      -o /lustre/test/ior_file

    讀取總處理量

    這個階段會讀回在 60 秒寫入處理量測試期間成功寫入的資料量。雖然每個工作的檔案大小 (-b) 設為 50 TiB,與寫入階段的幾何結構相符,但 -O stoneWallingWearOut=1 標記會指示 IOR 在達到 stonewall 狀態檔案中記錄的確切資料界線時,立即停止讀取。

    mpirun \
      --allow-run-as-root \
      --mca plm_rsh_no_tree_spawn 1 \
      --mca opal_set_max_sys_limits 1 \
      --mca plm_rsh_num_concurrent ${NUM_NODES} \
      --mca plm_rsh_args "-o StrictHostKeyChecking=no" \
      --npernode ${PROCESSES_PER_NODE} \
      --np ${NUM_PROCESSES} \
      --hostfile ~/hostfile \
      /usr/local/bin/ior \
      -a AIO \
      --posix.odirect \
      -F -g -v -r -k \
      -t 4m -b 50t \
      -D 60 \
      -O stoneWallingWearOut=1 \
      -O stoneWallingStatusFile=/lustre/test/ior-easy.stonewall \
      -o /lustre/test/ior_file

    寫入 IOPS

    這項測試會使用 4 KiB 的小型傳輸大小,以及每個工作 8 GiB 的檔案大小,測量檔案系統可處理的每秒最大輸入/輸出作業數 (IOPS)。

    mpirun \
      --allow-run-as-root \
      --mca plm_rsh_no_tree_spawn 1 \
      --mca opal_set_max_sys_limits 1 \
      --mca plm_rsh_num_concurrent ${NUM_NODES} \
      --mca plm_rsh_args "-o StrictHostKeyChecking=no" \
      --np ${NUM_PROCESSES} \
      --oversubscribe \
      --map-by node \
      --bind-to socket \
      --hostfile ~/hostfile \
      /usr/local/bin/ior \
      -e \
      -t 4k \
      -b 8g \
      -s 1 \
      -a AIO \
      --posix.odirect \
      --aio.max-pending=256 \
      -w \
      -F \
      -z \
      -Q 1 \
      -G 1745405099 \
      -D 45 \
      -O stoneWallingWearOut=1 \
      -o /lustre/test/ior-random

    讀取 IOPS

    為避免從稀疏檔案讀取資料,這項測試會使用兩項指令:先寫入資料,以 4 MiB 的傳輸大小建立實體檔案,每個工作檔案的大小為 8 GiB,然後執行實際的 4 KiB 隨機讀取 IOPS 測試。

    1. 建立要讀取的檔案:

      mpirun \
        --allow-run-as-root \
        --mca plm_rsh_no_tree_spawn 1 \
        --mca opal_set_max_sys_limits 1 \
        --mca plm_rsh_num_concurrent ${NUM_NODES} \
        --mca plm_rsh_args "-o StrictHostKeyChecking=no" \
        --npernode ${PROCESSES_PER_NODE} \
        --np ${NUM_PROCESSES} \
        --oversubscribe \
        --map-by node \
        --bind-to socket \
        --hostfile ~/hostfile \
        /usr/local/bin/ior \
        -a AIO \
        --posix.odirect \
        -w \
        -F \
        -k \
        -t 4m \
        -b 8g \
        -s 1 \
        -Q 1 \
        -G 1745405099 \
        -o /lustre/test/ior_rand_read
    2. 執行 IOR 讀取測試:

      mpirun \
        --allow-run-as-root \
        --mca plm_rsh_no_tree_spawn 1 \
        --mca opal_set_max_sys_limits 1 \
        --mca plm_rsh_num_concurrent ${NUM_NODES} \
        --mca plm_rsh_args "-o StrictHostKeyChecking=no" \
        --oversubscribe \
        --map-by node \
        --bind-to socket \
        --npernode ${PROCESSES_PER_NODE} \
        --np ${NUM_PROCESSES} \
        --hostfile ~/hostfile \
        /usr/local/bin/ior \
        -a AIO \
        --posix.odirect \
        --aio.max-pending 256 \
        -r \
        -F \
        -z \
        -t 4k \
        -b 8g \
        -s 1 \
        -Q 1 \
        -G 1745405099 \
        -D 45 \
        -O stoneWallingWearOut=1 \
        -o /lustre/test/ior_rand_read

    mpirun 標記如下:

    • --mca plm_rsh_no_tree_spawn 1:停用樹狀結構的 Daemon 產生,以提升節點的啟動可靠性。
    • --mca opal_set_max_sys_limits 1:自動嘗試將系統限制 (例如開啟的檔案數上限) 設為允許的最高值。
    • --mca plm_rsh_num_concurrent:設定啟動工作站 Daemon 時,mpirun 可使用的並行 SSH 連線數量上限。
    • --mca plm_rsh_args ...:略過嚴格的主機金鑰檢查,防止互動式 SSH 提示導致 MPI 程序啟動作業停滯。
    • --prefix ...:明確定義 Rocky Linux 和 RHEL 的 OpenMPI 安裝路徑,以便 worker 節點找到所需 Daemon (orted)。
    • --allow-run-as-root:允許 mpirun 以超級使用者身分執行。
    • --oversubscribe:允許 MPI 在節點上排程的程序數量,超過可用的實體核心數量。
    • --map-by node:以循環方式將 MPI 程序平均分配到可用節點。
    • --bind-to socket:將 MPI 程序繫結至實體 CPU 插槽,以最佳化記憶體存取和快取效能。
    • --npernode:每個節點的程序數量。
    • --np:要啟動的 MPI 程序總數。
    • --hostfile:指定包含要執行主機清單的檔案。

    ior 標記如下:

    • -a AIO --posix.odirect:使用非同步 I/O (AIO) 引擎,並結合 POSIX 直接 I/O。這會略過用戶端 RAM 頁面快取,並強制將並行非封鎖寫入作業直接傳送至儲存伺服器,確保基準測試測量的是實際網路儲存空間效能,而非記憶體緩衝區。
    • --aio.max-pending=256:決定每個程序中進行中的並行非同步 I/O 作業數量上限。
    • -C:重新排序工作,以獲得最佳讀取效能。
    • -F:每個程序一個檔案的模式。
    • -g:使用屏障分隔測試的寫入和讀取階段。
    • -v:輸出詳細記錄。
    • -w / -r:指示 IOR 執行寫入 (-w) 或讀取 (-r) 效能測試。
    • -k:防止 IOR 在寫入後刪除測試檔案,以便進行讀取測試。
    • -e:在寫入階段後執行 fsync,確保資料已提交至儲存硬碟。
    • -z:指示 IOR 執行隨機存取 I/O,而非循序存取。
    • -s 1:將區隔數量設為 1。
    • -Q 1:設定每個節點的任務偏移量,對齊任務,使基準座標在所有節點中正確對齊。
    • -G 1745405099:將隨機種子時間戳記硬式編碼,確保讀取階段產生的隨機檔案位移與寫入階段使用的完全相同。
    • -t:為每個 I/O 作業設定轉移大小 (例如 4m 代表總處理量,4k 代表 IOPS)。
    • -b:設定每個程序的目標區塊大小 (例如 50t 代表處理量,8g 代表 IOPS),確保測試在計時執行期間不會用盡酬載資料。
    • -D:將測試執行時間限制為特定秒數 (例如 6045)。這項「阻斷」作業會平均終止基準測試,以擷取真正的穩定狀態測量結果。
    • -O stoneWallingWearOut=1:強制所有並行執行緒在整個期間持續產生寫入負載,避免較快的執行緒提早完成,進而降低整體網路壓力。
    • -O stoneWallingStatusFile=<path>:在寫入階段結束時寫入狀態驗證檔案。後續的讀取階段會使用這個檔案,只讀取已成功提交的區塊,避免在讀取期間發生空指標錯誤。
    • -o:Managed Lustre 檔案系統中測試檔案的路徑。

查看結果

基準測試完成後,終端機就會直接顯示所有用戶端 VM 或 Pod 的匯總成效指標。在輸出內容底部尋找「結果」表格,即可查看最大輸送量或 IOPS。

重要指標

  • aggregate filesize:所有參與測試的用戶端在測試期間寫入或讀取的資料總量。

  • bw(MiB/s) / Max Write / Max Read:連續測試最重要的指標。這會顯示 Managed Lustre 檔案系統達成的總頻寬。

  • IOPS:隨機 I/O 測試最重要的指標。這會顯示每秒輸入/輸出作業數上限。

將結果匯出至檔案

如要以程式輔助方式剖析結果、將結果匯入資料庫,或儲存結果以供日後分析,可以指示 IOR 匯出 JSON 和 CSV 格式的摘要資料,而非僅將資料列印到螢幕上。

如要這麼做,請在 ior 指令字串結尾附加 -O 標記:

-O summaryFormat=JSON \
-O summaryFile=/lustre/test/perf-results/summary.json \
-O saveRankPerformanceDetailsCSV=/lustre/test/perf-results/details.csv

執行基準測試前,檔案系統中必須存在輸出目錄。

預期與實際效能

您可以根據檔案系統的佈建容量和效能層級,計算其數學最大處理量。由於儲存空間容量是以 GiB 為單位配置,而層級是以 TiB 為單位計費,因此您必須先轉換容量:

(Capacity GiB / 1024) * Tier MBps = 理論最高 MBps

舉例來說,在「每 TiB 500 MBps」層級中,216,000 GiB 的執行個體在數學上可提供 105,469 MBps 的處理量 ((216000 / 1024) * 500)。

觀察到的最大輸送量一律會受限於檔案系統的佈建輸送量容量,或用戶端機器的合併網路輸出限制,以較低者為準。

基準測試結果未達理論速度的常見原因包括:

  • TCP/IP 額外負荷:標準網路封裝和封包標頭會耗用約 5% 到 10% 的原始頻寬。您的數學最大值包含這項額外負荷,但 IOR 基準僅測量寫入磁碟的原始酬載。

  • 用戶端網路限制:用戶端電腦有嚴格的輸出頻寬上限。如果您使用少量用戶端或節點,或是未啟用第 1 層網路的機器類型,用戶端會在 Managed Lustre 檔案系統達到上限前,先限制基準。

  • MPI 內容切換:如果 PROCESSES_PER_NODE 設定值高於用戶端電腦上的實體核心數量,CPU 爭用和內容切換負擔會人為降低基準測試的 I/O 效能。

  • 缺少直接 I/O:如果省略 --posix.odirect 標記,資料會通過用戶端的 RAM 頁面快取。這會造成記憶體瓶頸和 CPU 負擔,掩蓋網路儲存空間的實際效能。

清除所用資源

如要避免系統向您的 Google Cloud 帳戶收取本頁面所用資源的費用,請按照下列步驟操作:

  1. 從受管理 Lustre 磁碟區刪除測試檔案:

    kubectl exec mpi-worker-0 -- rm -rf /lustre/test
    
  2. 刪除 GKE 叢集:

    gcloud container clusters delete CLUSTER_NAME --zone=ZONE
    

    刪除叢集時,系統也會刪除 GKE Pod、Kubernetes Secret 和永久磁碟區要求。

  3. 如果您已推送基準 Docker 映像檔,且不再需要該映像檔,請從存放區中刪除:

    gcloud container images delete gcr.io/PROJECT_ID/lustre-ior-benchmark:latest --force-delete-tags
    
  4. 如果您是為了這項測試建立 Managed Lustre 執行個體,且不再需要該執行個體,請刪除:

    gcloud lustre instances delete INSTANCE_ID --location=LOCATION
    

排解常見的基準化瓶頸

如果基準測試結果遠低於預期的儲存空間效能層級,請參閱「排解常見瓶頸」。