사용자가 1000만이 넘는다면? - 인프라 상상하기

2023년 8월 30일 작성

대규모 트래픽
! 이 글은 현재 집사의 고민에 적용되어있는 내용을 설명하는 문서가 아닙니다 😊
! 또한 이 글에서는 '인프라'를 백엔드 인프라로 한정합니다.

가상 개선 목적

집사의 고민이 100만, 1000만 그 이상의 유저를 가지는 서비스가 되어도 안정적으로 트래픽을 처리하려면 어떻게 해야할까요? 그리고 만약 장애가 발생한다면, 가장 빠르게 해결할 수 있는 방법은 무엇일까요?

그렇게 많은 유저가 서비스를 이용하는 것은 아주 먼 미래의 일 또는 일어나지 않을 일이라 생각할 수 있습니다. 하지만 현재 서비스가 운영 되고 있는 인프라 환경을 분석하고 장애 발생 가능성을 파악하고 개선 방안을 미리 고안하는 것은 지속적인, 성장하는 서비스 운영에 반드시 선행돼야하는 일입니다.

따라서 이번 글에서 저는 '가상'으로 집사의고민 인프라를 개선해보려고 합니다.

순서

  • 집사의 고민이 현재 어떤 기술들로 어떻게 운영되고 있는지, 그리고 중요한 피처들에 사용되는 구조를 설명합니다.

  • 현재 구조에서 문제가 될 수 있는 부분을 설명합니다.

    • 그리고 앞서 발견한 요소들을 어떻게 해결할 수 있을지 대안을 설명합니다.

현재 집고 인프라 설명

image

백엔드 인프라 중에서도, 서비스를 제공하는데 사용되는 리소스만을 간단히 그려보았습니다. 그림과 같이 AWS 에서 동일 VPC에서 웹 애플리케이션 서버와 데이터베이스를 운영하고 있습니다. 퍼블릭 서브넷의 인스턴스 1대에서 nginx를 리버스 프록시로 사용(도메인네임으로 포트포워딩)하고, 프라이빗 서브넷의 인스턴스 1대에서 데이터베이스를 설치해 운영합니다.

현 구조에서 개선할만한 점

기능적인 부분

사진 - 집사의 고민 서비스 스크린샷.

image image

집사의고민은 사료 정보 제공 서비스로, 서비스의 주요 기능들이 데이터를 조회하는데 집중되어있습니다. 특히 메인 화면의 기능인 사료 필터링 기능은 고도화될 수록 데이터베이스의 연산량을 증가시킬 수 있는데요. 이런 상황에서 많은 유저가 조회 요청을 하게 되면 수많은 읽기요청으로 데이터베이스 성능의 저하를 초래하게 됩니다.

이 문제는 어떻게 해결할 수 있을까요?

조회 성능 개선

애플리케이션의 조회 성능을 개선하기 위해 집사의고민에서 도입을 계획한 방법은 다음 두 가지입니다.

  1. 반복되는 쿼리 결과를 캐싱 - 메모리 DB 사용
  2. 읽기 전용 replica를 구성하기

캐싱 사용하기

집사의 고민은 사료 정보를 제공하는 서비스로써 사료 필터링이 매우 자주 일어나는데요, 이후에도 사료 탐색에 검색과 같은 기능들이 추가될 가능성이 높습니다. 반면, 사료의 데이터는 집고 팀원(관리자)가 주기적으로 추가하기에 변화가 적은 데이터들입니다.

따라서, 자주 반복되는 사료 조회 쿼리 결과를 캐싱한다면 데이터베이스의 연산량을 크게 줄일 수 있습니다. 캐싱은 여러가지 방식이 있지만 곧 설명할 서버 다중화를 위해 리모트 캐싱을 사용하는 방향으로 개선해보겠습니다.

캐싱은 Redis를 이용해볼 예정입니다. Redis는 EC2에서 직접 운용(Self-hosted)할 수도, Elasicache라는 관리형 서비스를 택할 수도 있는데, 이 둘 중 선택하는 일은 이번 과정에서 생략하도록 하겠습니다.

참고: Self-hosted Redis on EC2 vs Elasticache

MySQL 읽기 전용 복제본 사용하기

추가적으로 DB 부하를 줄여볼 수 있는 방법은 바로 Replication(복제) 입니다. 읽기 전용 복제본을 만들어, 쓰기 요청과 읽기 요청을 분리하면 성능을 향상할 수 있습니다.

반영 결과

image

반영 결과, 스프링에서는 조회 연산 시 먼저 캐시를 조회하고 히트 시 바로 결과를 반환, 미스 시 reader 데이터베이스를 조회합니다. 그리고 쓰기 연산은 writer 데이터베이스에 직접 합니다.

단일 장애 지점

현재 인프라에서는 웹 애플리케이션 서버를 실행 중인 인스턴스가 1대 뿐입니다. 따라서 인스턴스에 문제가 생기면 서비스는 완전히 이용이 불가능한 상태가 됩니다. 이런 장애 지점을 단일 장애 지점 (SPOF)이라고 하죠. 원래 구조에서는 단일 장애 지점이 두 군데였습니다. 다시 한 번 볼까요?

image

웹 애플리케이션 서버

