어떤 로그?
어디에서 로그를 수집할까
- 게임 클라이언트 로그
- 클라이언트의 데이터는 믿지 않는 것이 원칙
- 디버깅용 (크래시 위치 등)
- 재현 스텝 파악용
- 클라이언트의 데이터는 믿지 않는 것이 원칙
- 게임 서버 로그
- 추적되어야 하는 모든 이벤트를 기록
- 재화의 증감
- 액션 히스토리
- 이벤트 처리 결과
- 추적되어야 하는 모든 이벤트를 기록
어떤 형태의 로그를 수집할까
- 로그 형태를 직접 정의하여 사용하는것이 일반적
- 공통 헤더를 정의 후, 사용하는 것이 일반적이다.
- 일반적으로 탭으로 구분되는 TSV 형태를 주로 사용한다.
- JSON 형태로 저장하는 경우도 더러 존재한다.
- 텍스트 파일로 로컬 저장하는 것이 일반적
- 바로 DB나 네트워크 저장소에 넣기에는 부하와 안정성 문제
- 로컬 텍스트 파일로 저장하여 서버가 크래시나도 보존되도록 한다
신경써야 하는 점?
- 누락이 존재하여서는 안된다.
- 게임 서버가 크래시가 나더라도 게임 로그가 보존되고 로그서버까지 가도록 한다.
- 로그 파일 처리가 완벽해야 한다.
- 로그를 RDBMS insert할 때, 트랜잭션을 사용하여 원자적인 처리를 보장한다.
- 부하 테스트가 이루어져야 한다.
- 이 글이 쓰이게 된 이유
목표 성능
- 사내 100명 테스트 피크 시 5분간 5000 라인의 로그 발생
- 40만명의 유저, 모두 동시 오픈 및 풀 로드 가정하였을 경우
- 66,666 log/sec 처리 필요
ELK Stack
로그 처리에 가장 대표적인 서비스 스택
- ElasticSearch
- 다양한 로그 분석과 검색에 항상 사용된다.
- 데이터를 분석해주는 역할
- Logstash
- 다양한 소스로부터 데이터를 수집하여 전환하여, 타겟으로 전송한다.
- 데이터를 모아주는 역할, 정제하는 역할
- Kibana
- 데이터 시각화 및 탐색 도구
Logstash 성능 지표
RDBMS에 로그를 적재해야 하는 상황이었기에, logstash-output-jdbc plugin 을 사용하였다.
- 폴더에 로그 파일이 존재하는지 확인한다
- 로그 파일이 존재하면, 읽기 시작한다 (라인별로 프로세싱)
- TSV를 해석한다.
- JDBC를 이용하여 INSERT한다.
- 결과 : 300 log/second …
- 너무 멍청하여, logstash-output-jdbc 에 bulk insert를 도입하였다.
- 결과 : 2,000 log/second …
- 워커 스레드를 늘려서, 한 번에 여러 파일이 각각의 파이프라인으로 돌도록 하였다.
- 결과 : 3,000 log/second (4 worker) …
뭐가 문제지
- JDBC와 별개로, TSV 파싱에서 병목이 걸리고 있었다.
- 메모리도 최소 4GB, 8GB 를 권장하고 있다.
- 자바의 한계? 다중화를 통해 해결?
Rust 도와줘!
러스트를 이용하여 멀티스레드 프로그램을 만들었다.
- 150,000 log/second 속도로 TSV를 해석해낸다.
- 20,000라인 TSV 해석 → INSERT 문 변환 → .sql 파일로 저장
- MYSQL bulk insert 했을 때, 원하는 속도가 안 나왔따..
- Rust의 성능을 절륜한데 이제는 RDBMS에서의 병목이.
LOAD DATA INFILE
- 애초에 TSV를 바로 RDBMS로 입력하는 방법이 존재했다.
- 이는 DB 머신의 코어 성능 지표를 따라간다.
성능 테스트
- c5.xlarge (4core 8GB) 기준 (4 worker thread)
- Aurora db.r5.large (2core 16GB) 사용
- Metrics: processed 60 files, failed 0 files, processed 1,162,080 logline, max 23,241 logline/second, processing time 233,260ms, max processing time 4,734ms
- 60개의 파일 (개당 5MB, 20,000라인) 처리에 약 58초 소요
- 23,000 log/second
- Aurora db.r5.2xlarge (8core 64GB) 사용
- Metrics: processed 60 files, failed 0 files, processed 1,162,080 logline, max 54,230 logline/second, processing time 81,585ms, max processing time 1,503ms
- 60개의 파일 (개당 5MB, 20,000라인) 처리에 약 20초 소요
- 54,000 log/second
- Aurora db.r5.large (2core 16GB) 사용
- c5.2xlarge (8core 16GB) 기준 (8 worker thread)
- Aurora db.r5.large (2core 16GB) 사용
- Metrics: processed 60 files, failed 0 files, processed 1,162,080 logline, max 29,052 logline/second, processing time 424,471ms, max processing time 8,356ms
- 60개의 파일 (개당 5MB, 20,000라인) 처리에 약 16초 소요
- 29,000 log/second
- Aurora db.r5.2xlarge (8core 64GB) 사용
- Metrics: processed 60 files, failed 0 files, processed 1,162,080 logline, max 61,977 logline/second, processing time 131,549ms, max processing time 2,426ms
- 60개의 파일 (개당 5MB, 20,000라인) 처리에 약 16초 소요
- 60,000 log/second
- Aurora db.r5.large (2core 16GB) 사용
'연구한 이야기 > 맡은 업무 이야기' 카테고리의 다른 글
드로우콜을 낮춰보자 (0) | 2024.09.01 |
---|---|
conan 패키지 관리자 도입하기 : 개론 (1) | 2024.02.04 |
ASTC 포맷, 어떤 점이 왜 좋을까? (0) | 2024.01.28 |
Prometheus + Grafana 패키징 (0) | 2024.01.19 |
텍스처 압축과 ASTC (3) | 2024.01.14 |