Ce diaporama a bien été signalé.
Nous utilisons votre profil LinkedIn et vos données d’activité pour vous proposer des publicités personnalisées et pertinentes. Vous pouvez changer vos préférences de publicités à tout moment.

[MGDC] 리눅스 게임 서버 성능 분석하기 - 아이펀팩토리 김진욱 CTO

436 vues

Publié le

넷마블 몬스터의 MGDC 행사에서 아이펀팩토로 김진욱 CTO 가 발표한 "리눅스 게임 서버 성능 분석하기" 의 발표자료입니다.

Publié dans : Logiciels
  • Soyez le premier à commenter

[MGDC] 리눅스 게임 서버 성능 분석하기 - 아이펀팩토리 김진욱 CTO

  1. 1. 리눅스 게임 서버 성능 분석하기 김진욱 (jinuk.kim@ifunfactory.com)
  2. 2. 2 성능 분석하기
  3. 3. 성능 지표 3 처리량: 얼마나 많은 일을 하고 있는가 = 이 서버 동시 접속 몇 명까지 받을 수 있나? 응답 시간: 하나의 일을 얼마나 빨리 처리하는가 = 이 서버 랙 있는지?
  4. 4. 게임 서비스 4 ……게임 서버 (PvP) … Game Server Game Server Matchmaking Server Matchmaking Server 게임 서버 … 게임 서버 군 유저 세션 랭킹 캐시 … Redis / memcached 유저 데이터 랭킹 통계 데이터 Databases 로드 밸런서 Nutcracker 캐시 / 디비 미들웨어 유저 인증 서비스 GooglePlay 결제 3rd 파티 플랫폼 서비스 BI / 통계 운영 API 운영 서비스 유저 랭킹 CDN Matchmaking Server Matchmaking Server매치메이킹 실시간 PVP 서버 군
  5. 5. 게임 서비스 5 전체 시스템이 여러 개의 다양한 요소로 구성 각 요소 중에 성능 문제가 있는 부분을 찾을 수 있을까?
  6. 6. 6 접근 방법
  7. 7. 원하는 것: 성능 저하가 작을 것 7 • 가장 이상적인 테스트 환경: 실 서비스 환경 • Valgrind 처럼 수 배 - 수십배의 성능 저하는 쓸 수 없다 • 프로덕션 환경에서 크래시 할 수 있다면 사용하기 힘들다
  8. 8. 원하는 것: 상시 모니터링 8 • 빨리 성능 문제를 감지할 수록 좋다 • 문제 생긴 환경에서 바로 확인할 수록 문제 해결이 쉽다.
  9. 9. 원하는 것: 충분한 데이터 9 • 실행 속도가 평균 얼마 (X) • 실행 속도의 분포가 이렇다 (O) 실행 시간의 분포 (백분율이나 유사한 통계) 가 있어야 파악하기 쉽다
 (예: 평균 응답시간이 1ms 라도 1,000 ms 걸려서 처리하는 요청이 있다)
  10. 10. 어떻게 측정할까? 10 서버 코드에 측정하는 기능을 추가 성능 모델의 각 요소를 측정 측정 코드를 (직접) 추가 외부에서 관찰 측정 도구 이용 (프로파일러, 성능 모니터링 툴)
  11. 11. 11 접근 방법 1: 측정 코드 삽입
  12. 12. 성능 측정 코드 삽입 12 게임 서버 실행 방법을 (단순화해서) 모델링 필요한 코드를 서버에 추가 측정 결과를 얻고 분석 및 코드 개선
  13. 13. 간단한 성능 모델 13
  14. 14. 성능 모델 (1) 14 요청 하나를 여러 개의 이벤트로 쪼개서 측정한다 • 웹 API의 요청 - 응답 쌍 하나 vs. 1. 유저 로그인 요청을 받고 해당하는 DB 객체 가져오기 2. DB객체와 로그인 요청을 비교해서 바른 요청인지 확인 3. 인증 서비스에 유저 로그인 요청 보내기 4. 인증 서비스 결과를 클라이언트에 응답
  15. 15. 성능 모델 (2) 15 이벤트 하나 → 클라이언트 메시지, 타이머 만료, DB 연산 완료, API 호출에 대한 응답 측정할 성능: 하나의 작업 (= 하나 혹은 여러 개의 이벤트) 가 얼마나 걸렸는가 각 이벤트 실행 시간을 작업 별로 모아서 합산 평균적/최소/최대로는 얼마나 빠른지 확인 (혹은 분포 확인)
  16. 16. 성능 측정 코드 넣기 16 모든 분리된 이벤트 단위에서 측정한다. • 클라이언트 메시지 받고 처리 시작까지 걸린 시간 (큐 대기 시간) • DB 혹은 redis 요청 전송 / 요청 완료 콜백에 걸린 시간 • 외부 API 요청 / 완료 콜백까지 걸린 시간 • 데이터를 가지고 복잡한 연산 (예를 들어 길찾기, AI tick, …) 에 걸린 시간 • DB 에서 가져온 row 수 혹은 ORM 객체 수 • 외부 API 호출 실패에 대한 재시도 횟수
  17. 17. 측정 예시 17 아이펀 엔진에서 측정하는 실행 통계
  18. 18. 장점 18 개발자가 원하는 수치를 측정한다: 외부 API 성능이 궁금하다면 호출 시간 분포나, 호출 실패 분포를 측정 측정 범위를 제어하기 쉽다: 코드를 추가한 곳만 측정한다. 원하는 추상화 수준을 고를 수 있다: 시스템, 프로세스, 스레드, …
  19. 19. 단점 19 측정 코드를 직접 추가 해야한다: 작업량 문제 빠진 부분이 있다면 측정 자체가 유의미하지 않을 수 있다 성능 모델을 직접 만들어야 한다 측정 방법/내용을 바꾸려면 다시 실행하거나 로드해야
  20. 20. 20 접근 방법 2: 외부 도구 이용
  21. 21. 리눅스 성능 측정 도구들 21 Linux perf kernel 이벤트 카운터 (v2.6 이상) Linux eBPF 프레임웍의 트레이싱 기능 (v3.18 이상; 가능하면 v4.9 이상) 그리고 이를 이용한 스크립트 / 성능 모니터링 툴 • BCC (https://github.com/iovisor/bcc): eBPF 프로그램 컴파일러 (python, lua) • PLY (https://github.com/wkz/ply): eBPF 스크립트 툴 • eBPF trace (https://github.com/iovisor/bpftrace): D-Trace 유사 기능
  22. 22. eBPF + BCC 22 eBPF: 리눅스 커널 안에서 동작하는 (제한된) VM BCC: eBPF 프로그램을 생성하고,
 해당 프로그램과 통신하는 python / lua 스크립트 성능 측정이 커널 안에서 이뤄져서 빠르다 (컨텍스트 스위칭 부하 없음)
  23. 23. eBPF: 할 수 있는 일 23 eBPF: 리눅스 커널 안에서 동작하는 (제한된) VM; OS 실행 중 아무때나 추가하거나 제거할 수 있다. 커널 안의 함수, 시스템 콜, 프로그램 내의 함수를 후킹 해당 함수 호출할 때 마다 특정 동작(=호출 내용 통계내기)을 하거나, 동작을 변경할 수 있다. (=패킷 필터링)
  24. 24. BCC: 할 수 있는 일 24 eBPF 프로그램을 (자동으로) 생성하고 해당 프로그램을 커널에 넣어 실행 한 후, 통신한다. 시스템/프로그램의 동작을 지속적으로 추적하거나 (트레이싱) 성능 지표 등을 통계낼 수 있다. (프로파일링)
  25. 25. 예: CPU 프로파일링 25 1초에 수십-수백번 정도 특정 프로세스나 스레드를 살펴본다 이때 각 CPU가 실행 중인 콜스택을 key로 map에 저장 Map에 있는 데이터를 FlameGraph 로 시각화
 (https://github.com/brendangregg/FlameGraph )
  26. 26. 예: CPU 프로파일링 26
  27. 27. 어떤 질문을 답할 수 있을까? 27 어떤 lock 을 제일 오래 기다리는가? Disk 읽기 요청을 어떤 크기로 주로 보내는가? CPU 수가 작업 수에 비해서 적은가? 어느 시스템 콜을 제일 많이 하는가? 어떤 Disk IO가 제일 느린가?
  28. 28. Kernel - 유저 스페이스 역할 분리 28 데이터를 측정 대상의 kernel 영역 안에 안전하게 갈무리하고 (eBPF) 분석 프로그램의 유저 스페이스에서 후처리 (BCC) 성능 측정 / 디버깅 코드가 문제를 일으키기 어렵다 (측정 대상과 측정하는 툴이 서로 다른 프로그램)
  29. 29. 장점 29 안전성 - OS나 측정 대상을 크래시하지 않는다 (eBPF 컴파일러가 보장) 시스템 / 프로그램 재시작 없이 측정 방식 변경 3rd 파티 라이브러리 / 프레임웍이 존재
  30. 30. 단점 30 최신 커널 필요 - 일정 이상의 kernel 버전이 필요하다 (CentOS 7 은 커널 추가 설치 필요) 측정 대상이 사용하는 라이브러리 / OS 지식이 필요
 (측정할 함수나 결과로 나온 함수들을 추측해야) 컴파일러 최적화로 인해서 변경된 콜스택 분석이 어려운 경우가 있다
  31. 31. 31 예제 프로그램 분석
  32. 32. flaskr 32 Python 웹 개발 프레임워크인 flask 의 튜토리얼 프로그램 해당 프로그램을 C++ 로 포팅하고, 간단한 블로깅 서버 제작 (MySQL에 데이터 + 웹서버 및 HTML 생성 부분을 C++ 로 구현)
  33. 33. 33 • HTTP 요청을 처리하는 몇 개의 핸들러 • HTML 템플릿 로드 • DB 연결 • 서버 시작 (4 스레드)
  34. 34. 34 • HTTP 요청 핸들러 • DB 에서 글 목록을 획득 • key, value 로 묶기 • HTML 렌더링
  35. 35. 35 • DB 미들웨어
 (커넥션 풀링형식) • 초기화 • 요청마다 DB 연결 제공
  36. 36. flaskr 36 다음과 같은 라이브러리 이용 Crow: C++14 기반의 고성능 HTTP 서버 CrowDB: C++14 기반의 DB API (MySQL / Mariadb 연결해서 사용) Jinja2cpp: C++14 기반의 HTML 템플릿 렌더링 라이브러리
  37. 37. flaskr: 성능 목표 37 • 4 core 머신에서 초당 1000 요청 이상 처리하기 ⇨ 코어마다 초당 250건 이상 처리해야 ⇨ 요청 당 쓸 수 있는 CPU 시간 ≦ 4 ms
  38. 38. 부하 테스트 38 • HTTP 부하 테스트: siege 이용 siege -c 32 -r 16384 -b http://example.com
 
 지정한 URL에 32개의 연결로,
 총 16k 개의 요청을,
 요청 사이에 쉬는 것 없이 전송
  39. 39. 부하 테스트: 결과 39 개선이 필요한 수치 • 초당 110요청 • CPU 코어 당 27.5 요청 • 요청 당 처리 시간 ≒ 36 ms
  40. 40. 40 예제 프로그램: 직접 측정
  41. 41. flaskr: 직접 측정 41 웹 요청에 대한 비동기 처리 없음 - DB 및 기타 처리는 직렬로 처리 하나의 HTTP 요청에 대한 처리 시간을 구분해서 기록 • 총 처리 시간 • DB 요청 - 응답 시간 • HTML 응답 생성 시간 (template -> HTML 렌더링 시간)
  42. 42. 측정 코드 삽입 (1) 42 HTTP Request 에 대한 middleware 추가 • 요청 처리 시작 / 종료 시각 기록 (총 요청 처리 시간 계산) • DB 요청 시작 / 완료 시각 기록 • HTML 렌더링 시작 / 완료 시각 기록 만일, DB / HTML 렌더링 두 부분이 문제가 아니라면, 총 요청 시간 - DB 요청 시간 - HTML 렌더링 시간 값이 크게 나올 것이다. (예측)
  43. 43. 측정 코드 삽입 (2) 43 각 측정 값 (총, DB, 렌더링)에 대해서 처리 시간 분포를 측정 (log 분포 이용) 시간은 마이크로 초 (us) 수준에서 측정
  44. 44. 측정 결과 44 16 - 32 32 - 64 64 - 128 128 - 256 256 - 512 512 - 1024 1024 - 2048 0 750 1500 2250 3000 응답시간 (ms)
  45. 45. 측정 결과: DB 처리 45 32 - 64 64 - 128 128 - 256 256 - 512 0 1000 2000 3000 4000 DB 처리 시간 (us)
  46. 46. 측정 결과: HTML 렌더링 46 32 - 64 64 - 128 128 - 256 256 - 512 0 1000 2000 3000 4000 페이지 렌더링 시간 (us)
  47. 47. 측정 결과 47 • 요청 당 소요 시간인 36 ms (= 36,000 us) 에 가까운 값이 없다. • 잠정 결론: DB, HTML 렌더링은 병목이 아니다. (전체 시간 중 일부) • 추가 분석을 미뤄두고, 다른 방법으로 접근 시도.
  48. 48. 48 예제 프로그램: 툴을 써서 측정
  49. 49. flaskr: eBPF + BCC를 써서 측정 49 BCC 에서는 예제 및 기본 기능 제공을 위해서 몇 가지 도구를 제공한다. 해당 도구를 사용해서 성능 측정 / 병목 분석을 진행한다.
  50. 50. 측정 진행 방식 50 1. 가설을 세운다 2. 가설을 확인할 적당한 도구로 측정한다 (혹은 도구를 만든다) 3. 측정 후 가설이 맞는지 확인한다
 예를 들어 CPU가 병목이라고 가정한다면,
 profile 을 실행해서 CPU 병목을 찾아보고,
 CPU 병목이 있다면 많이 실행되는 곳을 최적화한다
  51. 51. 가설 #1: CPU 병목이 있는가? 51 CPU 를 많이 쓰는 코드를 어떻게 찾을까? BCC profile 명령 이용 앞서 언급한 샘플링 방식의 프로파일링 도구 예: 30초간, 매초 199번 샘플링 sudo profile -p $(pgrep -nx flaskr) -f -F 199 30
  52. 52. CPU 프로파일링 결과 52
  53. 53. CPU 병목이 있는가? 53 전체 프로파일링 시간 중 3% 이상 사용하는 곳: • System call (open, read, TCP send, …) • DNS 설정 읽기 (libnss)
  54. 54. CPU 병목이 있는가? 54 htop 으로 확인
 
 CPU를 다 쓰고 있지 않다. (코어 1.4개 = 140% 수준)
  55. 55. 가설 #2: 어딘가에서 대기하는 문제? 55 Lock 이나 메모리 같은 다른 무언가에서 대기하면 오래 걸린다 코드의 대기 시간을 측정해보자: BCC offcputime 스레드가 락을 기다리거나 / 블럭킹 시스템 콜을 해서
 CPU를 반납하는 순간의 콜 스택 분포를 구한다 sudo offcputime -p $(pgrep -nx flaskr) -f 30
  56. 56. 대기하는 코드는 어디인가? 56 poll() 에서 3/4 이상의 대기가 발생 epoll() 에서 나머지가 대기
  57. 57. poll() 은 누가 부르는가? 57 프로파일링 결과의 콜 스택에서 찾아보면?
  58. 58. 누가 poll() 대기를 깨우는가? 58 poll() 호출한 코드가 무엇을 기다렸는지 반대방향에서 보기 깨우는 쪽에서 뭘 했는지 확인한다 offwaketime 툴 이용
 다른 스레드를 실행 가능하게 해줬을 때의 콜 스택 분포를 본다 sudo offwaketime -p $(pgrep -nx flaskr) 30
  59. 59. Waker stack 확인 59 UDP 메시지를 받은 경우 깨운다
  60. 60. UDP 문제 확인 60 웹 서버 어떤 부분에 UDP를 쓰는가? • Google QUIC (HTTP/2 의 비호환 이전 버전) • RTP • DNS • … DNS 호스트 주소 조회를 위해 사용하고 있다 (미들웨어)
  61. 61. 가설 #3: DNS 조회 시간 문제? 61 DNS 조회 시간을 어떻게 확인하는가? 특정 함수를 후킹해서, 함수 호출/종료 사이의 시간을 잰다. BCC funclatency: DNS 주소를 조회하는 C API 를 측정한다
  62. 62. DNS 응답 시간: getnameinfo 62 16 - 32 32 - 64 64 - 128 128 - 256 0 1000 2000 3000 4000 응답 시간 (ms) DNS 응답시간: 16 - 128 ms 요청 당 소요 시간 36 ms 과 비슷함
  63. 63. 재확인: 직접 측정 63 16 - 32 32 - 64 64 - 128 128 - 256 0 750 1500 2250 3000 응답 시간 (ms) DNS resolver 시간을 앞서 사용한
 코드 수준의 측정 방식으로 확인
  64. 64. DNS 문제 수정 64 DNS 처리가 대기하는 호출이 되지 않도록 수정한다. • Caching DNS 서버를 사용한다 (dnsmasq) • 비동기 DNS resolver 를 이용한다. (boost::asio 의 async_resolve)
  65. 65. 수정 후 실행 결과 65 • 4 core 머신에서 초당 1400 요청 처리 ⇨ 코어마다 초당 350건 이상 (vs. 27.5건) ⇨ 요청 당 소요 시간 ≦ 3 ms (vs. 36 ms)
  66. 66. 수정 후 실행 결과: 대기 시간 66 • 이전 부하 테스트에선 poll() 이 전체 대기시간의 3/4 이상
  67. 67. 해석 67 • poll() 함수 실행 시간 개선 (대기 시간 개선) • 요청 하나를 처리하는 응답 시간 감소 ⇨ 하나의 CPU 코어가 처리하는 초당 요청 수 증가
  68. 68. 68 정리
  69. 69. 어떻게 접근할까? 69 서버 코드에 측정하는 기능을 추가 모델에 대해서 한 번에 측정/확인 모델 범위를 벗어나는 경우 문제 외부에서 관찰 도구가 제공하는 범위 내에서 측정 한 번에 모두 볼 수 없으나 반복/수정
  70. 70. 더 해보고 싶은 것: 클라이언트 70 • 직접 측정: Unity 나 Unreal Engine 의 자체 프로파일러 • 도구 사용: Android adeb (https://github.com/joelagnel/adeb)
 adb root 사용 가능한 arm64 / android N 이상의 기기 지원
 (사실상 커스텀 빌드 커널 필요)
  71. 71. 더 해보고 싶은 것: Windows 서버 71 • 직접 측정: Linux 의 경우와 차이가 크지 않다 • 도구 사용 • Event Tracing for Windows / Xperf
 (https://randomascii.wordpress.com/2012/05/11/the-lost-xperf- documentationcpu-scheduling/ ) • Windows Performance Analyzer (WPA)
 (https://docs.microsoft.com/ko-kr/windows-hardware/test/wpt/ graphs#flame_graphs)
  72. 72. Q&A 72
  73. 73. 감사합니다 73
  74. 74. 리눅스 게임 서버 성능 분석하기 김진욱 (jinuk.kim@ifunfactory.com)

×