在文件系统之间转移数据

本页面介绍了如何在两个 POSIX 文件系统之间转移数据。常见用例包括:

  • 分流到云和混合 HPC:快速将大型数据集从本地转移到云进行处理。
  • 迁移并同步到 Filestore:将数据从本地文件系统迁移或同步到 Filestore
  • 代管式文件转移:在数据中心之间或两个云内文件系统之间安全可靠地转移数据。

转移性能指南

以下指南可帮助您最大限度地提高文件系统到文件系统转移作业的性能。

代理部署

通常,我们建议在每个源代理池和目标代理池中使用三个代理。监控转移作业并根据需要添加更多代理。每个代理需要 4 个 vCPU 和 8 GiB RAM。

如果您要迁移到 Filestore 实例,Filestore 建议为每个代理使用 n2-standard-8 实例类型。在将实例装载到 Compute Engine 虚拟机时指定 nconnect=2。如需详细了解如何优化和测试实例性能,请参阅 Filestore 的性能指南

转移大量小文件

在转移大量小文件时,为了提高性能,我们建议将文件拆分为多个目录,并避免在单个目录中包含数百万个文件。

准备工作

在执行本页面中所述的任务之前,请先完成前提步骤

创建代理池并安装代理

对于文件系统之间的转移作业,您需要为来源文件系统和目标文件系统创建代理池代理。需要在有权访问来源文件系统的机器或虚拟机上为来源代理池安装代理。需要在有权访问目标文件系统的机器或虚拟机上为目标代理池安装代理。

请勿在代理 ID 前缀或代理池名称中包含敏感信息,例如个人身份信息 (PII) 或安全数据。资源名称可能会传播到其他 Google Cloud 资源的名称,并且可能会向您项目之外的 Google 内部系统公开。

创建来源代理池

使用以下方法之一创建来源代理池:

gcloud CLI

通过运行以下命令创建来源代理池:

gcloud transfer agent-pools create SOURCE_AGENT_POOL

SOURCE_AGENT_POOL 替换为您要为来源代理池指定的名称。

Google Cloud 控制台

  1. 在 Google Cloud 控制台中,转到代理池页面。

    转到“代理池”

    系统随即将显示代理池页面,其中列出了现有的代理池。

  2. 点击创建另一个池

  3. 输入池的名称。

  4. 点击创建

为来源代理池安装代理

在有权访问来源文件系统的机器或虚拟机上为来源代理池安装代理:

gcloud CLI

通过运行以下命令为来源代理池安装代理:

gcloud transfer agents install --pool=SOURCE_AGENT_POOL --count=NUMBER_OF_AGENTS \
  --mount-directories=MOUNT_DIRECTORIES

替换以下内容:

  • SOURCE_AGENT_POOL 替换为来源代理池的名称。
  • NUMBER_OF_AGENTS 替换为您要为来源代理池安装的代理数量。如需确定最适合您的环境的代理数量,请参阅代理要求和最佳实践
  • MOUNT_DIRECTORIES 替换为要从中复制的源文件系统上的目录的逗号分隔列表。如果省略此标志,系统会装载整个文件系统,这可能会带来安全风险

Google Cloud 控制台

  1. 在 Google Cloud 控制台中,转到代理池页面。

    转到“代理池”

    系统随即将显示代理池页面,其中列出了现有的代理池。

  2. 点击刚刚创建的来源代理池的名称。

  3. 代理标签页下,点击安装代理

  4. 按照 Google Cloud 控制台中的说明安装 Docker 并启动代理。

创建目标代理池并安装代理

重复上述步骤,以创建目标代理池并安装代理。

创建 Cloud Storage 存储桶作为中间存储桶

文件系统之间的转移需要使用 Cloud Storage 存储桶作为数据转移的中间存储桶。

  1. 创建一个具有以下设置的 Cloud Storage 标准类存储桶:

    • 加密:您可以指定客户管理的加密密钥 (CMEK)。否则,系统会使用 Google 拥有的密钥和 Google 管理的密钥。
    • 对象版本控制存储桶锁定默认对象保全:保持这些功能处于停用状态。
  2. 使用以下方法之一授予权限和角色:

    • 为 Storage Transfer Service 服务账号授予对该存储桶的 Storage Admin 角色 (roles/storage.admin)。
    • 使用 gcloud transfer authorize 授权您的账号使用所有 Storage Transfer Service 功能。此命令会授予项目级 Storage Admin 权限:

      gcloud transfer authorize --add-missing
      

创建转移作业

gcloud CLI

如需创建从来源文件系统到目标文件系统的转移作业,请运行以下命令:

