아래와 같이 Lambda를 통해 Account A의 S3에 저장된 데이터를 그대로 Account B의 S3로 복사하는 과정을 알아보겠습니다.

┌─ AWS Account A───┐     ┌──AWS Account B ──┐
│                  │     │                  │
│                  │     │                  │
│ ┌─────────────┐  │     │  ┌────────────┐  │
│ │     S3      ├──┼─────┼─►│     S3     │  │
│ └─────────────┘  │ ▲   │  └────────────┘  │
│                  │ │   │                  │
│                  │ │   │                  │
│ ┌─────────────┐  │ │   │                  │
│ │ Lambda Func ├──┼─┘   │                  │
│ └─────────────┘  │     │                  │
│                  │     │                  │
└──────────────────┘     └──────────────────┘

Lambda는 python을 사용하여 작성하였습니다.

import json
import boto3
from urllib.parse import unquote_plus

def lambda_handler(event, context):
    source_client = boto3.client('s3')
    
    destination_client = boto3.client('s3', aws_access_key_id='XXXXX', aws_secret_access_key='XXXXX')
    
    for record in event['Records']:
        bucket = record['s3']['bucket']['name']
        key = unquote_plus(record['s3']['object']['key'])
        
        # 복사할 소스 파일 가져오기
        source_response = source_client.get_object(Bucket=bucket, Key=key)
        
        # 대상 s3에 그대로 내용 복사
        destination_client.upload_fileobj(source_response['Body'], 'DESTINATION_BUCKET_NAME',key)
        
        # 복사한 뒤 소스 S3 내 파일 삭제
        source_client.delete_object(Bucket=bucket, Key=key)

작업을 하던 중 아래와 같은 요청 사항이 추가되었습니다.

  • 소스 S3에 폴더(yyyy-MM-dd-HH-mm-ss) 단위로 데이터가 저장
  • 기존 대상 S3에 복사된 데이터를 모두 삭제하고 소스 S3에 마지막에 추가된 폴더만 복사될 수 있도록 처리

따라서 아래와 같은 코드를 추가하였습니다.

import json
import boto3
from urllib.parse import unquote_plus

def lambda_handler(event, context):
    source_client = boto3.client('s3')
    
    destination_client = boto3.client('s3', aws_access_key_id='XXXXX', aws_secret_access_key='XXXXX')
    
    for record in event['Records']:
        bucket = record['s3']['bucket']['name']
        key = unquote_plus(record['s3']['object']['key'])
        source_response = source_client.get_object(Bucket=bucket, Key=key)
        
        # 파일 복사 전 이미 복사된 파일 삭제
        delete_bucket(destination_client, 'DESTINATION_BUCKET_NAME', key.split('/')[0])
        
        destination_client.upload_fileobj(source_response['Body'], 'DESTINATION_BUCKET_NAME',key)
    
# 대상 S3 내 기존 데이터 삭제 (대상 s3, 대상 bucket 이름, 디렉토리명) - 같은 디렉토리에 저장된 파일은 삭제하지 않기 위함
def delete_bucket(s3_client, bucket_name, dir_name):
    objects = s3_client.list_objects(Bucket=bucket_name)
    content = objects.get('Contents', [])
    # bucket 내 모든 파일을 하나씩 확인하면서 
    for obj in content:
        key = obj['Key']
        # 상위 디렉토리가 다른 경우에만 파일 삭제
        if dir_name != key.split('/')[0]:
            s3_client.delete_object(Bucket=bucket_name, Key=key)
    # s3_client.delete_bucket(Bucket=bucket_name) # 버킷 자체를 삭제

이 방식은 운영 계정의 로그 데이터를 개발 계정으로 복사해서 분석하고 싶은 경우와 같이 여러가지 목적으로 계정간 데이터 공유를 필요로 할 때 사용할 수 있습니다.


  • Lambda 함수 생성 후 트리거로 S3를 추가합니다. 원하는 버킷을 선택하고 원하는 이벤트를 지정하여 연결하면 S3에 파일이 추가될 때마다 해당 람다 함수가 호출되어 복사를 진행합니다.

  • Account A에서 Account B 계정의 S3에 접근하기 위해 람다 내에서 대상 S3 호출 시 아래와 같이 access_key와 secret_access_key를 추가해주었습니다.

    destination_client = boto3.client('s3', aws_access_key_id='XXXXX', aws_secret_access_key='XXXXX')
    

    이 키는 Account B에서 IAM으로 사용자를 생성하고 S3에 접근할 수 있는 권한을 부여한 뒤 발급 받은 액세스 키를 적어주면 됩니다.