Todos los productos
Search
Centro de documentos

Container Service for Kubernetes:Best practices for OSS read/write splitting

Última actualización: Sep 12, 2026

Los volúmenes persistentes (PV) de OSS admiten varios clientes, pero la compatibilidad total con escrituras degrada el rendimiento de lectura. La separación de lectura y escritura resuelve este problema al enrutar las lecturas y las escrituras por rutas de montaje distintas. Esto mejora el rendimiento en cargas de trabajo intensivas en lectura, como el entrenamiento de modelos, la inference y el análisis de datos.

Implemente la separación de lectura y escritura para los PV de OSS con ossfs o el SDK de OSS. Este documento lo demuestra mediante un trabajo de entrenamiento de reconocimiento de escritura a mano MNIST.

Requisitos previos

Asegúrese de contar con lo siguiente:

Importante

No se recomienda acceder a OSS entre cuentas distintas.

Elija un cliente

Los PV de OSS son compatibles con tres clientes: ossfs 1.0, ossfs 2.0 y strmvol. Todos permiten el acceso de solo lectura, pero sus capacidades de escritura difieren:

Client

Read-only

Read/write

Best for

ossfs 1.0

Sí

Escritura completa

Cargas de trabajo generales de lectura y escritura; modo de lectura directa disponible (v1.91+)

ossfs 2.0

Sí

Solo escrituras secuenciales por anexión

Cargas de trabajo intensivas en lectura; requiere CSI >= 1.33.1

strmvol

Sí

—

Muchos archivos pequeños (conjuntos de datos, registros de series temporales, backtesting cuantitativo)

Consulte la referencia de selección de clientes.

Casos de uso

Acceso de solo lectura

Establezca el modo de acceso del PV en ReadOnlyMany para evitar modificaciones accidentales de los datos. Esta opción es adecuada para inference, análisis de datos y consultas de registros.

Configure los siguientes parámetros de otherOpts para ajustar ossfs 1.0 en escenarios de solo lectura. La mayoría de las cargas de trabajo pueden usar los valores predeterminados.

Parameter

Default

Description

kernel_cache

Deshabilitado

Habilita la caché de búfer del kernel para lecturas no en tiempo real. Utiliza memoria libre para la caché.

parallel_count

20

Fragmentos simultáneos para cargar y descargar archivos grandes.

max_multireq

20

Número máximo de solicitudes simultáneas de listado de metadatos. Debe ser >= parallel_count.

max_stat_cache_size

1000

Cantidad de entradas de metadatos almacenadas en caché. Establézcalo en 0 para deshabilitarlo. Auméntelo para acelerar ls en directorios grandes: 10,000 entradas consumen alrededor de 40 MB.

direct_read

Deshabilitado

Modo de lectura directa para escenarios de solo lectura (ossfs >= 1.91). Consulte Características y pruebas de rendimiento de la nueva versión de ossfs 1.0 y Optimización del rendimiento para escenarios de solo lectura.

Acceso de lectura y escritura

Establezca el modo de acceso del PV en ReadWriteMany para las cargas de trabajo que escriben datos.

Nota

ossfs no garantiza la consistencia en escrituras simultáneas. Varios escritores sobre los mismos objetos pueden corromper los datos. Use un único escritor por ruta para las escrituras de puntos de control.

Aviso

Eliminar o modificar archivos en la ruta montada también elimina o modifica los objetos del bucket de OSS. Habilite el control de versiones para protegerse contra la pérdida de datos.

En cargas de trabajo intensivas en lectura con rutas separadas para leer y escribir (como el entrenamiento de modelos), monte la ruta de lectura como ReadOnlyMany con la caché habilitada. Gestione las escrituras mediante un PV ReadWriteMany o el SDK de OSS.

Cómo funciona la separación de lectura y escritura

La separación de lectura y escritura enruta las operaciones por puntos de montaje distintos. Cada uno apunta a una subruta diferente del mismo bucket de OSS, lo que aísla la E/S de lectura de la E/S de escritura.

  • Ruta de lectura: monte una subruta (por ejemplo, /tf-train/train/data) como ReadOnlyMany con la caché habilitada. Las lecturas repetidas se atienden desde la memoria.

  • Ruta de escritura: monte otra subruta (por ejemplo, /tf-train/training_logs) como ReadWriteMany, o escriba directamente mediante el SDK.