gcloud transfer jobs create SOURCE_DIRECTORY DESTINATION_DIRECTORY \
    --source-agent-pool=SOURCE_AGENT_POOL \
    --destination-agent-pool=DESTINATION_AGENT_POOL \
    --intermediate-storage-path= gs://STORAGE_BUCKET/FOLDER/

执行以下变量替换操作:

  • SOURCE_DIRECTORY 替换为来源目录的路径。
  • DESTINATION_DIRECTORY 替换为目标目录的路径。
  • SOURCE_AGENT_POOL 替换为来源代理池的名称。
  • DESTINATION_AGENT_POOL 替换为目标代理池的名称。
  • STORAGE_BUCKET 替换为 Cloud Storage 存储桶的名称。
  • FOLDER 替换为您要将数据转移到的文件夹的名称。

启动转移作业时,系统会首先计算来源和目标位置中的数据,以确定自上次转移后新增或更新的源数据。只有新数据会被转移。

客户端库

Go


import (
	"context"
	"fmt"
	"io"

	storagetransfer "cloud.google.com/go/storagetransfer/apiv1"
	"cloud.google.com/go/storagetransfer/apiv1/storagetransferpb"
)

func transferFromPosix(w io.Writer, projectID string, sourceAgentPoolName string, rootDirectory string, gcsSinkBucket string) (*storagetransferpb.TransferJob, error) {
	// Your project id
	// projectId := "myproject-id"

	// The agent pool associated with the POSIX data source. If not provided, defaults to the default agent
	// sourceAgentPoolName := "projects/my-project/agentPools/transfer_service_default"

	// The root directory path on the source filesystem
	// rootDirectory := "/directory/to/transfer/source"

	// The ID of the GCS bucket to transfer data to
	// gcsSinkBucket := "my-sink-bucket"

	ctx := context.Background()
	client, err := storagetransfer.NewClient(ctx)
	if err != nil {
		return nil, fmt.Errorf("storagetransfer.NewClient: %w", err)
	}
	defer client.Close()

	req := &storagetransferpb.CreateTransferJobRequest{
		TransferJob: &storagetransferpb.TransferJob{
			ProjectId: projectID,
			TransferSpec: &storagetransferpb.TransferSpec{
				SourceAgentPoolName: sourceAgentPoolName,
				DataSource: &storagetransferpb.TransferSpec_PosixDataSource{
					PosixDataSource: &storagetransferpb.PosixFilesystem{RootDirectory: rootDirectory},
				},
				DataSink: &storagetransferpb.TransferSpec_GcsDataSink{
					GcsDataSink: &storagetransferpb.GcsData{BucketName: gcsSinkBucket},
				},
			},
			Status: storagetransferpb.TransferJob_ENABLED,
		},
	}

	resp, err := client.CreateTransferJob(ctx, req)
	if err != nil {
		return nil, fmt.Errorf("failed to create transfer job: %w", err)
	}
	if _, err = client.RunTransferJob(ctx, &storagetransferpb.RunTransferJobRequest{
		ProjectId: projectID,
		JobName:   resp.Name,
	}); err != nil {
		return nil, fmt.Errorf("failed to run transfer job: %w", err)
	}
	fmt.Fprintf(w, "Created and ran transfer job from %v to %v with name %v", rootDirectory, gcsSinkBucket, resp.Name)
	return resp, nil
}

Java

import com.google.storagetransfer.v1.proto.StorageTransferServiceClient;
import com.google.storagetransfer.v1.proto.TransferProto;
import com.google.storagetransfer.v1.proto.TransferTypes.GcsData;
import com.google.storagetransfer.v1.proto.TransferTypes.PosixFilesystem;
import com.google.storagetransfer.v1.proto.TransferTypes.TransferJob;
import com.google.storagetransfer.v1.proto.TransferTypes.TransferSpec;
import java.io.IOException;

public class TransferBetweenPosix {

  public static void main(String[] args) throws IOException {
    // TODO(developer): Replace these variables before running the sample.

    // Your project id
    String projectId = "my-project-id";

    // The agent pool associated with the POSIX data source. If not provided, defaults to the
    // default agent
    String sourceAgentPoolName = "projects/my-project-id/agentPools/transfer_service_default";

    // The agent pool associated with the POSIX data sink. If not provided, defaults to the default
    // agent
    String sinkAgentPoolName = "projects/my-project-id/agentPools/transfer_service_default";

    // The root directory path on the source filesystem
    String rootDirectory = "/directory/to/transfer/source";

    // The root directory path on the sink filesystem
    String destinationDirectory = "/directory/to/transfer/sink";

    // The ID of the GCS bucket for intermediate storage
    String bucketName = "my-intermediate-bucket";

    transferBetweenPosix(
        projectId,
        sourceAgentPoolName,
        sinkAgentPoolName,
        rootDirectory,
        destinationDirectory,
        bucketName);
  }

