Scaling to 100k Users
https://alexpareto.com/scalability/systems/2020/02/03/scaling-100k.html
2020 년 2 월 3 일
많은 신생 기업이 있습니다. 수많은 신규 사용자가 매일 계정에 등록하는 것처럼 느껴지고 엔지니어링 팀은 일을 계속하기 위해 노력하고 있습니다.
문제가 있지만 0에서 수십만 명의 사용자를 대상으로 웹앱을 가져 오는 방법에 대한 정보는 부족할 수 있습니다. 일반적으로 솔루션은 대규모 화재가 발생하거나 병목 현상을 식별하여 발생합니다 (종종 두 번).
그렇게 말하면서, 측면 프로젝트를 확장 성이 높은 것으로 가져 오는 주요 패턴 중 상당수가 상대적으로 공식적이라는 것을 알았습니다.
이것은 수식 주위의 기본 사항을 글로 표현하려는 시도입니다. 새로운 사진 공유 웹 사이트 인 Graminsta를 1 ~ 100 만 명의 사용자로 만들 것입니다.
웹 사이트 또는 모바일 앱과 같은 거의 모든 응용 프로그램-API, 데이터베이스 및 클라이언트 (일반적으로 앱 또는 웹 사이트)의 세 가지 주요 구성 요소가 있습니다. 데이터베이스는 지속적 데이터를 저장합니다. API는 해당 데이터에 대한 요청을 처리합니다. 클라이언트는 해당 데이터를 사용자에게 렌더링합니다.
현대적인 응용 프로그램 개발에서 클라이언트를 API와 완전히 별개의 엔티티로 생각하면 응용 프로그램의 확장에 대해 추론하기가 훨씬 쉽다는 것을 알았습니다.
애플리케이션 구축을 처음 시작할 때이 세 가지를 모두 하나의 서버에서 실행하는 것이 좋습니다. 어떤면에서 이것은 개발 환경과 비슷합니다. 한 엔지니어가 데이터베이스, API 및 클라이언트를 모두 같은 컴퓨터에서 실행합니다.
이론적으로는 아래와 같이 단일 DigitalOcean Droplet 또는 AWS EC2 인스턴스의 클라우드에 배포 할 수 있습니다.
따라서 Graminsta를 한 사람 이상이 사용할 것으로 예상되는 경우 거의 항상 데이터베이스 계층을 분리하는 것이 좋습니다.
데이터베이스를 Amazon RDS 또는 Digital Ocean의 Managed Database와 같은 관리 형 서비스로 분리하면 오랫동안 우리에게 도움이 될 것입니다. 단일 머신 또는 EC2 인스턴스에서 자체 호스팅보다 약간 비싸지 만 이러한 서비스를 사용하면 여러 지역 중복성, 읽기 전용 복제본, 자동화 된 기능을 쉽게 사용할 수 있습니다. 백업 등
Graminsta 시스템의 모습은 다음과 같습니다.
운 좋게도 처음 몇 명의 사용자는 Graminsta를 좋아합니다. 이제 트래픽이 더욱 안정적으로 시작되었으므로 이제 클라이언트를 분리해야합니다. 한 가지 주목할 점은 엔티티 를 분리하는 것이 확장 가능한 애플리케이션을 빌드하는 데있어 핵심적인 요소라는 것입니다. 시스템의 한 부분이 더 많은 트래픽을 얻으면 자체 트래픽 패턴을 기반으로 서비스 확장을 처리 할 수 있도록 분할 할 수 있습니다.
이것이 제가 클라이언트를 API와 분리 된 것으로 생각하는 이유입니다. 웹, 모바일 웹, iOS, Android, 데스크톱 앱, 타사 서비스 등 여러 플랫폼을 구축하는 것이 매우 쉽습니다. 이들은 모두 동일한 API를 사용하는 클라이언트 일뿐입니다.
같은 맥락에서 우리가 사용자로부터 얻는 가장 큰 피드백은 Graminsta가 자신의 휴대 전화를 원한다는 것입니다. 우리는 모바일 앱을 시작하려고합니다.
시스템의 모습은 다음과 같습니다.
Graminsta에서 물건을 가져오고 있습니다. 사용자가 사진을 왼쪽과 오른쪽으로 업로드하고 있습니다. 더 많은 가입을 시작하고 있습니다. 외로운 API 인스턴스가 모든 트래픽을 유지하는 데 문제가 있습니다. 더 많은 컴퓨팅 파워가 필요합니다!
로드 밸런서는 매우 강력합니다. 핵심 아이디어는 API 앞에로드 밸런서를 배치하고 트래픽을 해당 서비스의 인스턴스로 라우팅한다는 것입니다. 이를 통해 수평 적 확장이 가능합니다 (동일한 코드를 실행하는 서버를 더 추가하여 처리 할 수있는 요청 량 증가).
우리는 웹 클라이언트와 API 앞에 별도의로드 밸런서를 배치 할 것입니다. 즉, API 및 웹 클라이언트 코드를 실행하는 인스턴스가 여러 개있을 수 있습니다. 로드 밸런서는 트래픽이 가장 적은 인스턴스로 요청을 라우팅합니다.
우리가 이것에서 벗어나는 것은 중복입니다. 한 인스턴스가 다운되면 (오버로드 또는 충돌이 발생하더라도) 전체 시스템이 중단되는 대신 들어오는 요청에 응답 할 수있는 다른 인스턴스가 있습니다.
로드 밸런서는 또한 자동 확장을 활성화합니다. 모든 사용자가 온라인 상태 일 때 수퍼볼 동안 인스턴스 수를 늘리고 모든 사용자가 잠들 때 인스턴스 수를 줄 이도록로드 밸런서를 설정할 수 있습니다.
로드 밸런서를 사용하면 API 계층이 실질적으로 무한대로 확장 될 수 있으므로 더 많은 요청을받을 때 인스턴스를 계속 추가 할 수 있습니다.
참고 사항 : 지금까지 우리가 보유한 것은 Heroku 또는 AWS의 Elastic Beanstalk와 같은 PaaS 회사가 기본적으로 제공하는 것과 매우 유사합니다 (그리고 그 인기가 높은 이유). Heroku는 데이터베이스를 별도의 호스트에 배치하고 자동 확장으로로드 밸런서를 관리하며 API와 별도로 웹 클라이언트를 호스팅 할 수 있습니다. 이것은 프로젝트 또는 초기 단계의 스타트 업에 Heroku와 같은 서비스를 사용하는 좋은 이유입니다. 필요한 모든 기본 사항이 기본적으로 제공됩니다.
우리는 아마도 처음부터 이것을 했어야했지만, Graminsta에서 빠르게 움직입니다. 이 모든 이미지를 제공하고 업로드하면 서버에 너무 많은 부하가 걸리기 시작합니다.
이 시점에서 이미지, 비디오 등의 생각 (AWS의 S3 또는 Digital Ocean 's Spaces)과 같은 정적 컨텐츠를 호스팅하기 위해 클라우드 스토리지 서비스를 사용해야합니다. 일반적으로 API는 이미지 및 이미지 업로드와 같은 작업을 처리하지 않아야합니다.
우리가 클라우드 스토리지 서비스에서 얻는 또 다른 것은 CDN입니다 (AWS에서는 이것이 Cloudfront라는 애드온이지만 많은 클라우드 스토리지 서비스가 즉시 제공합니다). CDN은 전 세계의 다른 데이터 센터에 이미지를 자동으로 캐시합니다.
주요 데이터 센터가 오하이오에서 호스팅 될 수 있지만 누군가 일본에서 이미지를 요청하면 클라우드 공급자가 사본을 만들어 일본의 데이터 센터에 저장합니다. 다음으로 일본에서 이미지를 요청한 사람은 이미지를 훨씬 더 빨리받습니다. 이는 전 세계에로드 및 전송하는 데 오랜 시간이 걸리는 이미지 또는 비디오와 같은 더 큰 파일 크기를 제공해야 할 때 중요합니다.
CDN은 우리에게 많은 도움을주었습니다. Graminsta에서 많은 일들이 일어나고 있습니다. YouTube 유명 인사 인 Mavid Mobrick이 방금 가입하여 이야기를 게시했습니다. 환경에 10 개의 API 인스턴스를 추가하는로드 밸런서 덕분에 API CPU 및 메모리 사용량이 전반적으로 낮습니다. 그러나 요청에 대해 많은 시간 초과가 발생하기 시작했습니다. 왜 모든 것이 오래 걸리나요?
파고 들자 데이터베이스 CPU가 80-90 %로 떠 오릅니다. 우리는 끝났다.
데이터 계층의 스케일링은 아마도 방정식의 가장 까다로운 부분 일 것입니다. 상태 비 저장 요청을 제공하는 API 서버의 경우 더 많은 인스턴스를 추가 할 수 있지만 대부분의 데이터베이스 시스템 에서도 마찬가지입니다 . 이 경우 널리 사용되는 관계형 데이터베이스 시스템 (PostgreSQL, MySQL 등)을 살펴 보겠습니다.
데이터베이스를 최대한 활용하는 가장 쉬운 방법 중 하나는 시스템에 새로운 구성 요소 인 캐시 계층을 도입하는 것입니다. 캐시를 구현하는 가장 일반적인 방법은 Redis 또는 Memcached와 같은 메모리 내 키 값 저장소를 사용하는 것입니다. 대부분의 클라우드에는 AWS의 Elasticache 및 Google Cloud의 Memorystore와 같은 관리 서비스 버전이 있습니다.
서비스가 동일한 정보에 대해 데이터베이스를 반복적으로 많이 호출 할 때 캐시가 유용합니다. 기본적으로 데이터베이스를 한 번 누르고 캐시에 정보를 저장하며 데이터베이스를 다시 만질 필요가 없습니다.
예를 들어, Graminsta에서 누군가가 Mavid Mobrick의 프로파일 페이지로 갈 때마다 API 계층은 데이터베이스에서 Mavid Mobrick의 프로파일 정보를 요청합니다. 이것은 계속해서 또 다시 일어나고 있습니다. Mavid Mobrick의 프로필 정보는 모든 요청에 따라 변경되지 않으므로 해당 정보는 캐시에 적합한 후보입니다.
Redis의 데이터베이스 결과를
user:id
만료 시간이 30 초인 키 아래에 캐시합니다 .
이제 누군가 Mavid Mobrick의 프로필을 방문하면 Redis를 먼저 확인하고 Redis에서 데이터를 제공합니다.
Mavid Mobrick이 사이트에서 가장 인기가 있지만 프로파일을 요청하면 데이터베이스에 거의 부하가 걸리지 않습니다.
대부분의 캐시 서비스의 또 다른 장점은 데이터베이스보다 쉽게 확장 할 수 있다는 것입니다. Redis에는로드 밸런서 1 과 유사한 방식으로 Redis 클러스터 모드가 내장되어있어 Redis 캐시를 여러 머신에 분산시킬 수 있습니다.
거의 모든 고도로 확장 된 응용 프로그램은 캐싱을 충분히 활용하므로 빠른 API를 만드는 데 절대적으로 필요한 부분입니다. 더 나은 쿼리와 성능이 좋은 코드는 모두 방정식의 일부이지만 캐시가 없으면 수백만 명의 사용자로 확장하기에 충분하지 않습니다.
우리 데이터베이스가 꽤 많은 타격을 받기 시작한 지금 우리가 할 수있는 또 다른 일은 데이터베이스 관리 시스템을 사용하여 읽기 복제본을 추가하는 것입니다. 위의 관리 서비스를 사용하면 한 번의 클릭으로 수행 할 수 있습니다. 읽기 전용 복제본은 마스터 DB를 최신 상태로 유지하며 SELECT 문에 사용될 수 있습니다.
우리 시스템은 다음과 같습니다.
앱이 계속 확장됨에 따라 독립적으로 확장 할 수있는 서비스를 분할하는 데 집중하려고합니다. 예를 들어, 웹 소켓을 사용하기 시작하면 웹 소켓 처리 코드를 꺼내는 것이 좋습니다. 우리는 HTTP로드 수에 관계없이 개방 또는 폐쇄 된 웹 소켓 연결 수에 따라 확장 및 축소 할 수있는 자체로드 밸런서 뒤에 새로운 안정성을 제공 할 수 있습니다.
또한 데이터 계층의 한계에 부딪 치려고 계속 노력할 것입니다. 데이터베이스 파티션과 샤딩을 살펴보고 싶을 때입니다. 이 두 가지 모두 더 많은 오버 헤드가 필요하지만 효과적으로 데이터 계층을 무한대로 확장 할 수 있습니다.
New Relic 또는 Datadog과 같은 서비스를 사용하여 모니터링을 설치했는지 확인하려고합니다. 이를 통해 요청이 느리고 개선이 필요한 부분을 이해할 수 있습니다. 규모를 확장 할 때 종종 이전 섹션의 일부 아이디어를 활용하여 병목 현상을 찾아 수정하는 데 집중하려고합니다.
이 시점에서 우리는 팀에 도움을 줄 다른 사람들도 있습니다!
이 게시물은 High Scalability에 대해 제가 가장 좋아하는 게시물 중 하나에서 영감을 받았습니다 . 나는 초기 단계에서 기사를 조금 더 살려 내서 조금 더 구름에 얽매이지 않게 만들고 싶었다. 이런 것들에 관심이 있다면 꼭 확인하십시오.