분산환경에서 핀포인트로 트랜잭션 관리하기

분산 환경 End-to-End 모니터링에 강점을 가진 APM, 핀포인트에 대해 알아보았습니다. 대시보드의 기능과 활용 사례를 통해 핀포인트의 특징을 확인해 보세요.

작성일 2023년 09월 06일

안녕하세요. 저는 IMQA 개발팀 DevOps 파트의 양승훈 연구원입니다. 이번 포스팅에서는 분산 환경에서 핀포인트로 트랜잭션을 관리하는 방법에 대해 정리해 보았습니다.

1. 핀포인트란?

핀포인트는 분산 환경 End-to-End 모니터링에서 강점을 가진 APM입니다. 서버의 구성은 과거로부터 현재에 이르기까지 복잡성이 빠르게 가속화되었고 대규모 서비스는 수십, 수백 대의 서버를 가지게 되고 운영하는 모듈의 수도 많아졌을뿐더러 서버와 모듈이 복잡하게 연동하고 있습니다.

따라서 이들이 어떻게 연동되고 있는지 파악이 힘들고 개별 서버의 모니터링으로는 전체 서비스의 상황 파악이 불가능했습니다. 심지어 다른 서비스의 장애가 연동된 서비스에게 장애를 전파하는 경우도 발생합니다.

핀포인트는 이런 분산 환경에서의 모니터링에 유리한 ‘Server Map’이라는 기능으로 애플리케이션 토폴로지를 자동으로 발견하고 이를 가시화해 줍니다.

이렇게 분산 환경에서 서버 간의 트랜잭션 흐름을 파악할 수 있고 호출 수와 응답 시간까지 알려주는 Server Map과 같은 기능은 제가 아는 한, APM들을 모두 모아놓고 봐도 ‘오직 핀포인트만이 가진 장점'입니다.

이미지 출처: Enbian Blog, 2015.03.07, naver pinpoint

Server Map을 통해 전체 서버의 트랜잭션 흐름을 파악할 수 있는 것은 물론, 에러가 발생한 트랜잭션이 있다면 빨간색 하이라이트로 표시해 줍니다. 이러한 특징 때문에 애플리케이션이 복잡하게 연동된 분산 환경 모니터링에 강점을 가진 것입니다.

2. 대시보드 소개

핀포인트 활용 사례에 대해 알아보기 전, 핀포인트의 대시보드를 간단하게 살펴보겠습니다.

2.1. Server Map

대시보드 왼쪽에 표현하고 있는 것은 앞서 소개한 Server Map입니다.

화살표는 ‘트랜잭션의 흐름’을 나타내며, 화살표의 숫자는 '호출 수와 응답 시간'을 나타냅니다. 임계치 이상의 응답시간이나 에러를 포함하면 아래 이미지와 같이 숫자가 빨간색으로 보이기 때문에 Server Map에서도 병목 구간을 특정할 수 있습니다.

이미지 출처: Enbian Blog, 2015.03.07, naver pinpoint

2.2. Timeline chart

핀포인트는 타임라인 차트의 x축에 트랜잭션의 흐름을 시간순으로 정렬하여 보여주고 있는데요. y축에서는 특정 시간에 트랜잭션의 응답 시간이 얼마나 걸렸는지 알려주기 때문에 역시 병목 지점을 특정해 내는 데에 큰 도움을 줍니다.

Call Stack을 추적하고 싶은 트랜잭션을 드래그합니다.

타임라인 차트에서 병목이 발생하는 구간을 특정한 뒤, 특정 시점을 그래프에서 드래그해 주면, 상세한 Call Stack 추적을 위한 팝업창이 실행됩니다.

Call Stack 트리에서 어떤 메서드가 실행되었는지, 메서드가 실행될 때 전달된 Argument가 무엇인지 제공하고 있습니다.

메서드가 실행된 실행 시간을 제공하고 있기 때문에, 어떤 메서드가 실행에 얼마나 오래 걸렸는지도 손쉽게 파악할 수 있습니다.

3. 활용 사례

핀포인트가 가진 ‘분산 환경에서의 강점’을 이용한 트러블 슈팅에 대한 사례를 소개해 드리겠습니다.

3.1. 에러 발생 구간 추적

