Tous les produits
Search
Centre de documentation

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

Dernière mise à jour :Sep 12, 2026

Les volumes persistants (PV) OSS prennent en charge plusieurs clients, mais une prise en charge complète de l'écriture dégrade les performances en lecture. La séparation lecture/écriture résout ce problème en acheminant les lectures et les écritures via des chemins de montage distincts, améliorant ainsi le débit pour les charges de travail intensives en lecture, telles que l'entraînement de modèles, l'inférence et l'analyse de données.

Mettez en œuvre la séparation lecture/écriture pour les PV OSS avec ossfs ou le SDK OSS, comme illustré par une tâche d'entraînement de reconnaissance d'écriture manuscrite MNIST.

Prérequis

Assurez-vous d'avoir :

Important

L'accès OSS inter-comptes n'est pas recommandé.

Choisir un client

Les PV OSS prennent en charge trois clients — ossfs 1,0, ossfs 2,0 et strmvol. Chaque client prend en charge l'accès en lecture seule ; les capacités d'écriture diffèrent :

Client

Lecture seule

Lecture/écriture

Idéal pour

ossfs 1,0

Oui

Écriture complète

Charges de travail générales en lecture/écriture ; mode de lecture directe disponible (v1.91+)

ossfs 2,0

Oui

Écritures séquentielles en ajout uniquement

Charges de travail à forte dominance de lecture ; nécessite CSI >= 1.33.1

strmvol

Oui

—

Nombreux petits fichiers (ensembles de données, journaux de séries temporelles, backtesting quantitatif)

Consultez référence de sélection du client.

Cas d'utilisation

Accès en lecture seule

Définissez le mode d'accès du PV sur ReadOnlyMany pour empêcher toute modification accidentelle des données. Cette configuration convient à l'inférence, à l'analyse de données et aux requêtes de journaux.

Configurez ces paramètres otherOpts pour optimiser ossfs 1.0 dans les scénarios en lecture seule. La plupart des charges de travail peuvent utiliser les valeurs par défaut.

Paramètre

Valeur par défaut

Description

kernel_cache

Désactivé

Active le cache tampon du noyau pour les lectures non temps réel. Utilise la mémoire libre pour la mise en cache.

parallel_count

20

Shards concurrents pour les téléchargements et téléversements de grands fichiers.

max_multireq

20

Nombre maximal de requêtes de liste de métadonnées concurrentes. Doit être >= parallel_count.

max_stat_cache_size

1000

Nombre d'entrées de métadonnées mises en cache. Définissez sur 0 pour désactiver. Augmentez cette valeur pour accélérer ls dans les grands répertoires — 10 000 entrées utilisent environ 40 Mo.

direct_read

Désactivé

Mode de lecture directe pour les scénarios en lecture seule (ossfs >= 1.91). Consultez Fonctionnalités et tests de performance de la nouvelle version ossfs 1.0 et Optimisation des performances pour les scénarios en lecture seule.

Accès en lecture/écriture

Définissez le mode d'accès du PV sur ReadWriteMany pour les charges de travail qui écrivent des données.

Remarque

ossfs ne garantit pas la cohérence pour les écritures simultanées — plusieurs rédacteurs sur les mêmes objets peuvent entraîner une corruption des données. Utilisez un seul rédacteur par chemin pour les écritures de points de contrôle.

Avertissement

La suppression ou la modification de fichiers dans le chemin monté supprime ou modifie également les objets dans le bucket OSS. Activez versioning pour protéger contre la perte de données.

Pour les charges de travail intensives en lecture avec des chemins de lecture et d'écriture séparés (comme l'entraînement de modèles), montez le chemin de lecture en tant que ReadOnlyMany avec la mise en cache activée, et gérez les écritures via un PV ReadWriteMany ou le SDK OSS.

Fonctionnement de la séparation lecture/écriture

La séparation lecture/écriture achemine les lectures et les écritures via des points de montage distincts, chacun pointant vers un sous-chemin différent du même bucket OSS. Cela isole les E/S de lecture des E/S d'écriture.

  • Chemin de lecture — Montez un sous-chemin (par exemple, /tf-train/train/data) en tant que ReadOnlyMany avec la mise en cache activée. Les lectures répétées sont servies depuis la mémoire.

  • Chemin d'écriture — Montez un autre sous-chemin (par exemple, /tf-train/training_logs) en tant que ReadWriteMany, ou écrivez directement via le SDK.

Exemple : Entraînement de reconnaissance d'écriture manuscrite MNIST

La tâche d'entraînement :

  1. Lit l'ensemble de données d'entraînement depuis /tf-train/train/data dans le bucket OSS à l'aide d'un PV en lecture seule.

  2. Écrit les points de contrôle d'entraînement dans /tf-train/training_logs en utilisant soit un PV en lecture/écriture, soit le SDK OSS.

Téléchargez l'ensemble de données MNIST et téléversez-le dans /tf-train/train/data de votre bucket OSS :

Organisation des fichiers dans le bucket OSS :

oss-read-write-splitting-1

Implémenter les opérations de lecture/écriture à l'aide d'ossfs

Étant donné que les écritures de points de contrôle sont des ajouts séquentiels, ossfs 1,0 et ossfs 2,0 fonctionnent tous deux pour le chemin d'écriture.

  1. Déployez l'application d'entraînement. L'application monte le sous-chemin /tf-train du bucket OSS dans le répertoire /mnt du pod. Consultez Utiliser les volumes provisionnés statiquement ossfs 1.0 ou Utiliser les PV ossfs 2.0.

    1. Créez un PV 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. Créez le pod d'entraînement :

      Pendant l'entraînement, ossfs téléverse les fichiers depuis /mnt/training_logs (pod) vers /tf-train/training_logs (bucket 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. Vérifiez les lectures et écritures de données.

    1. Vérifiez l'état du pod :

      kubectl get pod tf-mnist

      Attendez que l'état passe de Running à Completed :

      NAME       READY   STATUS      RESTARTS   AGE
      tf-mnist   0/1     Completed   0          2m12s
    2. Vérifiez le temps de chargement des données :

      kubectl logs tf-mnist | grep dataload

      Sortie attendue :

      dataload cost time:  1.54191803932
    3. Connectez-vous à la Console de gestion OSS et vérifiez que les fichiers existent sous /tf-train/training_logs dans le bucket.

