如要從多個 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 叢集。如要處理高效能儲存空間工作負載,請使用運算最佳化機器家族 (例如 c2 或 c3) 和 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
將 ZONE 和 NETWORK 替換為特定部署值。叢集必須與 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)。
- 使用標準執行個體:如果每個節點的已發布輸出為 2,000 MBps,您應佈建至少 60 個節點 (
建立 IOR Docker 映像檔
建構已安裝 OpenMPI 和 IOR 的容器映像檔。使用支援非同步 I/O (AIO) 的 IOR 進行編譯,提升效能。
在本機建立名為
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"]建構這個映像檔,並推送至偏好的容器登錄服務。本文中的操作說明使用 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 中。
產生 RSA 金鑰:
ssh-keygen -t rsa -b 4096 -C "mpi-user" -N '' -f "./id_rsa"建立 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 執行個體。
建立名為
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套用資訊清單:
kubectl apply -f lustre-pv.yaml
部署 MPI 工作站
如要在多個節點中擴展 IOR 工作,請部署StatefulSet,並使用基準圖片。
建立名為
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套用資訊清單:
kubectl apply -f mpi-workers.yaml
執行 IOR 基準測試
從第一個 Pod (mpi-worker-0) 啟動基準測試,將其視為主要節點。
產生包含工作站內部 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在頭部 Pod 中開啟 bash 工作階段:
kubectl exec -it mpi-worker-0 -- /bin/bash在 Pod 內建立測試目錄:
mkdir -p /lustre/test定義測試變數:
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 數的一半) 相符。對於高效能機器類型,將此值設為
8和16之間,通常可產生最佳網路處理量。
執行基準測試指令:
寫入總處理量
這項指令會持續寫入 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 測試。
建立要讀取的檔案:
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
執行 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:將測試執行時間限制為特定秒數 (例如60或45)。這項「阻斷」作業會平均終止基準測試,以擷取真正的穩定狀態測量結果。-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 帳戶收取本頁面所用資源的費用,請按照下列步驟操作:
從受管理 Lustre 磁碟區刪除測試檔案:
kubectl exec mpi-worker-0 -- rm -rf /lustre/test刪除 GKE 叢集:
gcloud container clusters delete CLUSTER_NAME --zone=ZONE刪除叢集時,系統也會刪除 GKE Pod、Kubernetes Secret 和永久磁碟區要求。
如果您已推送基準 Docker 映像檔,且不再需要該映像檔,請從存放區中刪除:
gcloud container images delete gcr.io/PROJECT_ID/lustre-ior-benchmark:latest --force-delete-tags如果您是為了這項測試建立 Managed Lustre 執行個體,且不再需要該執行個體,請刪除:
gcloud lustre instances delete INSTANCE_ID --location=LOCATION
排解常見的基準化瓶頸
如果基準測試結果遠低於預期的儲存空間效能層級,請參閱「排解常見瓶頸」。