Go 언어에서의 Prometheus 메트릭 수집 🚀📊
안녕하세요, 여러분! 오늘은 Go 언어에서 Prometheus 메트릭을 수집하는 방법에 대해 알아볼 거예요. 이거 완전 꿀팁이에요! 🍯 프로그래밍 세계에서 성능 모니터링은 정말 중요하죠. 그래서 우리는 Prometheus라는 강력한 도구를 사용할 거예요. 이 글을 읽고 나면 여러분도 Go 언어로 Prometheus 메트릭을 수집하는 프로 개발자가 될 수 있을 거예요! 😎
참고: 이 글은 '재능넷'의 '지식인의 숲' 메뉴에 등록될 예정이에요. 재능넷은 다양한 재능을 거래하는 플랫폼인데, 여러분의 Go 언어와 Prometheus skills도 충분히 재능이 될 수 있답니다! 👨💻👩💻
1. Prometheus란 무엇인가요? 🤔
Prometheus는 오픈소스 모니터링 및 알림 툴킷이에요. 2012년에 SoundCloud에서 시작됐고, 지금은 Cloud Native Computing Foundation의 두 번째 졸업 프로젝트랍니다. 대박이죠? 👏
Prometheus의 주요 특징들을 살펴볼까요?
- 다차원 데이터 모델 (시계열 데이터는 메트릭 이름과 키/값 쌍으로 식별)
- PromQL이라는 유연한 쿼리 언어
- HTTP pull 모델을 통한 시계열 수집
- 푸시 게이트웨이를 통한 short-lived 작업 지원
- 서비스 디스커버리 또는 정적 구성을 통한 대상 발견
- 다양한 그래프 및 대시보드 모드
와! 이렇게 보니까 Prometheus 완전 만능이네요! 😮
2. Go 언어와 Prometheus의 만남 💑
Go 언어와 Prometheus는 찰떡궁합이에요! Go는 성능이 뛰어나고 동시성 처리가 쉬워서, 대규모 시스템 모니터링에 딱이거든요. Prometheus는 Go로 작성되었기 때문에, Go 애플리케이션과의 통합이 아주 smooth해요. 마치 재능넷에서 딱 맞는 재능을 찾은 것처럼요! 😉
Go에서 Prometheus를 사용하려면 'github.com/prometheus/client_golang' 패키지를 사용해야 해요. 이 패키지는 Prometheus 클라이언트 라이브러리의 Go 버전이에요.
import (
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
이렇게 임포트하면 준비 끝! 이제 메트릭을 수집할 준비가 됐어요. 👍
3. Prometheus 메트릭 타입 알아보기 📊
Prometheus는 네 가지 핵심 메트릭 타입을 제공해요. 각각의 특징을 알아볼까요?
1. Counter (카운터) 🔢
누적되는 값을 표현할 때 사용해요. 예를 들면 요청 수, 완료된 작업 수 등이 있죠. 값은 항상 증가하거나 리셋될 때 0으로 초기화돼요.
2. Gauge (게이지) 🌡️
임의로 오르내릴 수 있는 값을 나타내요. 메모리 사용량, 동시 요청 수 같은 걸 측정할 때 쓰죠.
3. Histogram (히스토그램) 📊
값의 분포를 측정해요. 요청 지속 시간이나 응답 크기 같은 것들을 측정할 때 유용하죠.
4. Summary (요약) 📋
히스토그램과 비슷하지만, 백분위수를 직접 계산해요. 클라이언트 측에서 더 정확한 백분위수를 얻을 수 있어요.
이렇게 네 가지 타입이 있어요. 각각의 상황에 맞게 쓰면 되겠죠? 👌
4. Go에서 Prometheus 메트릭 구현하기 💻
자, 이제 실제로 Go 코드에서 Prometheus 메트릭을 어떻게 구현하는지 알아볼까요? 예제와 함께 설명해드릴게요!
4.1 Counter 구현하기
먼저 Counter를 구현해볼게요. 웹 서버의 요청 수를 세는 카운터를 만들어봐요.
var (
httpRequests = promauto.NewCounter(prometheus.CounterOpts{
Name: "http_requests_total",
Help: "총 HTTP 요청 수",
})
)
func handleRequest(w http.ResponseWriter, r *http.Request) {
httpRequests.Inc()
// 요청 처리 로직
}
여기서 'httpRequests.Inc()'를 호출할 때마다 카운터가 1씩 증가해요. 간단하죠? 😃
4.2 Gauge 구현하기
이번엔 Gauge를 구현해볼게요. 현재 활성 고루틴 수를 측정하는 게이지를 만들어봐요.
var (
goroutines = promauto.NewGauge(prometheus.GaugeOpts{
Name: "goroutines_count",
Help: "현재 실행 중인 고루틴 수",
})
)
func updateGoroutinesCount() {
for {
goroutines.Set(float64(runtime.NumGoroutine()))
time.Sleep(time.Second)
}
}
이 코드는 1초마다 현재 고루틴 수를 업데이트해요. 실시간으로 변하는 값을 측정하기에 딱이죠! 👍
4.3 Histogram 구현하기
이제 Histogram을 구현해볼 차례예요. HTTP 요청 처리 시간을 측정하는 히스토그램을 만들어볼게요.
var (
httpDuration = promauto.NewHistogram(prometheus.HistogramOpts{
Name: "http_request_duration_seconds",
Help: "HTTP 요청 처리 시간 (초)",
Buckets: prometheus.DefBuckets,
})
)
func handleRequestWithTimer(w http.ResponseWriter, r *http.Request) {
start := time.Now()
// 요청 처리 로직
duration := time.Since(start).Seconds()
httpDuration.Observe(duration)
}
이 코드는 각 HTTP 요청의 처리 시간을 측정하고, 그 분포를 기록해요. 성능 분석에 아주 유용하답니다! 📊
4.4 Summary 구현하기
마지막으로 Summary를 구현해볼게요. 데이터베이스 쿼리 실행 시간을 측정하는 summary를 만들어봐요.
var (
dbQueryDuration = promauto.NewSummary(prometheus.SummaryOpts{
Name: "db_query_duration_seconds",
Help: "데이터베이스 쿼리 실행 시간 (초)",
Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001},
})
)
func executeDBQuery() {
start := time.Now()
// DB 쿼리 실행 로직
duration := time.Since(start).Seconds()
dbQueryDuration.Observe(duration)
}
이 코드는 DB 쿼리 실행 시간의 중앙값(50%), 90%, 99% 백분위수를 계산해요. DB 성능 최적화에 딱이죠! 🎯
5. Prometheus 메트릭 노출하기 🌐
자, 이제 메트릭을 수집했으니 이를 Prometheus 서버에 노출시켜야 해요. Go에서는 이를 위한 간단한 HTTP 핸들러를 제공해요.
import (
"net/http"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
func main() {
http.Handle("/metrics", promhttp.Handler())
http.ListenAndServe(":8080", nil)
}
이 코드를 실행하면, http://localhost:8080/metrics 엔드포인트에서 모든 등록된 메트릭을 볼 수 있어요. Prometheus 서버는 이 엔드포인트를 주기적으로 스크랩하여 데이터를 수집하죠. 완전 쉽죠? 😎
6. 커스텀 메트릭 만들기 🛠️
Prometheus 클라이언트 라이브러리가 제공하는 기본 메트릭 외에도, 여러분만의 커스텀 메트릭을 만들 수 있어요. 이게 바로 Prometheus의 강력한 점이죠!
예를 들어, 우리 서비스의 사용자 수를 추적하는 커스텀 게이지를 만들어볼까요?
var (
activeUsers = promauto.NewGauge(prometheus.GaugeOpts{
Name: "active_users",
Help: "현재 활성 사용자 수",
})
)
func updateActiveUsers(count int) {
activeUsers.Set(float64(count))
}
// 사용 예:
updateActiveUsers(100) // 활성 사용자가 100명일 때
updateActiveUsers(150) // 활성 사용자가 150명으로 증가했을 때
이렇게 하면 우리 서비스의 실시간 사용자 수를 추적할 수 있어요. 재능넷 같은 플랫폼에서 이런 메트릭은 서비스의 인기도를 실시간으로 파악하는 데 유용할 거예요! 📈
7. 레이블 사용하기 🏷️
Prometheus의 또 다른 강력한 기능은 레이블이에요. 레이블을 사용하면 같은 메트릭에 대해 다양한 차원의 데이터를 수집할 수 있죠.
예를 들어, HTTP 요청을 경로별로 구분해서 카운트하고 싶다면 이렇게 할 수 있어요:
var (
httpRequestsTotal = promauto.NewCounterVec(
prometheus.CounterOpts{
Name: "http_requests_total",
Help: "경로별 총 HTTP 요청 수",
},
[]string{"path"},
)
)
func handleRequest(w http.ResponseWriter, r *http.Request) {
httpRequestsTotal.WithLabelValues(r.URL.Path).Inc()
// 요청 처리 로직
}
이렇게 하면 각 경로별로 요청 수를 따로 추적할 수 있어요. "/home", "/profile", "/settings" 등 각 페이지의 인기도를 한눈에 파악할 수 있겠죠? 완전 꿀팁이에요! 🍯
8. 메트릭 그룹화하기 👥
프로젝트가 커지면 메트릭의 수도 늘어나겠죠? 이럴 때 메트릭을 그룹화하면 관리하기 훨씬 편해져요.
예를 들어, 데이터베이스 관련 메트릭을 하나의 구조체로 그룹화해볼까요?
type DBMetrics struct {
QueryDuration prometheus.Summary
ConnectionCount prometheus.Gauge
ErrorRate prometheus.Counter
}
func NewDBMetrics() *DBMetrics {
return &DBMetrics{
QueryDuration: promauto.NewSummary(prometheus.SummaryOpts{
Name: "db_query_duration_seconds",
Help: "데이터베이스 쿼리 실행 시간 (초)",
}),
ConnectionCount: promauto.NewGauge(prometheus.GaugeOpts{
Name: "db_connection_count",
Help: "현재 데이터베이스 연결 수",
}),
ErrorRate: promauto.NewCounter(prometheus.CounterOpts{
Name: "db_error_total",
Help: "데이터베이스 오류 총 횟수",
}),
}
}
// 사용 예:
dbMetrics := NewDBMetrics()
dbMetrics.QueryDuration.Observe(0.5)
dbMetrics.ConnectionCount.Set(10)
dbMetrics.ErrorRate.Inc()
이렇게 그룹화하면 코드도 깔끔해지고, 관리도 쉬워져요. 완전 개이득! 👍
9. 고급 기능: 커스텀 콜렉터 만들기 🚀
때로는 기본 메트릭 타입으로는 부족할 때가 있어요. 이럴 때 커스텀 콜렉터를 만들어 사용할 수 있답니다.
예를 들어, 시스템의 현재 메모리 사용량을 수집하는 커스텀 콜렉터를 만들어볼까요?
type memoryCollector struct {
memoryUsage *prometheus.Desc
}
func NewMemoryCollector() *memoryCollector {
return &memoryCollector{
memoryUsage: prometheus.NewDesc(
"system_memory_usage_bytes",
"현재 시스템 메모리 사용량 (바이트)",
nil, nil,
),
}
}
func (collector *memoryCollector) Describe(ch chan<- *prometheus.Desc) {
ch <- collector.memoryUsage
}
func (collector *memoryCollector) Collect(ch chan<- prometheus.Metric) {
var m runtime.MemStats
runtime.ReadMemStats(&m)
ch <- prometheus.MustNewConstMetric(
collector.memoryUsage,
prometheus.GaugeValue,
float64(m.Alloc),
)
}
// 사용 예:
collector := NewMemoryCollector()
prometheus.MustRegister(collector)
이 코드는 Go 런타임의 메모리 통계를 읽어와 Prometheus 메트릭으로 변환해요. 이렇게 하면 시스템의 메모리 사용량을 실시간으로 모니터링할 수 있죠. 완전 프로 개발자 느낌 나지 않나요? 😎
10. 메트릭 테스트하기 🧪
메트릭을 구현했다면, 당연히 테스트도 해야겠죠? Go에서는 Prometheus 메트릭을 쉽게 테스트할 수 있는 방법을 제공해요.
카운터 메트릭을 테스트하는 예제를 볼까요?
func TestHTTPRequestsCounter(t *testing.T) {
// 테스트를 위한 레지스트리 생성
registry := prometheus.NewRegistry()
// 테스트할 카운터 생성
counter := promauto.With(registry).NewCounter(prometheus.CounterOpts{
Name: "http_requests_total",
Help: "총 HTTP 요청 수",
})
// 카운터 증가
counter.Inc()
counter.Inc()
// 메트릭 수집
metricFamilies, err := registry.Gather()
if err != nil {
t.Fatalf("메트릭 수집 실패: %v", err)
}
// 결과 확인
if len(metricFamilies) != 1 {
t.Fatalf("예상치 못한 메트릭 수: got %v, want 1", len(metricFamilies))
}
mf := metricFamilies[0]
if len(mf.Metric) != 1 {
t.Fatalf("예상치 못한 샘플 수: got %v, want 1", len(mf.Metric))
}
if got := *mf.Metric[0].Counter.Value; got != 2 {
t.Errorf("예상치 못한 카운터 값: got %v, want 2", got)
}
}