שימוש בקובץ אימג' מותאם אישית של מערכת ההפעלה
אתם יכולים להשתמש בקובץ אימג' מותאם אישית של מערכת ההפעלה למכונות הווירטואליות של ה-TPU כדי לטעון מראש תוכנה, להשתמש בהפצה ספציפית של מערכת ההפעלה או להחיל שינויים מותאמים אישית בליבה (kernel). כדי ליצור קובץ אימג' בהתאמה אישית, צריך לבצע שינויים ספציפיים במערכת במהלך תהליך היצירה של קובץ האימג', ולהגדיר את קובץ האימג' כך שיטפל במשימות בזמן ההפעלה שנדרשות לפונקציונליות של ה-TPU.
אם אתם משתמשים בקובץ אימג' של מערכת הפעלה בהתאמה אישית עם יחידות TPU, חשוב לזכור את כתבי הוויתור הבאים:
- Google מספקת קובצי אימג' של Ubuntu עם תמיכה לטווח ארוך (LTS) שמותאמים כברירת מחדל ל-TPU. השינויים במערכת ההפעלה שמפורטים בדף הזה אומתו רק לתמונות Ubuntu LTS שמותאמות ל-TPU ונתמכות על ידי Google.
- אתם אחראים להסיק מהם השינויים הנדרשים במערכת ההפעלה לכל הפצה אחרת של מערכת ההפעלה או לתמונות בהתאמה אישית. Google לא מבטיחה שהשינויים ב-Ubuntu שמפורטים בדף הזה יפעלו עם הפצות אחרות של מערכת ההפעלה או עם קובץ אימג' אחר של Ubuntu עם ליבה מותאמת אישית.
- Google לא יוצרת או מספקת בדיקות לקובצי אימג' של מערכות הפעלה, מלבד קובצי האימג' של Ubuntu LTS שמותאמים כברירת מחדל ל-TPU. אתם צריכים ליצור ולבדוק את קובץ האימג' של מערכת ההפעלה בהתאמה אישית.
למידע נוסף על קובצי האימג' של Ubuntu LTS שמותאמים כברירת מחדל ל-TPU, קראו את המאמר קובצי אימג' של מערכת ההפעלה ל-TPU.
דרישות מוקדמות
בתמונת הבסיס צריכים להיות מותקנים הרכיבים הבאים:
- Docker
-
ethtool, גרסה 6.7 ואילך אם משתמשים ב-TPU v6e או ב-TPU7x - Python 3 עם החבילות
PyYAMLו-requests, שזמינות ב-Ubuntu בתורpython3-yamlו-python3-requests - CLI של gcloud
במכונות וירטואליות מסוג TPU7x, קובץ האימג' הבסיסי צריך להשתמש בגרסה 6.8 לפחות של ליבת Linux של מערכת ההפעלה של האורח.
שינויים בתהליך יצירת התמונה
מבצעים את השינויים הבאים בזמן שיוצרים את קובץ האימג' בהתאמה אישית של Ubuntu.
השבתת שדרוגים אוטומטיים
משביתים ומסירים את unattended-upgrades כדי שעדכוני חבילות ברקע לא יחזיקו את הנעילה של dpkg או יפריעו לעומסי העבודה של ה-TPU:
echo 'APT::Periodic::Unattended-Upgrade "0";' > /etc/apt/apt.conf.d/99cloudtpuconf
DEBIAN_FRONTEND=noninteractive apt-get -y purge unattended-upgrades
קישור מכשירי TPU ל-VFIO
כדי לאפשר למערכת ההפעלה של המכונה להתחבר לחומרת ה-TPU, צריך לקשר את מכשירי ה-TPU למנהל ההתקן vfio-pci.
יוצרים קובץ כללים של 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"יוצרים סקריפט בשם
bind_to_vfio_pci.shב-/lib/udev/:#!/bin/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!"הופכים את הסקריפט לניתן להפעלה:
chmod +x /lib/udev/bind_to_vfio_pci.shנותנים לכל המשתמשים במערכת גישה למכשיר ה-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
פרמטרים של ליבה (kernel)
מעדכנים את הגדרות ה-GRUB (בדרך כלל ב-/etc/default/grub) כך שיכללו את הפרמטרים הבאים ב-GRUB_CMDLINE_LINUX:
-
idle=poll: מונעת מהמעבד (CPU) להיכנס למצבי חוסר פעילות בצריכת חשמל נמוכה. -
transparent_hugepage=always: הפעלה של Transparent Huge Pages (THP). -
iommu.passthrough=0: השבתת מצב מעבר של יחידת ניהול זיכרון קלט-פלט (IOMMU). -
panic=-1: הגדרת הליבה להפעלה מחדש באופן מיידי אם מתרחשת תגובה לשגיאת ליבה קריטית. -
intel_iommu=on,sm_on: מפעיל את Intel IOMMU במצב שניתן להתאמה. נדרש לארכיטקטורות TPU7x ו-v5p.
כך מעדכנים את הפרמטרים האלה של הליבה:
מגדירים את המשתנה של שורת הפקודה של הליבה באמצעות הפרמטרים הנפוצים:
kernel_cmdline="idle=poll transparent_hugepage=always iommu.passthrough=0 panic=-1"אם אתם יוצרים קובץ אימג' ל-TPU7x או ל-TPU v5p, הוסיפו את הפרמטר Intel IOMMU:
kernel_cmdline="${kernel_cmdline} intel_iommu=on,sm_on"מעדכנים את
GRUB_CMDLINE_LINUXב-/etc/default/grubויוצרים מחדש את הגדרת ה-GRUB:sed -i "s/^GRUB_CMDLINE_LINUX=\".*\"/GRUB_CMDLINE_LINUX=\"${kernel_cmdline}\"/" /etc/default/grub echo "Status: New kernel cmdline: $(grep -e '^GRUB_CMDLINE_LINUX=' /etc/default/grub)" update-grub
התקנת סוכן vBar
סוכן vBar נדרש כדי שרשת החיבורים בין השבבים (ICI) תפעל.
כדי לשלוף את קובץ האימג' של קונטיינר הסוכן של vBar במהלך יצירת קובץ האימג', מריצים את הפקודות הבאות:
מאמתים את Docker באמצעות
gcr.io:gcloud auth configure-docker gcr.ioמורידים את קובץ האימג' של Docker של נציג vBar:
docker pull gcr.io/cloud-tpu-v2-images/vbar_control_agent:0.0.3
כדי ללמוד איך להגדיר את קובץ האימג' להפעלת הקונטיינר של סוכן vBar בזמן האתחול, קראו את המאמר הפעלת סוכן vBar.
אופציונלי: התקנה והפעלה של AI Telemetry Collector
הכלי AI Telemetry Collector פועל בתוך ה-TPU VM ומאפשר לכם לגשת למדדי זמן ריצה ותשתית דרך Cloud Monitoring או דרך צינור עיבוד הנתונים שלכם למעקב שמבוסס על Prometheus. כדי להשתמש ב-AI Telemetry Collector עם מערכת הפעלה בהתאמה אישית, צריך להשתמש בקובץ האימג' של Docker ב-ai-telemetry-collector. אתם יכולים להתקין את קובץ האימג' במערכת ההפעלה בהתאמה אישית, ולהשתמש בקובץ config.yaml כדי לקבוע את מרווחי הזמן לאיסוף, להפעיל או להשבית מדדים ספציפיים או לשנות את יעדי הייצוא.
כדי להתקין ולהפעיל את AI Telemetry Collector, מריצים את הפקודות הבאות:
מאמתים את Docker באמצעות
gcr.io:gcloud auth configure-docker gcr.ioמורידים את קובץ האימג' של Docker ל-AI Telemetry Collector:
docker pull gcr.io/cloud-tpu-v2-images/ai-telemetry-collector:latestמריצים קונטיינר באמצעות קובץ האימג' של AI Telemetry Collector עם הגדרת ברירת המחדל:
docker run -d --restart=always --privileged --net=host gcr.io/cloud-tpu-v2-images/ai-telemetry-collector:latestבמאמר AI Telemetry Collector מוסבר איך להשתמש בקובץ תצורה בהתאמה אישית או להוסיף קובצי תצורה נוספים.
ביצוע שינויים בזמן ההפעלה
מגדירים את קובץ האימג' כך שיבצע את המשימות שבקטעים הבאים בכל פעם ש-VM מופעל. אתם יכולים להשתמש בכלי cloud-init כדי להגדיר משימות בזמן ההפעלה, על ידי העברת מטא-נתונים למכונות. ההגדרות בקטעים הבאים משתמשות במודולים כמו write_files ו-runcmd.
קטעי קוד שמגדירים קבצים לכתיבה צריכים להיכלל במפתח write_files:, ופקודות שצריך להריץ בזמן ההפעלה צריכות להיכלל במפתח runcmd: בהגדרות של cloud-init.
הפעלת הסוכן של vBar
מפעילים את מאגר סוכן הבקרה של vBar בזמן האתחול:
docker run -d --restart=always --privileged --net=host \
-e HOME=/home/tpu-runtime --name=vbar_control_agent \
gcr.io/cloud-tpu-v2-images/vbar_control_agent:0.0.3 \
vbar_control_agent_files/bin/vbar_control_agent --logtostderr
הגדרת משתני סביבה
כדי לוודא שהסביבה שלכם מאותחלת בצורה נכונה לעומסי עבודה של TPU, אתם צריכים לאחזר משתני הגדרה של זמן ריצה משרת המטא-נתונים של Compute Engine במהלך תהליך האתחול של המערכת. כדי לעשות את זה, מוסיפים את קטע הקוד הבא לקטע write_files: של ההגדרה cloud-init, שיוצר סקריפט בשם /var/scripts/configure-env-vars.sh. הסקריפט הזה מאחזר באופן אוטומטי מאפיינים ממפתח המטא-נתונים tpu-env ושומר אותם ב-/home/tpu-runtime/tpu-env, כדי שמחסנית התוכנה של ה-TPU תוכל להשתמש בהם.
write_files:
- path: /var/scripts/configure-env-vars.sh
permissions: '0755'
owner: root
content: |
#!/bin/bash
grep -q CLOUDSDK_PYTHON /etc/environment || echo "CLOUDSDK_PYTHON=/usr/bin/python3" >> /etc/environment
export HOME=/home/tpu-runtime
mkdir -p "${HOME}"
curl -s 'http://metadata.google.internal/computeMetadata/v1/instance/attributes/tpu-env' -H 'Metadata-Flavor: Google' > /tmp/tpu-env.yaml
python3 -c '
import yaml
with open("/tmp/tpu-env.yaml", "r") as stream_in:
for k, v in (yaml.safe_load(stream_in) or {}).items():
print("{var}={value}".format(var=k, value=str(v)))
' > "${HOME}/tpu-env"
rm -f /tmp/tpu-env.yaml
set -a
source "${HOME}/tpu-env"
set +a
קבלת מטא-נתונים של מכונות וירטואליות
קטע הקוד הבא יוצר סקריפט בשם /var/scripts/get-vm-metadata.py, כלי Python לשליחת שאילתות באופן פרוגרמטי לשרת המטא-נתונים, כדי לקבל מאפייני מכונה ספציפיים ותגי מטא-נתונים בהתאמה אישית. מוסיפים את הפרטים הבאים לקטע write_files: בהגדרות של cloud-init:
write_files:
- path: /var/scripts/get-vm-metadata.py
permissions: '0755'
owner: root
content: |
#!/usr/bin/env python3
import os
import sys
import requests
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.text)
elif request.status_code in (403, 404):
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.
write_files:
- path: /var/scripts/configure-gcs-timeouts.sh
permissions: '0755'
owner: root
content: |
#!/bin/bash
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
קביעת הגדרות הרשת
כדי לשפר את קצב העברת הנתונים ואת זמן האחזור ברשת במכונות וירטואליות של TPU, מוסיפים את הסקריפטים הבאים לקטע write_files: בהגדרות של cloud-init. במכונות וירטואליות מסוג TPU v6e ו-TPU7x, הסקריפטים האלה גם מאפשרים פיצול נתוני TCP, מגדילים את מגבלות מאגר הנתונים (buffer) לקבלת TCP ומפזרים את הפסיקות של ה-NIC בין ליבות NUMA. הסקריפטים האלה דורשים את הסקריפט /var/scripts/get-vm-metadata.py מהקטע קבלת מטא-נתונים של מכונות וירטואליות.
write_files:
- path: /var/scripts/configure-networking.sh
permissions: '0755'
owner: root
content: |
#!/bin/bash
ACCELERATOR_TYPE=$(/usr/bin/python3 /var/scripts/get-vm-metadata.py accelerator-type)
if [[ "$ACCELERATOR_TYPE" =~ ^v6e ]] || [[ "$ACCELERATOR_TYPE" =~ ^tpu7 ]]; then
echo "Enabling max rx buffer size for v6e, tpu7x & tpu7"
for port in $(ls /sys/class/net | grep ens); do
dhclient -r "$port"
if ethtool -h | grep -q tcp-data-split; then
if ethtool -G "$port" tcp-data-split on; then
echo "Max RX buffer size and TCP data split enabled for $port"
else
echo "Unable to enable max RX buffer size and TCP data split for $port"
fi
else
if ethtool --set-priv-flags "$port" enable-max-rx-buffer-size on; then
echo "Max RX buffer size enabled for $port"
else
echo "Unable to enable max RX buffer size for $port"
fi
fi
dhclient "$port"
done
else
echo "Skipping max rx buffer size for TPU generation $ACCELERATOR_TYPE before v6e"
fi
- path: /var/scripts/set_tcp_config.sh
permissions: '0755'
owner: root
content: |
#!/bin/bash
echo "Setting TCP config"
# Disable metrics cache
sysctl -w net.ipv4.tcp_no_metrics_save=1
# Disable slow start after idle
sysctl -w net.ipv4.tcp_slow_start_after_idle=0
# Set rto_min 5ms and enable quickack
sysctl_rto_min_exists=$(sysctl -a | grep tcp_rto_min_us)
if [[ -z "$sysctl_rto_min_exists" ]]; then
ip route show | while IFS= read -r route; do
if ! echo "$route" | grep -q "linkdown"; then
ip route change ${route/lock/} rto_min 5ms quickack 1
fi
done
else
sysctl -w net.ipv4.tcp_rto_min_us=5000
ip route show | while IFS= read -r route; do
if ! echo "$route" | grep -q "linkdown"; then
ip route change ${route/lock/} quickack 1
fi
done
fi
# Increase TCP zerocopy control memory
sysctl -w net.core.optmem_max=131072
# Disable Cubic Hystart Ack-Train
echo 2 > /sys/module/tcp_cubic/parameters/hystart_detect
ACCELERATOR_TYPE=$(/usr/bin/python3 /var/scripts/get-vm-metadata.py accelerator-type)
if [[ "$ACCELERATOR_TYPE" =~ ^v6e ]] || [[ "$ACCELERATOR_TYPE" =~ ^tpu7 ]]; then
# Increase TCP receive buffer limits
echo "4096 41943040 314572800" > /proc/sys/net/ipv4/tcp_rmem
fi
- path: /var/scripts/spread-nic-irqs.sh
permissions: '0755'
owner: root
content: |
#!/bin/bash
ACCELERATOR_TYPE=$(/usr/bin/python3 /var/scripts/get-vm-metadata.py accelerator-type)
if [[ "$ACCELERATOR_TYPE" == v6e* ]] || [[ "$ACCELERATOR_TYPE" == tpu7* ]]; then
node0_cores=$(echo /sys/devices/system/node/node0/cpu[0-9]* | wc -w)
ht_buddies=$(cat /sys/devices/system/cpu/cpu0/topology/core_cpus_list | tr ',' ' ' | wc -w)
total_schedulable_cores=$((node0_cores / ht_buddies))
second_numa_starting_core=${total_schedulable_cores}
first_numa_core=0
second_numa_core=${second_numa_starting_core}
for nic in $(ls -1 /sys/class/net); do
if [[ -d "/sys/class/net/$nic/device" ]]; then
NUM_QUEUES=$(echo /sys/class/net/"$nic"/queues/tx* | wc -w)
base_ntfy_block=$(ls -1 /sys/class/net/"${nic}"/device/msi_irqs | sort -n | head -n 1)
num_ntfy_blocks=$(ls /sys/class/net/"${nic}"/device/msi_irqs/ | wc -w)
tx_irq_base_directory=$((base_ntfy_block))
rx_irq_base_directory=$((base_ntfy_block + (num_ntfy_blocks / 2)))
core=0
max_core=${total_schedulable_cores}
nic_numa=$(cat /sys/class/net/"${nic}"/device/numa_node)
if [[ "${nic_numa}" -eq "1" ]]; then
core=${second_numa_core}
max_core=$((second_numa_starting_core + total_schedulable_cores))
else
core=${first_numa_core}
fi
for ((queue = 0; queue < $NUM_QUEUES; queue++)); do
echo $core > /proc/irq/$((tx_irq_base_directory + $queue))/smp_affinity_list
echo $core > /proc/irq/$((rx_irq_base_directory + $queue))/smp_affinity_list
cp /proc/irq/$((tx_irq_base_directory + $queue))/smp_affinity /sys/class/net/"$nic"/queues/tx-"$queue"/xps_cpus
core=$((core + 1))
if [[ $core -ge $max_core ]]; then
if [[ "${nic_numa}" -eq "1" ]]; then
core=${second_numa_starting_core}
else
core=0
fi
fi
done
if [[ "${nic_numa}" -eq "1" ]]; then
second_numa_core=${core}
else
first_numa_core=${core}
fi
fi
done
else
echo "Skipping NIC IRQ spreading for TPU generation $ACCELERATOR_TYPE before v6e"
fi
מריצים את הסקריפטים של האתחול
כדי להריץ את הסקריפטים של ההגדרות באופן אוטומטי כשהמכונה הווירטואלית מופעלת, מוסיפים את הקטע runcmd: להגדרות של cloud-init:
runcmd:
- /var/scripts/configure-env-vars.sh
- /var/scripts/configure-gcs-timeouts.sh
- /var/scripts/configure-networking.sh
- /var/scripts/set_tcp_config.sh
- /var/scripts/spread-nic-irqs.sh
המאמרים הבאים
- איך יוצרים מכונה של TPU VM עם קובץ אימג' בהתאמה אישית
- בודקים את קובצי האימג' הזמינים של מערכת ההפעלה של TPU.
- איך מנהלים מכונות וירטואליות של TPU