Ejemplo: entrenamiento de reconocimiento de escritura a mano MNIST

El trabajo de entrenamiento realiza lo siguiente:

  1. Lee el conjunto de datos de entrenamiento desde /tf-train/train/data en el bucket de OSS mediante un PV de solo lectura.

  2. Escribe los puntos de control del entrenamiento en /tf-train/training_logs con un PV de lectura y escritura o con el SDK de OSS.

Descargue el conjunto de datos MNIST y cárguelo en /tf-train/train/data dentro de su bucket de OSS:

Organización de los archivos en el bucket de OSS:

oss-read-write-splitting-1

Implemente las operaciones de lectura y escritura con ossfs

Las escrituras de puntos de control son anexiones secuenciales, por lo que tanto ossfs 1.0 como ossfs 2.0 funcionan para la ruta de escritura.

  1. Despliegue la aplicación de entrenamiento. La aplicación monta la subruta /tf-train del bucket de OSS en el directorio /mnt del pod. Consulte Uso de volúmenes aprovisionados estáticamente con ossfs 1.0 o Uso de PV de ossfs 2.0.

    1. Cree un PV de ossfs 1.0:

      cat << EOF | kubectl apply -f -
      apiVersion: v1
      kind: Secret
      metadata:
        name: oss-secret
        namespace: default
      stringData:
        akId: "<your-accesskey-id>"
        akSecret: "<your-accesskey-secret>"
      ---
      apiVersion: v1
      kind: PersistentVolume
      metadata:
        name: tf-train-pv
        labels:
          alicloud-pvname: tf-train-pv
      spec:
        capacity:
          storage: 10Gi
        accessModes:
          - ReadWriteMany
        persistentVolumeReclaimPolicy: Retain
        csi:
          driver: ossplugin.csi.alibabacloud.com
          volumeHandle: tf-train-pv
          nodePublishSecretRef:
            name: oss-secret
            namespace: default
          volumeAttributes:
            bucket: "<your-bucket-name>"
            url: "oss-<region>.aliyuncs.com"
            otherOpts: "-o max_stat_cache_size=0 -o allow_other"
            path: "/tf-train"
      ---
      apiVersion: v1
      kind: PersistentVolumeClaim
      metadata:
        name: tf-train-pvc
      spec:
        accessModes:
        - ReadWriteMany
        resources:
          requests:
            storage: 10Gi
        selector:
          matchLabels:
            alicloud-pvname: tf-train-pv
      EOF
    2. Cree el pod de entrenamiento:

      Durante el entrenamiento, ossfs carga los archivos de /mnt/training_logs (pod) hacia /tf-train/training_logs (bucket de OSS).

      cat << EOF | kubectl apply -f -
      apiVersion: v1
      kind: Pod
      metadata:
        labels:
          app: tfjob
        name: tf-mnist
        namespace: default
      spec:
        containers:
        - command:
          - sh
          - -c
          - python /app/main.py
          env:
          - name: NVIDIA_VISIBLE_DEVICES
            value: void
          - name: gpus
            value: "0"
          - name: workers
            value: "1"
          - name: TEST_TMPDIR
            value: "/mnt"
          image: registry.cn-beijing.aliyuncs.com/tool-sys/tf-train-demo:rw
          imagePullPolicy: Always
          name: tensorflow
          ports:
          - containerPort: 20000
            name: tfjob-port
            protocol: TCP
          volumeMounts:
            - name: train
              mountPath: "/mnt"
          workingDir: /root
        priority: 0
        restartPolicy: Never
        securityContext: {}
        terminationGracePeriodSeconds: 30
        volumes:
        - name: train
          persistentVolumeClaim:
            claimName: tf-train-pvc
      EOF
  2. Compruebe las lecturas y escrituras de datos.

    1. Revise el estado del pod:

      kubectl get pod tf-mnist

      Espere a que el estado cambie de Running a Completed:

      NAME       READY   STATUS      RESTARTS   AGE
      tf-mnist   0/1     Completed   0          2m12s
    2. Revise el tiempo de carga de datos:

      kubectl logs tf-mnist | grep dataload

      Resultado esperado:

      dataload cost time:  1.54191803932
    3. Inicie sesión en la consola de administración de OSS y compruebe que los archivos existen bajo /tf-train/training_logs en el bucket.

Optimice el rendimiento de lectura con la separación de lectura y escritura

