בדיקות ביצועים עם לקוחות Compute Engine

בדף הזה מוסבר איך לבדוק את הביצועים של מופע Google Cloud Managed Lustre באמצעות לקוחות Compute Engine. במדריך מוסבר איך למדוד את הביצועים של לקוח יחיד באמצעות fio, ואיך למדוד את הביצועים המצטברים של כמה לקוחות באמצעות כלי ההשוואה של IOR.

מדידת הביצועים של לקוח יחיד

כדי לבדוק את ביצועי הקריאה והכתיבה מלקוח יחיד של Compute Engine, משתמשים בכלי fio (Flexible I/O tester) של שורת הפקודה.

  1. מתקינים את fio:

    Rocky 8

    sudo dnf install fio -y
    

    ‫Ubuntu 20.04 ו-22.04

    sudo apt update
    sudo install fio
    
  2. מריצים את הפקודה הבאה:

    fio --ioengine=libaio --filesize=32G --ramp_time=2s \
    --runtime=5m --numjobs=16 --direct=1 --verify=0 --randrepeat=0 \
    --group_reporting --directory=/lustre --buffer_compress_percentage=50 \
    --name=read --blocksize=1m --iodepth=64 --readwrite=read
    

הבדיקה נמשכת כ-5 דקות. בסיום, התוצאות מוצגות. בהתאם להגדרה, אפשר לצפות לתפוקה של עד מהירות הרשת המקסימלית של המכונה הווירטואלית, ולאלפי פעולות קלט/פלט בשנייה לכל ‎TiB.

מדידת הביצועים בחשבון מרובה לקוחות

כדי לבדוק את ביצועי הקריאה והכתיבה של Managed Lustre מכמה לקוחות של Compute Engine, אפשר להשתמש בכלי ההשוואה IOR. בהוראות הבאות מוסבר איך להפוך את הגדרת הלקוח לאוטומטית ולהשתמש ב-IOR כדי לבדוק את צבירת הקלט/פלט ממספר מכונות לקוח. ‫IOR משתמש ב-MPI – פרוטוקול להעברת הודעות – כדי לאפשר למכונות הלקוח המרובות לתאם ביניהן.

לפני שמתחילים, מוודאים שהערך של mtu ברשת מוגדר ל-8896.

הגדרת משתני סביבה ויצירת מפתח SSH

לפני שמפעילים את האשכול, צריך ליצור מפתח SSH במחשב המקומי. המפתח הזה יופץ למכונות הלקוח במהלך היצירה כדי לאפשר תקשורת ללא סיסמה עבור MPI.

export SSH_USER="lustre-user"
export CLIENT_PREFIX="lustre-client"

# Generate an SSH key for the specified user
ssh-keygen -t rsa -b 4096 -C "${SSH_USER}" -N '' -f "./id_rsa"
chmod 600 "./id_rsa"

# Create a metadata file formatted for Google Cloud
echo "${SSH_USER}:$(cat "./id_rsa.pub") ${SSH_USER}" > "./keys.txt"

יצירת סקריפט לטעינה בזמן ההפעלה

שומרים את התוכן הבא בקובץ בשם install-ior.sh במחשב המקומי. הסקריפט הזה מזהה את מערכת ההפעלה, מחכה עד שהנעילות של ההפעלה ישוחררו, מתקין בצורה בטוחה את לקוח Lustre ואת יחסי התלות, מהדר גרסה יציבה של IOR ומטמיע את מערכת הקבצים של Managed Lustre.

מחליפים את LUSTRE_IP בכתובת ה-IP של מופע Managed Lustre ואת FS_NAME בשם של מערכת הקבצים.

#!/bin/bash
source /etc/os-release

