본문 바로가기

블로그 / 기술

$5짜리 서버 한 대에 몇 명이 들어갈까: AvoidNow 부하 테스트 결과

월 5달러짜리 서울 서버 한 대로 AvoidNow 멀티플레이를 얼마나 받을 수 있는지 부하 테스트로 쟀다. 1명당 CPU 0.4%, 그리고 버스트 CPU 때문에 오래 버티는 인원은 생각보다 적다는 결론까지.

Salmonholic ·· 5분 읽기

노을빛 모래 아레나에서 원시인이 거대 공룡 발자국 사이로 공룡 알을 줍는 AvoidNow 플레이 화면

AvoidNow는 모든 모드를 서버가 판정한다. 혼자 하는 판도 서버에 방을 하나 만들어서 돈다. 장판이 언제 어디 떨어지는지, 누가 맞았는지, 부활과 밀치기까지 서버가 정한다. 그래야 랭킹에 조작된 기록이 올라오지 않는다. 대신 사람이 늘면 서버가 그만큼 일을 한다.

한국 이용자가 붙는 서버는 서울에 둔 월 5달러짜리 가장 작은 클라우드 서버 한 대다. 해외 이용자는 Cloudflare Durable Objects로 가니 방 하나당 따로 뜨지만, 서울 쪽은 이 한 대가 전부다. 한국에서 해외 서버로 붙으면 응답이 약 324ms였고 서울 서버로 약 14ms가 됐기 때문에 이 한 대는 꼭 있어야 한다. 그러면 이 한 대가 버티는 사람 수는 몇 명일까. 추측하지 않고 재 보기로 했다. 9월 27일 새벽의 일이다.

무엇으로 쟀나

부하 발생기를 따로 만들었다(커밋 8343f67). 봇 N명이 실제 게임과 같은 바이너리 프로토콜로 접속해서, 초당 30번 이동 입력을 보낸다. 입력은 무작위로 움직이되 곧 떨어질 장판에서는 대충 멀어지게 했다. 판이 끝나면 봇이 다시 방을 만들어 들어가서 부하를 계속 유지한다. 봇의 절반은 혼자 하는 싱글 방, 절반은 네 명이 들어가는 협동 방으로 나눴다. 인원은 단계별로 늘린다. 예를 들어 10명, 25명, 50명, 100명을 40초씩.

단계마다 네 가지를 기록한다.

  • 스냅샷 간격: 서버는 초당 15번 게임 상태를 보낸다. 정상이면 간격이 66.7ms다. p50과 p99를 본다.
  • 서버 시간 비율: 서버 시각이 실제 시간만큼 흐르는지. 서버의 30Hz 틱이 밀리면 1 아래로 떨어진다.
  • 입력 확인 지연: 입력을 보낸 뒤 서버가 “적용했다”고 알려 오기까지의 시간.
  • 측정기 자신의 지연: 부하 발생기 쪽 타이머가 밀리면 서버가 아니라 측정기가 병목이라는 뜻이다.

방 하나의 계산 비용만 따로 재는 벤치마크도 만들었다. 네트워크 없이 방의 틱 함수만 돌리고, 플레이어가 죽지 않게 매 틱 하트를 채워서 가장 오래 가는 판, 즉 가장 어려운 후반까지 비용을 잰다.

하나 걸리는 게 있었다. 한 IP에서 수백 개의 연결을 열면 서버의 남용 방지 한도(IP당 연결 수, 분당 입장 수)에 바로 걸린다. 그래서 이 한도를 환경 변수로 덮어쓸 수 있게 하고, 운영 서버가 아닌 테스트용 임시 컨테이너에만 큰 값을 줘서 띄웠다. 운영 설정에는 이 변수를 넣지 않는다. 기본값은 그대로다.

결과: 1명당 CPU 0.4%

결과는 커밋 4eaedb8 메시지에 남겼다. TLS 처리를 맡은 앞단 프록시까지 포함해서 플레이어 1명당 vCPU 약 0.4%. 150명을 넣었을 때 CPU가 약 60%였다. 1명당 0.4%로 환산하면 이렇게 된다.

플레이어 10명 4%, 25명 10%, 50명 20%, 100명 40%, 150명 60%의 CPU 사용량 막대그래프 1명당 0.4%로 환산한 CPU 사용량. 150명·60%가 실측이고 나머지는 같은 비율로 계산한 값이다

