Para probar el rendimiento de lectura y escritura
de una carga de trabajo de Google Kubernetes Engine (GKE) desde varios clientes de GKE, usa la
herramienta de comparativas IOR. En las siguientes instrucciones, se muestra cómo automatizar la configuración del cliente y usar IOR con mpirun a través de SSH sin contraseña entre los Pods de Kubernetes para probar la E/S agregada.
Requisitos previos
Una instancia de Managed Lustre ya aprovisionada
Un entorno de Docker local configurado y autenticado para enviar a Google Artifact Registry o Container Registry (consulta Métodos de autenticación).
Asegúrate de que el valor
mtude tu red esté establecido en8896.
Crea un clúster de GKE
Para probar el rendimiento, necesitas un clúster de GKE con el controlador de CSI de Managed Lustre habilitado. Para las cargas de trabajo de almacenamiento de alto rendimiento, configura tus grupos de nodos de GKE con familias de máquinas optimizadas para procesamiento (p.ej., c2 o c3) y redes TIER_1.
Ejecuta el siguiente comando para crear un clúster de GKE Standard optimizado para pruebas de rendimiento:
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
Reemplaza ZONE y NETWORK por los valores de implementación específicos. El clúster debe residir en la misma red de VPC que tu instancia de Managed Lustre.
Elige un MACHINE_TYPE. Consulta Consideraciones sobre el rendimiento para obtener información sobre cómo elegir tipos de máquinas para obtener la mejor capacidad de procesamiento.
Si tu tipo de máquina no admite redes TIER_1, borra la línea
--network-performance-configsdel comando.Especifica el NUM_NODES. Para saturar tu sistema de archivos, la capacidad de red agregada de tu clúster debe superar la capacidad de procesamiento aprovisionada del sistema de archivos en un 20%.
En el caso de las máquinas con redes de nivel 1 habilitadas, un solo nodo puede enviar entre 25 Gbps y 200 Gbps (aproximadamente 3,000 y 25,000 MBps), según la familia de VM y la cantidad de CPU. En el caso de las instancias estándar, la salida suele estar limitada a alrededor de 2 Gbps por CPU virtual.
Por ejemplo, si la capacidad de tu instancia de Managed Lustre genera 100,000 MBps de capacidad de procesamiento teórica, necesitas una salida agregada del cliente de 120,000 MBps (
100,000 * 1.2) para saturarla:- Con instancias estándar: Si cada nodo tiene una salida publicada de 2,000 MBps, debes aprovisionar al menos 60 nodos (
120,000 / 2,000). - Con redes de nivel 1: Si cada nodo tiene una salida publicada de 10,000 MBps (aproximadamente 80 Gbps), debes aprovisionar al menos 12 nodos (
120,000 / 10,000).
- Con instancias estándar: Si cada nodo tiene una salida publicada de 2,000 MBps, debes aprovisionar al menos 60 nodos (
Crea la imagen de Docker de IOR
Crea una imagen de contenedor con OpenMPI y IOR instalados. Compila IOR con compatibilidad de E/S asíncrona (AIO) para obtener un mejor rendimiento.
Crea un archivo llamado
Dockerfilede forma local: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"]Compila y envía esta imagen a tu registro de contenedores preferido. En las instrucciones de este documento, se usa Artifact Registry.
export IMAGE_TAG="gcr.io/PROJECT_ID/lustre-ior-benchmark:latest" docker build -t $IMAGE_TAG . docker push $IMAGE_TAG
Genera claves SSH sin contraseña para MPI
OpenMPI requiere comunicación entre nodos mediante SSH sin contraseña. Crea una clave SSH y guárdala en un Secret de Kubernetes.
Genera las claves RSA:
ssh-keygen -t rsa -b 4096 -C "mpi-user" -N '' -f "./id_rsa"Crea el Secret de 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 un volumen persistente y una reclamación
Conecta tus pods de GKE a la instancia de Managed Lustre mediante el aprovisionamiento estático.
Crea un archivo llamado
lustre-pv.yaml. Reemplaza lo siguiente:- CAPACITY por la capacidad de almacenamiento de tu instancia en GiB.
- EXTENDED_LUSTRE_ID por tu identificador de Managed Lustre, en el formato PROJECT_ID/ZONE/INSTANCE_NAME
Por ejemplo,
project-123/us-west1-a/my-lustre-instance - LUSTRE_IP por la dirección IP de activación de tu instancia
- FS_NAME por el nombre del sistema de archivos de la instancia
Estos valores se pueden recuperar con el
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: CAPACITYGiAplica el manifiesto
kubectl apply -f lustre-pv.yaml
Implementa los trabajadores de MPI
Para escalar las tareas de IOR en varios nodos, implementa un
StatefulSet con tu
imagen de comparativas.
Crea un archivo llamado
mpi-workers.yaml. Especifica tu PROJECT_ID, y establece NUM_NODES en la cantidad de nodos de tu clúster.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-secretAplica el manifiesto
kubectl apply -f mpi-workers.yaml
Ejecuta la comparativa de IOR
Inicia la comparativa desde el primer pod (mpi-worker-0) y trátalo como el nodo principal.
Genera un archivo host que contenga las direcciones IP internas de los trabajadores y cópialo en el nodo principal:
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/hostfileAbre una sesión de bash dentro de tu pod principal:
kubectl exec -it mpi-worker-0 -- /bin/bashDentro del pod, crea un directorio de prueba:
mkdir -p /lustre/testDefine las variables de prueba:
export NUM_NODES="NUM_NODES" export PROCESSES_PER_NODE="PROCESSES_PER_NODE" export NUM_PROCESSES=$(( NUM_NODES * PROCESSES_PER_NODE ))Aquí:
NUM_NODES: La cantidad total de pods de trabajador que participan en la prueba.
PROCESSES_PER_NODE: La cantidad de rangos de MPI que se ejecutarán en cada contenedor. Te recomendamos que comiences por establecer este valor para que coincida con la cantidad de núcleos físicos (o la mitad de la cantidad de CPU virtuales) en tus máquinas cliente. Para los tipos de máquinas de alto rendimiento, establecer este valor entre
8y16suele producir la mejor capacidad de procesamiento de red.
Ejecuta los comandos de comparativas:
Capacidad de procesamiento de escritura
Este comando escribe de forma continua durante 60 segundos para probar la capacidad de procesamiento máxima en estado estable. El tamaño de archivo por tarea se establece en un límite máximo de 50 TiB arbitrariamente grande para que las tareas sigan escribiendo hasta que venza el temporizador de 60 segundos.
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
Capacidad de procesamiento de lectura
En esta fase, se vuelve a leer exactamente la cantidad de datos que se escribieron correctamente durante la prueba de rendimiento de escritura de 60 segundos. Aunque el tamaño de archivo por tarea (
-b) se establece en 50 TiB para que coincida con la geometría de la fase de escritura, la marca-O stoneWallingWearOut=1le indica a IOR que deje de leer en cuanto alcance el límite de datos exacto registrado en el archivo de estado de 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 de escritura
En esta prueba, se usan tamaños de transferencia pequeños de 4 KiB y tamaños de archivo por tarea de 8 GiB para medir las operaciones de entrada y salida por segundo (IOPS) máximas que puede controlar el sistema de archivos.
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 de lectura
Para evitar la lectura de un archivo disperso, esta prueba usa dos comandos: una escritura para crear un archivo sólido con tamaños de transferencia de 4 MiB y tamaños de archivo por tarea de 8 GiB, seguida de la prueba real de IOPS de lectura aleatoria de 4 KiB.
Crea el archivo para leer:
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} \ --oversubscribe \ --map-by node \ --bind-to socket \ --hostfile ~/hostfile \ /usr/local/bin/ior \ -a AIO \ --posix.odirect \ -w \ -F \ -k \ -t 4m \ -b 8g \ -s 1 \ -Q 1 \ -G 1745405099 \ -o /lustre/test/ior_rand_read
Ejecuta la prueba de lectura de IOR:
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" \ --oversubscribe \ --map-by node \ --bind-to socket \ --npernode ${PROCESSES_PER_NODE} \ --np ${NUM_PROCESSES} \ --hostfile ~/hostfile \ /usr/local/bin/ior \ -a AIO \ --posix.odirect \ --aio.max-pending 256 \ -r \ -F \ -z \ -t 4k \ -b 8g \ -s 1 \ -Q 1 \ -G 1745405099 \ -D 45 \ -O stoneWallingWearOut=1 \ -o /lustre/test/ior_rand_read
Las marcas
mpirunson las siguientes:--mca plm_rsh_no_tree_spawn 1: Inhabilita la generación de daemons basados en árboles para mejorar la confiabilidad del lanzamiento en los nodos.--mca opal_set_max_sys_limits 1: Intenta establecer automáticamente los límites del sistema (como la cantidad máxima de archivos abiertos) en sus valores máximos permitidos.--mca plm_rsh_num_concurrent: Establece la cantidad máxima de conexiones SSH simultáneas quempirunusará cuando lance daemons de trabajador.--mca plm_rsh_args ...: Omite la verificación estricta de la clave de host para evitar que las solicitudes interactivas de SSH cuelguen el lanzamiento del proceso de MPI.--prefix ...: Define de forma explícita la ruta de instalación de OpenMPI para Rocky Linux y RHEL, de modo que los nodos de trabajador puedan encontrar el daemon requerido (orted).--allow-run-as-root: Permite quempirunse ejecute como el usuario raíz.--oversubscribe: Permite que MPI programe más procesos en un nodo de los que hay núcleos físicos disponibles.--map-by node: Distribuye los procesos de MPI de forma rotativa y uniforme en los nodos disponibles.--bind-to socket: Vincula los procesos de MPI a los sockets de CPU físicos para optimizar el acceso a la memoria y el rendimiento de la caché.--npernode: La cantidad de procesos por nodo.--np: La cantidad total de procesos de MPI que se lanzarán.--hostfile: Especifica el archivo que contiene la lista de hosts en los que se ejecutará.
Las marcas
iorson las siguientes:-a AIO --posix.odirect: Usa el motor de E/S asíncrona (AIO) combinado con la E/S directa de POSIX. Esto omite la caché de página de RAM del cliente y fuerza las escrituras simultáneas sin bloqueo directamente a los servidores de almacenamiento, lo que garantiza que la comparativa mida el rendimiento real del almacenamiento de red en lugar de los búferes de memoria.--aio.max-pending=256: Determina la cantidad máxima de operaciones de E/S asíncronas simultáneas en tránsito por proceso.-C: Reordena las tareas para obtener un rendimiento de lectura óptimo.-F: Modo de archivo por proceso.-g: Usa barreras para separar las fases de escritura y lectura de la prueba.-v: Muestra el registro detallado.-w/-r: Le indica a IOR que ejecute la prueba de rendimiento de escritura (-w) o lectura (-r) .-k: Evita que IOR borre el archivo de prueba después de escribir, lo que lo hace disponible para la prueba de lectura.-e: Realiza unfsyncdespués de la fase de escritura para garantizar que los datos se confirmen en las unidades de almacenamiento.-z: Le indica a IOR que realice E/S de acceso aleatorio en lugar de acceso secuencial.-s 1: Establece la cantidad de segmentos en 1.-Q 1: Establece el desplazamiento de tarea por nodo, alinea las tareas para que la comparativa se coordine correctamente en todos los nodos.-G 1745405099: Codifica de forma rígida la marca de tiempo de la semilla aleatoria para que la fase de lectura genere los mismos desplazamientos de archivo aleatorios que usó la fase de escritura.-t: Establece el tamaño de transferencia para cada operación de E/S (p.ej.,4mpara la capacidad de procesamiento,4kpara IOPS).-b: Establece el tamaño de bloque objetivo por proceso (p.ej.,50tpara la capacidad de procesamiento,8gpara IOPS) para garantizar que la prueba no se quede sin datos de carga útil durante la ejecución cronometrada.-D: Restringe la duración del tiempo de ejecución de la prueba a una cantidad específica de segundos (p.ej.,60o45). Este "stonewalling" finaliza la comparativa de manera uniforme para capturar una medición real en estado estable.-O stoneWallingWearOut=1: Fuerza a todos los subprocesos simultáneos a seguir generando una carga de escritura continua durante toda la duración, lo que evita que los subprocesos más rápidos se completen antes y disminuyan la presión general de la red. presión.-O stoneWallingStatusFile=<path>: Escribe un archivo de verificación de estado al final de la fase de escritura. La fase de lectura posterior usa este archivo para leer solo los bloques que se confirmaron correctamente, lo que evita errores de puntero nulo durante las lecturas.-o: La ruta de acceso al archivo de prueba en el sistema de archivos de Managed Lustre.
Vea los resultados
Cuando se completa la comparativa, se muestran las métricas de rendimiento agregadas de todas las VMs o pods del cliente directamente en tu terminal. Busca la tabla Results en la parte inferior del resultado para encontrar tu capacidad de procesamiento o IOPS máximas.
Métricas clave
aggregate filesize: La cantidad total de datos escritos o leídos durante la prueba en todos los clientes participantes.bw(MiB/s)/Max Write/Max Read: La métrica más importante para las pruebas secuenciales. Muestra el ancho de banda agregado que logró el sistema de archivos de Managed Lustre.IOPS: La métrica más importante para las pruebas de E/S aleatorias. Muestra las operaciones de entrada y salida máximas por segundo.
Exporta los resultados a un archivo
Si deseas analizar tus resultados de forma programática, ingresarlos en una base de datos o guardarlos para su análisis posterior, puedes indicarle a IOR que exporte los datos de resumen en formatos JSON y CSV en lugar de solo imprimirlos en la pantalla.
Para ello, agrega las marcas -O al final de la cadena de comandos ior:
-O summaryFormat=JSON \
-O summaryFile=/lustre/test/perf-results/summary.json \
-O saveRankPerformanceDetailsCSV=/lustre/test/perf-results/details.csv
El directorio de salida debe existir en el sistema de archivos antes de ejecutar la comparativa.
Rendimiento esperado en comparación con el rendimiento real
Puedes calcular la capacidad de procesamiento máxima matemática de tu sistema de archivos en función de su capacidad aprovisionada y su nivel de rendimiento. Debido a que la capacidad de almacenamiento se aprovisiona en gibibytes (GiB) y los niveles se clasifican en tebibytes (TiB), primero debes convertir tu capacidad:
(Capacity GiB / 1024) * Tier MBps = Capacidad máxima teórica en MBps
Por ejemplo, una instancia de 216,000 GiB en el nivel de 500 MBps por TiB proporciona matemáticamente 105,469 MBps de capacidad de procesamiento ((216000 / 1024) * 500).
La capacidad de procesamiento máxima observada siempre estará limitada por la capacidad de procesamiento aprovisionada de tu sistema de archivos o por los límites combinados de salida de red de tus máquinas cliente, lo que sea menor.
Estos son algunos de los motivos comunes por los que es posible que los números de tu comparativa no alcancen las velocidades teóricas:
Sobrecarga de TCP/IP: El encabezado de paquetes y la encapsulación de red estándar consumen aproximadamente el 5% al 10% del ancho de banda sin procesar. Tu máximo matemático incluye esta sobrecarga, pero la comparativa de IOR solo mide la carga útil sin procesar escrita en el disco.
Límites de red del cliente: Las máquinas cliente tienen límites estrictos de ancho de banda de salida. Si usas una pequeña cantidad de clientes o nodos, o tipos de máquinas sin redes de nivel 1 habilitadas, los clientes limitarán la comparativa antes de que el sistema de archivos de Managed Lustre alcance su límite.
Cambio de contexto de MPI: Si
PROCESSES_PER_NODEse establece en un valor superior a la cantidad de núcleos físicos de tus máquinas cliente, la contención de CPU y la sobrecarga de cambio de contexto degradarán artificialmente el rendimiento de E/S de la comparativa.Falta de E/S directa: Si se omite la marca
--posix.odirect, los datos pasan por la caché de página de RAM del cliente. Esto introduce cuellos de botella en la memoria y sobrecarga de CPU que enmascaran el rendimiento real del almacenamiento de red.
Limpia
Sigue estos pasos para evitar que se apliquen cargos a tu Google Cloud cuenta por los recursos que usaste en esta página:
Borra los archivos de prueba del volumen de Managed Lustre:
kubectl exec mpi-worker-0 -- rm -rf /lustre/testBorra el clúster de GKE:
gcloud container clusters delete CLUSTER_NAME --zone=ZONESi borras el clúster, también se borrarán los pods de GKE, el Secret de Kubernetes y la reclamación de volumen persistente.
Si enviaste la imagen de Docker de comparativas y ya no la necesitas, bórrala de tu repositorio:
gcloud container images delete gcr.io/PROJECT_ID/lustre-ior-benchmark:latest --force-delete-tagsSi creaste la instancia de Managed Lustre específicamente para esta prueba y ya no la necesitas, bórrala:
gcloud lustre instances delete INSTANCE_ID --location=LOCATION
Soluciona problemas comunes de cuellos de botella en las comparativas
Si los resultados de la comparativa son significativamente más bajos que el nivel de rendimiento de almacenamiento esperado, consulta Soluciona problemas comunes de cuellos de botella.