핀포인트만이 가진 Server Map은 서버 간의 트랜잭션 흐름을 나타낸다고 말씀드렸는데요. 만약 에러가 발생하는 구간이 있으면, 아래 이미지와 같이 Server Map의 화살표 안의 숫자가 빨간색으로 표시됩니다. 타임라인 차트에서도 에러가 발생한 트랜잭션은 빨간 점으로 표시됩니다.

이미지 출처: Enbian Blog, 2015.03.07, naver pinpoint

타임라인 차트에서 특정 트랜잭션을 드래그하면 콜스택 추적 팝업창이 실행됩니다.

이미지 출처: 2021.05.30, Pinpoint APM Node 사용하기

convertStackMpm 메서드 실행 후 Exception이 발생한 것을 확인할 수 있습니다.

callStack.setCallStack(callStack.getCallStack().substring(0, 7000));

Exception에서 어떤 Argument를 전달받았는지 제공해 주고 있기 때문에 빠르게 문제가 되는 부분을 특정할 수 있었습니다. Argument 지표가 [ begin 0, end 7000, length 388 ] 라는 정보를 제공해 주고 있는데, callStack.getCallStack()이 가져온 문자열의 길이가 substring이 동작할 7,000자 이하라서 발생한 이슈였습니다.

if(callStack.getCallStack().length() >= 7000){
       callStack.setCallStack(callStack.getCallStack().substring(0, 7000));
}

그래서 callStack.getCallStack()이 가져온 문자열의 길이가 7,000자 이상일 때만 substring이 동작하도록 작업을 진행했고, 실제로 Exception은 더는 발생하지 않았습니다.

직접적인 에러가 발생한 경우에는 애플리케이션의 로그에서 에러 로그가 출력되기 때문에 에러 로그를 보고 어느 부분이 문제인지 알 수 있습니다. 하지만 여러 애플리케이션이 복잡하게 연동하고 있는 분산 환경의 경우에는 기능을 실행할 때 호출되는 애플리케이션들을 파악하고 애플리케이션의 로그를 하나하나 확인하는 작업에 많은 시간이 낭비됩니다.

반면 핀포인트는 Server Map에서 에러가 발생한 애플리케이션을 한눈에 확인하고, 콜스택 추적을 통해 에러를 콕 집어주기 때문에 분산환경 모니터링에 적합하다고 할 수 있습니다.

기존 트러블 슈팅 방식과 비교하면 장애 대응 프로세스가 크게 바뀐 것을 확인할 수 있습니다.

  • 기존 트러블 슈팅
    - 병목이 발생하는 것을 확인
    - 기능을 실행할 때 호출되는 애플리케이션들 파악
    - 애플리케이션들의 로그를 하나하나 확인
    - 에러가 발생한 부분 개선
  • 핀포인트를 활용한 트러블 슈팅
    - 대시보드에서 서버맵 확인
    - 한눈에 어느 애플리케이션의 어느 호출 지점에서 병목이 발생하는지를 파악
    - 콜스택 추적으로 병목이 발생한 부분 빠르게 파악
    - 에러가 발생한 부분 개선

3.2. 응답 시간이 느린 병목 구간 트러블슈팅

방금 사례에서 직접적으로 에러가 발생한 사례를 소개해 드렸다면, 이번에는 에러가 발생하지 않았지만, 트랜잭션의 응답시간이 느려서 병목이 발생한 케이스를 소개해 드리겠습니다.

사실 직접적으로 에러가 발생했을 경우에는 우리가 핀포인트를 활용하지 않고도 애플리케이션의 로그를 직접 확인하는 방식으로 '너무 느리지 않은 대응'이 가능했습니다.

하지만 단순히 응답시간이 느려서 병목이 발생하는 경우에는 병목 구간을 특정하는 것이 쉬운일이 아닙니다. 애플리케이션의 로그 상에서 에러 로그와 같이 문제를 짚어주는 정보가 제공되지 않기 때문에 응답 시간이 느린 api 호출을 일일이 디버깅해야하기 때문입니다.

아래의 이미지를 보시면 일부 트랜잭션의 응답시간이 2,250ms 정도에 몰려있는 것을 보실 수 있습니다.

이미지 출처: 2021.10.11, nGrinder와 Pinpoint를 이용한 성능 / 부하 테스트3 - Smoke 테스트 & Load 테스트

2,250ms.. 2.25초인데, 이게 참 애매한 시간입니다.

