앞서 쿠버네티스 클러스터에서 Prometheus를 구축할 수 있는 Prometheus Operator에 대해서 개략적으로만 알아보았다.
Prometheus Operator의 CRD들을 통해 간편하게 Prometheus 스택을 설치하고, 서비스 중단없이 스크래핑 대상을 추가할 수 있는 등의 장점을 봤는데, 이번에는 직접 Prometheus에 스크래핑 Job을 동적으로 추가할 수 있도록 알아본다.
Exporter
하드웨어나 프로세스로부터의 Metric을 수집해서 Prometheus가 스크래핑해갈 수 있도록 서빙하는 객체
Application이나 시스템이 자체적으로 노출하지 않는 내부의 지표들을 Prometheus가 이해할 수 있는 포맷으로 변환해준다.
즉, 현지어(각 Application 별 Metric)를 영어(PromQL)로 번역해주는 통역사 역할인 것이다.
구성 요소
Exporter는 크게 Registry와 Collector로 구성되어있다. Collector가 대상으로부터 Metric을 수집하고, Registry를 통해 Http Body에 수집한 Metric을 노출시킨다.
이렇게 Prometheus가 스크래핑할 수 있는 HTTP 엔드포인트(`/metrics`)를 서빙하는 구조를 갖게된다.
Exporter는 Java, Python, Go 등의 다양한 언어들의 라이브러리를 통해 커스텀하게 제작할 수도 있다.
이를 통해 현재 수많은 서드파티 Exporter가 공개되어서 다양한 하드웨어부터 데이터베이스, 스토리지, 웹 서버 등 다양한 서비스들로부터 발생되는 Metric을 수집할 수 있게 된 것이다.
Exporters and integrations | Prometheus
An open-source monitoring system with a dimensional data model, flexible query language, efficient time series database and modern alerting approach.
prometheus.io
ServiceMonitor
Prometheus 서버가 스크래핑 작업을 위한 '타깃'을 지정한 커스텀 리소스
즉, 어떤 Service를 '언제, 어떻게' 스크래핑할 것인지의 내용을 선언하는 리소스다.
ServiceMonitor는 이름에 맞게 'Service'를 스크래핑 대상으로 설정하고, PodMonitor는 파드를 대상으로 지정한다.
앞번에 알아보았듯이, Prometheus Operator로 설치되는 Prometheus 서버(커스텀 리소스)에는 ServiceMonitor나 PodMonitor 들을 선택할 Selector가 지정되어있다.
그렇기에 Monitor 객체에는 지정되어있는 라벨을 지정해주어야 Prometheus가 정상적으로 스크래핑해갈 수 있다.
# News 서비스의 /metrics endpoint로부터 메트릭 수집하는 ServiceMonitor
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: servicemonitor-news
namespace: monitoring
labels:
release: prometheus-stack
spec:
selector: # Metric을 가져올 서비스 지정
matchLabels:
app: rss-news-service
namespaceSelector:
matchNames: ["default"]
endpoints: # 서비스의 Metric 엔드포인트
- port: http
path: /metrics
interval: 15s
실습
Kubernetes 클러스터 내의 Application에서 발생하는 Metric들을 ServiceMonitor를 통해 Prometheus가 스크래핑할 수 있도록 구성해본다.
커스텀 Exporter 만들기
먼저 간단한 웹 application에 대한 Metric을 수집할 수 있도록 Exporter를 직접 만들어본다.
Application 코드 내에서 지표들을 스크래핑하고, 엔드포인트로 바로 노출시키는 식으로 구성할 수도 있지만, 이번에는 App 프로세스와 분리시켜서 '커스텀 Exporter'라는 의미에 조금 더 부합하도록 구성해보도록 한다.
이번에 만들 Exporter는 Application에 사이드카 형태로 붙여서 지연이나 복잡도를 줄이는 방향으로 구성할 것이다.
### Application
from flask import Flask
import time
app = Flask(__name__)
@app.route('/')
def index():
return "Hello from main app!"
# 앱 자체에는 메트릭 내보내기 로직이 없고, 사이드카가 스크랩
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8000)
### 커스텀 Exporter
from prometheus_client import Gauge, generate_latest, CONTENT_TYPE_LATEST
from flask import Flask, Response
import requests
import time
import os
app = Flask(__name__)
# Gauge 지표 정의 (서비스 생존여부, 레이턴시)
APP_UP = Gauge('custom_app_up', 'Main app HTTP status (1=up, 0=down)')
APP_LATENCY = Gauge('custom_app_latency_seconds', 'Main app response latency in seconds')
# Application을 타겟으로
TARGET = os.getenv('TARGET_URL', 'http://127.0.0.1:8000/')
def collect():
start = time.time()
try:
r = requests.get(TARGET, timeout=2)
latency = time.time() - start
APP_LATENCY.set(latency)
APP_UP.set(1 if r.status_code == 200 else 0)
except Exception:
APP_LATENCY.set(time.time() - start)
APP_UP.set(0)
@app.route('/metrics')
def metrics():
collect()
data = generate_latest()
return Response(data, mimetype=CONTENT_TYPE_LATEST)
if __name__ == '__main__':
app.run(host='0.0.0.0', port=9000)
수집할 수 있는 지표의 타입으로는 4종류(Gauge, Counter, Histogram, Summary)가 있고, 각 타입의 내용은 아래 문서에서 참고할 수 있다.
Metric types | Prometheus
An open-source monitoring system with a dimensional data model, flexible query language, efficient time series database and modern alerting approach.
prometheus.io
Application을 클러스터에 구동
### Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: python-webapp
namespace: demo
spec:
replicas: 2
selector:
matchLabels:
app: python-webapp
template:
metadata:
labels:
app: python-webapp
spec:
containers:
# 1) 메인 애플리케이션 컨테이너
- name: web
image: python-webapp:latest
imagePullPolicy: Never
ports:
- containerPort: 8000
# 2) 사이드카 커스텀 Exporter 컨테이너
- name: custom-exporter
image: custom-exporter:latest
imagePullPolicy: Never
env:
- name: TARGET_URL
value: "http://127.0.0.1:8000/"
ports:
- containerPort: 9000
name: metrics
### Service
apiVersion: v1
kind: Service
metadata:
name: python-webapp-svc
namespace: demo
labels:
app: python-webapp
spec:
selector:
app: python-webapp
ports:
- name: http
port: 80
targetPort: 8000
- name: metrics
port: 9100
targetPort: 9000
ServiceMonitor로 Prometheus와 연결
이제 ServiceMonitor로 앞에서 만든 Application을 연결한 Service에 대해 Prometheus 서버가 스크래핑할 수 있도록 연결한다.
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: python-webapp-sm
namespace: monitoring
labels:
release: prometheus-stack
spec:
selector:
matchLabels:
app: python-webapp # Application Service의 label
namespaceSelector:
matchNames:
- demo # Application이랑 Service가 배치된 namespace
endpoints:
- port: metrics
path: /metrics
interval: 15s
scrapeTimeout: 10s
Prometheus에서 ServiceMonitor 객체를 통해, 재시작되지 않고 Job이 바로 추가되는 것을 볼 수 있다.