Menggunakan image OS kustom

Anda dapat menggunakan image OS kustom untuk VM TPU guna melakukan pra-pemuatan software, menggunakan distribusi OS tertentu, atau menerapkan modifikasi kernel kustom. Membuat image kustom melibatkan pembuatan modifikasi sistem tertentu selama proses pembuatan image dan mengonfigurasi image untuk menangani tugas waktu booting yang diperlukan untuk fungsionalitas TPU.

Perhatikan pernyataan penyangkalan berikut jika Anda menggunakan image OS kustom dengan TPU:

  • Google menyediakan image dukungan jangka panjang (LTS) Ubuntu yang dioptimalkan untuk TPU secara default. Perubahan OS yang tercantum di halaman ini hanya divalidasi untuk image Ubuntu LTS yang dioptimalkan untuk TPU dan didukung Google.
  • Anda bertanggung jawab untuk mengekstrapolasi perubahan OS yang diperlukan untuk distribusi OS atau image kustom lainnya. Google tidak menjamin bahwa modifikasi untuk Ubuntu yang tercantum di halaman ini berfungsi dengan distribusi OS lain atau image Ubuntu lain dengan kernel kustom.
  • Google tidak membuat atau menyediakan pengujian untuk OS image apa pun selain OS image Ubuntu LTS default yang dioptimalkan untuk TPU. Anda harus membangun dan menguji image OS kustom Anda.

Untuk mengetahui informasi selengkapnya tentang image Ubuntu LTS yang dioptimalkan untuk TPU default, lihat Image OS TPU.

Prasyarat

Image dasar Anda harus menginstal komponen berikut:

  • Docker
  • ethtool, versi 6.7 atau yang lebih baru jika Anda menggunakan TPU v6e atau TPU7x
  • Python 3 dengan paket PyYAML dan requests, tersedia di Ubuntu sebagai python3-yaml dan python3-requests
  • gcloud CLI

Untuk VM TPU7x, image dasar Anda harus menggunakan versi kernel Linux OS tamu minimum 6.8.

Melakukan perubahan selama pembuatan image

Terapkan perubahan berikut saat membuat image Ubuntu kustom Anda.

Menonaktifkan upgrade tanpa pengawasan

Nonaktifkan dan hapus unattended-upgrades agar update paket latar belakang tidak menahan kunci dpkg atau mengganggu workload TPU:

echo 'APT::Periodic::Unattended-Upgrade "0";' > /etc/apt/apt.conf.d/99cloudtpuconf
DEBIAN_FRONTEND=noninteractive apt-get -y purge unattended-upgrades

Mengikat perangkat TPU ke VFIO

Agar OS tamu dapat mengakses hardware TPU, Anda harus mengikat perangkat TPU ke driver vfio-pci.

  1. Buat file aturan udev bernama 99-tpu-vfiopci.rules di /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. Buat skrip bernama bind_to_vfio_pci.sh di /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!"
    
  3. Buat agar skrip dapat dieksekusi:

    chmod +x /lib/udev/bind_to_vfio_pci.sh
    
  4. Beri semua pengguna di sistem akses ke perangkat TPU:

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

Ubah gambar untuk meningkatkan performa

Untuk memastikan performa yang optimal, sesuaikan batas dan parameter sistem berikut.

Batas memori

Izinkan satu proses untuk mengunci memori tanpa batas dengan mengupdate /etc/security/limits.conf:

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

Batas file

Tingkatkan jumlah file yang dapat dibuka dengan memperbarui /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

Parameter kernel

Perbarui konfigurasi GRUB Anda (biasanya di /etc/default/grub) untuk menyertakan parameter berikut di GRUB_CMDLINE_LINUX:

  • idle=poll: Mencegah CPU memasuki status tidak ada aktivitas berdaya rendah.
  • transparent_hugepage=always: Mengaktifkan Transparent Huge Pages (THP).
  • iommu.passthrough=0: Menonaktifkan mode passthrough Unit Manajemen Memori Input-Output (IOMMU).
  • panic=-1: Mengonfigurasi kernel untuk segera memulai ulang jika terjadi kernel panic.
  • intel_iommu=on,sm_on: Mengaktifkan Intel IOMMU dalam mode skalabel. Diperlukan untuk arsitektur TPU7x dan v5p.

Langkah-langkah berikut menunjukkan cara memperbarui parameter kernel ini:

  1. Tetapkan variabel command line kernel dengan parameter umum:

    kernel_cmdline="idle=poll transparent_hugepage=always iommu.passthrough=0 panic=-1"
    
  2. Jika Anda membangun image untuk TPU7x atau TPU v5p, tambahkan parameter Intel IOMMU:

    kernel_cmdline="${kernel_cmdline} intel_iommu=on,sm_on"
    
  3. Perbarui GRUB_CMDLINE_LINUX di /etc/default/grub dan buat ulang konfigurasi 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
    

