Amazon Web Services 한국 블로그

Amazon S3 멀티 파트 다운로드 이용하기

Amazon S3는 내구성 및 확장성을 요구하는 업무에 활발하게 이용되고 있는 오브젝트 스토리지입니다. 현재까지 꾸준히 기능이 추가 및 개선되고 있으며, AWS 모든 서비스의 맏형격으로 다양한 워크로드와 서비스들의 중앙에 위치해서 정적인 멀티미디어 컨텐츠부터 로그 데이터 저장, 백업과 아카이빙, 빅데이터 분석 및 재난 복구 등을 위해 널리 활용되고 있습니다.

Amazon S3에 저장된 오브젝트는 HTTP/HTTPS프로토콜을 통해 일반적으로 인터넷을 경유해서 이동하게 됩니다. 작은 크기의 파일은 짧은 시간 내에 전송되겠지만, 큰 크기의 파일을 하나의 작업으로 전송하는 것은 시간적인 측면에서 이용자에게 적잖은 부담으로 남게 됩니다. AWS는 하나의 큰 오브젝트를 Amazon S3상에 빠르게 올릴 수 있는 방법을 제공하고 있습니다.

일명 멀티파트 업로드. 기본적으로 Amazon S3에 단일 작업으로 올릴 수 있는 객체 크기는 최대 5GB입니다. 따라서 5GB보다 큰 오브젝트는 멀티파트 업로드를 반드시 이용해서 업로드해야하며, 최대 5TB 크기 오브젝트를 업로드할 수 있습니다.

일반적으로 오브젝트 크기가 100MB를 넘게되면 멀티파트 업로드를 권장하며, 멀티파트 업로드 API를 이용해서 큰 오브젝트를 여러 조각으로 나눠서 병렬적으로 빠르게 업로드 할 수 있습니다. 또한 AWS 명령줄 인터페이스를 이용하여 쉽게 멀티 파트 업로드할 수 있는 환경도 제공하고 있습니다.

그러면, Amazon S3상에서 있는 큰 크기의 오브젝트를 내려받는 것은 어떨까요? 아마도 ‘멀티파트 다운로드할 수 있는 API를 동일하게 제공하겠지?’라고 생각할 수 있습니다. 하지만 멀티파드 업로드 API처럼 오브젝트를 간편하게 멀티파트로 다운로드하는 API는 존재하지 않습니다. 다행히 AWS 명령줄 인터페이스의 s3 cp명령은 사용자에게 투명하게 오브젝트를 멀티파트로 내려받을 수 있도록 설계되어있습니다.

$ time aws s3 cp s3://shared/sample.zip sample.zip
download: s3://shared/sample.zip to ./sample.zip
real       3m46.765s
user       0m18.265s
sys        0m10.454s

기본적으로8MB 크기로 구성된 파트들로 멀티파트 다운로드하게 되며, 아래 예처럼 aws 명령줄 인터페이스 구성파일(~/.aws/credentials)을 수정해서 사용자화할 수 있습니다.

[profile development]
aws_access_key_id = xxxxxxxxxxxxxxxxxxxx
aws_secret_access_key = yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
max_concurrent_requests = 20
max_queue_size = 10000
multipart_threshold = 64MB
multipart_chunksize = 16MB

이 블로그 포스팅의 목적은 AWS 명령줄 인터페이스의 s3명령을 통해서 멀티 파트 다운로드 받는 방법을 소개하려는 것은 아닙니다. 사용자가 직접 간단하게 구현할 수 있는 멀티 파트로 다운로드 방법을 소개하고자 합니다. 이 방법을 이용하면 자신만의 코드로 작성된 앱 내에서도 멀티 파트 다운로드를 간단히 구현할 수 있고, 경우에 따라서는 이어받기 기능까지 응용할 수 있습니다.

원리는 W3 표준인 RANGE GET를 활용하는 것입니다. AWS에서 제공하는 GET Object REST API를 이용해서 RANGE 헤더를 사용할 수 있고, AWS 명령줄 인터페이스의 s3api 명령을 이용해서도 RANGE 헤더를 사용할 수 있습니다.