if [[ "$ID" == "ubuntu" ]]; then
    # Ubuntu
    # Wait for apt lock
    while fuser /var/lib/dpkg/lock-frontend >/dev/null 2>&1; do sleep 5; done

    # Configure Artifact Registry repo for Ubuntu
    curl -fsSL https://us-apt.pkg.dev/doc/repo-signing-key.gpg | sudo gpg --dearmor -o /usr/share/keyrings/us-apt-pkg-dev.gpg

    if [[ "$VERSION_ID" == "22.04" ]]; then
        REPO_NAME="lustre-client-ubuntu-jammy"
    elif [[ "$VERSION_ID" == "24.04" ]]; then
        REPO_NAME="lustre-client-ubuntu-noble"
    fi

    echo "deb [signed-by=/usr/share/keyrings/us-apt-pkg-dev.gpg] https://us-apt.pkg.dev/projects/lustre-client-binaries $REPO_NAME main" | sudo tee /etc/apt/sources.list.d/lustre-client.list

    sudo apt-get update
    while ! sudo apt-get install -y lustre-client-modules-$(uname -r) lustre-client-utils openmpi-bin libopenmpi-dev make gcc g++ wget git automake autoconf libaio-dev; do
        sleep 5
    done
    sudo modprobe lustre
else
    # Red Hat / Rocky Linux
    while systemctl is-active --quiet dnf-makecache.service; do sleep 5; done

    if [[ "$ID" == "rocky" && "$VERSION_ID" == 8* ]]; then
        REPO="lustre-client-rocky-8"
    elif [[ "$ID" == "rocky" && "$VERSION_ID" == 9* ]]; then
        REPO="lustre-client-rocky-9"
    elif [[ "$ID" == "rhel" && "$VERSION_ID" == 9* ]]; then
        REPO="lustre-client-rocky-9"
    fi

    gcloud beta artifacts print-settings yum --repository=$REPO --location=us --project=lustre-client-binaries | sudo bash

    while ! sudo dnf -y --enablerepo=$REPO install kmod-lustre-client lustre-client; do sleep 5; done
    sudo modprobe lustre

    while ! sudo dnf install -y openmpi openmpi-devel make gcc gcc-c++ wget git automake autoconf libaio-devel; do sleep 5; done

    export PATH=$PATH:/usr/lib64/openmpi/bin
fi

# Build IOR from source
echo "Cloning repo: https://github.com/hpc/ior.git and building IOR"
pushd /tmp
git clone -b 4.0.0 https://github.com/hpc/ior
cd ior
./bootstrap
./configure --disable-dependency-tracking --with-aio
make clean
make -j"$(nproc)"
sudo make install
popd
echo "Finished building IOR"

# Mount the Managed Lustre file system
mkdir -p /lustre

if ! grep -q "/lustre" /etc/fstab; then
    echo "LUSTRE_IP@tcp:/FS_NAME /lustre lustre defaults,_netdev 0 0" >> /etc/fstab
fi

mount -a

פריסת מכונות הלקוח

מריצים את הפקודה הבאה כדי ליצור בכמות גדולה את מכונות הלקוח של Compute Engine.

gcloud compute instances bulk create \
  --name-pattern="${CLIENT_PREFIX}-####" \
  --zone="ZONE" \
  --machine-type="MACHINE_TYPE" \
  --scopes="https://www.googleapis.com/auth/cloud-platform" \
  --network-interface=subnet=SUBNET,nic-type=GVNIC \
  --network-performance-configs=total-egress-bandwidth-tier=TIER_1 \
  --metadata-from-file=ssh-keys=./keys.txt,startup-script=install-ior.sh \
  --create-disk=auto-delete=yes,boot=yes,\
