rein's world

NDC 참관기: Server Burner: 범용 네트워크 스트레스 테스트 툴

NDC 2011 2일 차(5/30)에 있었던 하재승 군의 세션.

이하는 내용 요약

게임 오픈 혹은 대규모 패치 후에 서버가 폭주(?)하는 사례가 많다.

네트워크 스트레스 테스트 툴이 필요하다?

  • 예상되는 스트레스 상황 테스트 / 반복 실행
  • 미리 문제를 찾고 해결하자

Pros.

  • 쉽게 테스트 작성
  • 대량의 테스트 진행
  • 테스트 진행 중 데이터 자동 수집 / 모니터링

미리 작성된 테스트 시연

간단한 채팅 서버를 가지고,

  • 1 명 들어와서 채널에 메시지 말하고 나가기
  • 100 명 들어와서 같은 동작
  • 테스트 동안 모니터링한 결과 보여주기

잘 알려진 올해의 게임 Minecraft를 가지고,

  • 여러 마리의 봇이 로그인해서,
  • 봇들은 (미리 지정한) 유저를 따라다니고, 유저가 삽질(마우스 왼쪽 클릭)하는걸 따라함
  • 100 명 수준이 되어가면 서버가 심하게 랙이 걸리는 걸 볼 수 있음 ((테스트할 때 쓴 서버는 대략 Win7 x64, RAM 4GiB, Q8600 정도))
  • 더불어, 이 동안의 시스템 모니터링한 그래프를 보여주기

테스트 자동으로 작성하기

  • 클라이언트 -> 복수의 서버 군(로그인, 채널, 게임, 챗, 몬스터, …)의 통신을 캡쳐해서 _자동_으로 테스트를 작성
  • 상대적으로 쉽고 간편하게 + 버그 재현에 써먹기
  • 바로 캡쳐해서 쓸 수는 없다 -> 툴을 이용해서 쉽게
  • 미리 만든 스트레스 테스트 용 아이디 (특정 prefix + 숫자)가 필요함

툴을 이용해서 테스트 스크립트 수정

한 눈에 패킷 보기, 패킷에 들어가는 값을 특정 전역 변수랑 연동/편집, 스크립트 내보내기… 를 처리하는 GUI 편집툴 제공

Client.Send_LoginRequest(A, "test"..index(), "testpass")
local pkt = Recv(A)
if pkt.__id__ ~= Server.LoginResult then fail() end

이런 형식의 스크립트가 최종적으로 나옴.

결과 분석

  • 가상 유저 별로 받은 패킷 순서, 전달된 패킷의 개별 값, 특정 메시지 간 시간, 특정 행동(로그인)에 걸린 시간 값 등등을 측정
  • 서버 자원 모니터링

요약

