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 :
La dernière version du composant Container Storage Interface (CSI) est installée. Chaque client nécessite une version CSI spécifique. Consultez Gérer les composants csi-plugin et csi-provisioner.
Un bucket OSS situé dans le même compte Alibaba Cloud que le cluster.
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.
Pour les charges de travail intensives en lecture, utilisez ossfs 2.0 (CSI >= 1.33.1). Reportez-vous à Utiliser des PV ossfs 2,0.
Pour de nombreux petits fichiers, privilégiez strmvol. Voir Utiliser des PV strmvol.
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 |
|
|
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. |
|
|
20 |
Nombre de shards simultanés pour les téléchargements et uploads de fichiers volumineux. |
|
|
20 |
Nombre maximal de requêtes de listing de métadonnées simultanées. Doit être >= |
|
|
1000 |
Nombre d'entrées de métadonnées mises en cache. Définissez cette valeur sur |
|
|
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.
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.
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 modeReadOnlyManyavec 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 modeReadWriteMany, 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 :
Lit le jeu de données d'entraînement depuis
/tf-train/train/datadans le bucket OSS à l'aide d'un PV en lecture seule.Écrit les checkpoints d'entraînement dans
/tf-train/training_logsen 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 :

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.
-
Déployez l'application d'entraînement. Celle-ci monte le sous-chemin
/tf-traindu bucket OSS dans le répertoire/mntdu pod. Consultez Utiliser des volumes provisionnés statiquement ossfs 1.0 ou Utiliser des PV ossfs 2.0.-
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 -
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
-
-
Vérifiez les lectures et écritures de données.
-
Vérifiez l'état du pod :
kubectl get pod tf-mnistPatientez jusqu'à ce que l'état passe de
RunningàCompleted:NAME READY STATUS RESTARTS AGE tf-mnist 0/1 Completed 0 2m12s -
Vérifiez le temps de chargement des données :
kubectl logs tf-mnist | grep dataloadSortie attendue :
dataload cost time: 1.54191803932 Connectez-vous à la Console de gestion OSS et vérifiez la présence des fichiers sous
/tf-train/training_logsdans 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
-
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
accessModessurReadOnlyManypour 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_cacheactive la mise en cache des lectures en mémoire.max_stat_cache_size=10000met 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=022accorde 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 -
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 -
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/dataet 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.
-
Ajoutez le SDK Python OSS à l'image du conteneur :
RUN pip install oss2Consultez Installation.
-
Modifiez le code d'entraînement pour uploader les checkpoints à l'aide du SDK. Le code original enregistre les checkpoints dans
log_dirtoutes les 100 itérations viatf.train.Saveravecmax_to_keep=0, ce qui génère 10 jeux de checkpoints après 1 000 itérations.Définissez
max_to_keep=1pour ne conserver que le dernier checkpoint et réduire l'utilisation de la mémoire.Uploadez chaque checkpoint vers OSS avec
put_object_from_fileaprè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. -
Déployez le pod avec le PV en lecture seule et les identifiants du SDK. Le pod définit
accessModessurReadOnlyManyet transmetOSS_ACCESS_KEY_IDainsi queOSS_ACCESS_KEY_SECRETafin 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 :
-
Vérifiez l'état du pod :
kubectl get pod tf-mnistAttendez que l'état passe à
Completed:NAME READY STATUS RESTARTS AGE tf-mnist 0/1 Completed 0 2m25s -
Vérifiez le temps de chargement des données :
kubectl logs tf-mnist | grep dataloadAvec la séparation lecture/écriture et le cache noyau activé, le temps de chargement des données diminue :
dataload cost time: 0.843528985977Le 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.
-
Connectez-vous à la Console de gestion OSS et vérifiez la présence des fichiers de checkpoints sous
/tf-train/training_logsdans le bucket.
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 |
|
OpenAPI |
|
|
Interface de ligne de commande ossutil |
|
|
Outil de gestion graphique ossbrowser |