Prometheus (프로메테우스) 대해 알아보고 설치해보자
Prometheus (프로메테우스란)?
지표들을 시계열 데이터로 수집하고 측정하는 오픈 소스 모니터링 툴킷을 말한다.
여기서 지표라는 것은 예를 들어 웹 서버같은 경우 "요청수", "지연시간" 등이 될 수 있겠고 데이터 베이스 서버같은 경우에는 "커넥션 수", "쿼리 수" 등을 말할 수 있다.
이런 지표들을 통하여 서비스 부하, 통계등을 측정하는데 유용할 수 있다.
특징
- 측정항목 이름과 키/값 쌍으로 식별되는 다차원 데이터 모델
- 유연한 쿼리 언어인 PromQL 지원
- 분산 서버에 의존하지 않고 단일 서버 노드에 자율적
- 시계열 수집은 HTTP를 통한 풀 모델을 통해 발생
- 푸쉬 시계열은 중간 게이트웨이를 지원
- 다양한 그래프 및 대시보드 지원
언제 적합하고 적합하지 않은가?
프로메테우스는 빠르게 수집하고 모니터링 할 수 있는 시스템에 적합하나 100% 정확성이 필요한 시스템에는 다른 도구와 사용하는 것이 좋다.
프로메테우스 동작 방식이 일정 주기를 간격으로 수집하기 때문에 간격 내에 갑작스럽게 발생하는 변동성이나 이벤트를 놓칠 수 있으며 100% 캡쳐하기 어려울 수 있다. 또한 분산 시스템에서는 네트워크나 다운타임으로 데이터 손실이 발생할 수 있다. 그렇기때문에 모든 데이터를 수집하는 것이 아니고 일정 간격으로 샘플링 된 데이터를 수집하기때문에 근사치를 활용하는 방식으로 가는 것이 좋다.
구성요소
Prometheus 생태계는 여러 구성 요소로 구성되며 그 중 다수는 선택 사항이다.
- 시계열 데이터를 스크랩하고 저장하는 기본 Prometheus 서버
- 애플리케이션 코드 계측을 위한 클라이언트 라이브러리
- 단기 작업 지원을 위한 푸시 게이트웨이
- HAProxy, StatsD, Graphite 등과 같은 서비스를 위한 특수 목적 수출업체
- 경고를 처리하는 경고 관리자
- 다양한 지원 도구
아키텍처
중간 푸쉬 게이트웨이를 통하여 측정항목을 스크랩한다. 스크랩을 통하여 모든 샘플을 로컬에 저장하고 이 데이터에 대한 규칙을 실행하여 기존 데이터에서 새로운 집계 및 기록하거나 경고를 생성한다. Grafana 또는 기타 API 소비자를 사용하여 수집된 데이터를 시각화할 수 있다.
여기서 TSDB란 "Time Series Database"의 약자로, 프로메테우스에서 사용되는 데이터베이스 형태를 나타낸다.
RDB vs Prometheus
로그를 RDB에 기록하여 측정하는 것과 Prometheus를 사용하여 측정하는 것이 무엇이 다른가? 에 대한 의문점이 들 수 있다.
- RDB는 정형화된 데이터를 저장하는 용도지만 Prometheus는 모니터링에 최적화된 시스템으로 설계되었다.
- Prometheus는 데이터 기반의 경고 시스템을 내장하고 있어 문제 발생 시 신속한 대응이 가능하다.
- 시스템의 상태 및 성능을 실시간으로 파악하는 데 강점이 있다.
📘 설치 개요
Docker를 사용하여 Prometheus 서버를 구동하고 호스트에서는 FastAPI를 사용하여 웹 서버를 실행한다.
백엔드 개발에 중점을 둔 것으로 로그 출력과 Prometheus의 스크랩 동작을 확인하는 것을 목표로 한다.
Github에 풀소스를 업로드하였으니 여기에서 다운받아 설치해보면된다.
https://github.com/yscho03/sample-fastapi-with-prometheus
💻 설치 방법
1. Docker로 Promethues 서버 설치
먼저 docker-compose.yml 파일을 작성해보자. 여기서 volumes 부분은 호스트에 있는 prometheus.yml 파일을 연결하도록 설정했고 호스트 네트워크와 연결하기 위해 extra_hosts를 사용하여 호스트 매핑을 설정했다.
version: '3'
services:
prometheus:
image: prom/prometheus
extra_hosts:
- "host.docker.internal:${HOST_IP}"
ports:
- "9090:9090"
volumes:
- ./config/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
command:
- "--log.level=debug"
- "--config.file=/etc/prometheus/prometheus.yml"
그 다음은 프로메테우스 설정 파일 ./config/prometheus/prometheus.yml 을 작성하자.
전체 디폴트는 15초로 설정하였고 sample이라는 job_name으로 수집주기는 기본 5초로 설정하였다. 이를 통하여 프로메테우스 서버는 호스트 웹 서버의 8000 포트를 5초 간격으로 요청할 것이다.
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: 'sample'
scrape_interval: 5s
static_configs:
- targets: ['host.docker.internal:8000']
이제 docker-compose 실행하여 container를 생성해보자.
$ export HOST_IP=$(echo `hostname -I | awk '{print $1}'`)
$ docker-compose up -d
2. FastAPI 환경 설치
FastAPI 웹 서버를 만들어보자. 필요한 패키지를 설치한다.
$ pip install fastapi uvicorn[standard]
$ pip install prometheus-fastapi-instrumentator
main.py에 다음과 같이 코딩을 한다.
from fastapi import FastAPI
from fastapi.openapi.utils import get_openapi
from prometheus_fastapi_instrumentator import Instrumentator
app = FastAPI()
def custom_openapi():
if app.openapi_schema:
return app.openapi_schema
openapi_schema = get_openapi(
title="Sample API",
version="1.0.0",
description="Sample API",
routes=app.routes,
)
app.openapi_schema = openapi_schema
return app.openapi_schema
app.openapi = custom_openapi
instrumentator = Instrumentator().instrument(app)
instrumentator.expose(app, include_in_schema=False)
@app.get("/api/v1/health", description="API status check", tags=["Common"])
def check_health():
return {
"status": "OK"
}
주목할 부분은 기본 측정항목으로 앱을 계측하고 측정항목을 노출하는 부분이다.
실제로 동작하는 소스를 열어보면 FastAPI의 middleware 레이어에 등록하여 요청수, 응답수등을 컨트롤하여 전처리와 후처리를 하는 부분을 볼 수 있다.
instrumentator = Instrumentator().instrument(app)
instrumentator.expose(app, include_in_schema=False)
3. FastAPI 웹 서버 기동
자 그럼 이제 웹 서버를 기동시켜보자.
기본은 8000포트이고 변경시는 config/prometheus/prometheus.yml 안의 port도 수정하셔야 합니다.
$ uvicorn main:app --host=0.0.0.0 --port=8000
4. FastAPI /metrics 확인
브라우저 또는 curl를 호출하여 http://localhost:8000/metrics 을 정상적으로 출력하고 있는지 확인한다.
...
http_requests_total{handler="/api/v1/health",method="GET",status="2xx"} 1.0
http_request_duration_seconds_bucket{handler="/api/v1/health",le="0.1",method="GET"} 1.0
...
✅ 동작 확인
Promethues 로그 확인
ts=2024-02-04T00:28:41.267Z caller=main.go:1282 level=info msg="Completed loading of configuration file" filename=/etc/prometheus/prometheus.yml totalDuration=43.533308ms db_storage=1.44µs remote_storage=1.888µs web_handler=504ns query_engine=1µs scrape=25.986125ms scrape_sd=200.677µs notify=1.567µs notify_sd=4.126µs rules=1.575µs tracing=16.302µs
ts=2024-02-04T00:28:41.267Z caller=main.go:1024 level=info msg="Server is ready to receive web requests."
ts=2024-02-04T00:28:41.268Z caller=manager.go:212 level=debug component="discovery manager scrape" msg="Discoverer channel closed" provider=static/0
ts=2024-02-04T00:28:41.268Z caller=manager.go:146 level=info component="rule manager" msg="Starting rule manager..."
FastAPI 로그 출력 확인
설정된 스크랩 주기 시간에 맞추어 다음과 같이 /metrics를 호출하여 스크랩을 시작한다.
INFO: Started server process [1514]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
INFO: 172.18.0.2:39004 - "GET /metrics HTTP/1.1" 200 OK
INFO: 172.18.0.2:39004 - "GET /metrics HTTP/1.1" 200 OK
❌ 오류
FastAPI 로그 출력 확인
Promethues 서버에서 host의 로그를 스크랩하지 못하는 경우이다.
$ docker logs -f <container-id>
ts=2024-02-04T00:28:51.143Z caller=scrape.go:1274 level=debug component="scrape manager" scrape_pool=sample target=http://host.docker.internal:8000/metrics msg="Scrape failed" err="Get \"http://host.docker.internal:8000/metrics\": dial tcp 20.100.0.101:8000: connect: connection refused"
다음과 같은 경우에는 docker 컨테이너 안의 /etc/hosts를 확인하여 host 도메인 설정이 잘 되어 있는지 확인한다. host.docker.internal 부분 확인하면 된다.
$ docker exec -it <container-id> sh
/prometheus $ cat /etc/hosts
/prometheus $ cat /etc/hosts
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
<HOST_IP> host.docker.internal
172.18.0.2 ca56b8cb6211