S3는 파일이지만, 파일시스템은 아님 - Cal Paterson
원문 -> S3 is files, but not a filesystem
2024년 3월, Cal Paterson
‘깊은(Deep)’ 모듈, 맞지 않는 인터페이스 - 그리고 SAP가 그토록 불편했던 이유
오직 나만의 물건보관함
Amazon S3는 최초의 클라우드 기술입니다. 2006년에 나왔죠. 그 당시에는 ‘객체(Objects)’가 유행이었고 S3는 ‘객체 저장소’라는 이름이 붙었지만, 사실 모두가 S3는 파일을 위한 것이라고 알고 있습니다.
S3는 객체-어쩌고가 아니라 클라우드 파일시스템인 셈이죠.
저는 S3가 사실은 ‘아마존 클라우드 파일시스템’이라는 생각이, 전체 구조를 떠받치기 위한 허구(load bearing fiction)에 가깝다고 봅니다. 어느 정도는 사실입니다. S3는 파일을 저장할 수 있죠. 또한, 근본적으로 좋은 기술인 S3를 사람들이 채택하게 만드는 데 매우 유용한 용어이기도 합니다. 그렇지 않았다면 사람들은 S3를 쓰지 않았을지도 모릅니다.
하지만 그것은 거짓입니다. S3는 파일시스템이 아니며 파일시스템을 대체할 수도 없습니다.
파일시스템의 본질과 모듈의 ‘깊이’
유닉스 파일 API는 꽤 직관적입니다. 기본적인 함수는 5개뿐이며, 인자도 많이 받지 않습니다.
여기 (파이썬 버전의) 5가지 기본 함수가 있습니다:
# 파일 열기
open(filepath) # `file` 객체 반환
# 파일에서 읽기 (위치를 앞으로 이동)
file.read(size=100) # 100바이트 반환
# 파일에 쓰기 (위치를 앞으로 이동)
file.write("hello, world")
# 위치를 94바이트로 이동
file.seek(94)
# 파일 닫기
file.close()
음, 약간의 부연 설명이 필요할 것 같습니다. 실제로는 훨씬 더 많은 호출이 있습니다. 하지만 이 다섯 가지 호출이 파일 API의 압축된 핵심입니다. 파일을 읽고 쓰는 데 필요한 전부는 이것뿐입니다.
이 다섯 가지 함수는 수많은 고려사항을 처리합니다:
-
버퍼링
-
페이지 캐시
-
단편화
-
권한
-
IO 스케줄링
-
그 외 여러 가지
파일 API가 이 모든 고려사항을 처리함에도 불구하고, 사용자에게 그것들을 노출하지 않습니다. 좁은 인터페이스로 수많은 고려사항을 처리하는 것, 이것이 바로 유닉스 파일 API를 ‘깊은(deep)’ 모듈로 만드는 점입니다.
깊은 모듈은 훌륭합니다. SD 카드의 웨어 레벨링(wear-levelling) 같은 기능의 혜택을 누리면서도, 휴대폰에 jpeg 파일을 저장할 때 그 어떤 것에 대해서도 생각해야 하는 정신적 소모를 겪지 않아도 되기 때문입니다. 행복한 날들이죠.
하지만 파일 API가 ‘깊다’면, 어떤 것이 ‘얕은(shallow)’ 모듈일까요?
얕은 모듈은 처리하는 기능에 비해 상대적으로 넓은 API 표면적을 가집니다. 요즘 어떤 모듈이 얕다는 힌트 중 하나는 그 인터페이스가 YAML이라는 점입니다. YAML은 단순한 마크업 언어처럼 보이지만, 실제로는 거의 모든 시맨틱(semantics)을 얹을 수 있는 재사용 가능한 구문입니다.
종종 YAML은 “데브옵스의 프로그래밍 언어”로 작동하며, 프로그래밍 언어는 가능한 가장 넓은 인터페이스를 제공합니다. 여러분의 YAML 마이크로 언어를 자세히 살펴보세요. 반복 구문을 제공하나요? 그렇다면 튜링 완전(Turing complete)할 가능성이 높습니다.
하지만 때로는 무언가를 보기 좋게 포장하기 어렵습니다. SQL ORM은 본질적으로 새는 추상화(leaky abstraction)입니다. SQL에 대한 어느 정도의 이해 없이는 사용할 수 없죠. 따라서 얕다는 것이 본질적으로 비판은 아닙니다. 때로는 얕은 모듈이 최선일 수 있습니다. 하지만 다른 조건이 같다면, 깊을수록 좋습니다.
S3의 본질 (S3 깊기도 합니다)
유닉스 파일 API는 1970년대 초반에 자리를 잡았습니다. 인터페이스는 유지되었고, 그 내부는 호환성을 위해 수없이 재구현되었습니다.
하지만 Amazon S3는 유닉스 파일시스템 API를 재구현하지 않습니다.
S3는 완전히 다른 구조를 가지고 있으며, 기본 기능(primitives)은 부분적으로만 호환됩니다. 위에서 언급한 5가지 유닉스 기본 호출에 상응하는 S3 호출에 대한 간략한 설명입니다:
# 객체의 (일부) 읽기
GetObject(Bucket, Key, Range=None) # 내용은 HTTP 본문(body)
# (전체) 객체 쓰기
PutObject(Bucket, Key) # 내용을 HTTP 본문으로 전송
# 음, 이게 다입니다!
5개 함수 대 2개 함수. 맞습니다, S3 API는 유닉스 파일 API보다 더 간단합니다. “버킷(buckets)”이라는 추가 개념이 하나 있지만, 종합적으로 보면 S3의 기능 대비 인터페이스 비율은 유닉스 파일 API보다 훨씬 낫다고 생각합니다.
하지만 빠진 것이 있습니다. GetObject의 Range 인자를 사용해 객체를 부분적으로 읽을 수는 있지만, 부분적으로 덮어쓸 수는 없습니다. 덮어쓰기는 반드시 파일 전체여야 합니다.
이는 사소해 보이지만, 실제로는 S3를 파일의 기존 사용 사례 중 일부에만 국한시키는 요인이 됩니다.
파일시스템 소프트웨어, 특히 데이터베이스는 Amazon S3로 포팅할 수 없습니다
모든 종류의 데이터베이스는 데이터를 저장할 장소가 필요합니다. 일반적으로 그 장소는 파일시스템 위의 다양한 파일들이었죠. Postgres는 테이블당 2~3개의 파일을 유지하고, 관리 목적으로 수많은 다른 파일들을 사용합니다. SQLite는 모든 것을 단일 파일에 저장하는 것으로 유명합니다. MySQL, MongoDB, Elasticsearch 등 무엇이든 모두 데이터를 파일에 저장합니다.
결정적으로, 이 데이터베이스들은 대부분 부분 덮어쓰기(partial overwrites) 기능에 압도적으로 의존합니다. 데이터를 “페이지(pages)”(예: 4 또는 8 킬로바이트 길이) 단위로 “힙(heap)” 파일에 저장하며, 쓰기 작업은 페이지 단위로 이루어집니다. 단일 파일에 수천 개의 페이지가 있을 수 있습니다. 페이지는 필요한 데이터를 저장하기 위해 필요에 따라 덮어쓰입니다. 이는 부분 덮어쓰기가 절대적으로 필수적임을 의미합니다.
어떤 소프트웨어 프로젝트들은 잘 검증된 두 기술인 Amazon S3와 SQLite(또는 DuckDB)를 결합하여 데이터를 ‘단순한’ 방식으로 저장하려는 꿈을 가지고 시작합니다. 결국, 이보다 더 간단하고 직관적인 것이 있을까요? 슬프게도, 이 둘은 기름과 물처럼 섞이지 않습니다.
SQLite 데이터베이스를 S3에 보관하면, 모든 쓰기 작업이 갑자기 전체 데이터베이스를 통째로 덮어쓰는 작업이 되어버립니다. S3가 대용량 쓰기를 빠르게 처리할 수 있더라도, 아주 작은 데이터셋을 제외하고는 이 전략이 작동할 만큼 빠르지는 않습니다. 그리고 데이터베이스 제작자들이 공들여 구현한 모든 트랜잭션 무결성을 내팽개치는 셈입니다. 데이터베이스 파일을 매번 다시 쓰는 것은 그 모든 것을 버리는 행위입니다. S3에서는 마지막 쓰기가 이깁니다.
S3가 잘하는 것과 못하는 것
S3의 즐거움은 읽기 및 쓰기 대역폭(“속도”)이 극도로, 극도로 높다는 점입니다. 초당 10 기가바이트 이상으로 S3에 쓰거나 읽은 사람들의 사례를 온라인에서 쉽게 찾을 수 있습니다. 실제로 저는 한 금융 고객사의 사무실 네트워크를 S3 쓰기 작업으로 가득 채운 적이 있습니다.
하지만 부분 덮어쓰기가 외에도 다른 몇 가지 문제가 더 있습니다.
S3에는 이름 변경(rename)이나 이동(move) 작업이 없습니다. 이름 변경은 CopyObject
를 한 다음 DeleteObject
를 하는 것입니다. CopyObject
는 파일 크기에 비례하는 시간이 걸립니다. 이는 누군가 많은 파일을 잘못된 위치에 썼을 때 파일을 다시 옮기는 작업이 매우 느리다는 것을 의미하므로 꽤 자주 문제가 됩니다.
그리고 파일 목록 조회가 느립니다. Amazon S3의 즐거움이 극도로 높은 대역폭으로 읽고 쓸 수 있다는 점인 반면, 거기에 무엇이 있는지 목록을 보는 것은 훨씬 더 느립니다. 느린 로컬 파일시스템보다도 느립니다.
하지만 S3는 파일시스템보다 유지보수가 훨씬 적게 듭니다. 버킷 이름과 키 이름만 정하면 클라우드 요정들이 나머지 모든 것을 처리해 줍니다. 백업 설정, 오프사이트 복제, 프로비저닝(용량뿐만 아니라 IO 연산에 대한 것도 포함)은 순전히 고된 잡일이라는 점을 기억하면 이는 큰 가치가 있습니다.
모듈의 깊이는 조직 간에 더욱 중요합니다
돌이켜보면 S3가 최초의 인기 있는 클라우드 API였다는 것은 놀라운 일이 아닙니다. 깊은 API가 (컴퓨터와 같은) 단일 시스템 내 다른 모듈 간의 복잡성을 억제하는 데 도움이 된다면, 상호작용 비용이 훨씬 더 높은 두 다른 비즈니스 간의 상호작용 복잡성을 억제하는 데는 훨씬 더 도움이 됩니다.
반대 사례를 생각해 봅시다. 전통적으로 한 기업이 다른 기업의 컴퓨터와 자사 컴퓨터를 연동시키려 할 때 이를 “통합(integration)”이라고 부릅니다. 이는 고통의 대명사입니다. 당신이 어떤 거대 기업용 소프트웨어 괴물을 조직에 통합하는 임무를 맡았다고 상상해 보세요. SAP 같은 것 말이죠. SAP가 깊은 모듈일까요? 아닙니다. SAP의 비극은 조직 전체가 거의 다 그것을 이해해야 한다는 점입니다. 그리고 그것을 당신이 하는 모든 것과 항상 조화시켜야 합니다. 결과적으로 SAP 통합 프로젝트는 비싸고, 거대하며, 정기적으로 실패합니다.
S3의 복잡성은 SAP 설치에 비해 그리 적지 않습니다. 아마존은 이를 “Simple Storage Service(단순 스토리지 서비스)”라고 이름 붙였지만, S3 내부의 복잡성은 꽤나 무서울 정도입니다. 큐잉 이론, IO 경합, 샤딩 등 문제 목록은 계속 이어집니다. 위에서 파일시스템이 처리한다고 나열했던 모든 것들에 더해서 말이죠. (그리고 그들이 이 모든 것을 온프레미스에서 한다는 게 믿어지시나요?)
S3의 “Simple”은 잘못된 이름입니다. S3는 실제로 단순하지 않습니다. 깊은(deep) 것입니다.
기타 메모
-
저는 이 글을 통해 S3가 가격에 비해 비싸지 않다고 주장하려는 의도가 전혀 없습니다. 헤지펀드에 대한 유명한 농담을 빌리자면, 때때로 클라우드는 서비스 모델을 가장한 수익 모델처럼 보입니다.
-
깊은 모듈 대 얕은 모듈의 개념은 John Ousterhout의 훌륭한 책에서 가져왔습니다. 이 책은 사실상 소프트웨어 설계에 대한 아이디어 목록입니다. 일부는 저에게 큰 울림을 주었고, 다른 일부는 그렇지 않았지만, 전반적으로 읽을 가치가 충분합니다. 간결하게 만든 점에 찬사를 보냅니다.
-
몇몇 데이터베이스는 처음부터 스토리지로 S3 API를 사용하도록 명시적으로 설계되었습니다. Snowflake가 그랬습니다. 따라서 가능은 합니다만, 투명하게 되지는 않습니다. 하지만 Snowflake는 제가 아는 몇 안 되는 사례 중 하나입니다 (그리고 그들은 이 결정을 매우 일찍, 적어도 2016년까지는 내렸습니다). 다른 사례를 알고 계시면 이메일로 알려주세요.
-
S3에서 어려움을 겪는 것은 데이터베이스뿐만이 아닙니다. 많은 파일 형식은 저렴한 비용으로 파일 내에서 위치를 이동(seek)할 수 있다고 가정하기 때문에, 디스크에서보다 S3에서 성능이 떨어집니다. Zip 파일이 대표적인 예입니다.
S3에 대한 기타 아쉬운 점들
저는 S3를 진심으로 좋아해서 글 중간에 불만사항 목록을 포함시켜 잘못된 인상을 주고 싶지 않았지만, 어쨌든 위에서 언급하지 않은 다른 주요 문제점들은 다음과 같습니다:
-
S3 API는 XML로만 제공됩니다. 2006년에도 JSON이 있었지만 XML이 여전히 지배적이어서 아마존이 처음에 XML을 선택한 것은 놀라운 일이 아닐 것입니다. 하지만 아마존이 JSON 버전을 결코 출시하지 않았다는 점은 놀랍습니다. 특히 SOAP에서 REST로 전환했을 때가 좋은 시기였을 텐데요.
-
아마존이 XSD 스키마 유지를 포기한 것도 아쉬운 점입니다. 이는 API에 XML을 사용하는 주요 이점 중 하나이기 때문입니다. 이제 공식 문서는 그냥 웹사이트일 뿐입니다.
-
범죄 수준으로, 아마존은 - 많은 클라우드 서비스 제공업체처럼 - 어떤 종류의 로컬 테스트 환경도 제공한 적이 없습니다. 파이썬에서는, 더 성실한 사람들이
moto
라이브러리로 테스트합니다.moto
는 자원봉사자들이 유지 관리하는데, 상용 제품을 위한 테스트 도구라는 점을 감안하면 이상한 일입니다. -
Amazon S3는 체크섬을 지원합니다. 어떤 이유에서인지 기본적으로 켜져 있지 않습니다. 아마존은 내구성에 대해 많은 주장을 합니다. 사람들이 문제를 겪었다는 이야기는 들어보지 못했지만, 마찬가지로 이 주장들이 테스트되는 것을 본 적도 없습니다. 적어도 저는 이 주장에 대해 약간의 호기심이 있습니다.
-
수년 동안 Amazon S3는 부주의한 사용자를 위한 또 다른 함정을 가지고 있었습니다: 최종적 일관성(eventual consistency)입니다. 파일을 읽고, 덮어쓴 다음, 다시 읽었을 때 아직 변경되지 않은 것을 발견할 수 있었습니다. 특히 짧은 시간 동안 가끔씩만 발생했기 때문에 온갖 종류의 혼란을 야기했습니다. 다른 S3 구현체들은 이 속성을 모방하지 않았고, 몇 년 전 아마존은 자사 구현에서 이 문제를 수정했습니다.