  public static void transferBetweenPosix(
      String projectId,
      String sourceAgentPoolName,
      String sinkAgentPoolName,
      String rootDirectory,
      String destinationDirectory,
      String bucketName)
      throws IOException {

    TransferJob transferJob =
        TransferJob.newBuilder()
            .setProjectId(projectId)
            .setTransferSpec(
                TransferSpec.newBuilder()
                    .setSinkAgentPoolName(sinkAgentPoolName)
                    .setSourceAgentPoolName(sourceAgentPoolName)
                    .setPosixDataSource(
                        PosixFilesystem.newBuilder().setRootDirectory(rootDirectory).build())
                    .setPosixDataSink(
                        PosixFilesystem.newBuilder().setRootDirectory(destinationDirectory).build())
                    .setGcsIntermediateDataLocation(
                        GcsData.newBuilder().setBucketName(bucketName).build())
                    .build())
            .setStatus(TransferJob.Status.ENABLED)
            .build();

    // Initialize client that will be used to send requests. This client only needs to be created
    // once, and can be reused for multiple requests. After completing all of your requests, call
    // the "close" method on the client to safely clean up any remaining background resources,
    // or use "try-with-close" statement to do this automatically.
    try (StorageTransferServiceClient storageTransfer = StorageTransferServiceClient.create()) {

      // Create the transfer job
      TransferJob response =
          storageTransfer.createTransferJob(
              TransferProto.CreateTransferJobRequest.newBuilder()
                  .setTransferJob(transferJob)
                  .build());

      System.out.println(
          "Created and ran a transfer job from "
              + rootDirectory
              + " to "
              + destinationDirectory
              + " with name "
              + response.getName());
    }
  }
}

Node.js


// Imports the Google Cloud client library
const {
  StorageTransferServiceClient,
} = require('@google-cloud/storage-transfer');

/**
 * TODO(developer): Uncomment the following lines before running the sample.
 */
// Your project id
// const projectId = 'my-project'

// The agent pool associated with the POSIX data source. Defaults to the default agent
// const sourceAgentPoolName = 'projects/my-project/agentPools/transfer_service_default'

// The agent pool associated with the POSIX data sink. Defaults to the default agent
// const sinkAgentPoolName = 'projects/my-project/agentPools/transfer_service_default'

// The root directory path on the source filesystem
// const rootDirectory = '/directory/to/transfer/source'

// The root directory path on the sink filesystem
// const destinationDirectory = '/directory/to/transfer/sink'

// The ID of the GCS bucket for intermediate storage
// const bucketName = 'my-intermediate-bucket'

// Creates a client
const client = new StorageTransferServiceClient();

/**
 * Creates a request to transfer from the local file system to the sink bucket
 */
async function transferDirectory() {
  const createRequest = {
    transferJob: {
      projectId,
      transferSpec: {
        sourceAgentPoolName,
        sinkAgentPoolName,
        posixDataSource: {
          rootDirectory,
        },
        posixDataSink: {
          rootDirectory: destinationDirectory,
        },
        gcsIntermediateDataLocation: {
          bucketName,
        },
      },
      status: 'ENABLED',
    },
  };

  // Runs the request and creates the job
  const [transferJob] = await client.createTransferJob(createRequest);

  const runRequest = {
    jobName: transferJob.name,
    projectId: projectId,
  };

  await client.runTransferJob(runRequest);

  console.log(
    `Created and ran a transfer job from '${rootDirectory}' to '${destinationDirectory}' with name ${transferJob.name}`
  );
}

transferDirectory();

Python

from google.cloud import storage_transfer


def transfer_between_posix(
    project_id: str,
    description: str,
    source_agent_pool_name: str,
    sink_agent_pool_name: str,
    root_directory: str,
    destination_directory: str,
    intermediate_bucket: str,
):
    """Creates a transfer between POSIX file systems."""

    client = storage_transfer.StorageTransferServiceClient()

    # The ID of the Google Cloud Platform Project that owns the job
    # project_id = 'my-project-id'

    # A useful description for your transfer job
    # description = 'My transfer job'

    # The agent pool associated with the POSIX data source.
    # Defaults to 'projects/{project_id}/agentPools/transfer_service_default'
    # source_agent_pool_name = 'projects/my-project/agentPools/my-agent'

    # The agent pool associated with the POSIX data sink.
    # Defaults to 'projects/{project_id}/agentPools/transfer_service_default'
    # sink_agent_pool_name = 'projects/my-project/agentPools/my-agent'

    # The root directory path on the source filesystem
    # root_directory = '/directory/to/transfer/source'

    # The root directory path on the destination filesystem
    # destination_directory = '/directory/to/transfer/sink'

    # The Google Cloud Storage bucket for intermediate storage
    # intermediate_bucket = 'my-intermediate-bucket'

    transfer_job_request = storage_transfer.CreateTransferJobRequest(
        {
            "transfer_job": {
                "project_id": project_id,
                "description": description,
                "status": storage_transfer.TransferJob.Status.ENABLED,
                "transfer_spec": {
                    "source_agent_pool_name": source_agent_pool_name,
                    "sink_agent_pool_name": sink_agent_pool_name,
                    "posix_data_source": {
                        "root_directory": root_directory,
                    },
                    "posix_data_sink": {
                        "root_directory": destination_directory,
                    },
                    "gcs_intermediate_data_location": {
                        "bucket_name": intermediate_bucket
                    },
                },
            }
        }
    )

    result = client.create_transfer_job(transfer_job_request)
    print(f"Created transferJob: {result.name}")