public subnet에 있는 웹 애플리케이션은 nginx를 리버스 프록시로 두고 있습니다. 이 때 인스턴스는 하나이죠. 인스턴스에 장애가 생기면 모든 시스템이 장애 상황이 됩니다.

image

이 문제는, 다음과 같이 인스턴스를 다중화하는 방법을 사용해 해결합니다. 따라서 하나의 인스턴스가 죽더라도, 서비스 전체의 장애로 이어지지 않습니다. 현재 전부 죽지 않으리라는 보장은 없지만, 단일 장애 지점은 없앨 수 있는 것입니다.

image

하지만 원래의 단일 인스턴스 구성을 단순히 다중화하게 되면, 부하를 분산 시켜줄 서버의 역할이 필요해집니다. 따라서 다음과 같이 엔진엑스를 인스턴스 내부가 아닌, 외부에 따로 운영을 하도록 하여, 다중 인스턴스로 부하를 분산할 수 있도록 구성합니다. 이렇게 해서 Nginx를 리버스 프록시 역할 뿐 아니라 로드 밸런싱도 하도록 해요.

image

하지만 이 그림에서 Nginx에서는 여러 EC2 인스턴스에게 부하를 분산하기 위해 EC2의 IP를 알아야합니다. 그런데, EC2 인스턴스가 새로운 배포 혹은 장애 해결로 인해 새로운 인스턴스로 바뀌면 어떻게 될까요? Nginx는 새로운 인스턴스의 IP를 모르는 상태이고, 해당 인스턴스로 요청을 전달할 수 없을 것입니다.

image

이 문제를 해결하기 위해, 부하분산기를 AWS의 완전 관리형 서비스인 ELB로 변경합니다. ELB를 사용할 경우, 대상 그룹(Target Group)을 설정할 수 있고 인스턴스 하나하나에 대한 정보를 알지 못해도 부하분산이 가능합니다. ELB는 본인이 부하를 나눠줄 대상 그룹에만 연결되면 되기 때문입니다.

추가적으로, 1000만 그 이상의 트래픽을 유연하게 감당하기 위해 자동으로 인스턴스를 스케일 인/아웃을 해주는 오토스케일링그룹(Auto-Scaling Group)으로 변경해줄 수 있습니다.

image image

데이터베이스

위에서 기능과 관련해 개선했을 때 DB 레플리케이션을 적용했던 것은 많은 읽기요청에도 DB 성능을 저하시키지 않기 위해 도입했었는데요.

사실 레플리케이션은 데이터베이스가 SPOF 라는 문제도 해결해 줍니다. 프라이빗 서브넷에서 운영 중인 데이터베이스 인스턴스도 웹애플리케이션과 마찬가지로 인스턴스 1대 밖에 없었습니다. 따라서 이 부분도 서비스의 단일 장애 지점이라고 할 수 있었어요. 그림과 같이 빨간색 EC2가 죽으면 모든 시스템이 작동 불가 했습니다.

image

웹 애플리케이션의 다중화와 DB 레플리케이션을 함께 적용한다면, 다음과 같은 형태로, MySQL 인스턴스 하나가 죽더라도 전체 시스템의 장애가 되지는 않습니다.

image

하지만, 레플리케이션의 경우 장애 지점이 Writer/Reader 어느쪽인지에 따라 해결방식이 달라집니다. 우선 MySQL 자체적으로 지원하는 기능은 다음과 같이 동작합니다.

  • Reader DB가 실패하는 경우: 괜찮습니다.

  • Writer DB가 실패하는 경우: 이 경우는 쓰기 연산이 이뤄지는 원본 DB에 장애가 생기는 것이기 때문에 신속한 후처리가 필요합니다. MySQL에서는 ReaderDB를 WriterDB로 승격시킵니다.

    Master 승격 전, 다른 Slave 간의 동기화, 정합성 문제를 MHA를 통해 비교적 쉽고 빠르게 장애를 처리합니다. MySQL의 MHA가 slave(reader)를 master(writer)로 승격하기 전, slave가 수신하지 못한 바이너리 로그 이벤트를 적용합니다.

데이터센터 재해 대비

우리는 AWS 를 사용중이고, AWS에서는 HA를 구현하기 위해서는 다중 가용영역을 구현해야합니다. 이 말인 즉슨, 여러 Availability Zone에 리소스를 구성해야한다는 뜻입니다.

AZ는 데이터센터 단위로, 재해발생을 고려해 여러 곳을 사용할 수 있습니다.

AZ에 대한 설명은 이 글에선 생략하고, 보너스로 적용을 해보는 정도로 끝내겠습니다. 지금 우리의 아키텍처에 multi AZ를 적용한다면 이런 그림이 될 것입니다.

image

지금까지 고가용성, 결함내성 등을 높이기 위해 인프라 아키텍처의 문제를 정의하고 가상으로 해결해보았습니다. 실제로 이런 일을 진행한다면 글에서와 같이 그림처럼 뚝딱 만들 수 있는 것이 아니라, 실제 트래픽 요구사항을 바탕으로 비용까지 고려해서 방법을 결정해야합니다. 또한 클라우드를 사용하므로 SLA과 기술 현황 파악은 필수이니 참고해주세요.