아래는 두번째 방법인 AWS 명령줄 인터페이스의 s3api 명령을 사용해서 멀티파트 다운로드 스크립트를 간단히 구현해 보고, 멀티파트 다운로드에 대한 기본 아이디어와 실제로 다운로드 속도가 어느정도 개선되는지 살펴보도록 하겠습니다. (참고로 AWS 명령줄 인터페이스에서 내부적으로 투명하게 제공되는 멀티파트 다운로드 기능은 s3 명령에서만 지원되고, s3api 명령에서는 지원되지 않습니다.)

사전 준비 사항
스크립트를 작성하기 위해서는 아래 AWS 명령줄 인터페이스가 준비되어야 합니다. AWS 명령줄 인터페이스는 여기를 클릭하시면 다운로드 받을 수 있으며, 설치 가이드와 사용법도 확인할 수 있습니다.

스크립트 작성하기
위 선행 작업이 완료되었다면, 이제 스크립트를 작성할 준비가 되었습니다. 멀티파트 다운로드 스트립트의 작업 순서를 아래와 같이 아주 단순합니다:

  1. 다운로드 하려는 S3 상의 오브젝트 크기 확인
  2. 사용자가 원하는 분할 개수에 맞도록 파트 크기 계산
  3. RANGE GET을 이용해서 오브젝트의 파트들을 병렬적으로 다운로드
  4. 모든 파트들이 다운로드 될 때까지 대기
  5. 다운로드된 모든 파트들을 하나로 묶기

우선 특정 버킷내에 있는 오브젝트 목록 및 속성을 얻는 명령을 알아보도록 하겠습니다. 이는 첫번째 단계인 오브젝트 크기를 확인하는데 사용됩니다. AWS 명령줄 인터페이스의 s3api list-objects 명령을 이용하면 오브젝트에 대한 속성을 알 수 있습니다. 아래는 shared 버킷내에 있는 모든 오브젝트의 정보를 얻을 수 있는 예입니다.

$ aws s3api list-objects –bucket shared

하지만, 실제로 필요한 값은 원하는 특정 오브젝트의 크기 정보입니다. 이는 --query 옵션을 통해서 가능하고, 오브젝트 이름은 Key, 크기는 Size 속성으로 참조할 수 있습니다. 아래 명령줄을 통해서 shared 버킷 내에 있는 sample.zip 오브젝트에대한 크기만 추출해보겠습니다.

$ aws s3api list-objects --bucket shared --query "Contents[?Key==`sample.zip`]|[0].Size"

위 명령어에서 `sample.zip`을 둘러싸고 있는 것은 백쿼터(`…`)인 것에 유념해야 합니다. 그리고 파이프 연산자를 이용해서 파이프 앞에 있는 쿼리 결과를 뒤로 전달하도록 하였으며, [0].Size를 통해서 오브젝트 크기만 추출합니다.

위에서 얻은 오브젝트의 크기를 분할하고 싶은 파트 개수로 나누게 되면 각 파트의 크기를 얻을 수 있습니다. 그리고 W3 표준인 RANGE GET에서 소개된 형식을 바탕으로 각 파트의 순서에 맞도록 BYTE RANGE 연산자를 활용하여 다운로드할 각 파트의 바이트 범위를 명시합니다. AWS 명령줄 인터페이스에서는 --range 옵션을 사용합니다. 아래는 몇가지 사용 예입니다:

$ aws s3api get-object --bucket shared --key sample.zip --range bytes=0-33554431 sample.zip.part.1

위 예는 sample.zip 오브젝트의 첫번째 파트입니다. 파트 크기를 첫 바이트부터 총 32MB(bytes=0-33554431) 크기로 내려받아서 sample.zip.part.1로 저장합니다.

$ aws s3api get-object --bucket shared --key sample.zip --range bytes=33554432-67108863 sample.zip.part.2

sample.zip 오브젝트의 두번째 파트는 이후 두번째 32MB(33554432-67108863)를 내려받아서 sample.zip.part.2에 저장합니다.

$ aws s3api get-object --bucket shared --key sample.zip --range bytes=268435456- sample.zip.part.9

그리고 sample.zip 오브젝트의 마지막 파트 부분으로 나머지 부분을 모두 받게 됩니다. 위 예는bytes=268435456-으로 표현되어 있는데요, 256MB지점부터 끝까지 내려받아서 sample.zip.part.9에 저장한 예입니다.

BASH 쉘 상에서 내려받는 명령어들은 모두 백그라운드로 수행시켜서 병렬로 다운로드 받게합니다. 그리고 이런 백그라운드 프로세스들이 모두 종료될 때까지 wait(1) 명령어를 이용해서 대기합니다.

