ファイル システム間でのデータ転送

このページでは、2 つの POSIX ファイル システム間でデータを転送する方法について説明します。一般的なユースケースは次のとおりです。

  • クラウドとハイブリッド HPC へのバースト: 大規模なデータセットを処理のためにオンプレミスからクラウドに迅速に転送します。
  • Filestore への移行と同期: オンプレミス ファイル システムから Filestore にデータを移行または同期します。
  • マネージド ファイル転送: データセンター間、または 2 つのクラウド上のファイル システム間で安全かつ確実にデータを転送します。

転送のパフォーマンス ガイドライン

次のガイドラインは、ファイル システム間の転送時にパフォーマンスを最大化するのに役立ちます。

エージェントのデプロイ

一般に、転送元と転送先のエージェント プールのそれぞれで 3 つのエージェントを使用することをおすすめします。転送をモニタリングし、必要に応じてエージェントを追加します。各エージェントには 4 個の vCPU と 8 GiB の RAM が必要です。

Filestore インスタンスに移行する場合は、エージェントごとに n2-standard-8 インスタンス タイプを使用することをおすすめします。Compute Engine VM にインスタンスをマウントするときに nconnect=2 を指定します。インスタンスのパフォーマンスの最適化とテスト方法の詳細については、Filestore のパフォーマンス ガイドラインをご覧ください。

多数の小さなファイルの転送

小さなファイルを数多く転送する場合のパフォーマンスを向上させるには、ファイルを複数のディレクトリに分割し、1 つのディレクトリに何百万ものファイルが含まれないようにすることをおすすめします。

始める前に

このページで説明するタスクを実行する前に、前提条件の手順を完了してください。

エージェント プールを作成し、エージェントをインストールする

ファイル システム間の転送には、転送元と転送先のファイル システムの両方にエージェント プールエージェントを作成する必要があります。転送元のエージェント プール用のエージェントは、転送元のファイル システムにアクセスできるマシンまたは VM にインストールする必要があります。転送先のエージェント プール用のエージェントは、転送先のファイル システムにアクセスできるマシンまたは VM にインストールする必要があります。

転送元のエージェント プールを作成する

次のいずれかの方法で転送元のエージェント プールを作成します。

gcloud CLI

次のコマンドを実行して、転送元のエージェント プールを作成します。

gcloud transfer agent-pools create SOURCE_AGENT_POOL

SOURCE_AGENT_POOL は、ソース エージェント プールに付ける名前に置き換えます。

Google Cloud コンソール

  1. Google Cloud コンソールで、[エージェント プール] ページに移動します。

    [エージェント プール] に移動

    [エージェント プール] ページが開き、既存のエージェント プールが一覧表示されます。

  2. [別のプールを作成] をクリックします。

  3. プールの名前を入力します。

  4. [作成] をクリックします。

転送元のエージェント プール用のエージェントをインストールする

転送元のファイル システムにアクセスできるマシンまたは VM に、転送元のエージェント プール用のエージェントをインストールします。

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 コンソールの手順に沿って、Pub/Sub リソースを作成し、Docker をインストールして、エージェントを起動します。

転送先のエージェント プールを作成してエージェントをインストールする

上記の手順を繰り返して、転送先のエージェント プールを作成し、エージェントをインストールします。

中継点として Cloud Storage バケットを作成する

ファイル システム間の転送には、データ転送の中継点として Cloud Storage バケットが必要です。

  1. 次の設定で Cloud Storage Standard クラスバケットを作成します。

    • 暗号化: 顧客管理の暗号鍵(CMEK)を指定できます。それ以外の場合は、Google 管理の暗号鍵が使用されます。
    • オブジェクトのバージョニングバケットロックデフォルトのオブジェクト保留: これらの機能は無効のままにします。
  2. 次のいずれかの方法で権限とロールを付与します。

    • Storage Transfer Service サービス アカウントに、バケットに対するストレージ管理者のロール(roles/storage.admin)を付与します。
    • gcloud transfer authorize を使用して、Storage Transfer Service のすべての機能についてアカウントを承認します。このコマンドは、プロジェクト全体のストレージ管理者権限を付与します。

      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 は、転送されたデータと転送されなかったデータのリストを含む転送ログをバケットに保存します。転送後、クリーンアップ タスクが自動的に開始し、中間データが削除されます。クリーンアップ タスクでバケット内のデータが完全に削除されないことがあります。クリーンアップ中に消去されないデータを削除するには、次の手順に沿ってデータを手動で削除するか、データを自動的に削除するライフサイクル ルールを設定します。

手動によるクリーンアップ

中間バケットからデータを削除するには、削除するデータの種類に応じて次の gsutil コマンドを実行します。

  • クリーンアップ中に削除されなかった中継バケットのデータを消去するには、次のコマンドを実行します。

    gsutil -m rm gs://STORAGE_BUCKET/PREFIX**
    
  • 転送ログを含むすべてのデータを削除するには、match-all(*)ワイルドカードを使用して、バケットのルートを指定します。

    gsutil -m rm gs://STORAGE_BUCKET/*
    
  • 次のコマンドを実行してクラスタを削除します。

    gsutil -m 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 という名前のバケットを作成します。

      gsutil mb 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
    

次のステップ