Divida el PV único de lectura y escritura en dos: un PV de solo lectura con ajustes de caché para el conjunto de datos y un PV de escritura para los puntos de control. Solo cambia la configuración de montaje; el código de entrenamiento permanece igual.

Existen dos opciones de escritura:

  • Opción 1: use un PV de ossfs de lectura y escritura independiente para escribir los puntos de control.

  • Opción 2: use el SDK de OSS para escribir los puntos de control directamente y omitir ossfs por completo.

Opción 1: escritura mediante un PV de ossfs de lectura y escritura

  1. Cree un PV de ossfs 1.0 de solo lectura para el conjunto de datos. Los cambios clave de configuración son:

    • Establezca accessModes en ReadOnlyMany tanto en el PV como en el PVC. Monte la subruta del conjunto de datos /tf-train/train/data.

    • En otherOpts, agregue -o kernel_cache -o max_stat_cache_size=10000 -o umask=022:

      • kernel_cache habilita la caché de lectura en memoria.

      • max_stat_cache_size=10000 almacena 10,000 entradas de metadatos en caché (~40 MB). Ajústelo según el tipo de instancia y el tamaño del conjunto de datos.

      • umask=022 otorga acceso de lectura a los procesos del contenedor que no son root.

    cat << EOF | kubectl apply -f -
    apiVersion: v1
    kind: Secret
    metadata:
      name: oss-secret
      namespace: default
    stringData:
      akId: "<your-accesskey-id>"
      akSecret: "<your-accesskey-secret>"
    ---
    apiVersion: v1
    kind: PersistentVolume
    metadata:
      name: tf-train-pv
      labels:
        alicloud-pvname: tf-train-pv
    spec:
      capacity:
        storage: 10Gi
      accessModes:
        - ReadOnlyMany
      persistentVolumeReclaimPolicy: Retain
      csi:
        driver: ossplugin.csi.alibabacloud.com
        volumeHandle: tf-train-pv
        nodePublishSecretRef:
          name: oss-secret
          namespace: default
        volumeAttributes:
          bucket: "<your-bucket-name>"
          url: "oss-<region>.aliyuncs.com"
          otherOpts: "-o kernel_cache -o max_stat_cache_size=10000 -o umask=022 -o allow_other"
          path: "/tf-train/train/data"
    ---
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
      name: tf-train-pvc
    spec:
      accessModes:
      - ReadOnlyMany
      resources:
        requests:
          storage: 10Gi
      selector:
        matchLabels:
          alicloud-pvname: tf-train-pv
    EOF
  2. Cree un PV de ossfs 1.0 de lectura y escritura para los puntos de control, y monte la subruta /tf-train/training_logs. La caché de metadatos se deshabilita (max_stat_cache_size=0) porque las escrituras secuenciales de puntos de control no se benefician de ella.

    cat << EOF | kubectl apply -f -
    apiVersion: v1
    kind: PersistentVolume
    metadata:
      name: tf-logging-pv
      labels:
        alicloud-pvname: tf-logging-pv
    spec:
      capacity:
        storage: 10Gi
      accessModes:
        - ReadWriteMany
      persistentVolumeReclaimPolicy: Retain
      csi:
        driver: ossplugin.csi.alibabacloud.com
        volumeHandle: tf-logging-pv
        nodePublishSecretRef:
          name: oss-secret
          namespace: default
        volumeAttributes:
          bucket: "<your-bucket-name>"
          url: "oss-<region>.aliyuncs.com"
          otherOpts: "-o max_stat_cache_size=0 -o allow_other"
          path: "/tf-train/training_logs"
    ---
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
      name: tf-logging-pvc
    spec:
      accessModes:
      - ReadWriteMany
      resources:
        requests:
          storage: 10Gi
      selector:
        matchLabels:
          alicloud-pvname: tf-logging-pv
    EOF
  3. Despliegue el pod de entrenamiento con ambos PV montados.

    No se requieren cambios en el código. Monte ambos PV: el de solo lectura en /mnt/train/data y el de lectura y escritura en /mnt/training_logs .
    cat << EOF | kubectl apply -f -
    apiVersion: v1
    kind: Pod
    metadata:
      labels:
        app: tfjob
      name: tf-mnist
      namespace: default
    spec:
      containers:
      - command:
        - sh
        - -c
        - python /app/main.py
        env:
        - name: NVIDIA_VISIBLE_DEVICES
          value: void
        - name: gpus
          value: "0"
        - name: workers
          value: "1"
        - name: TEST_TMPDIR
          value: "/mnt"
        image: registry.cn-beijing.aliyuncs.com/tool-sys/tf-train-demo:rw
        imagePullPolicy: Always
        name: tensorflow
        ports:
        - containerPort: 20000
          name: tfjob-port
          protocol: TCP
        volumeMounts:
          - name: train
            mountPath: "/mnt/train/data"
          - name: logging
            mountPath: "/mnt/training_logs"
        workingDir: /root
      priority: 0
      restartPolicy: Never
      securityContext: {}
      terminationGracePeriodSeconds: 30
      volumes:
      - name: train
        persistentVolumeClaim:
          claimName: tf-train-pvc
      - name: logging
        persistentVolumeClaim:
          claimName: tf-logging-pvc
    EOF

