[Snack] 디스코드의 Cloud 컴퓨팅 ( Super-disk )
서문
최근에 디스코드 DB 를 교체하는 이유와, 과정에 대한 글을 쓴적이 있는데,
이 과정에서 사용된 방식을 Super-Disk라고 부르더라 ㅋㅋㅋ
다시한번 말하지만, 디스코드 같이 전세계를 대상으로 서비스하는 회사는
엄청난 양의 데이터를 관리하고 있다.
또, 요즘에는 실제로 컴퓨터를 보유하고 직접관리하는 방식이 아닌,
전문적으로 컴퓨터 관리만하는 회사에게, PC를 대여하는 클라우드 컴퓨팅을 사용하는데,
이와 마찬가지로, 디스코드도 구글 서버를 빌려서 사용하는 중이다 (GCP)
이번 글에서는, 디스코드가 클라우드 컴퓨팅을 서비스를 이용하면서 겪은 어려움과,
이를 해결하기 위해 사용한 방식을 알아보면서,
실제 서비스에서 중요하게 여겨지는 기준과,
디스코드가 사용한 기술들을 살펴보자.
문제 배경
디스코드는 하루에 몇백만명이,
40억개의 메세지를 보낸다고 한다.
디스코드는 실시간 채팅 앱이므로,
이 메세지에 대한, DB응답시간을 최대한 줄여야만 했다.
그렇기 때문에, 받은 요청을 하나씩 처리하는 방식은 적절하지 않고 (동기)
받은 요청을 동시에 처리해야만 한다 (비동기)
동시 처리 ( pallarelism )와 문제점
하지만 동시처리 과정에도 한계가 있다.
순서가 중요한 요청이 그 중 하나이다.
이전 작업이 반드시 끝난 이후에,
다음 작업을 처리해야 하는 경우,
이에 따라 DB는 대기 작업을 해야만하고,
이런 과정이 쌓이면, 결국 DB가 아무런 작업도 하지 못하는 상태가 되어,
새로운 요청을 받지 못하는 일이 발생할 수 있다.
이런 일이 발생하면, 새로운 요청을 따로 "백업" 해두어야만 하고
실제 사용자가, 느린 응답을 겪게 만든다.
이 과정이 반복되면,
너무 많은 시간이 지체된다면, 요청이 타임아웃 될 수 있는데,
이는 데이터가 없어지는 것이므로, 아주 치명적이다.
그리고 이 모든 문제들이, 디스코드에서 일어났다.
디스코드는 이런 "응답지연" 을 해소해야 했으므로,
요청을 효율적으로 처리할 방법을 찾기 시작했다.
그리고 응답지연에 가장 많은 영향을 미치는 것은,
실제로 사용하는 기기, 즉 하드웨어이다.
하드웨어 특징
이전 게시글에서도 설명했다시피,
메모리 (on-memory disk)는 데이터 입력, 출력이 빠르고,
하드디스크는 느리다.
실제로 디스코드는,
구글 클라우드 서비스인 GCP에서 메모리와, 하드디스크를 둘다 빌려쓰고 있다.
메모리 - Local SSD
하드디스크 - Persistent Disk
실시간으로 운용되는 서버이므로,
단순히 빠른 메모리만 사용하면 되는가 하는 생각이 들 수 도 있지만,
한번 데이터가 없어지면, 복구 할 수 없으므로
메모리는 하나의 메모리가 망가지기만해도, 모든 데이터가 없어지는 단점때문에
중요한데이터를 저장하기에는 적절하지 않다.
하드디스크도 단점만 있는 것이 아니다.
1. 실제 운영중에, 원하는 만큼 붙였다, 떼었다 할 수 있다. (attach/detach)
2. 위의 과정을 시간 지체 없이 진행 할 수 있다.
3. 원할때마다, 특정 시점이 데이터를 백업(snapsho) 할수 있고, 마음대로 복사할 수 있다.
결국, 서비스를 안정적으로 운영하려면 두가지 디스크가 다 필요하다는 것..
디스크 선택
그러면, 디스코드는 어떤 선택을 내렸을까?
실제 서비스에서는, 쓰기 보단 읽기가 더 중요하다.
쓰기 응답 지연은 사용자가 잘 인지 할 수 없는 반면,
읽기 응답 지연은 사용자가 즉각 알 수 있기 떄문이다.
( 읽기 요청이 대다수인것도 중요한 포인트 )
데이터 읽기/쓰기 관점에서 보자면,
읽기 => 메모리
쓰기 => 디스크
정도로 추려볼 수 있다.
디스크 용량 확장은, 예측해서 미리 준비할 수 있다.
디스코드는, 실제로 하드디스크에 저장되는양을 충분히 정확하게 예측 할 수 있다고 보았다.
이 측면에서 생각해보면,
하드디스크의 장점중, 원하는 만큼 붙였다 떼었다 할 수 있는 유연성은 별로 필요가 없다.
하지만 그외에, 데이터를 안전하기 위한 스냅샷(복구), 복사된 디스크는 필수적이다.
읽기 => 메모리
쓰기, 스냅샷, 중요데이터 복사 => 디스크
디스코드는 이러한 과정을 통해,
Local SSD를 캐싱 (read 데이터 임시저장)
Persistent Disk를 저장소 용도로 사용하기로 결정했다.
추가 기능?
저장한 데이터가 아무런 실패없이,
완벽하게 동작하면 좋겠지만 여기에도 문제가 있다.
바로 bad sector 를 읽지 않아야 한다는 것.
bad sector는 읽는 순간 전체 읽기 작업이 실패하기 때문에,
이를 관리하는 별도의 작업이 필요하다.
그래서 디스코드는, 이 문제를 해결해주는 md 라이브러리를 사용하기로 결정했다.
( md를 사용하기 위해서는 Linux를 사용해야함 )
md란
여러개의 disk들을 array로 묶어,
하나의 가상 디스크로 관리하게 해주는 리눅스 라이브러리
(RAID 라고 부른다)
md 라이브러리는, disk들을 관리하는 방식에 따라 이름이 지어졌다.
RAID0 => 모든 디스크 다 묶기 ( 하나 망가지면 모든 데이터 손실 )
RAID1 => 데이터를 중복해서 저장하기
RAID5, RAID6 => 한개의 디스크를 예비용으로 사용
여기서 디스코드가 선택한 방식은 RAID1이다.
왜냐?
md의(다른데는 없음) RAID1이 "write-mostly" 설정을 지원하기 때문이다.
이 기능은, 말그대로 "대부분 쓰기만하는" 기능이다
위에서 봤듯이, 쓰기 기능은 읽기기능보다 응답 속도가 중요하지않다.
따라서, 하드디스크에 저장하는 것이 적합하다.
write-mostly 기능을 하드디스크에 설정해,
쓰는 작업을 하드디스크에 몰아줄 수 있는 것이다!
쓰기는 됐으니, 읽기 차례다.
읽기는 메모리에서 진행해야하니,
구글의 Local SSD를 이용하기로 결정!
하지만 이 디스크는 구글의 정책, 설정을 따른다.
이는 다음과 같다.
1. 최대 용량 256기가
2. 하나의 디스크가 실패 => 모든 데이터 다른 디스크로 이동 => 이전 디스크 삭제
이는 구글이 정하는 것이므로,
개개인이 바꿀 수 없고, 디스코드 또한 마찬가지다.
1 테라 이상의 실시간 데이터를 관리해야하므로,
최소 4개이상의 Local SSD를 사용해야한다.
일단 위에서 RAID1으로 쓰기와, 읽기 요청을 나누어주는 작업을 하게되는데,
읽기 전용인 4개의 디스크와 직접 연결하면, 내부적으로 더 많은 작업을 해야한다고 한다.
( 상세한 내용은 RAID관련 자료 참조 )
그래서 이 4개의 Local SSD를 RAID0으로 묶어, 하나로 관리하는 방식을 선택했다.
( 하나가 망가지면 다른 데이터 전부삭제 된다고 했는데, 이는 HD에서 다시 가져올 수 있으므로)
드디어 Super-disk
discord는 이렇게해서 만들어진 구조를, Super-disk 라고 부른다.
이 구조를 사용하고 나서
요청 응답시간이 줄어들고,
DB가 처리하지 못하는 요청도 줄어들고,
따라서 데이터 소실 문제도 없어졌다!
그리고 RAID를 사용해
다른 디스크를 Array에 추가만하면 간단하게 크기를 늘릴 수 있으므로,
지금의 안정적인 서비스를 운영할 수 있게 된것이다.
마무리
지금까지 디스코드가 자신의 서비스의 요구에 맞춰,
적절한 구조를 만들었던 과정을 살펴 보았다.
이는 모두 디스코드에서 실제로 사용하고 있는 기술이기 때문에,
실제 서비스를 만들고 싶어하는 사람들에게 유용할 것 이다.