Todos os produtos
Search
Central de documentação

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

Última atualização: Sep 12, 2026

Os volumes persistentes (PVs) do OSS aceitam múltiplos clientes, mas o suporte completo a gravação prejudica o desempenho de leitura. A separação de leitura e gravação resolve esse problema ao direcionar as operações por caminhos de montagem distintos, melhorando o throughput em cargas de trabalho com uso intenso de leitura, como treinamento de modelos, inferência e análise de dados.

Implemente a separação de leitura e gravação para PVs do OSS com ossfs ou o OSS SDK, conforme demonstrado em um job de treinamento de reconhecimento de escrita MNIST.

Pré-requisitos

Verifique se você possui:

Importante

Não recomendamos o acesso ao OSS entre contas diferentes.

Escolha um cliente

Os PVs do OSS suportam três clientes: ossfs 1,0, ossfs 2,0 e strmvol. Todos permitem acesso somente leitura, mas as capacidades de gravação variam:

Cliente

Somente leitura

Leitura/gravação

Mais indicado para

ossfs 1,0

Sim

Gravação completa

Cargas gerais de leitura/gravação; modo de leitura direta disponível (v1.91+)

ossfs 2,0

Sim

Apenas gravações sequenciais por acréscimo

Cargas com predominância de leitura; requer CSI >= 1.33.1

strmvol

Sim

Muitos arquivos pequenos (datasets, logs de séries temporais, backtesting quantitativo)

Consulte a Referência de seleção de cliente.

Casos de uso

Acesso somente leitura

Defina o modo de acesso do PV como ReadOnlyMany para evitar modificações acidentais nos dados. Essa configuração é adequada para inferência, análise de dados e consultas de logs.

Configure os parâmetros de otherOpts listados abaixo para ajustar o ossfs 1,0 em cenários somente leitura. A maioria das cargas funciona bem com os valores padrão.

Parâmetro

Padrão

Descrição

kernel_cache

Desativado

Habilita o cache de buffer do kernel para leituras não críticas em tempo real. Utiliza memória livre para armazenamento em cache.

parallel_count

20

Shards simultâneos para upload e download de arquivos grandes.

max_multireq

20

Número máximo de requisições simultâneas de listagem de metadados. Deve ser >= parallel_count.

max_stat_cache_size

1000

Quantidade de entradas de metadados mantidas em cache. Defina como 0 para desativar. Aumente este valor para acelerar comandos ls em diretórios grandes — 10.000 entradas consomem cerca de 40 MB.

direct_read

Desativado

Modo de leitura direta para cenários somente leitura (ossfs >= 1.91). Consulte Recursos e testes de desempenho da nova versão do ossfs 1,0 e Otimização de desempenho para cenários somente leitura.

Acesso de leitura e gravação

Defina o modo de acesso do PV como ReadWriteMany para cargas de trabalho que precisam gravar dados.

Nota

O ossfs não garante consistência em gravações concorrentes. Vários escritores operando sobre os mesmos objetos podem corromper os dados. Utilize apenas um escritor por caminho ao salvar checkpoints.

Aviso

Excluir ou modificar arquivos no caminho montado também exclui ou modifica os objetos correspondentes no bucket do OSS. Ative o versionamento para proteger seus dados contra perda acidental.

Em cargas intensivas de leitura que utilizam caminhos separados para leitura e gravação (como treinamento de modelos), monte o caminho de leitura como ReadOnlyMany com cache ativado e processe as gravações por meio de um PV ReadWriteMany ou diretamente pelo OSS SDK.

Como funciona a separação de leitura e gravação

A separação de leitura e gravação direciona as operações por pontos de montagem distintos, cada um apontando para um subcaminho diferente do mesmo bucket do OSS. Isso isola o I/O de leitura do I/O de gravação.

  • Caminho de leitura: monte um subcaminho (por exemplo, /tf-train/train/data) como ReadOnlyMany com cache ativado. Leituras repetidas são atendidas diretamente da memória.

  • Caminho de gravação: monte um subcaminho diferente (por exemplo, /tf-train/training_logs) como ReadWriteMany ou grave diretamente via SDK.

Exemplo: Treinamento de reconhecimento de escrita MNIST

O job de treinamento executa as seguintes etapas:

  1. Lê o dataset de treinamento de /tf-train/train/data no bucket do OSS usando um PV somente leitura.

  2. Grava os checkpoints de treinamento em /tf-train/training_logs utilizando um PV de leitura/gravação ou o OSS SDK.

Baixe o dataset MNIST e faça o upload para /tf-train/train/data no seu bucket do OSS:

Organização dos arquivos no bucket do OSS:

oss-read-write-splitting-1

Implementar operações de leitura e gravação com ossfs

