포스트

AWS 해부 (3) S3: bucket, object, boto3

bucket과 object, key와 prefix 구조를 정리하고, CLI로 bucket을 만들어 데이터를 올린 뒤 boto3로 같은 작업을 코드에서 합니다. MinIO에서 S3로 넘어가는 연결 지점과 S3 함정을 정리합니다.

AWS 해부 (3) S3: bucket, object, boto3

AWS 해부 시리즈의 3편입니다. 전체 목차는 0편에 있습니다.

bucket과 object

S3는 파일 시스템이 아니라 오브젝트 스토리지입니다. 구조는 두 단위뿐입니다.

  • bucket: object를 담는 최상위 컨테이너입니다. 이름은 전 세계 모든 AWS 계정을 통틀어 고유해야 하고, 하나의 region에 속합니다
  • object: 데이터 하나와 그 이름(key)의 쌍입니다. object 하나의 최대 크기는 5TB입니다

디렉토리는 없습니다. raw/2025/yellow_2025-01.parquet는 폴더 구조가 아니라 통짜 key 문자열이고, 콘솔이 /를 기준으로 폴더처럼 보여줄 뿐입니다. “폴더 안의 파일 목록”에 해당하는 것은 prefix가 raw/2025/인 object 목록 조회입니다. MLflow 해부 1편에서 artifact store를 “큰 파일 저장소”로 분리했는데, 그 자리에 들어가는 것이 이 구조입니다.

CLI로 첫 bucket

1
2
3
4
aws s3 mb s3://my-mlops-data-hoseung   # bucket 생성
aws s3 cp data/raw/yellow_2025-01.parquet s3://my-mlops-data-hoseung/raw/
aws s3 sync data/raw s3://my-mlops-data-hoseung/raw/   # 디렉토리 동기화
aws s3 ls s3://my-mlops-data-hoseung/raw/

cp는 파일 하나, sync는 디렉토리 단위이고, sync는 바뀐 파일만 다시 올립니다. NYC 택시 프로젝트의 원본 parquet처럼 반복해서 올리는 데이터는 sync가 기본입니다.

bucket은 기본으로 퍼블릭 액세스가 전부 차단되어 있습니다. 정적 웹사이트 호스팅 같은 목적이 아니면 이 차단을 풀 일이 없고, 이 시리즈에서도 풀지 않습니다.

boto3 최소 예제

같은 작업을 코드에서 합니다. boto3는 AWS 공식 Python SDK입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
import boto3

s3 = boto3.client("s3")

s3.upload_file(
    "data/raw/yellow_2025-01.parquet",
    "my-mlops-data-hoseung",
    "raw/yellow_2025-01.parquet",
)

resp = s3.list_objects_v2(Bucket="my-mlops-data-hoseung", Prefix="raw/")
for obj in resp.get("Contents", []):
    print(obj["Key"], obj["Size"])

자격 증명을 코드에 적지 않은 점이 중요합니다. boto3는 ~/.aws/credentials, 환경 변수, EC2에 붙은 IAM role 순으로 자격 증명을 자동으로 찾습니다. 로컬에서는 2편의 CLI 설정을 그대로 쓰고, 서버에서는 4편의 role을 씁니다.

MinIO에서 S3로

NYC 택시 프로젝트 6편의 MinIO는 S3 호환 API라서 클라이언트 코드가 boto3로 같습니다. 차이는 접속 설정뿐입니다.

  • MinIO: MLFLOW_S3_ENDPOINT_URL로 로컬 endpoint를 지정하고 MinIO 계정의 key를 씀
  • S3: endpoint 지정을 제거하면 boto3가 진짜 S3로 갑니다

MLflow 해부 6편의 tracking server 구성에서 이 설정을 걷어내는 실제 전환은 7편에서 합니다.

자주 밟는 함정

  1. bucket 이름이 이미 존재한다는 에러가 납니다. 이름은 전 세계에서 고유해야 하므로 data, test 같은 이름은 이미 없습니다. 계정이나 프로젝트 식별자를 이름에 넣습니다
  2. 다른 region의 bucket을 쓰면 느리고 비쌉니다. EC2와 bucket이 다른 region이면 요청마다 region 간 전송 비용과 지연이 붙습니다. 컴퓨팅과 데이터는 같은 region에 둡니다
  3. object가 남은 bucket은 삭제되지 않습니다. bucket 삭제는 비어 있어야 가능합니다. aws s3 rb s3://bucket --force가 object까지 지우는데, 복구 불가능하므로 대상 bucket 이름을 확인하고 실행합니다
  4. versioning을 켜면 삭제해도 용량이 남습니다. versioning이 켜진 bucket은 삭제가 삭제 마커 추가일 뿐이라 이전 버전 요금이 계속 나옵니다. 수명 주기(lifecycle) 규칙으로 이전 버전 만료를 함께 설정합니다

다음 편에서 가상 서버 EC2를 띄우고 접속합니다.

다음 글: AWS 해부 (4) EC2: instance, security group, SSH

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.