디어 서비스를 통해 보는 서버 지표 모니터링
현재 다빈치팀은 '디어'라는 전동 킥보드 공유 서비스를 직접 운영하고 있습니다. 디어는 하루에도 3만 건 이상의 킥보드 대여가 발생하고 있는 서비스입니다. 전국에 약 2만 대 정도 되는 킥보드 기기와 실시간으로 통신하며 365일 24시간 끊김 없이 서비스를 운영하고 있습니다. 적은 인력으로 이 정도 규모의 서비스를 운영하기 위해서는 중요한 지표들을 쉽게 모니터링할 수 있도록 정리하는 것이 매우 중요합니다. 백엔드 서버 측에서 모니터링해야 하는 중요한 지표에는 어떤 것들이 있는지 한 번 살펴보겠습니다.
모니터링의 종류
서버 측에서 어떤 종류의 지표들을 모니터링해야 할지 정리하기 전에, 먼저 다음 사례들을 한 번 살펴보겠습니다. 실제로 디어 서비스를 운영하는 과정에서 겪었던 사례들입니다.
사례 1
디어 서비스에서는 IoT 기기를 실시간으로 원격 조작해야 하는데, 하드웨어에 대한 조작이 들어가다 보니 실패율이 상당히 높습니다. 그러나 그 정확한 수치를 몰라서 아래와 같은 대화가 오고 가는 경우가 있었습니다.
개발자 A: 이번에 새로 기능을 개발해야 하는데, 킥보드의 속도를 실시간으로 바꾸는 기능이 필요해요. 저희가 연동해서 사용해도 될까요?
개발자 B: 어.. 가능은 한데, 하드웨어 조작 요청이라 실패할 수 있어요.
개발자 A: 그래요? 실패율이 얼마나 되는데요?
개발자 B: 음, 꽤 많이..?
개발자 A: …
사례 2
어느 날 갑자기 잘 보이던 로그가 뚝 끊기기 시작했습니다.
개발자 A: 어…? 지금 파트너스 API 로그가 하나도 안 남고 있어요! 서비스 이상 없어요?
개발자 B: 네?? 트래픽 잘 받고 있는 것 같은데요..?
개발자 A: 이상하다… 배포 한 번 다시 해볼게요… 어라 지금 배포도 계속 실패하는데요..?
개발자 B: 그럼 EC2 서버 한 번 직접 들어가 보시겠어요?
개발자 A: 어… 서버도 접근이 안 되는데요…?
개발자 B: ???
사례 3
어느 날 자정에 갑자기 타임아웃 에러가 발생하며 서비스가 느려지기 시작했습니다.
운영자 X: 지금 서비스가 갑자기 느려지면서 지도에 기기 조회가 잘 안 된대요!! 확인해 주시겠어요?
개발자 A: 어..? 갑자기 타임아웃 에러가 조금씩 발생하네요.. 지금 트래픽 많은 시간이 아닌데…?
운영자 Y: (새로고침을 계속하며) 지금 지표 보드가 안 보여요~ 이용 수요 추이를 좀 봐야 하는데 안 나오네요!
개발자 A: 으악 ㅠㅠ 죄송한데 잠시만 새로고침을 멈춰주시겠어요?
위 사례들은 모두 서로 다른 종류의 지표와 연관된 문제지만, 공통적으로 지표를 제대로 관리하지 못했기 때문에 발생한 문제들입니다. 그러면 서비스를 운영할 때 서버 측에서 잘 관리하여 모니터링해야 하는 지표에는 어떤 것들이 있을까요? 서비스 규모나 필요에 따라 다르겠지만, 일반적으로 서버에서 관리하는 지표들은 아래와 같은 세 가지 종류의 모니터링 지표들로 구분할 수 있습니다.
인프라 지표
어플리케이션 지표
서비스 지표
‘사례 1’은 어플리케이션 지표 모니터링을, ‘사례 2’는 인프라 지표 모니터링을, ‘사례 3’은 서비스 지표 모니터링을 제대로 하지 못해서 발생한 문제들이었습니다. 위에서부터 하나씩 살펴보겠습니다.
인프라 지표 모니터링
서버를 운영하는 환경에서 인프라란, 서버 어플리케이션을 구동하기 위해 필요한 물리 머신, 네트워크 환경, DB 등의 기반 환경을 모두 포함하는 단어로 많이 쓰입니다. 인프라 레벨에서 문제가 생기면 어플리케이션 레벨에서 문제가 생겼을 때보다 더 치명적인 문제로 이어지는 경우가 많기에, 인프라 지표는 매우 신경 써서 관리해야 하는 지표 중 하나입니다. 위에서 언급한 ‘사례 2’와 같은 경우가 인프라 레벨에서 문제가 생긴 경우인데요. 장애 원인은 어플리케이션이 구동되고 있는 물리 머신의 디스크 상태를 제대로 모니터링하지 않아 인지하지 못하는 사이에 디스크가 꽉 차버리는 바람에 생긴 문제였습니다.
디스크가 다 차버리면 로그 file에 추가 로그를 기록할 수가 없고, 어플리케이션 로그를 보기 위해 사용하고 있던 datadog agent나 배포를 위한 CodeDeploy agent도 동작할 수 없는 상태가 될 수 있습니다. 이렇게 되면 자동화된 배포 파이프라인을 통해서가 아니라 사람이 직접 서버를 교체해야 하는 상황이 되고, 서버 장애로 이어질 가능성이 높아집니다. 실제로 이 사례에서는 트래픽을 받으며 직접 서버를 교체하는 과정에서 네트워크 순단이 생겼고, 순단으로 인한 요청이 쌓임여 서버장애까지 이어졌던 사례입니다.
이러한 경험들을 바탕으로 디어 서비스에서는 서비스 운영에 치명적인 영향을 줄 수 있는 각종 인프라 지표들을 모니터링하고 있습니다. 디어 서비스는 AWS의 다양한 서비스를 활용하여 운영되고 있으며, AWS에서는 CloudWatch를 통해 인프라 관리에 필요한 지표들을 대부분 제공합니다. 사용하는 인스턴스의 CPU, 메모리, 디스크 사용률 등을 기본적으로 포함하며, DB의 경우 ReadIOPS, WriteIOPS, Database connection 등 중요한 모니터링 지표들도 상세하게 알 수 있습니다. CloudWatch 지표와 관련된 기본적인 설명은 AWS에서 상세하게 제공하고 있으므로, 이 중 디어에서 유용하게 사용하고 있는 지표 몇 가지만 간단히 공유하겠습니다.
RDS Performance Insights: 어플리케이션에서 작성한 쿼리가 인프라 지표라고 보기에 약간 애매한 영역에 있을 수도 있으나, 특정 쿼리의 DB 리소스 점유를 분석한 것이므로 일단은 인프라 지표로 분류하였습니다. AWS에서는 DB 인스턴스의 기본적인 지표를 제공하는 것에서 나아가 DB 내부에서 수행되는 쿼리들 중 어떤 쿼리가 리소스를 얼마나 많이 차지하는지에 대한 해상도 높은 지표를 제공합니다. 이 모니터링 기능을 잘 활용하면 서비스에서 문제가 되는 느린 쿼리들을 추적하고 개선하는 데에 큰 도움이 됩니다.
EC2 메모리 지표: AWS EC2에서는 기본적으로 메모리 사용량에 대한 모니터링 지표를 제공하지 않습니다. 보통 API 서버를 띄우면 메모리와 관련된 지표를 간접적으로 모니터링할 수 있으며, 대규모 서비스의 경우 약간의 오버헤드도 큰 비용으로 이어질 수 있기 때문에 경량화된 기본 모니터링에서는 제외되어 있습니다. 하지만 작은 규모의 서비스를 운영하거나, 팀 내에서 사용할 다양한 툴들을 서버에서 구동하는 경우 메모리 지표가 보이지 않는 것이 불편할 수 있습니다. CloudWatch Agent를 따로 설치하고 일련의 설정들을 간단하게 조정하면 EC2 메모리 지표도 CloudWatch에서 함께 모니터링할 수 있습니다.
어플리케이션 지표 모니터링
본문에서 언급하는 어플리케이션은 API 서버를 뜻합니다. 어플리케이션은 물리 서버 내에서 구동되는 일종의 프로세스입니다. 서비스를 원활하게 유지하려면 어플리케이션에서 사용하는 자원들이나 어플리케이션의 상태 등을 상세하게 모니터링할 수 있어야 합니다. 이러한 모니터링을 APM(Application Performance Monitoring)이라고 부르며, 시중에는 다양한 APM 도구들이 있습니다. APM 도구들마다 다양한 수집 방식이 있지만, 편의를 위해 에이전트를 사용하는 방식과 라이브러리를 사용하는 방식으로 나누어 정리해 보겠습니다.
에이전트를 사용하는 방식
에이전트 기반 APM 도구는 어플리케이션이 실행되는 서버에 에이전트를 설치하여 서버 지표를 수집합니다. 에이전트는 어플리케이션의 실행 환경을 모니터링하고, 메트릭 및 로그를 수집하여 중앙 모니터링 시스템으로 전송합니다.이 방식은 시스템 전반의 데이터를 수집하는 데 효과적이며, 어플리케이션 코드에 개입하지 않고 완성된 어플리케이션을 실행하는 과정에서 에이전트가 개입하기 때문에 코드 작업이 필요하지 않다는 장점이 있습니다. 반면 에이전트를 설치하고 관리하는 과정이 까다로울 수 있으며, JVM과 같은 환경에서는 바이트 코드 수준에서 개입하여 데이터를 수집하기 때문에 에이전트에 문제가 생기면 어플리케이션 코드 레벨에서는 수정하기가 어렵습니다.
에이전트를 사용하는 APM의 예시로는 New Relic, Naver Pinpoint 등이 있습니다.별도 에이전트 없이 라이브러리를 사용하는 방식
라이브러리 기반 APM 도구는 어플리케이션 코드를 개발할 때 직접 APM 라이브러리를 포함시켜 어플리케이션 지표들을 수집하는 방식입니다. 개발자가 특정 APM 라이브러리를 개발 단계에서 추가하면 해당 라이브러리는 다양한 형태로 어플리케이션 지표들을 외부에 노출합니다. 그러면 그 지표들을 외부에서 적절하게 수집하여 시각화하고 모니터링합니다.이 방식은 에이전트를 관리할 필요가 없고 코드 레벨에서 더 정밀하게 모니터링 지표들을 커스터마이징할 수 있다는 장점이 있습니다. 하지만 코드 레벨에서 모니터링 관련된 의존성이 개입하고 코드 레벨에서의 작업이 필요해 약간 침투적이며, 코드 작성 과정에서 지표를 잘못 쌓거나 다른 문제가 생기지는 않을지 고려하며 작업해야 합니다.
라이브러리를 사용하는 방식에서는 Prometheus Client Libraries, Micrometer 와 같은 도구들을 활용할 수 있습니다.
처음에 디어 서비스에서는 종합적인 데이터 수집에 강점이 있는 에이전트 방식의 모니터링을 고려하였습니다. 그런데 New Relic은 유료 서비스인데다가 비용이 너무 비쌌고, 오픈 소스인 Naver Pinpoint는 AWS EMR을 활용해 엔터프라이즈 환경에서 확장 가능하도록 구축해 보았으나 유지보수가 꽤 까다로웠습니다. Datadog도 에이전트 방식을 지원하지만 host 하나당 가격을 매기기 때문에 서버가 많아질수록 비쌌습니다. 반면 라이브러리 방식인 Spring Boot Micrometer를 이용하는 방식은 Prometheus와 연동하여 사용하였더니 생각보다 지표를 적재하고 관리하는 일이 어렵지 않았습니다. Prometheus 서버를 유지보수하는 것도 리소스가 크게 들어가지 않았고, 서버 비용도 SaaS 이용료보다는 훨씬 저렴했습니다. API 서버측에서 지표를 쌓기 위해 설정해야 하는 코드도 아래와 같이 무척 간단하여, 처음에 기반 코드만 잘 갖춰두면 지표를 커스터마이징하기도 수월했습니다.
# build.gradle
...
dependencies {
api("org.springframework.boot:spring-boot-actuator")
implementation("io.micrometer:micrometer-registry-prometheus")
}
...
# application.yml
...
management:
endpoints:
prometheus:
enabled: true
...
// 외부 API 호출에 사용하는 webClient 설정
import io.micrometer.core.instrument.MeterRegistry
...
webClient = WebClient.builder()
...
.filter(
MetricsWebClientFilterFunction(
meterRegistry,
DefaultWebClientExchangeTagsProvider(),
"web_client_metrics",
AutoTimer.ENABLED
)
)
...
.build()
...
위와 같은 간단한 설정만으로도 Spring boot에서는 모니터링을 위한 별도의 API endpoint를 자동으로 생성하여 JVM 지표, DB 관련 지표 등 기본적으로 중요한 지표들을 promehtues에서 scrape 할 수 있는 형태로 제공합니다. promehtues쪽에서는 아래와 같은 간단한 설정만 추가하면 지표를 scrape 할 수 있습니다.
# prometheus.yml
scrape_configs:
- job_name: 'spring-boot-app'
metrics_path: '/actuator/prometheus'
static_configs:
- targets: ['baseurl:8080']
이렇게 수집한 어플리케이션 지표들은 grafana와 연동하여 모니터링하고 있습니다. Grafana에서도 직접 모니터링 보드에 일일이 쿼리를 작성할 필요가 없으며, 지표에 맞는 template을 제공합니다. Template에서 필요한 부분만 커스터마이징하여 대시보드를 만들고 있습니다.
기본적인 JVM 지표들
DB connection 지표들
HTTP 요청 관련 지표들
이러한 지표들은 서비스가 정상적으로 동작하고 있는지 한눈에 알아볼 수 있도록 하며, 서비스 장애 상황에서 어느 부분이 문제인지 빠르게 특정할 수 있도록 도와줍니다. HTTP 요청 관련 지표들은 서비스 개발에도 무척 유용하게 사용할 수 있습니다. 이 지표를 모니터링하기 시작한 뒤로부터 하드웨어 조작 요청에 대한 성공 & 실패율이나 지연 시간을 시계열로 기록할 수 있게 되었습니다. 그 뒤로 위에서 언급한 ‘사례 1’과 같은 문제 상황에서 아래와 같은 대화가 가능해졌습니다.
개발자 A: 이번에 새로 기능을 개발해야 하는데, 킥보드의 속도를 실시간으로 바꾸는 기능이 필요해요. 저희가 연동해서 사용해도 될까요?
개발자 B: 어.. 가능은 한데, 하드웨어 조작 요청이라 실패할 수 있어요.
개발자 A: 그래요? 실패율이 얼마나 되는데요?
개발자 B: 6% 정도 됩니다.
개발자 A: 헉.. 너무 높네요. 사용자 경험에 안 좋은 영향이 클 것 같은데, 다른 방법을 고려해봐야겠어요.
서비스 지표
사업을 키워나가면서 데이터 기반의 의사 결정을 하기 위해 꼭 필요한 것이 바로 서비스 지표입니다. 사실 엄밀히 구분하면 서비스 지표는 서버 측에서 모니터링해야 하는 시스템 지표라고 보기 어려울 수도 있습니다. 규모가 큰 기업에서는 서비스 지표를 모니터링하기 위해 데이터를 담당하는 데이터 팀이 따로 있는 경우가 많으며, Airflow와 같은 툴을 이용해 팀별로 데이터를 추출하고 가공하는 별도의 파이프라인을 만들어 운영하기도 합니다. 하지만 디어 서비스에서는 사업적인 의사 결정을 하는 데 아주 중요하게 사용하는 특정 지표들이 정해져 있는 상황이었고, 새로운 지표들이 추가되는 경우는 비교적 적었습니다. 시스템별로 쌓이는 데이터들을 연동하여 복합적으로 분석해야 하는 일도 비교적 적었습니다. 이러한 상황을 고려할 때 대규모 데이터 파이프라인을 별도로 구축하는 것보다는, 각 서비스별로 지표를 수집하고 가공하는 작은 파이프라인을 독자적으로 구축하고, 각 백엔드 개발 조직에서 스스로 관리하는 것이 유리할 것으로 보았습니다. 디어 서비스에서는 아래와 같은 방식으로 서비스 지표들을 수집하고 있습니다.
사용자 지표와 각종 운영 지표
현재 디어에서는 운영 DB로 AWS RDS를 사용하고 있으며, 시스템별로 독립적인 DB를 관리합니다. 분 단위, 시간 단위, 일 단위로 분석해야 하는 지표들을 운영 데이터로부터 추출하여 별도 테이블에 적재한 뒤에 데이터 분석에 활용합니다. 서로 다른 DB에 있는 데이터들을 함께 모아 분석해야 하는 경우에는 AWS Glue를 활용해 S3에 데이터를 적재하고, Athena query를 활용하여 분석할 수 있습니다.
IoT 기기 하드웨어 로그
디어에서 관리하는 각각의 킥보드 기기는 모두 독립적인 IoT 기기이며, 기기에서 발생하는 이벤트들을 TCP 통신을 이용하여 지속적으로 서버에 스트리밍합니다. 기기 위치, 배터리 상태, 기기 에러 로그, 기기 넘어짐 여부, 기기 누적 주행 거리 등 다양한 이벤트가 존재하며, 이러한 이벤트를 시계열로 잘 모으면 하드웨어 기기를 관리하는 데 매우 중요한 정보가 됩니다. 2만 대 기기에서 나오는 이벤트들을 모두 쌓으면 한 달에도 10억 건이 넘는 하드웨어 로그들이 쌓입니다. 서비스에서 사용하는 RDB로는 이 로그들을 감당하기 매우 어렵습니다. 이에 디어 서비스에서는 이러한 이벤트 로그들을 Kafka와 Elastic cloud를 활용하여 적재하고 분석하고 있습니다.
글 서두에서 언급한 ‘사례 3’과 같은 문제는 실제 운영 데이터와 모니터링을 위한 서비스 지표를 제대로 분리하지 않고 관리하였기 때문에 발생한 문제입니다. 6개월 이상의 긴 시간의 데이터 추이를 보기 위해 시간축을 길게 늘렸는데, 실세 서비스 운영 테이블에 무거운 쿼리가 나가는 바람에 서비스 장애로 이어진 것이죠. 말도 안 되는 상황이라고 생각할 수 있지만, 빠르게 지표를 보기 위해 제대로 지표를 분리하지 않고 운영 데이터를 직접 가공하여 지표 보드에 연결하여 사용하다 보면 위와 같은 문제가 발생할 수 있습니다. 서비스 지표는 실제 운영 데이터와 분리하여 관리하는 것이 안전하며, 서비스 규모에 따라 파이프라인의 규모를 적절하게 유지하여 최적의 리소스로 관리해야 합니다. 앞서와 같은 방식으로 디어 서비스는 운영 데이터로부터 서비스 지표를 분리하였으며, 서비스에서 필요한 데이터를 운영 데이터에 직접 접근하지 않고도 빠르게 분석할 수 있는 환경을 구축하였습니다. 자세한 수치나 항목을 공개할 수는 없지만, 디어에서는 아래와 같은 형태로 지표들을 모니터링 하고있습니다.
사용자 지표
운영 지표
IoT 기기 하드웨어 지표
IoT 기기 하드웨어 로그는 마이크로모빌리티 서비스에서 수집되는 꽤 특이한 로그입니다. 이 로그들을 잘 수집하고 분석하면 서비스 운영에 큰 도움이 되는 정보들을 얻을 수 있습니다. 오고 가는 모든 command들을 분석하여 수치화하여 ‘어떤 command가 통신 비용에 가장 큰 영향을 미치는지’ 알아내고 연마다 1억원이 넘는 통신 비용을 절감하는 데에 사용하였습니다. 기기가 노후화 되면 아주 낮은 확률로 주행 중에 갑자기 멈추는 현상이 생기기도 하는데, 이러한 기기들에 대한 로그를 분석하여 문제되는 기기들을 특정하고 수리하는 데에 사용할 수도 있습니다.
이상으로 디어 서비스에서 관리하고 있는 중요한 모니터링 지표들을 정리해보았습니다. 서비스를 안정적으로 운영하면서도 사업적으로 좋은 의사 결정을 이어나가려면 서비스에서 무슨 일이 벌어지고 있는지 자세히, 지속적으로 들여다보는 것이 중요합니다. 모니터링 환경을 잘 구축하면 작은 규모의 팀으로도 시스템의 적재적소를 잘 살피며 관리할 수 있고, 충분히 유의미한 데이터 분석 환경을 만들 수도 있습니다. 앞으로도 지표를 들여다보기 위한 크고 작은 노력들은 계속될 예정입니다.