하위 디렉터리의 작동 원리

개요

이 섹션에서는 gsutil에서 하위 디렉터리가 작동하는 방식을 자세히 설명합니다. 대부분의 사용자는 이러한 세부정보를 알 필요가 없으며 하위 디렉터리와 호환되는 명령어(예: cp -r)를 사용하기만 하면 됩니다. 이 추가 문서에서는 gsutil이 대부분의 GUI/웹 기반 도구와 다르게 하위 디렉터리를 처리하는 방식(예: 다른 도구가 'dir_$folder$' 객체를 만드는 이유)을 사용자가 이해하도록 돕고, 이러한 세부정보에 관심이 있는 사용자에게는 gsutil 방식이 비용과 성능에 미치는 영향을 설명합니다.

gsutil은 Cloud Storage 서비스에서 지원되는 '단일 구조' 네임스페이스 위에 있는 계층적 파일 트리의 일환을 제공합니다. 서비스 입장에서 gs://your-bucket/abc/def.txt 객체는 이름에 '/' 문자가 있는 객체에 불과합니다. 'abc' 디렉터리는 존재하지 않으며 지정된 이름의 단일 객체만 존재할 뿐입니다. 다음 다이어그램은

https://cloud.google.com/storage/images/gsutil-subdirectories.svg

gsutil이 버킷에 있는 객체에 대한 계층적 뷰를 제공합니다.

gsutil은 다양한 규칙을 적용하여 사용자가 원하는 방식으로 이름을 지정함으로써 가상의 계층적 파일 트리를 구현합니다. 예를 들어 대상 URL을 객체 이름으로 취급할지 또는 객체를 복사할 디렉터리의 루트로 취급할지 판단하기 위해 gsutil은 다음 규칙을 따릅니다.

  1. 대상 객체가 '/'로 끝나면 gsutil은 객체를 디렉터리로 취급합니다. 예를 들어 다음 명령어를 실행합니다.

    gsutil cp your-file gs://your-bucket/abc/
    

    그러면 gsutil이 gs://your-bucket/abc/your-file 객체를 만듭니다.

  2. 대상 객체가 XYZ이고 XYZ_$folder$라는 객체가 있으면 gsutil은 XYZ를 디렉터리로 취급합니다. 예를 들어 다음 명령어를 실행합니다.

    gsutil cp your-file gs://your-bucket/abc
    

    abc_$folder$라는 객체가 있으면 gsutil은 gs://your-bucket/abc/your-file 객체를 만듭니다.

  3. 소스 파일 여러 개를 대상 URL 하나에 복사하려고 하면 gsutil이 대상 URL을 디렉터리로 취급합니다. 예를 들어 다음 명령어를 실행합니다.

    gsutil cp -r your-dir gs://your-bucket/abc
    

    그러면 gsutil은 gs://your-bucket/abc/your-dir/file1과 같은 객체를 만듭니다(file1이 소스 디렉터리 your-dir에 있는 파일이라고 가정).

  4. 위 규칙 중 어느 규칙도 적용되지 않으면 gsutil은 버킷을 나열하여 작업 대상의 프리픽스가 지정된 문자열과 일치하는지 확인합니다. 예를 들어 다음 명령어를 실행합니다.

    gsutil cp your-file gs://your-bucket/abc
    

    그러면 gsutil은 delimiter="/" 및 prefix="abc"를 사용하여 명명된 버킷에 버킷 나열을 요청합니다. 그런 다음 버킷 나열 결과를 검토하고 경로가 gs://your-bucket/abc/로 시작되는 객체가 버킷에 있는지 확인하여 대상을 객체 이름으로 취급할지 또는 디렉터리 이름으로 취급할지 여부를 결정합니다. 그리고 이는 만드는 객체의 이름에 영향을 미칩니다. 위의 확인 결과 'abc' 디렉터리가 있으면 gs://your-bucket/abc/your-file 객체가 생성됩니다. 그렇지 않으면 gs://your-bucket/abc 객체가 생성됩니다. 자세한 내용은 gsutil help cp에서 '이름은 어떻게 구성되나요?'를 참조하세요.

이러한 규칙 기반 방식은 대부분의 도구가 폴더 존재를 표시하는 객체(예: 'dir_$folder$')를 만드는 방식으로 작동하는 것과는 다릅니다. gsutil은 이러한 도구에서 사용되는 여러 규칙을 이해하지만 마커 객체에서 UNIX 명령어와 일관된 방식으로 명명 동작을 구현하도록 요구하지 않습니다.

gsutil 하위 디렉터리 명명 방식의 단점은 필요한 cp 또는 mv 명령어를 수행하기 전에 버킷을 추가적으로 나열해야 한다는 점입니다. 하지만 이러한 나열은 구분 기호와 프리픽스 매개변수를 사용하여 결과 데이터를 제한하므로 상대적으로 비용이 많이 들지 않습니다. 또한 gsutil은 cp/mv 명령어당 버킷 나열 요청을 1번만 수행하므로 모든 전송 객체에서 버킷 나열 비용을 절약할 수 있습니다(예: 디렉터리를 클라우드로 재귀 복사할 경우).

예기치 못한 대상 하위 디렉터리 이름 지정 가능성

위와 같이 규칙을 기반으로 대상 경로를 생성하는 방식을 사용하면 예기치 못한 상황이 발생할 수 있습니다. 예를 들어 로컬 디렉터리에 있는 모든 항목을 아직 존재하지 않는 버킷 '하위 디렉터리'로 업로드하려고 할 수 있습니다.

gsutil cp -r ./your-dir/* gs://your-bucket/new

여기서 your-dir 아래에 디렉터리가 있습니다(예: dir1 및 dir2). 이 명령어를 처음 실행하면 다음 객체가 생성됩니다.

gs://your-bucket/new/dir1/abc
gs://your-bucket/new/dir2/abc

gs://your-bucket/new가 아직 존재하지 않기 때문입니다. 같은 명령어를 다시 실행하면 gs://your-bucket/new가 이제 존재하므로 추가 객체가 생성됩니다.

gs://your-bucket/new/your-dir/dir1/abc
gs://your-bucket/new/your-dir/dir2/abc

이러한 이름 지정 동작은 사용자를 놀라게 할 수 있다는 점 외에도 gsutil 업로드를 재시도 루프로 스크립팅하는 경우에 주의해야 합니다. 이 방법으로 첫 번째 시도에서 모든 파일을 복사하지 않으면 두 번째 시도에서 소스 하위 디렉터리가 기존에 있게 되므로 위에서 설명한 이름 지정 문제가 발생합니다.

하지만 이 문제를 방지할 수 있는 몇 가지 방법이 있습니다.

1. gsutil rsync를 사용합니다. rsync는 Unix cp 정의 디렉터리 이름 지정 규칙을 사용하지 않으므로 대상 하위 디렉터리 존재 여부에 관계없이 일관되게 작동합니다.

2. rsync를 사용할 수 없다면 다음과 같은 명령어를 실행해 'placeholder' 객체를 만들어 대상이 하위 디렉터리임을 나타냅니다.

gsutil cp some-file gs://your-bucket/new/placeholder

이때 위에서 언급한 gsutil cp -r 명령어를 실행하면 gs://your-bucket/new가 하위 디렉터리로 일관되게 취급됩니다. 하위 디렉터리 아래에 객체가 최소 한 개 이상 있으면 자리표시자 객체를 삭제할 수 있으며 이후에 하위 디렉터리에 업로드하면 예상대로 계속 이름이 지정됩니다.