S3 Files의 출시를 통해 S3가 객체 스토어를 넘어 테이블, 벡터, 파일을 아우르는 데이터 기반으로 확장되어 온 과정과 그 설계상의 고민을 살펴봅니다.
2026년 04월 07일 • 6004단어

사진 제공: Ossewa 거의 모든 사람은 경력의 어느 시점에든 대량의 데이터를 한 장소에서 다른 장소로 옮기는 몹시 답답한 과정을 겪어 봤을 것입니다. 아직 그런 경험이 없다면, 아마 단지 충분히 큰 데이터셋을 다뤄보지 않았기 때문일 것입니다. Andy Warfield에게 그런 형성적 경험 중 하나는 UBC에서였습니다. 그는 유전체학 연구자들과 함께 일했는데, 이들은 엄청난 양의 시퀀싱 데이터를 생산하면서도 그 데이터를 필요한 곳으로 옮기는 기계적인 작업에 터무니없이 많은 시간을 쓰고 있었습니다. 데이터를 끝없이 이리저리 복사하고, 일관성 없는 여러 복사본을 관리하고 있었죠. 이것은 실험실의 과학자들부터 머신러닝 모델을 훈련하는 엔지니어들까지, 모든 산업의 빌더들을 좌절시켜 온 문제이며, 바로 우리가 고객을 위해 해결해야 하는 유형의 문제입니다.
이 글에서 Andy는 그의 팀이 내놓은 해법인 S3 Files에 대해 씁니다. 어렵게 얻은 교훈들, 몇 가지 정말 웃긴 순간들, 그리고 새로운 데이터 타입의 이름을 짓다가 적어도 한 번은 실패한 시도까지 말입니다. 무척 흥미로운 글이고, 여러분도 분명 즐기실 거라 생각합니다.
–W
먼저, 약간의 식물학 이야기
알고 보니 해바라기는 인간보다 훨씬 더 문란합니다.
약 10년 전, Amazon에 합류하기 직전 저는 두 번째 스타트업을 마무리하고 UBC에서 다시 가르치고 있었습니다. 연구 경험이 많지 않은 어떤 분야를 탐구해 보고 싶었고, 유전체학, 특히 컴퓨터 시스템과 생물학자들이 유전체 연구를 수행하는 방식의 교차점에 대해 배워보기로 했습니다. 그러다 UBC의 식물학 교수 Loren Rieseberg와 시간을 보내게 되었는데, 그는 해바라기 DNA를 연구했습니다. 즉, 가뭄이나 염분이 많은 토양 같은 까다로운 환경에서 식물이 잘 자라게 하는 형질이 어떻게 발달하는지를 이해하기 위해 유전체를 분석하는 일이었습니다.
문란함에 관한 식물학자들의 농담(이 블로그의 시작이 된 그 농담)은 Loren의 연구실과 함께 일하는 일이 무척 즐거웠던 이유 중 하나였습니다. 그들의 설명에 따르면, 인간 DNA는 약 30억 개의 염기쌍을 가지며, 어떤 두 사람도 유전체 수준에서는 99.9% 동일합니다. 우리의 DNA는 놀라울 정도로 비슷합니다. 하지만 해바라기는 꽃이고, 전혀 일부일처가 아니기 때문에, 더 큰 유전체(약 36억 개의 염기쌍)를 가지며 변이도 훨씬 많습니다(개체 간 유전적 변이가 10배 더 큽니다).
당시 제 박사과정 졸업생 중 한 명이었던 JS Legare는 이 모험에 함께하기로 했고, 이후 Loren의 연구실에서 박사후연구원으로 일하며 이런 워크로드를 클라우드로 옮길 방법을 탐구했습니다. 유전체 분석은 일부 연구자들이 말하는 “burst parallel” 컴퓨팅의 한 예입니다. DNA 분석은 대규모 병렬 계산으로 수행할 수 있고, 그렇게 할 경우 상대적으로 짧은 시간 동안 실행되는 경우가 많습니다. 이는 연구실의 로컬 하드웨어가 적합하지 않을 수 있음을 뜻합니다. 필요할 때 빠른 분석을 수행할 만큼 충분한 컴퓨팅 자원이 없는 경우가 많고, 반대로 작업하지 않을 때는 가진 컴퓨팅 자원이 놀게 되기 때문입니다. 우리의 아이디어는 S3와 서버리스 컴퓨팅을 사용해 수만 또는 수십만 개의 태스크를 병렬로 실행하여 연구자들이 매우 복잡한 분석도 아주 빠르게 수행하고, 끝나면 다시 0까지 축소할 수 있도록 하는 것이었습니다.
생물학자들은 Linux에서 GATK4라는 분석 프레임워크를 사용했는데, 이는 Apache Spark와 통합되는 유전체 분석 툴킷이었습니다. 그들의 모든 데이터는 공유 NFS filer에 저장되어 있었습니다. 클라우드로 연결하는 과정에서 JS는 “bunnies”(역시 또 다른 문란함 농담입니다)라고 부른 시스템을 만들어 분석을 컨테이너로 패키징하고 S3에서 실행했습니다. 이는 속도, 재현성, 병렬화를 통한 성능 면에서 큰 성과였습니다. 하지만 특히 두드러진 교훈은 스토리지 경계에서의 마찰이었습니다.
S3는 병렬성, 비용, 내구성 측면에서는 훌륭했지만, 유전체 연구자들이 쓰는 모든 도구는 로컬 Linux 파일시스템을 기대했습니다. 연구자들은 데이터를 계속 이리저리 복사하고, 때로는 일관성 없는 여러 복사본을 관리해야 했습니다. 이런 데이터 마찰—한쪽에는 S3, 다른 쪽에는 파일시스템, 그 사이에는 수동 복사 파이프라인—은 이후 수년간 제가 반복해서 본 문제입니다. 미디어 및 엔터테인먼트, 머신러닝 사전학습, 실리콘 설계, 과학 계산 등 여러 분야에서 말입니다. 서로 다른 도구는 서로 다른 방식으로 데이터에 접근하도록 작성되어 있고, 데이터 앞에 놓인 API가 작업을 더 어렵게 만드는 마찰의 원천이 될 때 정말 괴롭습니다.
에이전트는 데이터 마찰을 증폭시킨다
오늘날 에이전트형 도구가 소프트웨어 개발을 바꾸고 있는 방식에 대해 우리는 모두 알고 있고, 아마 여전히 조금은 놀라고 있다고 생각합니다. 에이전트는 코드를 정말 꽤 잘 작성하고 있으며, 그 능력은 너무 빠르게 좋아지고 있어서 우리 모두가 이것이 도대체 무엇을 의미하는지 꽤 많은 시간을 들여 생각하고 있습니다(Werner조차도 그렇습니다). 하지만 분명 사실로 보이는 한 가지는, 에이전트형 개발이 애플리케이션 구축 비용을 근본적으로 바꿨다는 점입니다. 달러 기준의 비용, 시간 기준의 비용, 그리고 특히 작동하는 코드를 작성하는 데 필요한 기술 측면에서의 비용 말입니다. 그리고 최근 제가 가장 흥미롭게 느끼는 부분은 바로 이 마지막 부분입니다. 소프트웨어가 존재한 거의 처음부터, 성공적인 애플리케이션은 늘 종종 분리된 두 가지 기술 집합의 결합을 필요로 했습니다. 한편으로는 유전체학, 금융, 디자인 같은 애플리케이션 도메인에 대한 기술이 있었고, 다른 한편으로는 실제로 코드를 작성하는 기술이 있었습니다. 여러 면에서 에이전트는 소프트웨어를 작성하는 진입장벽이 원래 얼마나 터무니없이 높았는지를 보여주고 있으며, 갑자기 훨씬 더 많은 사람들이 앱을 만들 수 있게 하고 있습니다. 즉, 작성 메커니즘이 아니라 애플리케이션이 다루는 도메인 자체에 깊은 전문성을 가진 사람들이 말입니다.
애플리케이션이 그 어느 때보다 더 빠르게, 더 실험적으로, 더 다양하게 작성되는 이 시점에서, 아이디어에서 실행되는 코드까지의 사이클 타임은 극적으로 줄어들고 있습니다. 애플리케이션 구축 비용이 무너지고, 우리가 만드는 각 애플리케이션이 다음 애플리케이션의 참고 사례가 될 수 있게 되면서, 코드와 데이터의 구분은 이전 어느 때보다 더 중요해지고 있는 듯합니다. 우리는 애플리케이션은 생겨났다 사라지고, 데이터는 언제나 그렇듯 그것들보다 오래 살아남는 시대로 들어가고 있습니다. 효과적인 스토리지 시스템의 역할은 단지 데이터를 안전하게 저장하는 것만이 아니라, 그것을 개별 애플리케이션으로부터 추상화하고 분리하는 데에도 있습니다. 애플리케이션 개발 속도가 빨라질수록, 스토리지의 이 특성은 그 어느 때보다 중요해집니다. 데이터에 더 쉽게 연결하고 더 쉽게 작업할 수 있을수록, 우리는 그것을 활용하는 새로운 방식을 더 많이 실험하고 만들고 탐구할 수 있기 때문입니다.
데이터의 수탁자로서의 S3
지난 몇 년 동안 S3 팀은 바로 이 마지막 지점에 크게 집중해 왔습니다. 우리는 S3에서 데이터에 접근하는 방식이 충분히 단순하지 않은 상황들—바로 Loren의 연구실 생물학자들이 도구에 맞는 위치에 데이터를 두기 위해 스크립트를 만들어 데이터를 복사해야 했던 사례 같은 경우—을 자세히 살펴보았고, 더 넓게는 고객들이 스토리지 작업 때문에 데이터 작업에 집중하지 못하는 곳들을 보기 시작했습니다. 여기서 우리가 얻은 첫 번째 교훈은 구조화된 데이터였습니다. S3는 엑사바이트 규모의 parquet 데이터를 저장하고 있으며, 그 형식만으로도 평균 초당 2,500만 건이 넘는 요청을 처리합니다. 이 중 많은 데이터는 단순 parquet이거나 Hive 테이블 형태로 구조화되어 있었습니다. 그리고 사람들이 이 데이터로 더 많은 일을 하고 싶어 한다는 점은 분명했습니다. 특히 Apache Iceberg 같은 오픈 테이블 포맷은 삽입과 변경, 스키마 변경, 테이블 스냅샷을 허용하는 기능적으로 더 풍부한 테이블 추상화로 부상하고 있었습니다. Iceberg가 S3 위의 표 형식 데이터를 위한 추상화 수준을 분명히 끌어올리고 있었지만, 객체 API 위에 엄격히 테이블을 드러내야 했기 때문에 여전히 여러 날카로운 모서리를 안고 있었습니다.
Iceberg의 인기가 높아지기 시작하자, 이를 대규모로 도입한 고객들은 보안 정책 관리가 어렵고, 테이블 유지관리와 compaction을 직접 관리하고 싶지 않으며, 표 형식 데이터 작업이 더 쉬워지길 원한다고 말해주었습니다. 더구나 Iceberg와 일반적인 Open Table Format(OTF)에 대한 많은 작업은 특히 Spark를 위해 추진되고 있었습니다. Spark는 분석 엔진으로 매우 중요하지만, 사람들이 S3에 데이터를 저장하는 이유는 원하는 어떤 도구로든 그 데이터를 다루고 싶기 때문입니다. 심지어, 아니 특히 아직 존재하지 않는 도구로도 말입니다. 그래서 2024년 re:Invent에서 우리는 S3 Tables를 출시했습니다. 이는 구조화된 데이터를 위한 빌딩 블록 역할을 하는 관리형의 퍼스트클래스 테이블 프리미티브입니다. S3 Tables는 데이터를 Iceberg 형식으로 저장하지만, 데이터 무결성과 내구성을 보호하는 가드레일을 추가합니다. compaction을 자동화하고, 리전 간 테이블 복제를 지원하며, 객체와 나란히 애플리케이션 구축 수단으로서 테이블이 퍼스트클래스 데이터 프리미티브여야 한다는 개념을 계속 다듬고 확장합니다. 오늘날 우리는 S3 Tables에 200만 개가 넘는 테이블이 저장된 것을 보고 있고, 그 위에 온갖 놀라운 애플리케이션이 구축되고 있습니다.
거의 같은 시기에, 우리는 S3 고객들과 유사도 검색 및 벡터 인덱스에 관한 대화를 많이 나누기 시작했습니다. 지난 몇 년간의 AI 발전은 저장된 모든 종류의 데이터에 대한 벡터 인덱스에 대해 기회와 필요를 동시에 만들어 냈습니다. 그 기회는 고급 임베딩 모델에서 왔는데, 이것들이 시맨틱 검색 능력을 한 단계 도약시켰기 때문입니다. 갑자기 역사적인 스포츠 영상 같은 대규모 아카이브 미디어 컬렉션을 가진 고객은 벡터 인덱스를 구축하고, 특정 선수가 다이빙 터치다운을 성공시키는 장면을 실시간으로 검색해, 생방송에 사용할 하이라이트 클립 모음을 즉시 얻을 수 있게 되었습니다. 시맨틱하게 관련성 높은 검색이라는 같은 특성은 RAG와 모델이 학습하지 않은 데이터에 모델을 적용하는 데에도 똑같이 가치가 있습니다.
고객들이 자사 데이터 위에 벡터 인덱스를 구축하고 운영하기 시작하면서, 그들은 약간 다른 종류의 데이터 마찰을 지적하기 시작했습니다. 강력한 벡터 데이터베이스는 이미 존재했고, 벡터는 Postgres 같은 기존 데이터베이스의 기능으로도 빠르게 들어가고 있었습니다. 하지만 이런 시스템은 인덱스를 메모리나 SSD에 저장하며, 라이브 인덱스를 가진 컴퓨팅 클러스터로 실행됩니다. 이는 지속적인 저지연 검색 기능에는 올바른 모델이지만, 스토리지 관점에서 데이터에 접근하는 경우에는 덜 유용합니다. 특히 코드나 PDF 같은 텍스트 기반 데이터에서는, 벡터 자체가 인덱싱되는 데이터보다 바이트 수가 더 많고, 훨씬 더 비싼 미디어에 저장되는 경우가 많다는 점을 고객들은 발견하고 있었습니다.
그래서 팀이 S3 Tables로 구조화된 데이터를 다룬 작업과 마찬가지로, 지난 re:Invent에서 우리는 벡터 인덱스를 위한 새로운 S3 네이티브 데이터 타입인 S3 Vectors를 출시했습니다. S3 Vectors는 벡터 저장에 아주 S3다운 방식을 취하는데, 그 설계가 성능, 비용, 내구성 프로파일 면에서 S3 객체와 매우 유사한 점을 중심에 두고 있기 때문입니다. 하지만 아마 가장 중요한 점은, S3 Vectors가 완전히 탄력적으로 설계되었다는 것입니다. 즉, 몇 백 개의 레코드만 가진 인덱스를 빠르게 만들고, 시간이 지나며 수십억 개의 레코드로 확장할 수 있습니다. S3 Vectors의 가장 큰 강점은, 사실 유사도 검색 인덱스를 지원할 수 있는 항상 사용 가능한 API 엔드포인트를 갖는다는 그 단순함에 있습니다. 객체와 테이블처럼, 애플리케이션 개발의 일부로 자연스럽게 꺼내 쓸 수 있는 또 하나의 데이터 프리미티브인 셈입니다.
그리고 이제… S3 Files
오늘 우리는 S3 Files를 출시합니다. 이는 Amazon Elastic File System(EFS)을 S3에 통합하여, 기존 S3 데이터를 네트워크 연결 파일시스템으로 직접 접근할 수 있게 하는 새로운 S3 기능입니다.
파일에 관한 이야기는 사실 더 길고, 테이블이나 벡터 어느 쪽보다도 더 흥미롭습니다. 파일은 객체 스토리지와 깔끔하게 통합하기에 복잡하고 까다로운 데이터 타입이라는 사실이 드러났기 때문입니다. 사실 우리는 S3 Tables를 출시하기 전부터 파일 아이디어를 작업하고 있었습니다. EFS 팀과 S3 팀의 공동 노력으로 말입니다. 하지만 그 이야기는 잠시 접어두죠.
제가 해바라기 DNA를 분석하던 유전체학 사례에서 설명했듯, 파일시스템 API를 통해 데이터와 작업하는 기존 소프트웨어는 엄청나게 많습니다. 데이터 과학 도구, 빌드 시스템, 로그 처리기, 구성 관리, 훈련 파이프라인 등이 모두 그렇습니다. 에이전트형 코딩 도구가 데이터와 작업하는 모습을 본 적이 있다면, 이들은 로컬 파일시스템에서 데이터와 직접 작업하기 위해 풍부한 Unix 도구들을 아주 빠르게 꺼내 듭니다. S3에서 데이터와 작업하려면, S3에서 파일 목록을 가져오고, 이를 로컬 디스크로 전송한 다음, 그 로컬 복사본에 대해 작업하는 더 깊은 추론을 해야 합니다. 그리고 이것은 분명 에이전트형 사용 사례에만 국한되지 않습니다. 오늘날 작업에서 로컬 파일시스템을 사용하는 모든 고객 애플리케이션에 해당합니다. S3에서 파일을 네이티브하게 지원하면, 그 모든 데이터는 즉시 더 접근하기 쉬워지고—결국 더 가치 있게 됩니다. pandas로 사용하기 위해 S3에서 데이터를 복사해 올 필요도, 훈련 작업을 거기에 맞출 필요도, 디자인 도구로 상호작용하기 위해 별도 처리를 할 필요도 없습니다.
S3 Files를 사용하면 아주 단순한 한 가지를 얻게 됩니다. 이제 어떤 S3 버킷이나 prefix든 EC2 VM, 컨테이너, 또는 Lambda 함수 안에 마운트하여 파일시스템을 통해 그 데이터에 접근할 수 있습니다. 변경을 가하면, 그 변경은 다시 S3로 전파됩니다. 결과적으로, 객체를 파일처럼, 파일을 객체처럼 다룰 수 있게 됩니다.
그리고 바로 여기서 이야기가 흥미로워집니다. 고객에게 단순한 것을 만들려고 할 때 우리가 자주 배우듯, 무언가를 단순하게 만드는 일은 종종 오히려 가장 복잡한 일 중 하나이기 때문입니다.
빌더들은 자신의 데이터가 파일시스템에 살게 될지 객체 스토어에 살게 될지를 초기에 결정해야 하고, 그 이후로는 그 결과에 묶여 있어야 한다는 사실을 싫어합니다. 그 결정과 함께, 그들은 지금뿐 아니라 먼 미래까지도 데이터와 어떻게 상호작용할지를 고르게 됩니다. 그리고 만약 그 선택이 잘못되면, 마이그레이션을 하거나 데이터를 복사하는 자동화 계층을 구축해야 합니다.
초기 아이디어는 기본적으로, EFS와 S3를 거대한 솥에 넣고 한동안 끓이면 최고의 두 세계를 얻게 된다는 것이었습니다. 우리는 프로젝트의 초기 버전을 “EFS3”라고 부르기까지 했습니다(그 이름을 유지하지 않아서 다행입니다!). 하지만 상황은 금세 까다로워졌습니다. 설계를 검토하기 위해 앉을 때마다, 어려운 기술적 도전과 힘든 결정이 나타났습니다. 그리고 이런 결정 하나하나에서, 데이터의 파일 표현 또는 객체 표현 중 하나는 설계상 무언가를 포기해야 했고, 그 결과 조금 덜 좋아질 수밖에 없었습니다. 팀의 한 엔지니어는 이를 “받아들이기 힘든 타협들의 전투”라고 표현했습니다. 파일과 객체를 하나의 스토리지 시스템으로 수렴시키는 일이 얼마나 어려운지 발견한 첫 번째 스토리지 담당자가 우리가 아니었다는 것은 분명하지만, 동시에 이 문제의 해법이 없다는 사실이 빌더들을 얼마나 좌절시키는지도 우리는 절실히 알고 있었습니다.
우리는 이 난관을 헤쳐나갈 길을 반드시 찾겠다고 결심했고, 아주 어려운 기술 설계 문제를 마주했을 때 할 수 있는 유일하게 합리적인 일을 했습니다. 가장 시니어한 엔지니어 몇 명을 한 방에 몰아넣고, 모두가 좋아하는 계획을 내오기 전까지는 내보내지 않겠다고 한 것입니다.
열정적이고 논쟁적인 토론이 이어졌습니다. 그리고 이어졌고. 또 이어졌습니다. 결국 우리는 포기했습니다. 누군가를, 아니 대부분의 경우 사실상 모두를 불행하게 만들지 않는 설계 해법에 도달할 수 없었기 때문입니다.
여기서 잠깐 곁가지로 말하자면, 사람들을 방에 가뒀다는 표현에는 제가 약간 극적인 과장을 했을 수도 있습니다. Amazon 회의실에는 잠금장치가 없거든요. 하지만 이 점은 분명히 하고 싶습니다. 정말 어려운 설계 문제를 다룰 때, 기술적 관점이 서로 다른 똑똑하고 열정적인 사람들을 화이트보드 앞에 세워 며칠에 걸쳐 깊이 파고들게 하면, 가장 빠르고 건설적인 진전을 이루는 경우가 많다는 사실을 저는 자주 느낍니다. 지구를 뒤흔드는 통찰은 아니지만, 큰 어려운 문제를 한 시간짜리 화상 회의 블록으로 나누어 이야기하려다 보면 얼마나 쉽게 잊히는지 종종 놀라울 정도입니다. 이 논의에 참여한 엔지니어들은 파일과 객체 워크로드, 그리고 둘이 얼마나 미묘하게 다를 수 있는지를 깊이 이해하고 있었습니다. 그래서 이 토론들은 깊었고, 때로는 뜨거웠으며, 정말로 매혹적이었습니다. 그런데도 우리는 마음에 드는 설계에 도달할 수 없었습니다. 정말로 답답한 일이었습니다.
이 시점은 2024년 크리스마스 무렵이었습니다. 연휴를 앞두고 팀은 방향을 바꿨습니다. 지금까지의 설계 문서와 토론 메모를 검토하며, 파일과 객체 인터페이스를 하나의 통합 시스템으로 제시하려면 우리가 받아들여야 하는 모든 구체적 설계 타협과 행동 특성을 나열하기 시작했습니다. 모두가 그것을 보고 동의했습니다. 이것은 최고의 두 세계가 아니라 최저 공통분모이며, 양쪽 모두에서 놀랍고도 미묘하며 항상 좌절스러운 방식으로 깨질 워크로드 예시를 누구나 떠올릴 수 있다는 데 말입니다.
제가 특히 두드러지게 느꼈던 사례는, 객체와 파일이 데이터 프리미티브로서 최상위 수준의 의미와 경험에서 실제로 어떻게 다른지에 관한 부분이었습니다. 지나치게 단순화해 말하면 이렇습니다. 파일은 운영체제의 구성요소입니다. 스토리지에 존재하고, 전원이 꺼져도 지속되지만, 실제로 사용될 때는 데이터를 표현하는 방식으로서 엄청나게 풍부해서, 스레드, 프로세스, 애플리케이션 간 통신 수단으로 아주 자주 사용됩니다. 파일용 애플리케이션 API는, 제가 데이터베이스의 레코드를 제자리에서 갱신하거나 로그에 데이터를 덧붙일 수 있고, 여러분이 동시에 그 파일에 접근해 제 변경을 파일의 임의 부분에서 거의 즉시 볼 수 있다는 발상을 지원하도록 만들어져 있습니다. mmap() 같은 풍부한 OS 기능은, 파일을 아주 미세한 단위로 변화할 수 있는 공유 영속 데이터이자 메모리 내 데이터 구조 집합처럼 다룬다는 개념을 더욱 강화합니다.
이제 객체 세계로 넘어가 보면, 누군가가 객체에 접근하는 도중 그 객체의 중간에 기록하는 일은 거의 금기입니다. 객체의 불변성은 API와 애플리케이션에 깊이 새겨진 가정입니다. 도구들은 콘텐츠 해시를 내려받고 검증하며, 객체 버전관리를 이용해 이전 복사본을 보존합니다. 무엇보다도, 이들은 종종 객체 전체 생성과 연결된 알림을 중심으로 정교하고 복잡한 워크플로를 구축합니다. 이 마지막 부분은 제가 S3 작업을 시작했을 때 놀랐던 부분인데, 사실 정말 멋집니다. S3 Cross Region Replication(CRR) 같은 시스템은 객체가 생성되거나 덮어써질 때 발생하는 알림을 기반으로 데이터를 복제하며, 객체 복제를 절대 놓치지 않기 위해 그 알림이 at-least-once 의미론을 갖는다고 신뢰합니다. 고객들도 비슷한 파이프라인을 사용해 로그 처리, 이미지 트랜스코딩 등 온갖 작업을 트리거합니다. 객체 위에서 애플리케이션을 설계하는 매우 인기 있는 패턴입니다. 실제로 알림은 제가 일하는 스토리지 시스템의 규모에 경탄하게 만드는 S3 하위 시스템의 한 예입니다. S3는 매일 3,000억 건이 넘는 이벤트 알림을, 새 객체를 처리하는 서버리스 이벤트 리스너에게만 보냅니다!
우리가 깨닫게 된 것은, 파일과 객체 사이에는 실제로 꽤 깊은 경계가 있다는 사실이었습니다. 파일 상호작용은 민첩하고, 종종 변경 중심이며, 의미론적으로 풍부합니다. 반면 객체는 비교적 집중되고 좁은 의미 집합을 지닙니다. 그리고 그 둘을 가르는 이 경계야말로 우리가 वास्तव में ध्यान을 기울여야 할 대상이며, 그것을 숨기려 하기보다 바로 그 경계 자체가 우리가 만들어야 할 기능이라는 사실을 깨달았습니다.
Stage and Commit
연휴를 마치고 돌아온 뒤, 우리는 다시 사람들을 방에 가두기 시작했습니다(뭐, 정확히는 아니지만요). 하지만 이번에는 파일과 객체 사이의 경계가 반드시 보이지 않아야 할 필요는 없다는 관점에서 시작했습니다. 그리고 이번에는 팀이 토론에서 훨씬 더 행복한 표정으로 나오기 시작했습니다.
첫 번째 결정은, S3에서의 퍼스트클래스 파일 접근을 데이터 작업을 위한 프레젠테이션 계층으로 취급하자는 것이었습니다. 고객이 버킷이나 prefix에 대해 S3 마운트를 정의할 수 있게 하고, 그 내부에서는 그 마운트가 S3의 메타데이터를 미러링하는 EFS 네임스페이스를 연결하도록 하자는 것이었습니다. 우리는 두 계층 사이에서 데이터가 이동하고 일관성을 유지하는 문제를 설계의 절대적인 중심으로 삼았습니다. 이를 우리는 “stage and commit”이라고 부르기 시작했는데, git 같은 버전 관리 시스템에서 빌려온 용어입니다. 변경은 EFS에 누적될 수 있고, 이후 집합적으로 S3로 밀어 넣을 수 있게 되는 것입니다. 그리고 데이터가 언제 어떻게 그 경계를 넘어 이동하는지에 대한 구체적인 사항은 시스템의 일부로 공개하여, 고객에게 명확하고, 시간이 지나며 프로그래밍 가능한 프리미티브로 계속 발전시키고 개선할 수 있도록 하자는 것이었습니다. (이 지점은 글 마지막에 조금 더 이야기하겠습니다. 이 표면 위에서 팀이 하고 싶어 하는 일이 훨씬 더 많기 때문입니다.)
파일과 객체 표현 사이의 경계를 명시적으로 드러내는 것이, 팀이 S3 Files 작업을 시작했을 때 제가 전혀 예상하지 못했던 점이었고, 지금은 제가 이 설계에서 정말 좋아하게 된 부분입니다. 아직 초기 단계이고 발전할 여지는 많지만, 팀 모두는 이것이 빌더들이 필요로 하는 것과 함께 개선하고 발전할 수 있는 길 위에 우리를 올려놓았다고 느끼는 것 같습니다. 더 이상 그 받아들이기 힘든 타협들 뒤에 갇히지 않게 된 것입니다.
아직 끝나지 않았다
이 stage and commit이라는 개념을 결정한 것은, 경계와 관심사의 분리를 제공한 설계 결정 중 하나였습니다. 분명한 구조를 주었지만, 어려운 문제들이 사라지게 하지는 않았습니다. 팀은 여전히 파일과 객체 의미론, 성능, 일관성 사이의 वास्तविक한 절충을 헤쳐나가야 했습니다. 이 두 추상화가 얼마나 미묘한지, 그리고 팀이 이 결정을 어떻게 접근했는지 보여주기 위해 몇 가지 예를 들어보겠습니다.
일관성과 원자성
S3 독자들은 종종 객체 전체 단위의 갱신, 알림, 그리고 많은 경우 과거 버전에 대한 접근을 가정합니다. 파일시스템은 세밀한 변경을 지원하지만, 일관성과 원자성 측면에서 중요한 기법도 가지고 있습니다. 많은 애플리케이션은 큰 변경을 한 번에 보이게 만드는 수단으로 원자적 파일 이름 변경에 의존합니다. 디렉터리 이동도 마찬가지입니다. S3 조건부 기능은 첫 번째 문제에는 조금 도움이 되지만 정확히 일치하지는 않으며, 두 번째에 해당하는 S3 대응물은 없습니다. 따라서 앞서 말했듯 계층을 분리하면, 같은 데이터에 대한 단일한 뷰를 가진 병렬 시스템 속에서 이러한 모드들이 공존할 수 있습니다. 파일은 마음껏 변경하고 이름을 바꿀 수 있으며, 나중 시점에 전체 단위로 S3에 기록됩니다.
권한 부여
권한 부여도 마찬가지로 까다롭습니다. S3와 파일시스템은 권한 부여를 매우 다른 방식으로 생각합니다. S3는 key prefix 범위의 IAM 정책을 지원합니다. 예를 들어 “/private/ 아래의 어떤 항목에 대해서도 GetObject를 거부하라”라고 말할 수 있습니다. 사실 네트워크나 요청 자체의 속성과 같은 조건에 따라 권한을 더 좁게 제한할 수도 있습니다. IAM 정책은 엄청나게 풍부하지만, 파일 권한보다 평가 비용이 훨씬 큽니다. 파일시스템은 오랫동안 권한 검사를 데이터 경로 밖으로 빼내기 위해 노력해 왔고, 종종 사전에 평가한 후 핸들을 사용해 이후의 영속적 접근을 수행합니다. 파일은 권한 정책을 둘러싸기에 약간 이상한 개체이기도 한데, 파일의 권한은 inode에 있기 때문입니다. hard link는 같은 파일에 대해 여러 inode를 가질 수 있게 하고, 애초에 그 파일에 도달할 수 있는지를 결정하는 디렉터리 권한도 생각해야 합니다. 물론 이미 핸들을 갖고 있다면 상황이 좀 다르죠. 이름이 바뀌고, 이동되고, 심지어 삭제되더라도 말입니다.
여기에는—특히 사용자 및 그룹 식별 같은 주제를 둘러싸고—이야기할 복잡성, 음, 풍부함이 훨씬 더 많습니다. 하지만 경계를 명시적으로 두는 방식으로 이동하면서, 팀은 모든 단일 객체 위에 양쪽 종류의 권한을 동시에 표현해야 하는 부담에서 벗어났습니다. 대신 권한은 마운트 자체에 지정될 수 있게 되었고(네트워크 파일시스템 사용자에게는 익숙한 영역입니다), 파일시스템 내부에서 강제되며, 두 세계 사이에는 특정 매핑이 적용될 수 있게 되었습니다.
이 설계는 또 다른 장점도 있었습니다. S3의 IAM 정책을 최후의 안전장치로 보존해 주었다는 점입니다. 데이터 경계를 바꿔야 할 필요가 있다면 언제든 S3 계층에서 접근을 차단할 수 있고, 각 마운트 내부에서는 권한 부여를 파일 계층에 위임할 수 있습니다. 그리고 미래에 같은 데이터 위에 서로 다른 여러 마운트를 탐색해 보고 싶어질 상황을 위해서도 길을 열어두었습니다.
네임스페이스 의미론의 끔찍한 부조화
파일 시스템과 객체 시스템 모두에 익숙하다면, 파일과 객체의 이름 동작이 꽤 다르게 나타나는 사례를 떠올리는 것은 어렵지 않습니다. 그런데 정말 앉아서 깊이 파고들기 시작하면, 상황은 거의 우스울 정도로 황량해집니다. 파일시스템에는 퍼스트클래스 경로 구분자가 있습니다. 보통 슬래시("/")죠. S3에도 이것이 있기는 하지만, 사실상 제안에 가깝습니다. 실제로 S3의 LIST 명령은 무엇이든 경로 구분자로 해석되길 원하는 값을 지정할 수 있고, 결과를 어떻게 구성할지에 따라 LIST에 다른 delimiter를 넘기며 같은 경로 안에 여러 구분자를 섞어 넣은 놀라운 다차원 네이밍 구조를 구축한 고객도 소수 존재합니다.
또 하나 단순하면서도 짜증나는 예가 있습니다. S3에는 디렉터리가 없기 때문에, 같은 슬래시로 끝나는 객체를 가질 수 있습니다. 다시 말해, 디렉터리처럼 보이지만 파일인 것을 가질 수 있다는 뜻입니다. 팀은 약 20분 동안 이것이 멋진 기능이라고 생각했고, 이것들을 “filerectories”라고 부르기도 했습니다. 그 이름을 유지하지 않아서 정말 다행입니다.
이런 차이는 수십 가지나 되며, 우리는 공통 구조 하나로 제한하는 방법이나 한쪽 의미론에 맞춰 고정하는 방법 등을 모두 신중하게 생각했습니다. 하지만 어느 길로 가든, 애플리케이션 내부의 이름 관련 가정을 깨뜨리게 된다는 사실을 깨달았습니다.
우리는 경계를 받아들이고, 양쪽이 기존의 이름 규칙과 의미론을 유지하도록 하기로 결정했습니다. 경계를 넘어 이동할 수 없는 객체나 파일이 생성될 경우, 우리는 그것들을 옮기지 않기로 했습니다(그리고 와, 이 문제를 두고 정말 열정적인 논의가 엄청나게 많았습니다). 대신 고객이 모니터링하고 필요시 조치를 취할 수 있도록 이벤트를 발생시키기로 했습니다. 이것은 분명히 복잡성을 개발자에게 전가하는 예입니다. 하지만 저는 이것이 올바른 일인 아주 훌륭한 사례라고도 생각합니다. 이미 실행되리라고 기대하는 각 도메인 안에서 실패를 만들지 않기로 선택하고 있고, 양쪽에서 실제로 잘 작동하는 대다수 경로 이름을 받아들이는 경계를 만들고 있으며, 문제가 발생할 때 이를 감지하고 수정할 수 있는 메커니즘을 구축하고 있기 때문입니다.
성능 경험
팀이 오랜 시간 이야기한 마지막 큰 차이 영역은 성능, 특히 네임스페이스 상호작용의 성능과 요청 지연시간이었습니다. 파일과 객체 네임스페이스는 매우 다른 것을 위해 최적화되어 있습니다. 파일시스템에서는 메타데이터에 대한 데이터 의존적 접근이 많습니다. 파일에 접근하려면 디렉터리 레코드도 접근해야 하고(일부 경우에는 갱신도 해야 합니다). 또 경로를 따라 모든 디렉터리 레코드를 순회하게 되는 작업도 많습니다. 그 결과 빠른 파일시스템 네임스페이스—even 큰 분산형 시스템도—이런 상호작용을 최대한 빠르게 하기 위해 디렉터리의 메타데이터를 단일 호스트에 함께 배치하는 경향이 있습니다. 반면 객체 네임스페이스는 완전히 평평하고, 매우 높은 병렬성의 점 질의와 갱신에 최적화되는 경향이 있습니다. S3에는 개별 “디렉터리” 하나에 수십억 개의 객체가 들어 있고, 수십만 클라이언트가 병렬로 접근하는 경우도 많습니다.
제가 방금 설명한 여러 도전 과제를 살펴보면서, 우리는 도입에 관해서도 많은 이야기를 나눴습니다. S3는 20년 된 서비스이고, 우리는 기존 S3 고객이 자기 데이터에 즉시 사용할 수 있는 해법을 원했습니다. 완전히 새로운 무언가로 마이그레이션해야 하는 해법이 아니라 말입니다. 이미 엄청난 수의 버킷이, 문서에 적힌 그대로 S3의 객체 의미론이 동작하는 데 의존하는 애플리케이션을 서비스하고 있습니다. 우리는 그런 애플리케이션을 깨뜨릴 수 있는 미묘한 새로운 동작을 도입할 생각이 전혀 없었습니다.
알고 보니 아주 적은 수의 애플리케이션만이 같은 데이터에 대해 동일한 순간에 파일과 객체 인터페이스를 동시에 사용합니다. 훨씬 더 일반적인 패턴은 다단계입니다. 데이터 처리 파이프라인이 한 단계에서는 파일시스템 도구를 사용해 결과를 생성하고, 다음 단계에서는 그 결과를 객체 기반 애플리케이션이 소비하는 식입니다. 또는 고객이 파일시스템을 통해 적극적으로 수정 중인 데이터의 스냅샷에 대해 분석 쿼리를 실행하고 싶어 하기도 합니다.
우리는 데이터 사일로 문제를 해결하기 위해 파일과 객체 의미론을 수렴시킬 필요가 없다는 사실을 깨달았습니다. 그들에게 필요한 것은 하나의 장소에 있는 같은 데이터였고, 각 접근 패턴에 맞는 올바른 뷰였습니다. 완전한 NFS close-to-open 일관성을 제공하는 파일 뷰. 완전한 S3 atomic-PUT 강한 일관성을 제공하는 객체 뷰. 그리고 둘을 연결하는 동기화 계층 말입니다.
그래서 우리는 출시했다
그 모든 논쟁—팀의 “받아들이기 힘든 타협” 목록, filerectories를 둘러싼 열정적이고 때로는 황량하기까지 한 토론—은 결국 우리가 반드시 해야 했던 작업이었습니다. 팀 모두는 그 과정을 거쳤기에 설계가 더 좋아졌다고 느끼는 것 같습니다. S3 Files를 사용하면 어떤 S3 버킷이나 prefix든 EC2 인스턴스, 컨테이너, 또는 Lambda 함수에서 파일시스템으로 마운트할 수 있습니다. 뒤에서는 EFS가 이를 뒷받침하며, 여러분의 도구가 이미 기대하는 파일 경험을 제공합니다. NFS 의미론, 디렉터리 연산, 권한 관리까지 말입니다. 애플리케이션 관점에서는 마운트된 디렉터리입니다. S3 관점에서는 버킷 안의 객체입니다.
동작 방식은 간단히 살펴볼 가치가 있습니다. 디렉터리에 처음 접근하면, S3 Files는 S3에서 메타데이터를 가져와 동기화된 뷰를 채웁니다. 128 KB 미만의 파일에 대해서는 데이터 자체도 함께 가져옵니다. 더 큰 파일은 메타데이터만 가져오고, 실제로 읽을 때 S3에서 데이터를 가져옵니다. 이런 지연 hydration은 중요합니다. 수백만 개의 객체가 있는 버킷도 마운트하고 즉시 작업을 시작할 수 있음을 의미하기 때문입니다. 이 “즉시 작업 시작” 부분은 사실 내부적으로는 꽤 정교한 단순 경험의 좋은 예입니다. S3에 있는 객체를 파일처럼 마운트하자마자 곧바로 작업할 수 있다는 것은 이 기능에 대해 당연하고 자연스러운 기대이며, 메타데이터의 파일 뷰가 채워질 때까지 몇 분이나 몇 시간을 기다려야 한다면 꽤 답답할 것입니다. 하지만 내부적으로 S3 Files는 S3 메타데이터를 스캔하고 파일 최적화된 네임스페이스를 채워야 하며, 팀은 이를 매우 빠르게, 그리고 단순하고 민첩한 고객 경험을 유지하는 백그라운드 작업으로 수행할 수 있게 만들었습니다.
파일을 생성하거나 수정하면, 변경 내용은 대략 60초마다 집계되어 하나의 PUT으로 S3에 커밋됩니다. 동기화는 양방향으로 수행되므로, 다른 애플리케이션이 버킷 내 객체를 수정하면 S3 Files가 그 변경을 자동으로 감지하고 파일시스템 뷰에 반영합니다. 파일이 양쪽에서 동시에 수정되어 충돌이 발생하면, S3가 소스 오브 트루스가 되며 파일시스템 버전은 lost+found 디렉터리로 이동하고 해당 이벤트를 식별하는 CloudWatch 메트릭이 남습니다. 30일 동안 접근되지 않은 파일 데이터는 파일시스템 뷰에서 축출되지만 S3에서는 삭제되지 않으므로, 스토리지 비용은 활성 작업 집합에 비례하게 유지됩니다.
팀이 시스템을 구축하며 수행한 더 작고도 정말 재미있는 작업들도 많이 있습니다. 제가 정말 멋지다고 생각하는 개선 중 하나는 우리가 “read bypass”라고 부르는 것입니다. 고처리량 순차 읽기의 경우, read bypass는 읽기 데이터 경로를 기존 NFS 접근 대신 자동으로 재라우팅하여, S3 자체에 직접 병렬 GET 요청을 수행하도록 합니다. 이 접근은 클라이언트당 3 GB/s를 달성하며(앞으로 더 개선할 여지도 있습니다), 여러 클라이언트에 걸쳐 초당 테라비트 규모로 확장됩니다. 관심 있는 분들을 위해 기술 문서에는 훨씬 더 자세한 내용이 있습니다(꽤 흥미롭게 읽히는 문서입니다).
제가 이 설계에서 정말 높이 평가하게 된 한 가지는, 자기 자신의 경계에 대해 얼마나 솔직한가입니다. 파일과 객체 도메인 사이의 명시적 경계는 우리가 얼버무리며 덮고 있는 한계가 아닙니다. 오히려 양쪽이 훼손되지 않도록 해 주는 핵심입니다. 그렇다고 해도, 여전히 우리가 해야 할 일이 남아 있다는 것을 아는 부분들도 있습니다. S3에는 네이티브 이름 변경 연산이 없기 때문에 이름 변경은 비용이 큽니다. 그래서 디렉터리 이름을 바꾸려면 해당 prefix 아래의 모든 객체를 복사하고 삭제해야 합니다. 바로 이 이유 때문에 마운트가 5천만 개가 넘는 객체를 포함할 경우 경고를 표시합니다. 명시적인 commit 제어는 출시 시점에는 없습니다. 60초 창은 대부분의 워크로드에는 맞지만, 모두에게 충분하지는 않으리라는 것을 알고 있습니다. 또 유효한 POSIX 파일명으로 표현할 수 없는 객체 key도 있으며, 이들은 파일시스템 뷰에 나타나지 않습니다. 우리는 약 9개월 동안 고객 베타를 진행해 왔고, 이것들이 바로 그 과정에서 배우고 초기 고객들과 함께 계속 개선하고 반복해 온 부분들입니다. 존재하지 않는 척하기보다는, 그것들을 분명히 드러내는 편을 택했습니다.
파일과 해바라기
UBC의 Loren 연구실과 함께 작업할 때, JS는 놀라울 정도로 많은 시간을 캐싱 계층과 네이밍 계층을 구축하는 데 썼습니다. 생물학을 하는 것이 아니라, 데이터가 살아 있는 곳과 도구가 기대하는 곳 사이를 오가게 하는 인프라를 작성하고 있었던 것입니다. 그 마찰은 제게 강하게 남았고, 지금 돌아보면 우리가 그 연구실에서, 그리고 이후 S3 팀이 Tables, Vectors, 그리고 이제 Files를 작업하면서 계속 배운 교훈은 이것이었다고 생각합니다. 데이터와 작업하는 서로 다른 방식은 억지로 하나로 축소해야 하는 문제가 아닙니다. 그것은 섬겨야 할 현실입니다. Loren의 연구실 해바라기는 다양성 속에서 잘 자랐고, 알고 보니 데이터 접근 패턴도 마찬가지입니다.
S3 Files에서 제가 가장 흥분되는 점은, 솔직히 처음에는 전혀 예상하지 못했던 것입니다. 파일과 객체 사이의 명시적 경계가 설계의 가장 좋은 부분이 되었다는 점입니다. 우리는 몇 달 동안 그 경계를 사라지게 만들려고 애썼고, 마침내 그것을 시스템의 퍼스트클래스 요소로 받아들이자 모든 것이 더 나아졌습니다. Stage and commit은 우리가 계속 발전시킬 수 있는 표면을 제공합니다. 데이터가 언제 어떻게 그 경계를 넘는지에 대한 더 많은 제어, 파이프라인 및 워크플로와의 더 풍부한 통합 말입니다. 그리고 그것은 어느 한쪽도 훼손하지 않고 그렇게 할 수 있게 해 줍니다.
20년 전, S3는 객체 스토어로 시작했습니다. 지난 몇 년 동안 Tables, Vectors, 그리고 이제 Files를 통해, 그것은 더 넓은 무언가가 되었습니다. 데이터가 내구성 있게 살아가며, 그때그때 작업에 맞는 어떤 방식으로든 다룰 수 있는 장소가 된 것입니다. 우리의 목표는 스토리지 시스템이 여러분의 작업을 방해하지 않고 뒤로 물러나는 것이지, 여러분이 우회하며 맞춰야 하는 대상이 되는 것이 아닙니다. 우리는 아직 한참 멀었지만, 우리가 향하고 있는 방향에 저는 정말로 기대가 큽니다.
Werner의 말을 빌리자면, “자, 이제 가서 만들어 보세요!”