프로토콜 파일 작성

  • 캡쳐/스트레스 테스트에 이용
  • 클라이언트 — 서버 간 패킷 내용 정의 ;(Python, lua, C#, C++ 코드 생성)
  • 패킷 정의가 없으면 해당 프로젝트에 쓰는 방식을 분석하고 정의 파일 만들기
  • 구조체 기반인 경우 아예 C++ 헤더를 파싱해서 만들기도 함
  • 헤더/푸터,암호화,체크섬 등 처리 가능

UDP 통신 / HTTP 통신 / 네트워킹 모듈 기능 테스트

계획

  • 복잡한 테스트 수행 (장시간 반복하기; 현재는 쉘 스크립트로 대체)
  • 테스트 결과 분석 강화
  • 네트워크 상태 시뮬레이션 (패킷 로스, 지연 상황)

촌평.

온라인 게임으로 한정할 경우, 많은 네트워크 테스트 도구가 갖는 문제가 뭐냐하면,

  • 부적당한 추상화 수준 : TCP/IP 패킷 레벨, HTTP 메시지, …
  • 부하 테스트 시나리오를 생성할 방법
  • 부하 테스트 시나리오를 유지 관리할 방법
  • 충분한 수준의 부하를 생성할 방법
  • 복수의 서버군이 있을 때 이에 대한 추상화는 어떻게 할지

등등?

이런 문제에 대한 접근법으로, 적당한 도메인 분리 + 개별 공략은 타당한 선택지라고 판단된다. 대략 서버 버너 구조 자체가,

  • 프로토콜 데이터의 별도 표현(yaml)
  • 메시지 캡쳐 / 편집 프로그램
  • (수동) 추가적인 스크립트 편집
  • 복수의 머신에서 동시에 부하 스크립트 동시 시작/종료/통계 처리

로 쪼개져 있다. 그리고 이 중 수동으로 스크립트를 추가 수정하는 부분만 빼면 많은 경우 그다지 복잡할 게 없다.

우선 추상화 수준의 문제.

사실 게임…이라고 해도 장르 자체가 다르면 기술적인 의미에서 추상화 수준이 서로 다르다. 그래서 ‘메시지’라는 좀 저수준이지만 공통(?)일 수 있는 수준을 잡고, 여기에 대해 UDP/TCP/혹은 응용프로그램 수준 메시지로 잘 쪼개서 처리하는 접근은 적당하다고 생각한다. 대략 게임이라면 TCP, TCP 기반의 메시지 (HTTP라거나? 전용 포맷?), UDP (전용 p2p 프로토콜 류) 정도를 생각할 수 있는데, 이거에 대해서 프로토콜 데이터만 기술해주면 대략 이 수준의 추상화는 메서드 호출처럼 표현할 수 있다. ((다만 UDP는 별도로 .dll을 짜서 python/lua 바인딩을 제공해줘야 한다… )) 메시지를 특정 형태로 기술하는 일은, 팀 내에서야 이게 공통 포맷이라(…) 시간이 0이 걸리고, 사내 다른 팀 껄로도 거의 전환 시간 없이 가능했던걸 생각하면 어찌어찌 가능은 할 듯 하다. 대략 엔지니어 하나 붙여놓고 하루 안에 적용이 가능했으니… ((강연 때도 나온 얘기지만 아예 xml 기반의 프로토콜이나 압축, 별도 체크섬 같은거 붙이는건 큰 일이 아님…))

예를 들어 인증 메시지 보내는 것은: SendAuthMessage(utf-8-string-id, bytes-credential, bytes-session-key) 수준 정도로?

다음으로 메시지 캡쳐, 편집.

일단, 부하테스트 시나리오 생성을

  • 패킷을 아예 캡쳐하고, 이걸 적당히 조작(시연 동영상을 보면 좀더 이해가..) 하거나,
  • 아예 중간에 이걸 릴레이하는 서버 (개발팀이라면 언제라도 가능한) 를 두고 모든 메시지를 가로채거나
  • 서버가 여러 대인 경우에도 이걸 적당한 스트림으로 보여주고, 일부를 병합하거나

하는 식으로 실제 메시지를 가지고 만들어내고, 이걸 lua 스크립트 수준에서 처리하면 된다는 점이…

복수 머신에서 부하 자체를 생성하는 일

머신 한 대에서 생성하는 부하에는 한계가 있다. 왠만큼 시간을 들여 짜고 + 튜닝하지 않고서야, 머신 당 유저 수는 크게 제한된다. 차라리 가짜 유저 간 상호 작용을 줄이고 머신 붙인 만큼 유저를 더 만들어 내는게 대략 맞는 방향인 거 같다. 부하자체는 테스트 스크립트를 시작하는 프로세스와, 이 테스트 스크립트를 받아 실제로 메시지를 보내는 k개의 프로세스를 써서 보내기 때문에 (물론 머신도 k개에 준하게…) 물리 머신 수만 충분하다면 원하는 수준의 부하를 만들 수 있다. 다만 밑단의 lua 스크립트는 짜야겠지만(…).

Q/A 팀에서 일일이 부하테스트를 짜는건 약간 힘들지 몰라도, 서버 프로세스를 짜는 프로그래머라면 큰 무리 없이 쓸 수 있는 수준의 도구라고 생각한다.