image-family=IMAGE_FAMILY,\
image-project=IMAGE_PROJECT,\
mode=rw,size=100,type=DISK_TYPE \
  --count NUM_NODES
  • מחליפים את ZONE ו-SUBNET בערכים הספציפיים של הפריסה.

  • בוחרים MACHINE_TYPE. הביצועים הכוללים של ההשוואה לשוק תלויים בסוגי המכונות של הלקוח. במאמר שיקולים לגבי ביצועים יש מידע על בחירת סוגי מכונות כדי להשיג את התפוקה הטובה ביותר.

  • אם סוג המכונה לא תומך ברשת TIER_1, צריך למחוק את השורה --network-performance-configs מהפקודה.

  • מגדירים את DISK_TYPE לאחד מהערכים הבאים: hyperdisk-balanced (לסוג מכונה עם 4 בשם הגנרציה, כמו c4a או n4) או pd-balanced.

  • מציינים את IMAGE_FAMILY ואת IMAGE_PROJECT. הערכים הנתמכים הם:

    מערכת הפעלה משפחת תמונות (x86) משפחת תמונות (ARM) פרויקט תמונות
    HPC Rocky Linux 8 hpc-rocky-linux-8 לא נתמך cloud-hpc-image-public
    Rocky Linux 9 rocky-linux-9 rocky-linux-9-arm64 rocky-linux-cloud
    RHEL 9 rhel-9 rhel-9-arm64 rhel-cloud
    ‫Ubuntu 22.04 LTS ubuntu-2204-lts ubuntu-2204-lts-arm64 ubuntu-os-cloud
    Ubuntu 24.04 LTS לא נתמך ubuntu-2404-lts-arm64 ubuntu-os-cloud
  • מציינים את NUM_NODES. כדי להגיע לרוויה במערכת הקבצים, קיבולת הרשת הכוללת של האשכול צריכה להיות גדולה ב-20% בערך מהתפוקה שהוקצתה למערכת הקבצים.

    במכונות שמופעל בהן Tier 1 networking, מכונה אחת יכולה להעביר נתונים במהירות של ‎25 Gbps עד ‎200 Gbps (בערך ‎3,000 MBps עד ‎25,000 MBps), בהתאם לסוג המכונה הווירטואלית ולמספר ליבות ה-CPU. במכונות רגילות, תעבורת נתונים יוצאת (egress) מוגבלת בדרך כלל ל-2Gbps לכל vCPU.

    לדוגמה, אם הקיבולת של מופע Managed Lustre שלכם מניבה 100,000MBps של תפוקה תיאורטית, אתם צריכים תעבורת נתונים יוצאת מצטברת של לקוח של 120,000MBps (100,000 * 1.2) כדי להגיע לניצול מלא:

    • עם מכונות רגילות: אם לכל מכונת לקוח יש תעבורת נתונים יוצאת (egress) שפורסמה של 2,000MBps, צריך להקצות לפחות 60 לקוחות (120,000 / 2,000).
    • עם רשת ברמה 1: אם לכל מכונת לקוח יש תעבורת נתונים יוצאת שפורסמה של 10,000 מגה-בייט לשנייה (MBps) (בערך 80 גיגה-בייט לשנייה (Gbps)), צריך להקצות לפחות 12 לקוחות (120,000 / 10,000).

העתקת מפתחות וקבצים

בזמן שמחכים שמכונות הלקוח יסיימו את סקריפטים ההפעלה שלהן, מריצים את הפקודות הבאות באופן מקומי כדי להגדיר באופן אוטומטי את כל הצמתים לתקשורת בין הצמתים.

  1. שומרים את כתובות ה-IP הפרטיות של מכונות הלקוח בקובץ המארח של MPI, ואת כתובות ה-IP הציבוריות לגישת SSH ו-SCP למכונות הלקוח:

    gcloud compute instances list --filter="name ~ '^${CLIENT_PREFIX}*'" --format="csv[no-heading](INTERNAL_IP)" > hosts.txt
    gcloud compute instances list --filter="name ~ '^${CLIENT_PREFIX}*'" --format="csv[no-heading](EXTERNAL_IP)" > external_ips.txt
    
  2. מעתיקים את המפתח הפרטי לכל הלקוחות. כך צמתי העובדים יכולים לתקשר אחד עם השני בצורה מאובטחת במהלך ההשוואה:

    while IFS= read -r IP || [[ -n "$IP" ]]
    do
      [[ -z "$IP" ]] && continue
      echo "Preparing and copying to ${SSH_USER}@${IP}..."
      # Ensure the .ssh directory exists
      ssh -i ./id_rsa -o StrictHostKeyChecking=no "${SSH_USER}@${IP}" "mkdir -p ~/.ssh && chmod 700 ~/.ssh"
      # Copy the file
      scp -i ./id_rsa -o StrictHostKeyChecking=no ./id_rsa "${SSH_USER}@${IP}:~/.ssh/id_rsa"
    done < "./external_ips.txt"
    
  3. מגדירים את הצומת הראשי ומעתיקים אליו את קובץ המארח. זו תהיה המכונה שממנה מריצים את הבדיקה.

    export HEAD_NODE=$(head -n 1 ./external_ips.txt)
    scp -i ./id_rsa -o "StrictHostKeyChecking=no" -o UserKnownHostsFile=/dev/null ./hosts.txt ${SSH_USER}@${HEAD_NODE}:~/hostfile
    

