שימוש בתמונת מערכת הפעלה מותאמת אישית

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

אם אתם משתמשים בתמונת מערכת הפעלה בהתאמה אישית עם TPU, חשוב לזכור את ההצהרות הבאות:

  • ‫Google מספקת תמונות של Ubuntu עם תמיכה לטווח ארוך (LTS) שעברו אופטימיזציה ל-TPU כברירת מחדל. השינויים במערכת ההפעלה שמפורטים בדף הזה מאומתים רק לתמונות של Ubuntu LTS שעברו אופטימיזציה ל-TPU ונתמכות על ידי Google.
  • באחריותכם לבצע אקסטרפולציה של השינויים הנדרשים במערכת ההפעלה עבור כל הפצה אחרת של מערכת ההפעלה או תמונות בהתאמה אישית. ‫Google לא מבטיחה שהשינויים ב-Ubuntu שמפורטים בדף הזה יפעלו עם הפצות אחרות של מערכת ההפעלה או עם תמונה אחרת של Ubuntu עם ליבה מותאמת אישית.
  • ‫Google לא יוצרת או מספקת בדיקות לתמונות של מערכות הפעלה אחרות מלבד תמונות ברירת המחדל של Ubuntu LTS שעברו אופטימיזציה ל-TPU. צריך ליצור ולבדוק את תמונת מערכת ההפעלה המותאמת אישית.

מידע נוסף על תמונות ברירת המחדל של Ubuntu LTS שעברו אופטימיזציה ל-TPU זמין במאמר בנושא תמונות של מערכת הפעלה ל-TPU.

דרישות מוקדמות

תמונת הבסיס צריכה לכלול את הרכיבים הבאים:

  • ‫Python 3
  • ‫CLI של gcloud

ביצוע שינויים במהלך יצירת התמונה

מבצעים את השינויים הבאים כשיוצרים תמונת Ubuntu מותאמת אישית.

קישור מכשירי TPU ל-VFIO