숫자만 보면 넉넉하다. 그런데 테스트를 하면서 설정의 문제 두 개가 같이 드러났다.

첫째, 방 개수 한도가 너무 작았다. 원래 한도가 방 64개였는데, 싱글 플레이어가 50명쯤 되자 새 입장이 막혔다. 싱글은 한 명이 방 하나를 쓰고, 끝난 방은 결과를 보여 주느라 30초 동안 남아 있다. 사람 수보다 방 수가 빨리 찬다.

둘째, 반대로 연결 수 한도는 너무 컸다. 400으로 잡혀 있었는데, 이 VM이 감당할 수 있는 양을 넘는 값이었다. 400명이 실제로 들어오면 모두가 느려진다.

그래서 연결 수와 방 수 한도를 측정 결과에 맞춰 낮췄다. 방 수를 인원보다 조금 넉넉하게 둔 건 끝난 방이 30초 남는 것 때문이다.

설정 전 후
최대 연결 수 여유 있게 측정치에 맞춰 낮춤
최대 방 수 적게 연결 수보다 조금 넉넉하게
가득 찼을 때 입장 실패 해외 서버로 한 번 넘어감
컨테이너 메모리 한도 384MB 384MB(그대로)

가득 차면 해외 서버로

한도를 정하면 한도에 닿았을 때 어떻게 할지도 정해야 한다. 서울 서버가 가득 차면 입장 요청에 503을 돌려준다. 이때 게임은 에러를 띄우는 대신 해외 서버로 한 번 넘어가서 다시 입장한다. 응답은 느리지만 게임은 할 수 있다. 다음 판에는 다시 두 서버에 핑을 재서 고른다. 그사이 서울이 비었을 수 있으니까.

단, 방 코드로 들어가는 경우는 넘기지 않는다. 친구가 만든 방은 그 서버에만 있다. 다른 서버로 넘어가 봐야 그 방은 없다.

함정: 버스트 CPU, 계속 버틸 수 있는 건 약 25명

여기서 끝났으면 “150명까지 된다”고 적었을 것이다. 그런데 이 요금제는 버스트 CPU다. 평소에는 기준치만큼만 CPU를 쓰고, 그 이상은 쌓아 둔 크레딧을 꺼내 쓴다. 크레딧이 떨어지면 기준치로 묶인다. 150명·60%는 크레딧이 남아 있을 때의 이야기다.

그래서 운영 문서에는 숫자를 둘로 나눠 적었다. 순간 한도와 별개로, 크레딧을 쓰지 않고 계속 버틸 수 있는 건 약 25명이다. 1명당 0.4%로 계산하면 25명이 CPU 10% 정도다. 그 이상은 크레딧을 쓰는 구간이고, 짧은 몰림은 버티지만 오래 가면 느려진다.

결정 문서에는 이렇게 남겼다. 공개 전에는 지금 요금제를 유지한다. 공개 후 동시 접속이 25명을 자주 넘으면 한 단계 위 요금제로 올린다. 버스트 크레딧 알림은 그 정도로 사람이 모이면 그때 설정한다. 아직 오지 않은 인기를 위해 미리 돈을 쓰지 않겠다는 뜻이다. 대신 크레딧 잔량을 확인하는 명령은 문서에 적어 두었다.

다음에 다시 잰다면

이번 측정은 한 IP에서 봇으로 만든 부하라, 실제 이용자와 다른 점이 있다. 휴대폰의 불안정한 망, 접속과 이탈의 들쭉날쭉함, 협동과 경쟁의 실제 비율은 반영되지 않았다. 그래도 “1명당 0.4%“라는 기준이 생긴 덕분에, 앞으로 서버를 올릴지 말지는 감이 아니라 동시 접속 수로 정할 수 있게 됐다.

같은 서버 구성을 먼저 쓴 게임과 한 달 동안의 작업 흐름은 한 달에 게임 네 개 회고에 있다. 이 서버 위에서 도는 게임은 AvoidNow 게임 페이지에서 바로 해 볼 수 있다. 서울 서버에 25명이 넘게 모이는 날이 오면, 이 글의 후속편을 쓰겠다.

#AvoidNow#게임 서버#부하 테스트#Node.js#인프라

함께 읽으면 좋은 글

전체 글 →