끝으로 모든 파트를 다운로드하면 파트 순서에 맞춰서 한 파일로 함께 연결하면 됩니다.

지금까지 스크립트를 구성하는 주요 내용에 대해 설명드렸습니다. 아래 스크립트는 앞서 설명드린 내용들을 기반으로 작성한 간단한 bash 쉘 스크립트입니다.

#!/bin/bash
#
[ $# -ne 6 ] && exit 1

while getopts ":b:k:s:" OPT; do
  case $OPT in
    b)
      BUCKET=$OPTARG
      ;;
    k)
      KEY=$OPTARG
      ;;
    s)
      SPLIT=$OPTARG
      ;;
    *)
      echo "Invalid option: -$OPTARG" >&2
      exit 2
      ;;
  esac
done

# MAIN
readonly THRESHOLD_PART_SIZE=8388608   # 8MB (8*1024*1024)
readonly TMP_DIR="./tmp.$$";

OBJECT_SIZE=$(aws s3api list-objects --bucket $BUCKET --query "Contents[?Key==\`${KEY}\`]|[0].Size")

let PART_SIZE=OBJECT_SIZE/SPLIT
[ $PART_SIZE -lt $THRESHOLD_PART_SIZE ] && echo "Part size is $PART_SIZE. Each part size except last one must be larger than $((${THRESHOLD_PART_SIZE}/1024/1024))MB" &&  exit 3
[ ! -d "${TMP_DIR}" ] && mkdir ${TMP_DIR}

I=1
START=0
let END=PART_SIZE-1
while [ $I -le $SPLIT ]
do
   aws s3api get-object --bucket $BUCKET --key $KEY   --range bytes=${START}-${END}  ${TMP_DIR}/${KEY}.${I} > /dev/null 2>&1 &
   let I=I+1
   let START=START+PART_SIZE
   let END=END+PART_SIZE; [ $I -eq $SPLIT ] && END=''
done

wait > /dev/null 2>&1

[ -f $KEY ] && rm $KEY
I=1
while [ $I -le $SPLIT ]
do
   cat ${TMP_DIR}/${KEY}.${I} >> $KEY
   let I=I+1
done
rm -rf $TMP_DIR

스크립트의 간결성을 유지하기 위해서 대부분의 에러처리 루틴을 배제하였습니다. 따라서 범용적으로 사용하기 위해서는 적절한 에러 처리 루틴들이 추가되어야 하고 충분한 테스트를 거친 후 사용하시길 권장합니다.

아래 두 예는 S3에 저장된 약 700MB sample.zip 파일을 위 스크립트를 활용해서 20개로 나눠 다운로드했을 때와 한번의 작업으로 다운로드 받을 때의 결과를 보여주고 있습니다.

$ time ./s3md.sh -b shared -k sample.zip -s 20
real       2m12.268s
user       0m26.010s
sys        0m11.952s

700MB의 단일 파일을 20개로 나눠서 받았을 때, 약 2분 12초 정도 시간이 소요되었습니다. 그리고 이어지는 AWS 명령줄 인터페이스는 단일 작업으로 해당 오브젝트를 내려받는 결과입니다.

$ time aws s3api get-object --bucket shared --key sample.zip sample.zip > /dev/null
real       4m17.417s
user       0m19.199s
sys        0m9.284s

단일 작업으로 다운로드 하였을 경우 약 4분 17초정도 소요되었습니다. 위 예에서는 멀티 파트로 다운로드 받을 경우 약 50%정도의 시간이 단축된 것을 확인할 수 있었습니다. 다만 고려 해야 할 것은 인터넷 환경 및 클라이언드 환경에 따라 위 결과와 다르게 나올 수 있습니다.

끝으로 Amazon S3상에서 큰 데이터를 자주 조작 하시는 IT팀이나 앱 내에서 S3로 파일을 주고 받아야 하는 경우, 오늘 소개해드린 멀티 파트 다운로드 방법이 조금이라도 아이디어를 제공해드렸기를 기대해봅니다.

본 글은 아마존웹서비스 코리아의 솔루션즈 아키텍트가 국내 고객을 위해 전해 드리는 AWS 활용 기술 팁을 보내드리는 코너로서, 이번 글은 박철수 솔루션즈 아키텍트께서 작성해주셨습니다.