Opción 2: escritura mediante el SDK de OSS

Escriba los puntos de control directamente en OSS con el SDK; no necesita un PV de lectura y escritura. El pod lee desde un PV de solo lectura y escribe mediante el SDK.

  1. Agregue el SDK de Python de OSS a la imagen del contenedor:

    RUN pip install oss2

    Consulte Installation.

  2. Modifique el código de entrenamiento para cargar los puntos de control con el SDK. El código original guarda los puntos de control en log_dir cada 100 iteraciones mediante tf.train.Saver con max_to_keep=0, lo que genera 10 conjuntos de puntos de control tras 1,000 iteraciones.

    • Establezca max_to_keep=1 para conservar solo el punto de control más reciente y reducir el consumo de memoria.

    • Cargue cada punto de control en OSS con put_object_from_file después de guardarlo.

    Use E/S asíncrona con el SDK para mejorar aún más el rendimiento cuando las rutas de lectura y escritura están separadas.
    def train():
        ...
    saver = tf.train.Saver(max_to_keep=0)
    
        for i in range(FLAGS.max_steps):
            if i % 10 == 0:  # Record summaries and test-set accuracy
                summary, acc = sess.run([merged, accuracy], feed_dict=feed_dict(False))
                print('Accuracy at step %s: %s' % (i, acc))
                if i % 100 == 0:
                    print('Save checkpoint at step %s: %s' % (i, acc))
                    saver.save(sess, FLAGS.log_dir + '/model.ckpt', global_step=i)

    Reemplace el código anterior por cargas basadas en el SDK. Dos cambios reducen el consumo de memoria y eliminan el PV de lectura y escritura: lea la AccessKey y la configuración del bucket desde variables de entorno. Consulte Configure access credentials.

    import oss2
    from oss2.credentials import EnvironmentVariableCredentialsProvider
    
    auth = oss2.ProviderAuth(EnvironmentVariableCredentialsProvider())
    url = os.getenv('URL','<default-url>')
    bucketname = os.getenv('BUCKET','<default-bucket-name>')
    bucket = oss2.Bucket(auth, url, bucketname)
    
    ...
    def train():
      ...
      saver = tf.train.Saver(max_to_keep=1)
    
     for i in range(FLAGS.max_steps):
        if i % 10 == 0:  # Record summaries and test-set accuracy
          summary, acc = sess.run([merged, accuracy], feed_dict=feed_dict(False))
          print('Accuracy at step %s: %s' % (i, acc))
          if i % 100 == 0:
            print('Save checkpoint at step %s: %s' % (i, acc))
            saver.save(sess, FLAGS.log_dir + '/model.ckpt', global_step=i)
            # FLAGS.log_dir = os.path.join(os.getenv('TEST_TMPDIR', '/mnt'),'training_logs')
            for path,_,file_list in os.walk(FLAGS.log_dir) :
              for file_name in file_list:
                bucket.put_object_from_file(os.path.join('tf-train/training_logs', file_name), os.path.join(path, file_name))

    La imagen del contenedor modificada es registry.cn-beijing.aliyuncs.com/tool-sys/tf-train-demo:ro.

  3. Despliegue el pod con el PV de solo lectura y las credenciales del SDK. El pod establece accessModes en ReadOnlyMany y pasa OSS_ACCESS_KEY_ID y OSS_ACCESS_KEY_SECRET para que el SDK se autentique con las mismas credenciales que el PV.

    cat << EOF | kubectl apply -f -
    apiVersion: v1
    kind: Secret
    metadata:
      name: oss-secret
      namespace: default
    stringData:
      akId: "<your-accesskey-id>"
      akSecret: "<your-accesskey-secret>"
    ---
    apiVersion: v1
    kind: PersistentVolume
    metadata:
      name: tf-train-pv
      labels:
        alicloud-pvname: tf-train-pv
    spec:
      capacity:
        storage: 10Gi
      accessModes:
        - ReadOnlyMany
      persistentVolumeReclaimPolicy: Retain
      csi:
        driver: ossplugin.csi.alibabacloud.com
        volumeHandle: tf-train-pv
        nodePublishSecretRef:
          name: oss-secret
          namespace: default
        volumeAttributes:
          bucket: "<your-bucket-name>"
          url: "oss-<region>.aliyuncs.com"
          otherOpts: "-o kernel_cache -o max_stat_cache_size=10000 -o umask=022 -o allow_other"
          path: "/tf-train/train/data"
    ---
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
      name: tf-train-pvc
    spec:
      accessModes:
      - ReadOnlyMany
      resources:
        requests:
          storage: 10Gi
      selector:
        matchLabels:
          alicloud-pvname: tf-train-pv
    ---
    apiVersion: v1
    kind: Pod
    metadata:
      labels:
        app: tfjob
      name: tf-mnist
      namespace: default
    spec:
      containers:
      - command:
        - sh
        - -c
        - python /app/main.py
        env:
        - name: NVIDIA_VISIBLE_DEVICES
          value: void
        - name: gpus
          value: "0"
        - name: workers
          value: "1"
        - name: TEST_TMPDIR
          value: "/mnt"
        - name: OSS_ACCESS_KEY_ID      #The source of the AccessKey is the same as that of the PV.
          valueFrom:
            secretKeyRef:
              name: oss-secret
              key: akId
        - name: OSS_ACCESS_KEY_SECRET  #The source of the AccessKey is the same as that of the PV.
          valueFrom:
            secretKeyRef:
              name: oss-secret
              key: akSecret
        - name: URL                    #You can ignore this if a default URL is configured.
          value: "https://oss-<region>.aliyuncs.com"
        - name: BUCKET                 #You can ignore this if a default BUCKET is configured.
          value: "<bucket-name>"
        image: registry.cn-beijing.aliyuncs.com/tool-sys/tf-train-demo:ro
        imagePullPolicy: Always
        name: tensorflow
        ports:
        - containerPort: 20000
          name: tfjob-port
          protocol: TCP
        volumeMounts:
          - name: train
            mountPath: "/mnt/train/data"
        workingDir: /root
      priority: 0
      restartPolicy: Never
      securityContext: {}
      terminationGracePeriodSeconds: 30
      volumes:
      - name: train
        persistentVolumeClaim:
          claimName: tf-train-pvc
    EOF