우리가 성능 차트에서 다른 750ms 이하의 응답시간을 가진 트랜잭션들과 차트상에서 비교했기 때문에 “아, 2,250ms가 걸리는 api를 개선해야겠구나”라고 생각할 수 있는 것이죠. 만약 위와 같이 극명하게 가시화된 자료가 없었다면 “2.25초? 이 정도는..” 하고 쉽게 넘어갈 수도 있죠.

어쨌든 우리가 핀포인트 없이도 “2.25초나 걸리는 api가 있는데? 개선해야겠네”라고 인지했다고 가정해 봅시다. 이 api를 개선하기 위해서는 어떤 메서드가 호출될 때 실행시간이 오래 걸리는지 확인하기 위해 디버깅을 실행해야 할 것입니다.

하지만 핀포인트를 사용했을 때, 타임라인 차트에서 특정 트랜잭션을 드래그하면 콜스택을 확인할 수 있습니다.

이미지 출처: 2021.10.11, nGrinder와 Pinpoint를 이용한 성능 / 부하 테스트3 - Smoke 테스트 & Load 테스트

콜스택 추적을 통해 확인한 결과, 위의 이미지와 같이 findAll()을 실행할 때 쿼리가 수행되면서 1,930ms의 실행시간이 소요된 것을 확인할 수 있습니다.

만약 트래픽이 일정 수준 이상으로 증가했을 때, 응답시간이 오래걸리는 병목이 발생하는 트랜잭션이 많아지게 되는 상황이라면, 개발 환경에서 디버깅으로도 캐치해내기 어렵습니다.

  • 트래픽이 증가하면서 전체적으로 응답시간이 늘어난 경우
이미지 출처: 2021.10.11, nGrinder와 Pinpoint를 이용한 성능 / 부하 테스트3 - Smoke 테스트 & Load 테스트

아래의 콜스택을 보면 이전의 findAll()이 실행되고 쿼리가 수행되는 시간이 10,490ms로 더욱 길어졌습니다. 추가로 getConnection() 메서드에서도 적지 않은 시간이 소요된 것을 확인할 수 있는데 findAll()의 개선에 추가로connection pool 역시 조정해야 하는 상황이라는 것을 인지할 수 있습니다.

이미지 출처: 2021.10.11, nGrinder와 Pinpoint를 이용한 성능 / 부하 테스트3 - Smoke 테스트 & Load 테스트

4. 장점과 단점

4.1. 장점

  • 오픈소스라 Cloud , On-Promise 환경에서도 편하게 사용이 가능함
  • 여러 서버간에 거쳐가는 MSA 서비스 분석에 강점  

4.2. 단점

  • 빈약한 통계 정보 - 통계 데이터로 트러블 슈팅이 힘듬
  • 리포트 분석 기능 빈약  
  • 흩어져 있는 정보 - 특정 정보를 얻기위해 많은 화면을 오가야 되는 단점이 큼
  • 상대적으로 타 apm 대비 agent 부하가 높음 (샘플링 기법으로 E2E 설치 권고)
  • 과거 정보 조회 기능 부족 - 과거 정보를 따로 모아놓은 요약 뷰등이 없어, 과거 장애에 대한 조회가 힘듬
  • 메소드 병목 구간 찾기 힘듬 - 메소드의 주요 병목구간을 보여주는 뷰가 없음
  • 특정 URL (트랜잭션)의 추이를 바라보는 기능이 없음
  • Java만 지원

5. 마치며

핀포인트를 활용한 트랜잭션 관리에 대해서 알아보았습니다.

Server Map이라는 핀포인트만의 특별한 기능을 제공하고 병목 구간을 특정해서 콜스택까지 추적할 수 있기 때문에 분산 환경  모니터링에 강점을 가졌다는 것을 알 수 있었습니다.

반면, 핀포인트에 대해서 '전체적으로 동작이 무겁고 지나간 데이터를 보는 것이 힘들다'라는 평도 있었는데요.

APM이 가진 강점과 약점을 냉정하게 판단하고 강점을 최대한 살리고 약점을 보완할 수 있는 안정적인 모니터링 시스템을 구축하는 것이 DevOps 파트가 가진 하나의 큰 숙제라고 생각합니다.

많이 부족하고 제 개인적인 견해가 많이 들어간 글이지만 읽어주셔서 감사합니다.

Share on

Tags

IMQA 뉴스레터 구독하기

국내외 다양한 기술 소식을 선별하여 매월 전달해드립니다. IMQA 뉴스레터를 통해 기술 이야기를 함께해보세요.

구독하기