חיבור ואימות

  1. מתחברים לצומת הראשי:

    ssh -i ./id_rsa -o "StrictHostKeyChecking=no" -o UserKnownHostsFile=/dev/null ${SSH_USER}@${HEAD_NODE}
    
  2. ודא שסקריפט לטעינה בזמן ההפעלה הסתיים ומערכת הקבצים נטענה בהצלחה לפני שמריצים את הערכת הביצועים.

    כדי לבדוק את היומנים של סקריפט לטעינה בזמן ההפעלה:

    sudo journalctl -u google-startup-scripts.service -f
    

    כדי לוודא שמערכת הקבצים נטענה:

    df -h | grep lustre
    

    אם מערכת הקבצים לא מופיעה, צריך לחכות עוד כמה דקות עד שסקריפט ההתקנה ברקע יסתיים. אחרי ההרכבה, הוא יהיה זמין בנתיב המוחלט /lustre.

הפעלת נקודת ההשוואה של IOR

  1. ב-Rocky Linux וב-RHEL בלבד: טוענים את מודול OpenMPI מצומת הראש.

    if [ -f /etc/profile.d/modules.sh ]; then
        source /etc/profile.d/modules.sh
        module load mpi/openmpi-$(arch)
    fi
    
  2. יוצרים ספריית בדיקה ומגדירים בעלות:

    sudo mkdir -p /lustre/test
    sudo chown -R $USER:$USER /lustre/test
    
  3. מגדירים את משתני הבדיקה:

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

    כאשר:

    • NUM_NODES: המספר הכולל של מכונות הלקוח שמשתתפות בבדיקה.
    • PROCESSES_PER_NODE: מספר דרגות ה-MPI להרצה בכל מכונת לקוח. מומלץ להתחיל בהגדרה שתתאים למספר הליבות הפיזיות (או למחצית ממספר יחידות ה-vCPU) במכונות הלקוח. במכונות עם ביצועים גבוהים, הגדרה של הערך הזה בין 8 ל-16 בדרך כלל מניבה את התפוקה הכי טובה ברשת. הגדרת ערך גבוה מדי עלולה לגרום לתקורה של החלפת הקשר ולהפחית את ביצועי ההשוואה.
  4. מריצים את הפקודה כדי להתחיל את נקודת ההשוואה:

    ‫Rocky Linux ו-RHEL

    קצב העברת נתונים לכתיבה

    הפקודה הזו כותבת ברציפות במשך 60 שניות כדי לבדוק את קצב העברת הנתונים המקסימלי במצב יציב. גודל הקובץ לכל משימה מוגדר ל-50TiB, שהוא גודל גדול באופן שרירותי, כדי שהמשימות ימשיכו לכתוב עד שתם הזמן של 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" \
      --prefix /usr/lib64/openmpi \
      --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) מוגדר ל-50TiB כדי להתאים לגיאומטריה של שלב הכתיבה, הדגל -O stoneWallingWearOut=1 מורה ל-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" \
      --prefix /usr/lib64/openmpi \
      --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) של כתיבה

    בבדיקה הזו נעשה שימוש בגדלי העברה קטנים של 4KiB ובגדלי קבצים של 8GiB לכל משימה כדי למדוד את מספר פעולות הקלט/פלט המקסימלי לשנייה (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" \
      --prefix /usr/lib64/openmpi \
      --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

    כדי למנוע קריאה מקובץ דליל, הבדיקה הזו משתמשת בשתי פקודות: פקודת כתיבה ליצירת קובץ מוצק בגודל העברה של 4MiB וגודל קובץ של 8GiB לכל משימה, ואחריה בדיקת IOPS של קריאה אקראית בגודל 4KiB.

    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" \
        --prefix /usr/lib64/openmpi \
        --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" \
        --prefix /usr/lib64/openmpi \
        --oversubscribe \
        --map-by node \
        --bind-to socket \
        --npernode ${PROCESSES_PER_NODE} \
        --np ${NUM_PROCESSES} \
        --hostfile ~/hostfile \
        /usr/local/bin/ior