管理中间存储桶

转移作业完成后,Storage Transfer Service 会将列出已转移和转移失败数据的转移日志保存到存储桶中。转移完成后,清理任务会自动启动以删除中间数据。在某些情况下,清理任务无法删除存储桶中的所有数据。如需删除清理过程中未清除的数据,请按照以下说明手动删除数据,或设置生命周期规则以自动删除数据。

手动清理

根据要删除的数据类型,运行以下命令,以删除中间存储桶中的数据。

  • 如需清除中间存储桶中未在清理期间删除的数据,请运行以下命令:

    gcloud storage rm gs://STORAGE_BUCKET/PREFIX**
    
  • 如要删除所有数据(包括转移日志),请使用全部匹配 (*) 通配符指定存储桶的根目录。

    gcloud storage rm gs://STORAGE_BUCKET/*
    
  • 运行以下命令,以删除存储桶:

    gcloud storage rm gs://STORAGE_BUCKET
    

执行以下变量替换操作:

  • STORAGE_BUCKET 替换为中间存储桶的名称。

  • PREFIX 替换为数据转移到的中间存储桶中文件夹的名称。

设置生命周期规则

如需删除未被自动清理周期清除的数据,请为 Cloud Storage 存储桶设置生命周期规则。使用 age 条件来清除存储桶中的中间数据,具体方法是指定一个时长,该时长应长于使用该存储桶作为中间存储桶的耗时最长的转移作业的时长。如果指定的存在时间条件比将文件从中间存储桶下载到目标位置所需的时间短,则文件转移将失败。

您也可以选择使用 matchesPrefix 条件清空您为中间存储桶指定的文件夹中的数据。如需删除转移日志以及存储桶中的数据,则不需要 matchesPrefix 条件。

保留文件元数据

如需保留文件元数据(包括数字 UID、GID、MODE 和符号链接),请按照以下所述操作:

gcloud CLI

使用 --preserve-metadata 字段指定转移作业的保留行为。适用于文件系统转移作业的选项包括:gidmodesymlinkuid

REST API

metadataOptions 对象中指定适当的选项。

如需了解详情,请参阅保留可选的 POSIX 特性

使用 gcloud CLI 进行转移的示例

在本示例中,我们会将数据从 VM1 上的 /tmp/datasource 目录转移到 VM2 上的 /tmp/destination

  1. 设置转移的来源位置。

    1. 创建来源代理池:

      gcloud transfer agent-pools create source_agent_pool
      
    2. 在 VM1 上,通过运行以下命令为 source_agent_pool 安装代理:

      gcloud transfer agents install --pool=source_agent_pool \
          --count=1 \
          --mount-directories="/tmp/datasource"
      
  2. 设置转移的目标位置。

    1. 创建目标代理池:

      gcloud transfer agent-pools create destination_agent_pool
      
    2. 在 VM2 上,通过运行以下命令为 destination_agent_pool 安装代理:

      gcloud transfer agents install --pool=destination_agent_pool \
          --count=3 \
          --mount-directories="/tmp/destination"
      
  3. 创建一个 Cloud Storage 存储桶作为中间存储桶。

    1. 创建一个名为 my-intermediary-bucket 的存储桶:

      gcloud storage buckets create gs://my-intermediary-bucket
      
    2. 通过运行以下命令授权您的账号使用所有 Storage Transfer Service 功能:

      gcloud transfer authorize --add-missing
      
  4. 通过运行以下命令创建转移作业:

    gcloud transfer jobs create posix:///tmp/datasource posix:///tmp/destination \
        --source-agent-pool=source_agent_pool \
        --destination-agent-pool=destination_agent_pool \
        --intermediate-storage-path=gs://my-intermediary-bucket
    

后续步骤