Per testare le prestazioni di lettura e scrittura di un carico di lavoro Google Kubernetes Engine (GKE) da più client GKE, utilizza lo strumento di benchmark IOR. Le seguenti istruzioni mostrano come automatizzare la configurazione del client e utilizzare IOR con mpirun tramite SSH senza password tra i pod Kubernetes per testare l'I/O aggregato.
Prerequisiti
Un'istanza Managed Lustre di cui è già stato eseguito il provisioning.
Un ambiente Docker locale configurato e autenticato per il push su Google Artifact Registry o Container Registry (vedi Metodi di autenticazione).
Assicurati che il valore
mtudella rete sia impostato su8896.
Crea un cluster GKE
Per testare le prestazioni, devi disporre di un cluster GKE con il driver CSI Managed Lustre abilitato. Per i carichi di lavoro di archiviazione ad alte prestazioni, configura i node pool GKE con famiglie di macchine ottimizzate per il calcolo (ad es. c2 o c3) e la rete TIER_1.
Esegui il comando seguente per creare un cluster GKE Standard ottimizzato per i test sulle prestazioni:
gcloud container clusters create CLUSTER_NAME \
--zone=ZONE \
--machine-type=MACHINE_TYPE \
--addons=LustreCsiDriver \
--network-performance-configs=total-egress-bandwidth-tier=TIER_1 \
--network=NETWORK \
--num-nodes=NUM_NODES
Sostituisci ZONE e NETWORK con i valori di deployment specifici. Il cluster deve risiedere nella stessa rete VPC dell'istanza Managed Lustre.
Scegli un MACHINE_TYPE. Per informazioni sulla scelta dei tipi di macchine per ottenere il miglior throughput, consulta Considerazioni sul rendimento.
Se il tipo di macchina non supporta la rete TIER_1, elimina la riga
--network-performance-configsdal comando.Specifica NUM_NODES. Per saturare il file system, la capacità di rete aggregata del cluster deve superare il throughput di cui è stato eseguito il provisioning del file system di circa il 20%.
Per le macchine con la rete Tier 1 abilitata, un singolo nodo può eseguire il push tra 25 Gbps e 200 Gbps (~3000-25.000 MBps), a seconda della famiglia di VM e del numero di CPU. Per le istanze standard, l'uscita è in genere limitata a circa 2 Gbps per vCPU.
Ad esempio, se la capacità dell'istanza Managed Lustre produce 100.000 MBps di throughput teorico, è necessaria un'uscita client aggregata di 120.000 MBps (
100,000 * 1.2) per saturarla:- Con le istanze standard: se ogni nodo ha un'uscita pubblicata di
2000 MBps, devi eseguire il provisioning di almeno 60 nodi (
120,000 / 2,000). - Con la rete Tier 1: se ogni nodo ha un'uscita pubblicata di
10.000 MBps (~80 Gbps), devi eseguire il provisioning di almeno 12 nodi
(
120,000 / 10,000).
- Con le istanze standard: se ogni nodo ha un'uscita pubblicata di
2000 MBps, devi eseguire il provisioning di almeno 60 nodi (
Crea l'immagine Docker IOR
Crea un'immagine container con OpenMPI e IOR installati. Compila IOR con il supporto I/O asincrono (AIO) per prestazioni migliori.
Crea un file denominato
Dockerfilein locale:FROM ubuntu:22.04 # Prevent interactive prompts during installation ENV DEBIAN_FRONTEND=noninteractive # Install dependencies, SSH, and required Autotools packages RUN apt-get update && apt-get install -y \ openssh-server \ openmpi-bin \ libopenmpi-dev \ wget \ git \ make \ gcc \ g++ \ automake \ autoconf \ libtool \ pkg-config \ libaio-dev \ sudo \ && rm -rf /var/lib/apt/lists/* # Build IOR from source (version 4.0.0) with Asynchronous I/O (AIO) support RUN git clone -b 4.0.0 https://github.com/hpc/ior /tmp/ior \ && cd /tmp/ior \ && ./bootstrap \ && ./configure --disable-dependency-tracking --with-aio \ && make -j"$(nproc)" \ && make install \ && rm -rf /tmp/ior # Configure SSH for OpenMPI passwordless communication RUN mkdir /var/run/sshd RUN echo 'root:root' | chpasswd RUN sed -i 's/^#PermitRootLogin.*/PermitRootLogin yes/' /etc/ssh/sshd_config RUN sed -i 's/^#PasswordAuthentication.*/PasswordAuthentication yes/' /etc/ssh/sshd_config # SSH login fix so user isn't kicked out after container initialization RUN sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd EXPOSE 22 CMD ["/usr/sbin/sshd", "-D"]Crea ed esegui il push di questa immagine nel registro container che preferisci. Le istruzioni riportate in questo documento utilizzano Artifact Registry.
export IMAGE_TAG="gcr.io/PROJECT_ID/lustre-ior-benchmark:latest" docker build -t $IMAGE_TAG . docker push $IMAGE_TAG
Genera chiavi SSH senza password per MPI
OpenMPI richiede la comunicazione tra i nodi tramite SSH senza password. Crea una chiave SSH e archiviala in un secret Kubernetes.
Genera le chiavi RSA:
ssh-keygen -t rsa -b 4096 -C "mpi-user" -N '' -f "./id_rsa"Crea il secret Kubernetes:
kubectl create secret generic mpi-ssh-secret \ --from-file=id_rsa=./id_rsa \ --from-file=id_rsa.pub=./id_rsa.pub \ --from-file=authorized_keys=./id_rsa.pub
Crea volume permanente e richiesta
Collega i pod GKE all'istanza Managed Lustre utilizzando il provisioning statico.
Crea un file denominato
lustre-pv.yaml. Sostituisci quanto segue:- CAPACITY con la capacità di archiviazione dell'istanza in GiB.
- EXTENDED_LUSTRE_ID con l'identificatore Managed Lustre, nel formato PROJECT_ID/ZONE/INSTANCE_NAME.
Ad esempio,
project-123/us-west1-a/my-lustre-instance. - LUSTRE_IP con l'indirizzo IP di montaggio dell'istanza.
- FS_NAME con il nome del file system dell'istanza.
Questi valori possono essere recuperati con il
gcloud lustre instances describecomando.apiVersion: v1 kind: PersistentVolume metadata: name: my-lustre-pv spec: storageClassName: "" claimRef: name: my-lustre-pvc namespace: default accessModes: - ReadWriteMany capacity: storage: CAPACITYGi # retain `Gi` suffix persistentVolumeReclaimPolicy: Retain volumeMode: Filesystem csi: driver: lustre.csi.storage.gke.io volumeHandle: EXTENDED_LUSTRE_ID # project-name/zone/instance-name volumeAttributes: ip: LUSTRE_IP filesystem: FS_NAME --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: my-lustre-pvc spec: storageClassName: "" volumeName: my-lustre-pv accessModes: - ReadWriteMany resources: requests: storage: CAPACITYGiApplica il manifest:
kubectl apply -f lustre-pv.yaml
Esegui il deployment dei worker MPI
Per scalare le attività IOR su più nodi, esegui il deployment di un
StatefulSet con l'immagine di
benchmark.
Crea un file denominato
mpi-workers.yaml. Specifica PROJECT_ID, e imposta NUM_NODES sul numero di nodi nel cluster.apiVersion: v1 kind: Service metadata: name: mpi-workers labels: app: mpi-worker spec: clusterIP: None selector: app: mpi-worker ports: - port: 22 name: ssh --- apiVersion: apps/v1 kind: StatefulSet metadata: name: mpi-worker spec: serviceName: "mpi-workers" replicas: NUM_NODES selector: matchLabels: app: mpi-worker template: metadata: labels: app: mpi-worker spec: tolerations: - operator: "Exists" containers: - name: mpi-worker image: gcr.io/PROJECT_ID/lustre-ior-benchmark:latest command: ["/bin/sh", "-c"] args: - >- mkdir -p /var/run/sshd && ssh-keygen -A && mkdir -p /root/.ssh && echo "Host *" > /root/.ssh/config && echo " StrictHostKeyChecking no" >> /root/.ssh/config && echo " UserKnownHostsFile=/dev/null" >> /root/.ssh/config && cp /mnt/mpi-ssh-keys/id_rsa /root/.ssh/id_rsa && cp /mnt/mpi-ssh-keys/id_rsa.pub /root/.ssh/id_rsa.pub && cp /mnt/mpi-ssh-keys/authorized_keys /root/.ssh/authorized_keys && chmod 700 /root/.ssh && chmod 600 /root/.ssh/* && exec /usr/sbin/sshd -D ports: - containerPort: 22 volumeMounts: - name: lustre-mount mountPath: /lustre - name: ssh-key-secret mountPath: /mnt/mpi-ssh-keys readOnly: true volumes: - name: lustre-mount persistentVolumeClaim: claimName: my-lustre-pvc - name: ssh-key-secret secret: secretName: mpi-ssh-secretApplica il manifest:
kubectl apply -f mpi-workers.yaml
Esegui il benchmark IOR
Avvia il benchmark dal primo pod (mpi-worker-0), trattandolo come nodo head.
Genera un file host contenente gli indirizzi IP interni dei worker e copialo nel nodo head:
kubectl get pods -l app=mpi-worker -o jsonpath='{range .items[*]}{.status.podIP}{"\n"}{end}' > hosts.txt kubectl cp hosts.txt mpi-worker-0:/root/hostfileApri una sessione bash all'interno del pod head:
kubectl exec -it mpi-worker-0 -- /bin/bashAll'interno del pod, crea una directory di test:
mkdir -p /lustre/testDefinisci le variabili di test:
export NUM_NODES="NUM_NODES" export PROCESSES_PER_NODE="PROCESSES_PER_NODE" export NUM_PROCESSES=$(( NUM_NODES * PROCESSES_PER_NODE ))Dove:
NUM_NODES: il numero totale di pod worker che partecipano al test.
PROCESSES_PER_NODE: il numero di ranghi MPI da eseguire su ogni container. Ti consigliamo di iniziare impostando questo valore in modo che corrisponda al numero di core fisici (o alla metà del numero di vCPU) sulle macchine client. Per i tipi di macchine ad alte prestazioni, l'impostazione di questo valore tra
8e16in genere produce il miglior throughput di rete.
Esegui i comandi di benchmark:
Throughput di scrittura
Questo comando scrive continuamente per 60 secondi per testare il throughput di stato stabile di picco. La dimensione del file per attività è impostata su un limite massimo arbitrariamente grande di 50 TiB per mantenere le attività di scrittura fino alla scadenza del timer di 60 secondi.
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" \ --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
Throughput di lettura
Questa fase legge esattamente la quantità di dati scritti correttamente durante il test del throughput di scrittura di 60 secondi. Sebbene la dimensione del file per attività (
-b) sia impostata su 50 TiB in modo che corrisponda alla geometria della fase di scrittura, il flag-O stoneWallingWearOut=1indica a IOR di interrompere la lettura non appena raggiunge il limite esatto dei dati registrato nel file di stato di stonewall.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" \ --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 di scrittura
Questo test utilizza dimensioni di trasferimento ridotte di 4 KiB e dimensioni di file per attività di 8 GiB per misurare il numero massimo di operazioni di input/output al secondo (IOPS) che il file system può gestire.
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" \ --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 di lettura
Per evitare la lettura da un file sparso, questo test utilizza due comandi: una scrittura per creare un file solido utilizzando dimensioni di trasferimento di 4 MiB e dimensioni di file per attività di 8 GiB, seguita dal test IOPS di lettura casuale di 4 KiB effettivo.
Crea il file da leggere:
mpirun \ --allow-run-as-root