Compruebe la separación de lectura y escritura

Después de desplegar con cualquiera de las opciones de escritura:

  1. Revise el estado del pod:

    kubectl get pod tf-mnist

    Espere a que el estado cambie a Completed:

    NAME       READY   STATUS      RESTARTS   AGE
    tf-mnist   0/1     Completed   0          2m25s
  2. Revise el tiempo de carga de datos:

    kubectl logs tf-mnist | grep dataload

    Con la separación de lectura y escritura y la caché del kernel habilitada, el tiempo de carga de datos disminuye:

    dataload cost time:  0.843528985977

    La línea base sin separación es de aproximadamente 1.54 segundos. Los trabajos de entrenamiento más grandes y las cargas de datos repetidas muestran una mejora mayor.

  3. Inicie sesión en la consola de administración de OSS y compruebe que los archivos de puntos de control están presentes bajo /tf-train/training_logs en el bucket.

    image.png

Referencias

Referencia del SDK de OSS

Este tema utiliza el SDK de Python. Otros SDK disponibles:

Para otros SDK (PHP, Node.js, Browser.js, .NET, Android, iOS, Ruby), consulte la referencia del SDK.

Otras herramientas de escritura

Estas herramientas también permiten escribir en OSS:

Tool

Reference

Consola de administración de OSS

Inicio rápido

OpenAPI

PutObject

Interfaz de línea de comandos de ossutil

cp (carga de archivos)

Herramienta de administración gráfica ossbrowser

Common operations