Menginstal agen vBar

Agen vBar diperlukan agar jaringan inter-chip interconnect (ICI) dapat berfungsi.

Untuk mengambil image container agen vBar selama pembuatan image, jalankan perintah berikut:

  1. Autentikasi Docker dengan gcr.io:

    gcloud auth configure-docker gcr.io
    
  2. Ambil image Docker agen vBar:

    docker pull gcr.io/cloud-tpu-v2-images/vbar_control_agent:0.0.3
    

Untuk mempelajari cara mengonfigurasi image untuk memulai container agen vBar pada waktu booting, lihat Memulai agen vBar.

Opsional: Menginstal dan menjalankan AI Telemetry Collector

AI Telemetry Collector berjalan di dalam VM TPU dan memungkinkan Anda mengakses metrik runtime dan infrastruktur melalui Cloud Monitoring atau melalui pipeline pemantauan berbasis Prometheus Anda sendiri. Anda dapat menggunakan AI Telemetry Collector dengan OS kustom menggunakan image Docker ai-telemetry-collector. Anda dapat menginstal image ke OS kustom dan menggunakan file config.yaml untuk menentukan interval pengumpulan, mengaktifkan atau menonaktifkan metrik tertentu, atau mengubah tujuan ekspor.

Untuk menginstal dan menjalankan AI Telemetry Collector, jalankan perintah berikut:

  1. Autentikasi Docker dengan gcr.io:

    gcloud auth configure-docker gcr.io
    
  2. Ambil image Docker AI Telemetry Collector:

    docker pull gcr.io/cloud-tpu-v2-images/ai-telemetry-collector:latest
    
  3. Jalankan container menggunakan image AI Telemetry Collector dengan konfigurasi default:

    docker run -d --restart=always --privileged --net=host gcr.io/cloud-tpu-v2-images/ai-telemetry-collector:latest
    

    Untuk mengetahui informasi tentang cara menggunakan file konfigurasi kustom atau menambahkan file konfigurasi tambahan, lihat AI Telemetry Collector.

Melakukan modifikasi waktu booting

Konfigurasikan image Anda untuk melakukan tugas di bagian berikut setiap kali VM melakukan booting. Anda dapat menggunakan alat cloud-init untuk mengonfigurasi tugas waktu booting dengan meneruskan metadata ke instance Anda. Konfigurasi di bagian berikut menggunakan modul seperti write_files dan runcmd. Cuplikan yang menentukan file yang akan ditulis harus disertakan di bagian kunci write_files:, dan perintah yang harus dijalankan pada waktu booting harus disertakan di bagian kunci runcmd: dalam konfigurasi cloud-init Anda.

Memulai agen vBar

Mulai container agen kontrol vBar pada waktu booting:

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

Mengonfigurasi variabel lingkungan

Untuk memastikan lingkungan Anda diinisialisasi dengan benar untuk workload TPU, Anda harus mengambil variabel konfigurasi runtime dari server metadata Compute Engine selama proses booting sistem. Untuk melakukannya, tambahkan cuplikan berikut ke bagian write_files: dari konfigurasi cloud-init Anda, yang membuat skrip bernama /var/scripts/configure-env-vars.sh. Skrip ini mengotomatiskan pengambilan atribut dari kunci metadata tpu-env dan menyimpannya di /home/tpu-runtime/tpu-env untuk digunakan oleh stack software 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

Mendapatkan metadata VM

Cuplikan berikut membuat skrip bernama /var/scripts/get-vm-metadata.py, yaitu utilitas Python untuk mengkueri server metadata secara terprogram guna mendapatkan atribut instance dan tag metadata kustom tertentu. Tambahkan kode berikut ke bagian write_files: pada konfigurasi cloud-init Anda:

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))

Meningkatkan waktu tunggu Cloud Storage

Jika workload Anda berinteraksi dengan Cloud Storage, tingkatkan durasi waktu tunggu dengan menambahkan nilai waktu tunggu ke /etc/environment. Untuk melakukannya, tambahkan cuplikan berikut ke bagian write_files: dari konfigurasi cloud-init Anda, yang akan membuat skrip bernama /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

Mengonfigurasi setelan jaringan

Untuk mengoptimalkan throughput dan latensi jaringan di VM TPU, tambahkan skrip berikut ke bagian write_files: pada konfigurasi cloud-init Anda. Di VM TPU v6e dan TPU7x, skrip ini juga mengaktifkan pemisahan data TCP, peningkatan batas buffer penerimaan TCP, dan penyebaran interupsi NIC di seluruh core NUMA. Skrip ini memerlukan skrip /var/scripts/get-vm-metadata.py dari bagian Mendapatkan metadata VM.

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

Menjalankan skrip booting

Untuk menjalankan skrip konfigurasi secara otomatis saat VM melakukan booting, tambahkan bagian runcmd: ke konfigurasi cloud-init Anda:

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

Langkah berikutnya