Kubernetes Custom Metrics Exporter Nasıl Yazılır?
Kubernetes, CPU ve bellek tüketimini görebilir; ancak her iş yükü bu iki sinyalle doğru ölçeklenmez. Kuyrukta bekleyen mesaj sayısı, açık WebSocket bağlantıları veya bir batch işinin tamamlanma süresi çoğu zaman daha anlamlıdır. Bu uygulama sinyallerini Prometheus’a taşımanın en temiz yollarından biri, küçük ve tek amaçlı bir metrics exporter geliştirmektir.
Bu rehberde Go ile çalışan bir exporter oluşturacak, /metrics uç noktasını yayınlayacak, imajı hazırlayacak ve Kubernetes üzerinde ServiceMonitor ile scrape edeceğiz. Örnek, Kubernetes’in resmî custom metrics exporter rehberindeki mimariyi temel alıyor; komutlar ve açıklamalar ise doğrudan uygulanabilir bir akış için yeniden düzenlendi.
Exporter, Metrics Server ve Adapter Aynı Şey Değil
Kuruluma geçmeden önce dört bileşenin görevini ayırmak önemli. Aksi hâlde /metrics uç noktasını açınca HPA’nın metriği otomatik göreceği gibi yanlış bir beklenti oluşabilir.
| Bileşen | Görevi | HPA ile ilişkisi |
|---|---|---|
| metrics-server | Pod ve node CPU/bellek kaynak metriklerini toplar. | Resource Metrics API üzerinden doğrudan kullanılabilir. |
| Metrics exporter | Uygulamaya özgü değerleri Prometheus biçiminde /metrics yolunda sunar. |
Tek başına Custom Metrics API oluşturmaz. |
| Prometheus | Exporter uç noktasını scrape eder ve zaman serilerini saklar. | Metriklerin sorgulanabilir kaynağıdır. |
| Prometheus Adapter | Prometheus sorgularını Kubernetes Custom Metrics API’ye taşır. | HPA’nın özel metrikleri kullanmasını sağlar. |
Bu ayrımın kısa özeti şu: Exporter metriği üretir, Prometheus saklar, adapter ise gerektiğinde Kubernetes API’ye sunar.
Mimari Akış Nasıl Çalışır?
- Exporter, veritabanı, mesaj kuyruğu veya dahili API gibi gerçek veri kaynağını okur.
- Değeri Counter, Gauge ya da Histogram olarak bellekte günceller.
/metricsisteği geldiğinde Prometheus metin biçiminde yanıt verir.- Prometheus, ServiceMonitor veya annotation tabanlı discovery ile bu uç noktayı düzenli aralıklarla scrape eder.
- İhtiyaç varsa Prometheus Adapter, seçilen seriyi Custom Metrics API üzerinden HPA’ya açar.
Doğru Metrik Türünü Seçin
- Counter: Yalnızca artan toplamlar içindir. İşlenen görev ve hata sayısı buna örnektir.
- Gauge: Artıp azalabilen anlık değerler içindir. Kuyruk derinliği veya aktif bağlantı sayısı gauge olmalıdır.
- Histogram: Süre ve boyut dağılımlarını bucket’lar hâlinde kaydeder. p50 ve p99 gibi yüzdelikler için uygundur.
Adlandırmada birim bilgisini sona ekleyin: worker_job_duration_seconds gibi. Sayaçlarda _total son ekini kullanmak, Prometheus ekosistemindeki araçların metriği doğru yorumlamasını kolaylaştırır.
Go Projesini Oluşturun
Terminal ortamınızda GITHUB_USER değişkeninin gerçek GitHub kullanıcı veya organizasyon adınızı içerdiğinden emin olun. Ardından modülü ve Prometheus bağımlılıklarını oluşturun:
test -n "${GITHUB_USER:?GITHUB_USER tanımlanmalı}"
mkdir custom-metrics-exporter && cd custom-metrics-exporter
go mod init "github.com/${GITHUB_USER}/custom-metrics-exporter"
go get github.com/prometheus/client_golang/prometheus
go get github.com/prometheus/client_golang/prometheus/promhttp
Aşağıdaki main.go, üç farklı metrik türünü kaydeder ve beş saniyede bir örnek değer üretir. Simülasyon bölümü daha sonra gerçek veri kaynağınızla değiştirilmelidir.
package main
import (
"log"
"math/rand"
"net/http"
"time"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
var (
jobsProcessed = prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "worker_jobs_processed_total",
Help: "Total number of jobs processed, partitioned by status.",
},
[]string{"status"},
)
queueDepth = prometheus.NewGauge(
prometheus.GaugeOpts{
Name: "worker_queue_depth",
Help: "Current number of jobs waiting in the queue.",
},
)
jobDuration = prometheus.NewHistogram(
prometheus.HistogramOpts{
Name: "worker_job_duration_seconds",
Help: "Time spent processing a single job.",
Buckets: prometheus.DefBuckets,
},
)
)
func init() {
prometheus.MustRegister(jobsProcessed, queueDepth, jobDuration)
}
func collectMetrics() {
ticker := time.NewTicker(5 * time.Second)
defer ticker.Stop()
for range ticker.C {
queueDepth.Set(float64(rand.Intn(50)))
jobDuration.Observe(
(time.Duration(rand.Intn(200)) * time.Millisecond).Seconds(),
)
jobsProcessed.WithLabelValues("success").Inc()
}
}
func main() {
go collectMetrics()
http.Handle("/metrics", promhttp.Handler())
http.HandleFunc("/healthz", func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusOK)
})
log.Println("Listening on :8080")
if err := http.ListenAndServe(":8080", nil); err != nil {
log.Fatalf("server error: %v", err)
}
}
Exporter’ı Yerelde Doğrulayın
Uygulamayı başlatın ve ikinci bir terminalden yalnızca kendi metriklerinizi filtreleyin:
go run .
curl -sS http://0.0.0.0:8080/metrics | grep '^worker_'
Çıktıda worker_queue_depth, worker_jobs_processed_total ve histogram serilerini görmelisiniz. /healthz yalnızca süreç sağlığını bildirir; metrik verisini sağlık kontrolüne taşımamak hem yanıtı küçük tutar hem de liveness probe’un amacını netleştirir.
Konteyner İmajını Hazırlayın
Çok aşamalı Dockerfile, derleme araçlarını çalışma zamanı imajına taşımaz. Aşağıdaki örnek, statik binary üretir ve root olmayan distroless imajında çalıştırır:
FROM golang:1.26-alpine AS builder
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build \
-trimpath -ldflags="-s -w" \
-o /out/exporter .
FROM gcr.io/distroless/static-debian12:nonroot
COPY --from=builder /out/exporter /exporter
EXPOSE 8080
ENTRYPOINT ["/exporter"]
docker build -t custom-metrics-exporter:v1.0.0 .
Yerel bir kind kümesi kullanıyorsanız imajı doğrudan node’lara yükleyebilirsiniz. Yönetilen veya çok node’lu kümelerde ise CI/CD hattınızla ACR, GHCR ya da kullandığınız kurumsal registry’ye push edin ve manifestte immutable bir tag veya digest kullanın.
Kubernetes’e Dağıtın
Aşağıdaki manifest yerel imaj adını kullanır. Uzak kümelerde image alanını CI/CD hattınızın ürettiği tam imaj adresiyle değiştirin.
apiVersion: apps/v1
kind: Deployment
metadata:
name: custom-metrics-exporter
namespace: monitoring
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: custom-metrics-exporter
template:
metadata:
labels:
app.kubernetes.io/name: custom-metrics-exporter
spec:
containers:
- name: exporter
image: custom-metrics-exporter:v1.0.0
imagePullPolicy: IfNotPresent
ports:
- name: metrics
containerPort: 8080
livenessProbe:
httpGet:
path: /healthz
port: metrics
initialDelaySeconds: 5
periodSeconds: 10
resources:
requests:
cpu: 50m
memory: 32Mi
limits:
cpu: 100m
memory: 64Mi
---
apiVersion: v1
kind: Service
metadata:
name: custom-metrics-exporter
namespace: monitoring
labels:
app.kubernetes.io/name: custom-metrics-exporter
spec:
selector:
app.kubernetes.io/name: custom-metrics-exporter
ports:
- name: metrics
port: 8080
targetPort: metrics
kubectl apply -f exporter.yaml
kubectl get pods -n monitoring \
-l app.kubernetes.io/name=custom-metrics-exporter
Prometheus ServiceMonitor Ekleyin
Prometheus Operator veya kube-prometheus-stack kullanıyorsanız ServiceMonitor, scrape hedefini açık biçimde tanımlar. release etiketi Prometheus kaynağınızdaki selector ile aynı olmalıdır; farklı bir Helm release adı kullandıysanız bu değeri ona göre düzenleyin.
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: custom-metrics-exporter
namespace: monitoring
labels:
release: kube-prometheus-stack
spec:
selector:
matchLabels:
app.kubernetes.io/name: custom-metrics-exporter
endpoints:
- port: metrics
path: /metrics
interval: 15s
kubectl apply -f servicemonitor.yaml
kubectl describe servicemonitor custom-metrics-exporter -n monitoring
Scrape Zincirini Kontrol Edin
Önce pod ve ServiceMonitor durumunu, ardından Prometheus hedefini kontrol edin:
kubectl get pods -n monitoring \
-l app.kubernetes.io/name=custom-metrics-exporter
kubectl port-forward svc/prometheus-operated 9090 -n monitoring
Port-forward açıkken tarayıcıda 9090 portundaki /targets yolunu açın. Hedef UP görünüyorsa expression browser’da şu sorguyu çalıştırın:
rate(worker_jobs_processed_total{status="success"}[2m])
Hedef görünmüyorsa önce ServiceMonitor selector ile Service etiketlerini karşılaştırın. Hedef DOWN ise endpoint, port adı, namespace ve NetworkPolicy kurallarını inceleyin.
HPA İçin Son Bir Katman Gerekir
Prometheus’un metriği scrape etmesi, HPA’nın onu otomatik kullanacağı anlamına gelmez. Kubernetes custom metrics belgelerinde açıklandığı gibi, Prometheus Adapter benzeri bir bileşenin metriği Custom Metrics API’ye kaydetmesi gerekir. Adapter yapılandırması tamamlandıktan sonra worker_queue_depth gibi bir gauge, HPA’nın metrics bloğunda hedef olarak kullanılabilir.
Üretimde En Sık Yapılan Hatalar
- Yüksek cardinality: Kullanıcı kimliği veya rastgele istek ID’si gibi sınırsız etiket değerleri zaman serisi sayısını hızla büyütür.
- Yanlış metrik türü: Azalabilen kuyruk derinliğini Counter yapmak hatalı sonuç üretir.
- Polling hatasını sıfır gibi göstermek: Veri kaynağı okunamadığında sahte bir sıfır yazmak yerine hata metriği ve log üretin.
- Gizli veriyi label’a koymak: E-posta, token veya müşteri kimliği gibi veriler metrik etiketi olmamalıdır.
- ServiceMonitor etiket uyuşmazlığı: Service doğru olsa bile Prometheus selector eşleşmiyorsa hedef hiç keşfedilmez.
- Exporter ile adapter’ı karıştırmak: Exporter veri sunar; HPA entegrasyonu için Custom Metrics API katmanı ayrıca kurulmalıdır.
Yayın Öncesi Kontrol Listesi
go test ./...ve yerel/metricsdoğrulaması başarılı mı?- İmaj root olmayan kullanıcıyla çalışıyor ve sürüm/digest sabitlenmiş mi?
- Service port adı ile ServiceMonitor endpoint adı aynı mı?
- Prometheus targets ekranında exporter UP görünüyor mu?
- Label değerleri kontrollü ve hassas veriden arındırılmış mı?
- HPA kullanılacaksa Prometheus Adapter ve Custom Metrics API doğrulandı mı?
Kaynaklar ve İleri Okuma
- Building a Custom Metrics Exporter for Kubernetes.
- Horizontal Pod Autoscaling.
- Prometheus Operator.
- Prometheus Exporters and Integrations.
- Kubernetes v1.36 Route Sync Metriği: CCM’de Yeni Bir Pencere.
- Kubernetes v1.36 Controller Staleness: Bayat Cache Sorunu Bitti mi?.
Bu içerik işinize yaradı mı?
Benzer içerikleri kaçırmamak için beni sosyal medyada takip edin.








Yorum gönder