Tous les produits
Search
Centre de documentation

Container Service for Kubernetes:Bonnes pratiques pour la séparation lecture/écriture OSS

Dernière mise à jour :Aug 28, 2026

Les volumes persistants (PV) OSS prennent en charge plusieurs clients, mais l'activation 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. Cette approche améliore 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 à la reconnaissance d'écriture manuscrite MNIST.

Prérequis

Assurez-vous de disposer des éléments suivants :

Important

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

Choisir un client

Les PV OSS sont compatibles avec trois clients : ossfs 1,0, ossfs 2,0 et strmvol. Chacun permet l'accès en lecture seule, mais leurs capacités d'écriture diffèrent :

Client

Lecture seule

Lecture/écriture

Cas d'usage optimal

ossfs 1,0

Oui

Écriture complète

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

ossfs 2,0

Oui

Écritures par ajout séquentiel uniquement

Charges de travail intensives en lecture ; nécessite CSI >= 1.33.1

strmvol

Oui

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

Consultez la Référence pour la sélection du client.

Cas d'utilisation

Accès en lecture seule

Définissez le mode d'accès du PV sur ReadOnlyMany afin d'éviter toute modification accidentelle des données. Ce mode convient à l'inférence, à l'analyse de données et aux requêtes sur les journaux.

Configurez les paramètres otherOpts suivants pour optimiser ossfs 1.0 dans les scénarios en lecture seule. Les valeurs par défaut suffisent pour la plupart des charges de travail.

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

Nombre de shards simultanés pour les téléchargements et uploads de fichiers volumineux.

max_multireq

20

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

max_stat_cache_size

1000

Nombre d'entrées de métadonnées mises en cache. Définissez cette valeur sur 0 pour désactiver le cache. Augmentez-la pour accélérer la commande ls dans les répertoires volumineux (10 000 entrées consomment 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 lors d'écritures simultanées : plusieurs processus écrivant sur les mêmes objets peuvent corrompre les données. Utilisez un seul processus d'écriture par chemin pour les checkpoints.

Avertissement

La suppression ou la modification de fichiers dans le chemin monté entraîne également la suppression ou la modification des objets correspondants dans le bucket OSS. Activez le versioning pour vous prémunir contre la perte de données.

Pour les charges de travail intensives en lecture disposant de chemins distincts pour la lecture et l'écriture (comme l'entraînement de modèles), montez le chemin de lecture en mode ReadOnlyMany avec le cache activé. Gérez ensuite les écritures via un PV ReadWriteMany ou le SDK OSS.

Fonctionnement de la séparation lecture/écriture

La séparation lecture/écriture achemine les opérations de lecture et d'écriture 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 mode ReadOnlyMany avec le cache activé. Les lectures répétées sont alors servies depuis la mémoire.

  • Chemin d'écriture : Montez un sous-chemin différent (par exemple, /tf-train/training_logs) en mode ReadWriteMany, ou effectuez les écritures directement via le SDK.

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

La tâche d'entraînement effectue les opérations suivantes :

  1. Lit le jeu 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 checkpoints d'entraînement dans /tf-train/training_logs en utilisant soit un PV en lecture/écriture, soit le SDK OSS.

Téléchargez le jeu de données MNIST et uploadez-le dans /tf-train/train/data de votre bucket OSS :

Organisation des fichiers dans le bucket OSS :

oss-read-write-splitting-1

Mettre en œuvre les opérations de lecture/écriture avec ossfs

Les écritures de checkpoints étant des ajouts séquentiels, ossfs 1,0 et ossfs 2,0 conviennent tous deux au chemin d'écriture.

  1. Déployez l'application d'entraînement. Celle-ci monte le sous-chemin /tf-train du bucket OSS dans le répertoire /mnt du pod. Consultez Utiliser des volumes provisionnés statiquement ossfs 1.0 ou Utiliser des 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 upload les fichiers de /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

      Patientez jusqu'à ce 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 la présence des fichiers sous /tf-train/training_logs dans le bucket.

Optimiser les performances de lecture grâce à la séparation lecture/écriture

Scindez le PV unique en lecture/écriture en deux volumes distincts : un PV en lecture seule avec réglage du cache pour le jeu de données, et un PV en écriture pour les checkpoints. Seule la configuration de montage change ; le code d'entraînement reste identique.

Deux options s'offrent à vous pour l'écriture :

  • Option 1 : Utiliser un PV ossfs en lecture/écriture distinct pour écrire les checkpoints.

  • Option 2 : Recourir au SDK OSS pour écrire les checkpoints directement, sans passer par ossfs.

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

  1. Créez un PV ossfs 1,0 en lecture seule pour le jeu de données. Voici les modifications clés de la configuration :

    • Définissez accessModes sur ReadOnlyMany pour le PV et le PVC. Montez le sous-chemin du jeu 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 cette valeur selon le type d'instance et la taille du jeu de données.

      • umask=022 accorde l'accès en lecture aux processus conteneurisés 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 checkpoints, 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 checkpoints ne bénéficient pas du 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 simplement 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 : Écriture à l'aide du SDK OSS

Écrivez les checkpoints directement dans OSS via le SDK, sans nécessiter de PV en lecture/écriture. Le pod lit les données depuis 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 uploader les checkpoints à l'aide du SDK. Le code original enregistre les checkpoints dans log_dir toutes les 100 itérations via tf.train.Saver avec max_to_keep=0, ce qui génère 10 jeux de checkpoints après 1 000 itérations.

    • Définissez max_to_keep=1 pour ne conserver que le dernier checkpoint et réduire l'utilisation de la mémoire.

    • Uploadez chaque checkpoint vers OSS avec put_object_from_file après l'enregistrement.

    Utilisez des 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 ce code par des uploads via le SDK. Deux modifications permettent de réduire l'utilisation de la mémoire et de supprimer le besoin d'un PV en lecture/écriture : lisez l'AccessKey et les paramètres du bucket depuis les variables d'environnement. Consultez Configurer les identifiants 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 identifiants du SDK. Le pod définit accessModes sur ReadOnlyMany et transmet OSS_ACCESS_KEY_ID ainsi que OSS_ACCESS_KEY_SECRET afin que le SDK s'authentifie avec les mêmes identifiants 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 des options 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 noyau activé, le temps de chargement des données diminue :

    dataload cost time:  0.843528985977

    Le temps de référence sans séparation est d'environ 1,54 seconde. Les tâches d'entraînement plus volumineuses et les chargements de données répétés montrent une amélioration encore plus marquée.

  3. Connectez-vous à la Console de gestion OSS et vérifiez la présence des fichiers de checkpoints 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. D'autres SDK sont également disponibles :

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

Autres outils d'écriture

Les outils suivants permettent également d'écrire dans OSS :

Outil

Référence

Console de gestion OSS

Démarrage rapide

OpenAPI

PutObject

Interface de ligne de commande ossutil

cp (uploader des fichiers)

Outil de gestion graphique ossbrowser

Opérations courantes