Prometheus를 서비스로 제공하기

안녕하세요, LINE 후쿠오카 개발 부서에서 근무하고 있는 폴 트레일러입니다. 저는 LINE Family App을 위한 다수의 서버를 모니터링하는 업무를 담당하고 있습니다.

대부분의 개발자들은 코딩과 신규 기능 개발에 더 큰 흥미를 느끼겠지만 기능이 제대로 동작하지 않을 때 그 원인을 파악하는 것 또한 매력적이며, 매우 유용하기도 합니다. 모니터링은 문제의 원인을 파악하는 데 큰 도움이 될 수 있는데, 개발자가 모니터링 환경을 직접 구축하고 설정하는 것은 까다로울 수 있습니다. 여기서 바로 제가 등장합니다. 저는 개발자들이 쉽게 서비스별 임계치를 모니터할 수 있도록 돕는 역할을 담당하고 있습니다. 개발자들이 쉽게 모니터링 대상을 등록하고, 담당 서비스에 대한 알림 요청을 설정할 수 있도록 Prometheus 설정 값들을 수월하게 관리하는 Promgen을 개발하여 여러분께 소개하고자 합니다.

모니터링 설정하기

대부분의 서비스는 간단하고 작은 규모로 시작하지만 시간이 지날수록 다양한 구성 요소들이 여럿 생겨나 수백 개의 서버들을 통해 제공되곤 합니다. 이런 환경에서 모니터링을 설정하는 것은 쉬운 일이 아닙니다. Promgen은 모든 모니터링 설정 항목들을 한 곳에서 한번에 볼 수 있도록 개발되었습니다.

targets

또한 개발자들의 사용 편의성을 높이기 위해 개발자들이 원하는 작업을 한번의 버튼 클릭만으로도 수행할 수 있는 인터페이스를 제공합니다. 새로운 모니터링 대상을 등록하거나 유지 보수 작업이 진행되고 있는 서버에 대한 알림을 잠시 꺼두거나, 혹은 모니터링 대상이 정상적으로 구동되고 있는지 확인하는 작업 등을 버튼 클릭 하나로 간단하게 수행할 수 있습니다.

알림 설정하기

모니터링 대상을 등록한 후 문제가 발생하면 이에 대한 알림을 받아야겠지요? Promgen은 이메일로 알림을 발송하는 기능을 지원하기도 합니다. (개인적으로 저는 LINE Notify를 통해 알림을 받는 것을 선호합니다.) 알림 등록 화면에서 본인의 프로젝트를 쉽게 찾고, 단 몇 번의 클릭만으로도 알림을 설정할 수 있도록 개발하였습니다.

notifications

위 그림은 Promgen의 알림 설정 UI입니다. 이메일 알림이나 LINE Notify 알림을 손쉽게 설정하여 담당 서비스에 대한 알림을 수신할 수 있습니다.

알림 규칙 설정하기

Prometheus는 성능 계측을 위해 다양한 함수연산자를 제공하는 PromQL이라는 자체 쿼리 언어를 지원합니다. 어떻게 사용할 수 있는지 예를 한번 살펴 보겠습니다.

어떠한 값이 얼마나 빠르게 변경되는지 알아보려면 흔히 rate() 함수를 사용합니다.

# 5분 동안의 알림 수신 속도 확인하기
rate(alertmanager_alerts_received_total[5m])

혹은 max()avg() 연산자를 사용하여 쿼리 대상 메트릭을 특정 서비스나 프로젝트로 설정할 수도 있습니다.

# 특정 프로젝트에 속한 Prometheus 인스턴스 중 실행중인 인스턴스마다
# configuration 리로딩이 성공했던 시간의 최대 값과 최소 값의 차이를 구하여
# 우리 샤드에 있는 서버 간의 configuration drift가 120초 이상인지 확인하기
max(prometheus_config_last_reload_success_timestamp_seconds{job="prometheus"}) BY (project)
  -
min(prometheus_config_last_reload_success_timestamp_seconds{job="prometheus"}) BY (project)
> 120

time() 함수를 사용하여 두 개의 타임스탬프 값을 비교함으로써 job이 지연되고 있는지 혹은 SSL/TLS 인증서를 연장해야 하는지 확인할 수도 있습니다.

# 보유하고 있는 SSL 인증서 중 30일 내 만료되는 것이 있는지 파악한다
probe_ssl_earliest_cert_expiry{job="blackbox"} - time() < 86400 * 30

Prometheus 사용 중 어려운 부분은 알림 규칙을 작성하는 것입니다. 불필요한 알림을 발생시키지 않으려면 많은 서비스에 적용할 수 있을 만큼 보편적이면서도 너무 상세하지 않은 규칙을 작성해야 합니다. 가령, Hadoop 서비스를 실행한다면 CPU를 최대한 많이 사용하고 싶을 텐데요, 만약 알림 규칙에 디폴트 CPU 로드에 대한 알림이 포함되어 있다면? 알림이 꽤 발생하여 무척 골치 아플 것입니다. 혹은, 파일 저장 노드를 특정 속도로 실행하고 있을 때, 저장 공간의 80%를 사용하는 시점에 알림을 받는 대신 남은 용량이 10GB일 때 알림을 받고 싶을 수도 있겠지요. 메모리 사용량이나 디스크 사용량에 대한 알림 규칙을 작성할 때, 다양한 서비스들을 모두 만족시키는 보편적인 규칙으로 작성하는 것은 어렵습니다. 따라서 우리에게 필요한 것은 여러 상황을 아우르는 합리적이고 보편적인 규칙을 작성하는 반면, 필요 시 특정 서비스를 위해 해당 규칙을 오버라이드할 수 있는 기능입니다.