Optimiser les performances de lecture à l'aide de la séparation lecture/écriture

Divisez le PV unique en lecture/écriture en deux : un PV en lecture seule avec réglage du cache pour l'ensemble de données, et un PV d'écriture pour les points de contrôle. Seule la configuration de montage change — le code d'entraînement reste identique.

Deux options d'écriture :

  • Option 1 : Utilisez un PV ossfs en lecture/écriture distinct pour les écritures de points de contrôle.

  • Option 2 : Utilisez le SDK OSS pour écrire les points de contrôle directement, en contournant complètement ossfs.

Option 1 : Écrire à l'aide d'un PV ossfs en lecture/écriture

  1. Créez un PV ossfs 1,0 en lecture seule pour l'ensemble de données. Les modifications clés de configuration :

    • Définissez accessModes sur ReadOnlyMany sur le PV et le PVC. Montez le sous-chemin de l'ensemble de données /tf-train/train/data.

    • Dans otherOpts, ajoutez -o kernel_cache -o max_stat_cache_size=10000 -o umask=022 :

      • kernel_cache active la mise en cache des lectures en mémoire.

      • max_stat_cache_size=10000 met en cache 10 000 entrées de métadonnées (~40 Mo). Ajustez en fonction du type d'instance et de la taille de l'ensemble de données.

      • umask=022 accorde l'accès en lecture aux processus de conteneur non 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. Créez un PV ossfs 1,0 en lecture/écriture pour les points de contrôle, en montant le sous-chemin /tf-train/training_logs. La mise en cache des métadonnées est désactivée (max_stat_cache_size=0) car les écritures séquentielles de points de contrôle ne bénéficient pas de la mise en cache.

    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. Déployez le pod d'entraînement avec les deux PV montés.

    Aucune modification de code n'est nécessaire. Montez les deux PV — le PV en lecture seule sur /mnt/train/data et le PV en lecture/écriture sur /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

Option 2 : Écrire à l'aide du SDK OSS

Écrivez les points de contrôle directement dans OSS avec le SDK — aucun PV en lecture/écriture n'est nécessaire. Le pod lit à partir d'un PV en lecture seule et écrit via le SDK.

  1. Ajoutez le SDK Python OSS à l'image du conteneur :

    RUN pip install oss2

    Consultez Installation.

  2. Modifiez le code d'entraînement pour téléverser les points de contrôle à l'aide du SDK. Le code original enregistre les points de contrôle dans log_dir toutes les 100 itérations en utilisant tf.train.Saver avec max_to_keep=0, produisant 10 ensembles de points de contrôle après 1 000 itérations.

    • Définissez max_to_keep=1 pour ne conserver que le dernier point de contrôle, réduisant ainsi l'utilisation de la mémoire.

    • Téléversez chaque point de contrôle dans OSS avec put_object_from_file après l'enregistrement.

    Utilisez les E/S asynchrones avec le SDK pour améliorer davantage le débit lorsque les chemins de lecture et d'écriture sont séparés.
    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)

    Remplacez par des téléversements basés sur le SDK. Deux modifications réduisent l'utilisation de la mémoire et éliminent le PV en lecture/écriture : lisez la AccessKey et les paramètres du bucket à partir des variables d'environnement. Consultez Configurer les informations d'identification d'accès.

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

    L'image de conteneur modifiée est registry.cn-beijing.aliyuncs.com/tool-sys/tf-train-demo:ro.

  3. Déployez le pod avec le PV en lecture seule et les informations d'identification du SDK. Le pod définit accessModes sur ReadOnlyMany et transmet OSS_ACCESS_KEY_ID et OSS_ACCESS_KEY_SECRET afin que le SDK s'authentifie avec les mêmes informations d'identification que le 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

Vérifier la séparation lecture/écriture

Après le déploiement avec l'une ou l'autre option d'écriture :

  1. Vérifiez l'état du pod :

    kubectl get pod tf-mnist

    Attendez que l'état passe à Completed :

    NAME       READY   STATUS      RESTARTS   AGE
    tf-mnist   0/1     Completed   0          2m25s
  2. Vérifiez le temps de chargement des données :

    kubectl logs tf-mnist | grep dataload

    Avec la séparation lecture/écriture et le cache du noyau activé, le temps de chargement des données diminue :

    dataload cost time:  0.843528985977

    La référence sans séparation est d'environ 1,54 seconde. Les tâches d'entraînement plus importantes et les chargements de données répétés montrent une amélioration plus importante.

  3. Connectez-vous à la Console de gestion OSS et vérifiez que les fichiers de points de contrôle sont présents sous /tf-train/training_logs dans le bucket.

    image.png

Références

Référence du SDK OSS

Cette rubrique utilise le SDK Python. Autres SDK disponibles :

Pour les autres SDK (PHP, Node.js, Browser.js, .NET, Android, iOS, Ruby), consultez la Référence SDK.

Autres outils d'écriture

Ces outils prennent également en charge l'écriture dans OSS :

Outil

Référence

Console de gestion OSS

Démarrage rapide

OpenAPI

PutObject

Interface de ligne de commande ossutil

cp (téléverser des fichiers)

Outil de gestion graphique ossbrowser

Opérations courantes