Como as gravações de checkpoint são acréscimos sequenciais, tanto o ossfs 1,0 quanto o ossfs 2,0 funcionam para o caminho de gravação.

  1. Implante a aplicação de treinamento. A aplicação monta o subcaminho /tf-train do bucket do OSS no diretório /mnt do pod. Consulte Usar volumes provisionados estaticamente com ossfs 1,0 ou Usar PVs com ossfs 2,0.

    1. Crie um PV com 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. Crie o pod de treinamento:

      Durante o treinamento, o ossfs envia os arquivos de /mnt/training_logs (pod) para /tf-train/training_logs (bucket do 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. Verifique as leituras e gravações de dados.

    1. Verifique o status do pod:

      kubectl get pod tf-mnist

      Aguarde até que o status mude de Running para Completed:

      NAME       READY   STATUS      RESTARTS   AGE
      tf-mnist   0/1     Completed   0          2m12s
    2. Verifique o tempo de carregamento dos dados:

      kubectl logs tf-mnist | grep dataload

      Saída esperada:

      dataload cost time:  1.54191803932
    3. Faça login no OSS Management Console e confirme a existência dos arquivos em /tf-train/training_logs dentro do bucket.

Otimizar o desempenho de leitura com separação de leitura e gravação

Divida o único PV de leitura/gravação em dois: um PV somente leitura com ajuste de cache para o dataset e um PV de gravação para os checkpoints. Apenas a configuração de montagem muda — o código de treinamento permanece igual.

Duas opções de gravação estão disponíveis:

  • Opção 1: usar um PV ossfs separado de leitura/gravação para escrever os checkpoints.

  • Opção 2: usar o OSS SDK para gravar os checkpoints diretamente, sem passar pelo ossfs.

Opção 1: Gravar usando um PV ossfs de leitura/gravação

  1. Crie um PV ossfs 1,0 somente leitura para o dataset. As principais alterações de configuração são:

    • Defina accessModes como ReadOnlyMany tanto no PV quanto no PVC. Monte o subcaminho do dataset /tf-train/train/data.

    • Em otherOpts, adicione -o kernel_cache -o max_stat_cache_size=10000 -o umask=022:

      • kernel_cache ativa o cache de leitura em memória.

      • max_stat_cache_size=10000 armazena 10.000 entradas de metadados em cache (~40 MB). Ajuste conforme o tipo de instância e o tamanho do dataset.

      • umask=022 concede permissão de leitura a processos não root no contêiner.

    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. Crie um PV ossfs 1,0 de leitura/gravação para os checkpoints, montando o subcaminho /tf-train/training_logs. O cache de metadados fica desativado (max_stat_cache_size=0) porque gravações sequenciais de checkpoint não se beneficiam de 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. Implante o pod de treinamento com ambos os PVs montados.

    Nenhuma alteração de código é necessária. Monte ambos os PVs — o PV somente leitura em /mnt/train/data e o PV de leitura/gravação em /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

Opção 2: Gravar usando o OSS SDK

Grave os checkpoints diretamente no OSS com o SDK — nenhum PV de leitura/gravação é necessário. O pod lê de um PV somente leitura e grava via SDK.

  1. Adicione o OSS Python SDK à imagem do contêiner:

    RUN pip install oss2

    Consulte Instalação.

  2. Modifique o código de treinamento para enviar os checkpoints usando o SDK. O código original salva checkpoints em log_dir a cada 100 iterações usando tf.train.Saver com max_to_keep=0, gerando 10 conjuntos de checkpoint após 1.000 iterações.

    • Defina max_to_keep=1 para reter apenas o checkpoint mais recente, reduzindo o uso de memória.

    • Envie cada checkpoint para o OSS com put_object_from_file logo após salvá-lo.

    Utilize I/O assíncrono com o SDK para aumentar ainda mais o throughput quando os caminhos de leitura e gravação estiverem separados.
    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)

    Substitua pelo envio baseado no SDK. Duas alterações reduzem o uso de memória e eliminam a necessidade do PV de leitura/gravação: leia o AccessKey e as configurações do bucket a partir de variáveis de ambiente. 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))

    A imagem modificada do contêiner é registry.cn-beijing.aliyuncs.com/tool-sys/tf-train-demo:ro.

  3. Implante o pod com o PV somente leitura e as credenciais do SDK. O pod define accessModes como ReadOnlyMany e passa OSS_ACCESS_KEY_ID e OSS_ACCESS_KEY_SECRET para que o SDK autentique com as mesmas credenciais do 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

Verificar a separação de leitura e gravação

Após implantar com qualquer uma das opções de gravação:

  1. Verifique o status do pod:

    kubectl get pod tf-mnist

    Aguarde até que o status mude para Completed:

    NAME       READY   STATUS      RESTARTS   AGE
    tf-mnist   0/1     Completed   0          2m25s
  2. Verifique o tempo de carregamento dos dados:

    kubectl logs tf-mnist | grep dataload

    Com a separação de leitura e gravação e o cache do kernel ativado, o tempo de carregamento diminui:

    dataload cost time:  0.843528985977

    A linha de base sem separação é de aproximadamente 1,54 segundo. Jobs de treinamento maiores e carregamentos repetidos de dados apresentam melhorias ainda mais significativas.

  3. Faça login no OSS Management Console e confirme que os arquivos de checkpoint estão presentes em /tf-train/training_logs dentro do bucket.

    image.png

Referências

Referência do OSS SDK

Este tópico utiliza o Python SDK. Outros SDKs disponíveis:

Para outros SDKs (PHP, Node.js, Browser.js, .NET, Android, iOS, Ruby), consulte a Referência do SDK.

Outras ferramentas de gravação

As ferramentas abaixo também permitem gravar no OSS:

Ferramenta

Referência

OSS Management Console

Guia de início rápido

OpenAPI

PutObject

Interface de linha de comando ossutil

cp (upload de arquivos)

Ferramenta gráfica de gerenciamento ossbrowser

Common operations