Rules

Promgen에서 매크로를 활용하면 Prometheus에 설정을 디플로이할 때 일부 규칙을 동적으로 생성할 수 있습니다.

  • 부모 규칙에는 기본적인 사항을 정의하고, 자식 규칙이 대체될 부분에 Promgen에 특화된 <exclude> 매크로를 삽입하여 규칙이 확장될 수 있도록 합니다.
  • 자식 서비스나 프로젝트에서 Overwrite 버튼을 클릭하여 기존의 규칙을 복사한 후 해당 서비스나 프로젝트에 특화된 규칙을 만듭니다.
  • Promgen은 규칙 파일을 생성할 때, 부모 규칙의 <exclude> 매크로 부분을 부모 규칙에서 파생된 자식 규칙을 모두 배제하는 비교식(matcher)으로 채워서 생성합니다.
# 자식 규칙은 특정 서비스만을 쿼리하기 위해 부모 규칙을 오버라이드함
node_load1{service="monitoring"} > 50
# 부모 규칙은 자식 규칙을 제외한 대상만 쿼리하기 위해 재작성됨
node_load1{service!~"monitoring|hadoop"} > 30

특정 서비스에 특화된 자식 규칙을 부모 규칙으로부터 생성(override)하면 해당 서비스는 자식 규칙의 영향만 받게 되고, 부모 규칙의 영향을 받지 않게 됩니다. 따라서, 특정 서비스에 대한 알림 수신을 원치 않을 때는 해당 서비스를 위한 자식 규칙을 생성한 후, 생성한 자식 규칙을 비활성화시키면 해당 서비스가 Prometheus 서버로 알림을 보내지 않습니다.

Prometheus 운영하기

Prometheus를 사용하는 LINE 개발자의 수가 늘면서 수요를 맞추기 위해 Prometheus 서버 수를 늘렸습니다. Prometheus는 확장성을 확보하기 위해 간단한 구조로 설계되었으며, 서버 간 아무것도 공유하지 않도록 설계되었습니다. 또한 가용성을 확대하기 위해 구성이 동일한 Prometheus 서버들을 묶어 운영 중입니다. 이렇게 연결된 서버들 중 모니터링 대상을 구별하여, 수천 개의 대상을 모니터링하고 주기적으로 수만 개의 샘플을 수집합니다.

Operation

다음은 위 그림에서 나타내고 있는 전반적인 Prometheus 운영 흐름입니다.

  1. Promgen은 Prometheus 서버들에 대한 서버 설정을 관리합니다. 참고로 관리 대상은 LINE에서 사용하고 있는 모든 Prometheus 서버들입니다.
  2. Prometheus는 모니터링 대상에 대한 데이터를 수집하고 성능 데이터를 기록합니다.
  3. 알림을 발송해야 하는 상황이 발생하면 먼저 알림 매니저(Alertmanager)가 알림에 대한 중복 여부를 확인한 후, LINE Notify를 통해 알림을 전달하거나 Promgen을 통해 이메일로 알림을 전달합니다.
  4. 개발자는 수신한 알림의 웹링크를 통해 바로 Grafana에 접속하여 문제를 진단할 수 있습니다.

Prometheus와 서비스 연동 시 발생하는 실수 대응하기

다른 모니터링 솔루션들과는 달리 Prometheus가 추적할 수 있는 데이터는 모니터링 대상별로 수천 가지에 이릅니다. 또한 Prometheus가 지원하는 쿼리 언어는 인프라의 다양한 레이어에 대해 쿼리할 수 있는 큰 강점을 가지고 있습니다. Prometheus는 너무 강력한 도구이기 때문에, 때론 Prometheus와 서비스를 연동하는 과정 중에 실수할 가능성이 있습니다.

Samples
rate(prometheus_local_storage_out_of_order_samples_total[5m]) > 10

Prometheus와 여러분의 서비스를 연동하는 작업 중 실수를 해도 Prometheus에 끼치는 직접적인 영향은 없습니다. 왜냐하면 Prometheus는 단순히 성능 데이터를 수집하고 기록할 뿐이기 때문입니다. 개발자들이 Prometheus를 쉽게 활용할 수 있도록 지원하기 위한 서비스를 제공함에 있어 중요한 것은 사용 편의성을 높이고, 개발자들이 실수하지 않도록 미연에 방지하는 것입니다. 이를 위해 개발자가 새로운 서비스나 업그레이드된 서비스를 출시하기 전, 발생할 만한 문제들을 미리 발견하고 보완할 수 있도록 테스트 환경을 제공하고 있습니다. 또한 개발자들이 문제점을 보다 더 빨리 발견할 수 있게 하기 위해, 우리는 Prometheus 규칙을 몇 가지 정의하여 Prometheus를 통해 Prometheus 자체를 모니터링하고 있습니다.

향후 계획

Prometheus는 매우 강력한 도구이기 때문에, 사용 방법을 습득하는 과정이 다소 어렵습니다. 모니터링 대상을 선정하고, 알림 메시지 수신처를 설정하는 등 설정해야 하는 값도 상당히 많습니다. Promgen은 개발자들의 편의를 도모하지만, 모두가 쉽게 이해할 수 있는 UI를 제공한다는 것은 여간 쉬운 일이 아닙니다. Promgen을 지속적으로 개선하여 지금보다 더 많은 개발자들이 Promgen을 통해 각자의 서비스를 보다 쉽게 모니터링할 수 있게 되기를 고대합니다.

Related Post