Membangun custom scaler gRPC dengan SDK KEDA, memahami jalur komunikasi KEDA dengan HPA lewat External Metrics API, dan meracik kombinasi multi-trigger AND/OR dalam satu ScaledObject dengan scalingModifiers.

Di episode 9 kita berkenalan dengan scaler untuk database dan HTTP. Meski KEDA punya lebih dari 70 scaler, dunia nyata selalu punya metrik yang belum tercover — misalnya backlog sistem internal, metrik SaaS milik sendiri, atau mainframe legacy. Di episode ini kalian belajar dua hal: cara KEDA berbicara dengan HPA melalui External Metrics API, dan cara membuat custom scaler gRPC untuk menghubungkan sistem internal ke KEDA. Terakhir kita racik multi-trigger dengan logika AND/OR yang presisi.
KEDA tidak menskalakan pod secara langsung. Alurnya berlapis: scaler membaca metrik dari sistem eksternal, lalu KEDA Metrics Server mengekspos nilainya melalui External Metrics API, dan HPA yang dibuat KEDA bertanya ke API itu setiap siklus sinkronisasi (biasanya 15 detik) untuk menetapkan jumlah replika.
kubectl get apiservice v1beta1.external.metrics.k8s.io
kubectl get --raw /apis/external.metrics.k8s.io/v1beta1
kubectl get hpa -n productionJalur inilah yang membuat KEDA kompatibel dengan HPA standar Kubernetes: KEDA hanya menjadi sumber metrik sekaligus pengelola siklus hidup HPA. Karena alasan yang sama, KEDA tidak bisa berbagi API server kustom dengan solusi lain di cluster yang sama.
Jika 70+ scaler bawaan belum memenuhi kebutuhan, KEDA menyediakan antarmuka external scaler berbasis gRPC. Kalian menulis server gRPC yang mengimplementasikan protokol externalscaler dengan tiga method inti:
IsActive — menentukan apakah sistem memiliki event (dipakai untuk keputusan scale-to-zero).GetMetricsSpec — memberitahu KEDA nama metrik dan ukuran target-nya.GetMetricsAndReset — mengembalikan nilai metrik terkini untuk dihitung HPA.package main
import (
"context"
pb "github.com/kedacore/keda/v2/pkg/scalers/externalscaler"
)
type Scaler struct {
pb.UnimplementedExternalScalerServer
}
func (s *Scaler) IsActive(ctx context.Context, req *pb.ScaledObjectRef) (*pb.IsActiveResponse, error) {
n := countInternalBacklog()
return &pb.IsActiveResponse{Result: n > 0}, nil
}
func (s *Scaler) GetMetricsSpec(ctx context.Context, req *pb.ScaledObjectRef) (*pb.GetMetricsSpecResponse, error) {
return &pb.GetMetricsSpecResponse{
MetricSpecs: []*pb.MetricSpec{{
MetricName: "internal-backlog",
TargetSize: 100,
}},
}, nil
}
func (s *Scaler) GetMetricsAndReset(ctx context.Context, req *pb.GetMetricsRequest) (*pb.GetMetricsResponse, error) {
return &pb.GetMetricsResponse{
MetricValues: []*pb.MetricValue{{
MetricName: "internal-backlog",
MetricValue: countInternalBacklog(),
}},
}, nil
}Server gRPC ini berjalan sebagai Deployment terpisah di dalam cluster, lalu dihubungkan ke KEDA lewat blok gRPCConfig. Perhatikan countInternalBacklog — ganti dengan logika nyata yang membaca sistem internal kalian.
KEDA menghubungi server gRPC tersebut lewat konfigurasi gRPCConfig, dan meneruskan metadata yang ditulis di ScaledObject sebagai parameter trigger.
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: internal-scaledobject
spec:
scaleTargetRef:
name: internal-worker
minReplicaCount: 0
maxReplicaCount: 10
triggers:
- type: external
metadata:
scalerAddress: internal-scaler.prod.svc:6000
metricName: internal-backlog
targetValue: "100"
gRPCConfig:
host: internal-scaler.prod.svc
port: "6000"
useCachedClients: "true"Tip
Aktifkan useCachedClients: true agar operator tidak membuat koneksi gRPC baru untuk tiap polling — koneksi dibuka sekali lalu dipakai ulang. Kalau scaler internal kalian butuh TLS, tambahkan sertifikat di blok gRPCConfig.
KEDA menggabungkan beberapa trigger dalam satu ScaledObject dengan dua aturan penting.
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: hybrid
spec:
scaleTargetRef:
name: hybrid-api
minReplicaCount: 1
maxReplicaCount: 20
triggers:
- type: cpu
metricType: Utilization
metadata:
type: Utilization
value: "60"
- type: prometheus
metricType: AverageValue
metadata:
serverAddress: http://prometheus.monitoring.svc:9090
query: sum(rate(http_requests_total[2m]))
threshold: "100"Karena penjumlahan tidak selalu sesuai kebutuhan, KEDA v2.20 menyediakan scalingModifiers dengan formula ekspresi — termasuk operator ternary untuk logika AND/OR yang presisi.
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: or-logic
spec:
scaleTargetRef:
name: worker
minReplicaCount: 0
maxReplicaCount: 10
advanced:
scalingModifiers:
formula: "max(trig_a, trig_b)"
target: "10"
metricType: "AverageValue"
triggers:
- type: prometheus
name: trig_a
metadata:
serverAddress: http://prometheus.monitoring.svc:9090
query: sum(rate(errors_total[1m]))
threshold: "5"
- type: metrics-api
name: trig_b
metadata:
url: "http://internal-api.prod.svc:8080/backlog"
valueLocation: "backlog"
targetValue: "10"Formula max(trig_a, trig_b) membuat KEDA memakai nilai tertinggi dari kedua trigger — logika OR murni. Untuk AND, gunakan min(...). Kombinasi lain seperti trig_a + trig_b atau ternary trig_a > 2 ? trig_a + trig_b : 1 juga bisa ditulis langsung.
Warning
Saat menggabungkan trigger, perhatikan skala metrik. Menggabungkan trigger bernilai ribuan dengan trigger bernilai satuan lewat penjumlahan akan membuat trigger kecil tidak pernah terlihat. Normalisasi nilai atau gunakan max/min lewat scalingModifiers.
kubectl get --raw /apis/external.metrics.k8s.io/v1beta1.IsActive, GetMetricsSpec, dan GetMetricsAndReset.gRPCConfig dan metadata di ScaledObject.scalingModifiers dengan formula memberi kontrol AND/OR yang presisi untuk kebutuhan kompleks.Scaler kalian sekarang bisa apa saja, tapi apa jadinya kalau scaler itu gagal membaca metrik? Di episode 11 kita membahas fallback dan advanced config: menjaga ketersediaan minimum saat scaler error, menyetel pollingInterval, cooldownPeriod, restoreToOriginalReplicaCount, behavior HPA, dan scalingStrategy. Sampai jumpa!