파일 시스템 간 데이터 전송

이 페이지에서는 두 POSIX 파일 시스템 간에 데이터를 전송하는 방법을 보여줍니다. 일반적인 사용 사례는 다음과 같습니다.

  • 클라우드 및 하이브리드 HPC로 버스트: 대규모 데이터 세트를 온프레미스에서 클라우드로 빠르게 전송하여 처리합니다.
  • Filestore로 마이그레이션 및 동기화: 온프레미스 파일 시스템에서 Filestore로 데이터를 마이그레이션하거나 동기화합니다.
  • 관리형 파일 전송: 데이터 센터 간 또는 두 개의 클라우드 내 파일 시스템 간에 안전하고 안정적으로 데이터를 전송합니다.

전송 성능 가이드라인

다음 가이드라인은 파일 시스템 간에 전송하는 동안 성능을 극대화하는 데 도움이 될 수 있습니다.

에이전트 배포

일반적으로 각 소스 및 대상 에이전트 풀에서 3개의 에이전트를 사용하는 것이 좋습니다. 전송을 모니터링하고 필요한 경우 에이전트를 더 추가하세요. 각 에이전트에는 vCPU 4개와 8GiB RAM이 필요합니다.

Filestore 인스턴스로 마이그레이션하는 경우에는 각 에이전트에서 n2-standard-8 인스턴스 유형을 사용하는 것이 좋습니다. 인스턴스를 Compute Engine VM에 마운트할 때 nconnect=2를 지정합니다. 인스턴스 성능 최적화 및 테스트 방법에 대한 자세한 내용은 Filestore 성능 가이드라인을 참조하세요.

다수의 작은 파일 전송

다수의 작은 파일을 전송할 때 성능을 향상시키려면 파일을 여러 디렉터리로 분할하여 단일 디렉터리에 수백만 개의 파일을 포함하지 않는 것이 좋습니다.

시작하기 전에

이 페이지에 설명된 태스크를 수행하기 전에 기본 요건 단계를 완료하세요.

에이전트 풀 만들기 및 에이전트 설치

파일 시스템에서 파일 시스템으로 전송하려면 소스 및 대상 파일 시스템 모두에 대해 에이전트 풀에이전트를 만들어야 합니다. 소스 에이전트 풀의 에이전트는 소스 파일 시스템에 액세스할 수 있는 머신 또는 VM에 설치해야 합니다. 대상 에이전트 풀의 에이전트는 대상 파일 시스템에 대한 액세스 권한이 있는 머신 또는 VM에 설치해야 합니다.

에이전트 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. 만들기를 클릭합니다.

소스 에이전트 풀의 에이전트 설치

소스 파일 시스템에 액세스할 수 있는 머신 또는 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 콘솔의 안내에 따라 Docker를 설치하고 에이전트를 시작합니다.

대상 에이전트 풀 만들기 및 에이전트 설치

이전 단계를 반복하여 대상 에이전트 풀을 만들고 에이전트를 설치합니다.

중개자로 Cloud Storage 버킷 만들기

파일 시스템 간 전송에는 데이터 전송을 위한 중개자로 Cloud Storage 버킷이 필요합니다.

  1. 다음 설정으로 Cloud Storage 표준 클래스 버킷을 만듭니다.

    • 암호화: 고객 관리 암호화 키(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**
    
  • 전송 로그를 포함하여 모든 데이터를 삭제하려면 모두 일치(*) 와일드 카드를 사용하여 버킷의 루트를 지정합니다.

    gsutil -m rm gs://STORAGE_BUCKET/*
    
  • 버킷을 삭제하려면 다음 명령어를 실행합니다.

    gsutil -m rm gs://STORAGE_BUCKET
    

다음 변수를 바꿉니다.

  • STORAGE_BUCKET을 중간 버킷의 이름으로 바꿉니다.

  • PREFIX를 중간 버킷 내에서 데이터가 전송된 폴더의 이름으로 바꿉니다.

수명 주기 규칙 설정

자동 삭제 주기에서 삭제되지 않은 데이터를 삭제하려면 Cloud Storage 버킷의 수명 주기 규칙을 설정합니다. age 조건을 사용하여 버킷을 중개 버킷으로 사용하는 가장 긴 전송 작업보다 긴 기간을 지정하여 버킷의 중간 데이터를 삭제합니다. 지정된 age 조건이 중간 버킷에서 대상으로 파일을 다운로드하는 데 필요한 시간보다 짧으면 파일 전송이 실패합니다.

필요한 경우 matchesPrefix 조건을 사용하여 중간 버킷에서 지정한 폴더의 데이터를 삭제합니다. 버킷의 데이터와 함께 전송 로그를 삭제할 때는 matchesPrefix 조건이 필요하지 않습니다.

파일 메타데이터 보존

숫자 UID, GID, MODE, 심볼릭 링크를 비롯한 파일 메타데이터를 보존하려면 다음 안내를 따르세요.

gcloud CLI

--preserve-metadata 필드를 사용하여 이 전송의 보존 동작을 지정합니다. 파일 시스템 전송에 적용되는 옵션은 gid, mode, symlink, uid입니다.

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
    

다음 단계