Utiliser une image d'OS personnalisée

Vous pouvez utiliser une image d'OS personnalisée pour vos VM TPU afin de précharger des logiciels, d'utiliser une distribution d'OS spécifique ou d'appliquer des modifications personnalisées au noyau. La création d'une image personnalisée implique d'apporter des modifications spécifiques au système lors du processus de création de l'image et de configurer l'image pour qu'elle gère les tâches de démarrage requises pour la fonctionnalité TPU.

Si vous utilisez une image d'OS personnalisée avec des TPU, tenez compte des avertissements suivants :

  • Google fournit des images Ubuntu LTS (support à long terme) optimisées pour les TPU par défaut. Les modifications de l'OS listées sur cette page ne sont validées que pour les images Ubuntu LTS optimisées pour les TPU et compatibles avec Google.
  • Il vous incombe d'extrapoler les modifications requises de l'OS pour toute autre distribution d'OS ou image personnalisée. Google ne garantit pas que les modifications pour Ubuntu listées sur cette page fonctionnent avec d'autres distributions d'OS ou une autre image Ubuntu avec un noyau personnalisé.
  • Google ne crée ni ne fournit de tests pour les images d'OS autres que les images Ubuntu LTS optimisées pour les TPU par défaut. Vous devez créer et tester votre image d'OS personnalisée.

Pour en savoir plus sur les images Ubuntu LTS optimisées pour les TPU par défaut, consultez Images d'OS TPU.

Prérequis

Les composants suivants doivent être installés sur votre image de base :

  • Python 3
  • gcloud CLI

Apporter des modifications lors de la création d'une image

Appliquez les modifications suivantes lorsque vous créez votre image Ubuntu personnalisée.

Associer des appareils TPU à VFIO

Pour permettre à l'OS invité d'accéder au matériel TPU, vous devez associer les appareils TPU au pilote vfio-pci.

  1. Créez un fichier de règles udev nommé 99-tpu-vfiopci.rules dans /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. Créez un script nommé bind_to_vfio_pci.sh dans /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. Rendez le script exécutable :

    chmod +x /lib/udev/bind_to_vfio_pci.sh
    
  4. Accordez à tous les utilisateurs du système l'accès au périphérique TPU :

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

Modifier l'image pour améliorer les performances

Pour garantir des performances optimales, ajustez les limites et les paramètres système suivants.

Limites de mémoire

Autorisez un processus unique à verrouiller une quantité illimitée de mémoire en mettant à jour /etc/security/limits.conf :

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

Limites de fichiers

Augmentez le nombre de fichiers ouverts en modifiant /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

Paramètres du noyau

Mettez à jour votre configuration GRUB (généralement dans /etc/default/grub) pour inclure les paramètres suivants dans GRUB_CMDLINE_LINUX :

  • idle=poll : empêche le processeur de passer en mode veille basse consommation.
  • intel_iommu=on,sm_on : active l'unité de gestion de la mémoire d'entrée/sortie (IOMMU) d'Intel. Obligatoire pour les architectures TPU7x et v5p.
  • transparent_hugepage=always : active les pages énormes transparentes (THP).

Pour mettre à jour ces paramètres du noyau, procédez comme suit :

  1. Empêchez le processeur de passer en état d'inactivité à faible consommation d'énergie en définissant la variable suivante, que vous utiliserez à l'étape suivante.

    kernel_cmdline="idle=poll"
    
  2. Activez l'unité de gestion de mémoire d'E/S (IOMMU) Intel. Cette étape est obligatoire pour TPU7x et 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. Activez les Transparent Huge Pages (THP) :

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

Installer l'agent vBar

L'agent vBar est nécessaire au fonctionnement du réseau d'interconnexion entre les puces (ICI).

Pour installer l'agent vBar, exécutez les commandes suivantes :

  1. Authentifiez Docker avec Artifact Registry :

    gcloud auth configure-docker us-docker.pkg.dev
    
  2. Extrayez l'image Docker d'Artifact Registry :

    docker pull gcr.io/cloud-tpu-v2-images/vbar_control_agent:0.0.1
    
  3. Exécutez un conteneur à l'aide de l'image de l'agent vBar :

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

Facultatif : Installez et exécutez le collecteur de télémétrie IA

Le collecteur de télémétrie d'IA s'exécute dans la VM TPU et vous permet d'accéder aux métriques d'exécution et d'infrastructure via Cloud Monitoring ou votre propre pipeline de surveillance basé sur Prometheus. Vous pouvez utiliser le collecteur de télémétrie IA avec un OS personnalisé à l'aide de l'image Docker ai-telemetry-collector. Vous pouvez installer l'image sur votre OS personnalisé et utiliser un fichier config.yaml pour définir les intervalles de collecte, activer ou désactiver des métriques spécifiques, ou modifier les destinations d'exportation.

Pour installer le collecteur de télémétrie de l'IA, exécutez les commandes suivantes :

  1. Authentifiez Docker avec Artifact Registry :

    gcloud auth configure-docker us-docker.pkg.dev
    
  2. Extrayez l'image Docker d'Artifact Registry :

    docker pull gcr.io/cloud-tpu-v2-images/ai-telemetry-collector:latest
    
  3. Exécutez un conteneur à l'aide de l'image du collecteur de télémétrie d'IA avec la configuration par défaut :

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

    Pour savoir comment utiliser un fichier de configuration personnalisé ou ajouter des fichiers de configuration supplémentaires, consultez Collecteur de télémétrie de l'IA.

Modifier le temps de démarrage

Configurez votre image pour qu'elle effectue les tâches décrites dans les sections suivantes chaque fois qu'une VM démarre. Vous pouvez utiliser l'outil cloud-init pour configurer les tâches au moment du démarrage en transmettant des métadonnées à vos instances. Les configurations des sections suivantes utilisent des modules tels que write_files et runcmd. Les extraits qui définissent les fichiers à écrire doivent être inclus sous la clé write_files:, et les commandes à exécuter au moment du démarrage doivent être incluses sous la clé runcmd: dans votre configuration cloud-init.

Démarrer l'agent vBar

Lancez l'agent de contrôle vBar avec les ID d'utilisateur et de groupe appropriés :

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

Configurer les variables d'environnement

Pour vous assurer que votre environnement est correctement initialisé pour les charges de travail TPU, vous devez récupérer les variables de configuration du runtime à partir du serveur de métadonnées Compute Engine lors du processus de démarrage du système. Pour ce faire, ajoutez l'extrait suivant à la section write_files: de votre configuration cloud-init, qui crée un script nommé /var/scripts/configure-env-vars.sh. Ce script automatise la récupération des attributs à partir de la clé de métadonnées tpu-env et les enregistre dans /${HOME}/tpu-env pour qu'ils soient utilisés par la pile logicielle 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

Obtenir les métadonnées de VM

L'extrait suivant crée un script nommé /var/scripts/get-vm-metadata.py, un utilitaire Python permettant d'interroger par programmation le serveur de métadonnées pour obtenir des attributs d'instance spécifiques et des tags de métadonnées personnalisés. Ajoutez ce qui suit à la section write_files: de votre configuration 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))

Augmenter les délais d'attente de Cloud Storage

Si votre charge de travail interagit avec Cloud Storage, augmentez les délais d'attente en ajoutant des valeurs de délai d'attente à /etc/environment. Pour ce faire, ajoutez l'extrait suivant à la section write_files: de votre configuration cloud-init, qui crée un script nommé /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

Étapes suivantes