כדי לאפשר למערכת ההפעלה של האורח לגשת לחומרת TPU, צריך לקשר את מכשירי ה-TPU למנהל ההתקן vfio-pci.

  1. יוצרים קובץ כללי udev בשם 99-tpu-vfiopci.rules ב-/etc/udev/rules.d/:

    # Rules for binding vfio-enabled TPU devices to vfio-pci.
    
    # v5p
    SUBSYSTEM=="pci", ACTION=="add", ATTRS{vendor}=="0x1ae0", ATTRS{device}=="0x0062", ATTRS{subsystem_vendor}=="0x1ae0", ATTRS{subsystem_device}=="0x00ad", DRIVER!="vfio-pci", TAG+="bind_to_vfio_pci"
    
    # v6e
    SUBSYSTEM=="pci", ACTION=="add", ATTRS{vendor}=="0x1ae0", ATTRS{device}=="0x006f", ATTRS{subsystem_vendor}=="0x1ae0", ATTRS{subsystem_device}=="0x00d1", DRIVER!="vfio-pci", TAG+="bind_to_vfio_pci"
    
    # TPU7x
    SUBSYSTEM=="pci", ACTION=="add", ATTRS{vendor}=="0x1ae0", ATTRS{device}=="0x0076", ATTRS{subsystem_vendor}=="0x1ae0", ATTRS{subsystem_device}=="0x00f2", DRIVER!="vfio-pci", TAG+="bind_to_vfio_pci"
    
    # Bind all 'bind_to_vfio_pci' tagged devices to vfio-pci.
    TAG=="bind_to_vfio_pci", RUN+="/lib/udev/bind_to_vfio_pci.sh $kernel"
    
  2. יוצרים סקריפט בשם bind_to_vfio_pci.sh ב-/lib/udev/:

    #!/bin/bash
    #!/usr/bin/env bash
    
    # Run ./bind_to_vfio_pci.sh <DBDF>
    # Binds the device at <DBDF> to vfio-pci.
    # If the device is already bound to a driver, unbinds it first.
    
    # Load the vfio-pci module into the kernel. No-op if already loaded.
    modprobe vfio-pci
    
    DBDF_REGEX="^[[:xdigit:]]{4}:[[:xdigit:]]{2}:[[:xdigit:]]{2}.[[:xdigit:]]$"
    
    unset BDF
    if [[ $1 =~ $DBDF_REGEX ]]; then
        BDF=$1
    else
        echo "Error: BDF arg ($1) is not in form dddd:bb:dd.f"
        exit 1
    fi
    
    PCI_PATH="/sys/bus/pci/devices/$BDF"
    
    echo "vfio-pci" > "$PCI_PATH/driver_override"
    
    PCI_DRIVER_PATH="$PCI_PATH/driver"
    if [[ -d "$PCI_DRIVER_PATH" ]]; then
        curr_driver=$(readlink "$PCI_DRIVER_PATH")
            curr_driver=${curr_driver##*/}
        if [[ $curr_driver == "vfio-pci" ]]; then
            echo "$BDF already bound to vfio-pci"
            exit 0
        else
            echo "$BDF" > "$PCI_DRIVER_PATH/unbind"
            if [[ -d "$PCI_DRIVER_PATH" ]]; then
                echo "Error: Unable to unbind $PCI_DRIVER_PATH"
                exit 1
            fi
            echo "Unbound $BDF from driver $curr_driver"
        fi
    fi
    echo "$BDF" > /sys/bus/pci/drivers_probe
    echo "Bound $BDF to vfio-pci"
    
    # Grant read/write access on VFIO device to all users
    IOMMU_GROUP=$(readlink "$PCI_PATH/iommu_group" | xargs basename)
    VFIO_DEV="/dev/vfio/$IOMMU_GROUP"
    if [[ -c "$VFIO_DEV" ]]; then
        chmod 0666 "$VFIO_DEV"
    else
        echo "$VFIO_DEV not found"
        exit 1
    fi
    
    # Set allow_unsafe_interrupts for x86 platforms.
    (uname -a | grep -q x86_64) && echo 1 > /sys/module/vfio_iommu_type1/parameters/allow_unsafe_interrupts
    
    # This is only needed to avoid non-zero exit code from previous command.
    echo "All Done!"
    
  3. הופכים את הסקריפט לניתן להרצה:

    chmod +x /lib/udev/bind_to_vfio_pci.sh
    
  4. נותנים לכל המשתמשים במערכת גישה למכשיר TPU:

    echo 'KERNEL=="accel*" MODE="0666"' >> /etc/udev/rules.d/99-tpu.rules
    

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

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

מגבלות זיכרון

אפשר לאפשר לתהליך יחיד לנעול זיכרון ללא הגבלה על ידי עדכון של /etc/security/limits.conf:

echo '*  hard  memlock  unlimited' >> /etc/security/limits.conf
echo '*  soft  memlock  unlimited' >> /etc/security/limits.conf

מגבלות על קבצים

כדי להגדיל את מספר הקבצים הפתוחים, מעדכנים את /etc/security/limits.conf:

echo "*    soft    nofile       100000" >> /etc/security/limits.conf
echo "*    hard    nofile       100000" >> /etc/security/limits.conf
echo "root soft    nofile       100000" >> /etc/security/limits.conf
echo "root hard    nofile       100000" >> /etc/security/limits.conf

פרמטרים של ליבה

מעדכנים את ההגדרות של GRUB (בדרך כלל ב-/etc/default/grub) כך שיכללו את הפרמטרים הבאים ב-GRUB_CMDLINE_LINUX:

  • idle=poll: מונע מהמעבד (CPU) להיכנס למצבי המתנה עם צריכת חשמל נמוכה.
  • intel_iommu=on,sm_on: מאפשרת את יחידת ניהול הזיכרון של קלט/פלט של אינטל (IOMMU). נדרש לארכיטקטורות TPU7x ו-v5p.
  • transparent_hugepage=always: הפעלה של Transparent Huge Pages (THP).

כדי לעדכן את פרמטרים של ליבת המערכת:

  1. כדי למנוע מהמעבד לעבור למצב המתנה עם צריכת חשמל נמוכה, מגדירים את המשתנה הבא, שבו תשתמשו בשלב הבא.

    kernel_cmdline="idle=poll"
    
  2. מפעילים את Intel Input-Output Memory Management Unit (IOMMU). השלב הזה נדרש עבור TPU7x ו-TPU v5p.

    kernel_cmdline="${kernel_cmdline} intel_iommu=on,sm_on";
    sed -i "s/GRUB_CMDLINE_LINUX=\"\"/GRUB_CMDLINE_LINUX=\"${kernel_cmdline}\"/" /etc/default/grub
    echo "Status: New kernel cmdline: $(cat /etc/default/grub | grep -e '^GRUB_CMDLINE_LINUX=')"
    
    update-grub
    
  3. הפעלת דפים גדולים שקופים (THP):

    echo "Status: Enabling THP"
    sed -i -r 's/GRUB_CMDLINE_LINUX="[a-zA-Z0-9_= ]*/& transparent_hugepage=always/' /etc/default/grub
    
    update-grub
    

התקנת סוכן vBar

סוכן vBar נדרש כדי שהרשת של חיבורים בין-שבביים (ICI) תפעל.

כדי להתקין את סוכן vBar, מריצים את הפקודות הבאות:

  1. אימות Docker באמצעות Artifact Registry:

    gcloud auth configure-docker us-docker.pkg.dev
    
  2. שליפת קובץ אימג' של Docker מ-Artifact Registry:

    docker pull gcr.io/cloud-tpu-v2-images/vbar_control_agent:0.0.1
    
  3. מריצים קונטיינר באמצעות קובץ האימג' של סוכן vBar:

    docker run --privileged --net=host vbar_control_agent:0.0.1
    

אופציונלי: התקנה והפעלה של הכלי AI Telemetry Collector

הכלי AI Telemetry Collector פועל בתוך מכונת ה-VM של TPU ומאפשר לכם לגשת למדדי זמן ריצה ולמדדי תשתית דרך Cloud Monitoring או דרך צינור ניטור משלכם שמבוסס על Prometheus. אתם יכולים להשתמש בכלי לאיסוף נתוני טלמטריה של AI עם מערכת הפעלה מותאמת אישית באמצעות ai-telemetry-collector קובץ האימג' של Docker. אפשר להתקין את התמונה במערכת ההפעלה המותאמת אישית ולהשתמש בקובץ config.yaml כדי להגדיר את מרווחי האיסוף, להפעיל או להשבית מדדים ספציפיים או לשנות את יעדי הייצוא.

כדי להתקין את הכלי לאיסוף נתוני טלמטריה של AI, מריצים את הפקודות הבאות:

  1. אימות Docker באמצעות Artifact Registry:

    gcloud auth configure-docker us-docker.pkg.dev
    
  2. שליפת קובץ אימג' של Docker מ-Artifact Registry:

    docker pull gcr.io/cloud-tpu-v2-images/ai-telemetry-collector:latest
    
  3. מריצים קונטיינר באמצעות תמונת ה-AI Telemetry Collector עם הגדרת ברירת המחדל:

    docker run --privileged --net=host ai-telemetry-collector:latest
    

    למידע על שימוש בקובץ תצורה בהתאמה אישית או על הוספה של קובצי תצורה נוספים, אפשר לעיין במאמר AI Telemetry Collector.

ביצוע שינויים בזמן האתחול

מגדירים את התמונה כך שתבצע את המשימות בקטעים הבאים בכל פעם שמפעילים מכונה וירטואלית. אתם יכולים להשתמש בכלי cloud-init כדי להגדיר משימות שמתבצעות בזמן האתחול על ידי העברת מטא-נתונים למופעים. ההגדרות בקטעים הבאים משתמשות במודולים כמו write_files ו-runcmd. קטעי קוד שמגדירים קבצים לכתיבה צריכים להיכלל במפתח write_files:, ופקודות שצריך להריץ בזמן האתחול צריכות להיכלל במפתח runcmd: בהגדרות cloud-init.

הפעלת סוכן vBar

מפעילים את סוכן השליטה vBar עם מזהי המשתמש והקבוצה המתאימים:

vbar_control_agent --logtostderr --gid= --uid=  --chroot= --census_enabled=false --loas_pwd_fallback_in_corp

הגדרת משתני סביבה

כדי לוודא שהסביבה שלכם מאותחלת בצורה נכונה עבור עומסי עבודה של TPU, אתם צריכים לאחזר משתני הגדרות זמן ריצה משרת המטא-נתונים של Compute Engine במהלך תהליך אתחול המערכת. כדי לעשות זאת, מוסיפים את קטע הקוד הבא לקטע write_files: בהגדרות של cloud-init. קטע הקוד הזה יוצר סקריפט בשם /var/scripts/configure-env-vars.sh. הסקריפט הזה מבצע אוטומטית אחזור של מאפיינים ממפתח המטא-נתונים tpu-env ושומר אותם ב-/${HOME}/tpu-env כדי להשתמש בהם במערך התוכנה של TPU.

 - path: /var/scripts/configure-env-vars.sh
    permissions: 0444
    owner: root
    content: |
      grep -q CLOUDSDK_PYTHON /etc/environment || echo "CLOUDSDK_PYTHON=/usr/bin/python3" >> /etc/environment

      export HOME=/home/tpu-runtime
      curl -s 'http://metadata.google.internal/computeMetadata/v1/instance/attributes/tpu-env' -H 'Metadata-Flavor: Google' > /tmp/tpu-env.yaml

      eval $(python3 -c '''
      import yaml
      stream_in=open("/tmp/tpu-env.yaml", "r")
      for k,v in yaml.safe_load(stream_in).items():
        print("{var}=\"{value}\"".format(var = k, value = str(v)))
      ''' > "/${HOME}/tpu-env"
      )

      rm -f "/tmp/tpu-env.yaml"

      printenv
      cat ${HOME}/tpu-env

אחזור מטא-נתונים של מכונה וירטואלית

קטע הקוד הבא יוצר סקריפט בשם /var/scripts/get-vm-metadata.py, כלי Python לשליחת שאילתות לשרת המטא-נתונים באופן פרוגרמטי לגבי מאפיינים ספציפיים של מופע ותגי מטא-נתונים בהתאמה אישית. מוסיפים את השורה הבאה לקטע write_files: בהגדרות של cloud-init:

 - path: /var/scripts/get-vm-metadata.py
    permissions: 0444
    owner: root
    content: |
      import sys, requests, os

      if len(sys.argv) < 2:
        sys.stderr.write('Must provide key')
        os._exit(1)

      key = sys.argv[1]
      default = None
      if len(sys.argv) > 2:
        default = sys.argv[2]

      attribute_type = 'attributes'
      if len(sys.argv) > 3:
        attribute_type = sys.argv[3]

      request = requests.get("http://metadata.google.internal/computeMetadata/v1/instance/{}/{}".format(attribute_type, key), headers={'Metadata-Flavor': 'Google'})
      if request.status_code == 200:
        print(request.content)
      elif request.status_code == 404 or request.status_code == '403':
        sys.stderr.write('Metadata key: {} does not exist\n'.format(key))
        if default:
          print(default)
      else:
        sys.stderr.write('Lookup failed with: {}'.format(request))

הגדלת הזמן הקצוב לתפוגה ב-Cloud Storage

אם עומס העבודה שלכם מתקשר עם Cloud Storage, כדאי להגדיל את משך הזמן הקצוב לתפוגה על ידי הוספת ערכים של זמן קצוב לתפוגה אל /etc/environment. כדי לעשות זאת, מוסיפים את קטע הקוד הבא לקטע write_files: בהגדרות של cloud-init, וכך נוצר סקריפט בשם /var/scripts/configure-gcs-timeouts.sh.

 - path: /var/scripts/configure-gcs-timeouts.sh
    permissions: 0444
    owner: root
    content: |
      echo "GCS_RESOLVE_REFRESH_SECS=60" >> /etc/environment
      echo "GCS_REQUEST_CONNECTION_TIMEOUT_SECS=300" >> /etc/environment
      echo "GCS_METADATA_REQUEST_TIMEOUT_SECS=300" >> /etc/environment
      echo "GCS_READ_REQUEST_TIMEOUT_SECS=300" >> /etc/environment
      echo "GCS_WRITE_REQUEST_TIMEOUT_SECS=600" >> /etc/environment

המאמרים הבאים