서비스 오픈 1년 3개월 시점에 시스템 구성을 통째로 공개한 글이다. 호스팅과 OS, 데이터베이스 다섯 종, 스토리지와 CDN, 캐시까지 각 계층에서 무엇을 쓰고 왜 그렇게 골랐는지, 그리고 어디서 데였는지를 함께 적었다. 선택의 근거보다 겪은 제약을 솔직하게 남긴 대목이 많다.
핵심 포인트- EC2를 쓰되 특정 클라우드에 지나치게 의존하지 않으려 그 외 서비스는 최소한만 쓴다.
- 초기에 EC2의 CPU 크레딧과 EBS의 IOPS 한도에 부딪혀 낭패를 겪었고 특히 메인 DB의 IOPS 문제가 컸다.
- 메인은 PostgreSQL이며 멀티 프로세스로 동작하는 특성상 커넥션 풀링이 특히 중요하다.
- 해시 태그 like 검색이 메인 DB 성능을 떨어뜨려 그 부분만 MariaDB로 분리했다.
- MariaDB의 기본 utf-8은 3바이트까지라 이모지를 넣으려면 별도 설정이 필요하고 varchar 길이 제약도 따라온다.
- 주요 데이터는 RDBMS에 바로 쓰지 않고 캐시에 먼저 넣은 뒤 지연해서 저장한다.
상세 정리- 호스팅: 도쿄 리전의 EC2를 쓰며 서울 리전으로 옮길 계획은 없다고 밝힌다.
- 자체 하드웨어: 사내에 맥 미니 서버 한 대가 있고 iOS·안드로이드 클라이언트 자동 빌드 용도다.
- OS: 맥 미니를 빼면 전부 Ubuntu 14.04를 쓰며, 설정과 관리가 편해 소규모 팀에 잘 맞는다고 평가한다.
- 규모: 개발자 4명이 250만 명 이상의 회원이 만드는 트래픽을 수십 대의 서버로 1년 넘게 운영했고 OS가 문제를 일으킨 적은 없다.
- 드라이버 이슈: 최적 성능을 내려면 특정 네트워크 모듈의 상위 버전이 필요한데 커널 충돌로 설치가 안 돼, 다음 LTS에 번들되기를 기대한다고 적는다.
- PostgreSQL: 메인 데이터베이스이며 서비스 관련 데이터가 사실상 전부 여기 저장된다.
- PostgreSQL 튜닝: 데몬이 멀티 스레드가 아닌 멀티 프로세스로 동작해 확인할 튜닝 지점이 여러 군데다. 오토 배큠과 커넥션 풀링, 커널 리소스까지 손봤다.
- 커넥션 풀링: 커넥터나 ORM 수준의 풀링도 있지만 별도의 풀러를 두고 쓴다.
- 워밍업 도구: 어떤 파일이 OS 캐시에 올라와 있는지 확인해주는 도구로 데이터 파일의 적재 상태를 보고 필요하면 미리 읽어 성능을 유지한다.
- MariaDB 도입 이유: 중복 제거 후에도 600만 개에 이르는 해시 태그를 메인 DB에서 like로 검색하면 상황에 따라 심각한 성능 저하가 났다.
- 선택 과정: 검색엔진부터 여러 데이터베이스를 시험한 끝에 MariaDB로 해시 태그 검색을 구현했고 필요한 성능이 나왔다.
- MariaDB 제약 1: 기본 utf-8이 3바이트까지만 다뤄 이모지 같은 4바이트 문자를 넣으면 오류가 나므로 mb4 옵션을 써야 하고, 대소문자 구분 검색에는 별도 collate가 필요하다.
- MariaDB 제약 2: InnoDB 기준 varchar가 767바이트까지라 mb4 기준으로는 약 190자만 저장된다.
- 저자의 당부: 이런 특성을 관계형 데이터베이스 전체의 특성으로 오해하지 않았으면 한다고 덧붙인다.
- MongoDB 용도: 카드의 순 조회수는 관계형 DB에 기록하되 누가 조회했는지의 로그는 MongoDB에 남겨 정확도를 지키면서 메인 DB 부하를 분산한다.
- MongoDB 함정: 메인으로 쓰다 데이터와 쿼리가 늘면 조회가 느려지고, 인덱스를 만들면 쓰기가 느려지고, 결국 다 느려지는 경로를 겪었다고 정리한다.
- MongoDB 평가: 스키마가 없는 만큼 관계형만큼의 인덱스 성능이 안 나오고 집계 쿼리도 마찬가지라, 대체재라기보다 단순하게 쓸 때 편한 저장소로 본다.
- DynamoDB: 구축이 쉽고 처리량 기준 과금이라 확장이 쉽지만 인덱스와 쿼리가 제한적이다. 초기 일부에 썼고 대부분 MongoDB로 옮겼으며 앞으로 걷어낼 계획이다.
- 스토리지: 수억 장의 이미지를 오브젝트 스토리지에 두며, 이런 단순 저장은 직접 구축하는 것보다 낫다고 강하게 권한다.
- CDN: 수백 테라바이트를 CDN으로 배포하는데 특별히 뛰어나서가 아니라 오브젝트 스토리지에서 CDN으로 나가는 비용이 무료이고 예약 요금으로 추가 할인이 가능해서다.
- 캐시: 주력 캐시로 redis를 쓰며 특정 배포판이 메모리 할당자와 함께 빌드돼 있어 성능 이점이 있다고 본다.
- 캐시 설계: 애플리케이션에 자체 샤딩 루틴이 있어 여러 인스턴스에 분산 저장하고, 히트하지 않으면 자동으로 DB에서 당겨온다.
- 쓰기 경로: 트랜잭션 부하를 우려해 주요 데이터를 관계형 DB에 바로 쓰지 않고 캐시에 먼저 넣은 뒤 일정 지연 후 저장한다.
왜 읽나소규모 팀이 클라우드에서 서비스를 키울 때 각 계층에 무엇을 두고 어디서 데이는지를, 선택 이유와 실패 경험을 함께 훑을 수 있다.