Tutorial KServe: Model Serving Serverless di Kubernetes

# Serverless Model Serving di Kubernetes dengan KServe KServe adalah platform native Kubernetes untuk menyajikan model machine learning, yang dibangun di sekitar satu custom resource bernama `Inferen...

By Ruby Abdullah · · tutorial
KServeKubernetesModel ServingMLOpsInferenceCloud Native

Serverless Model Serving di Kubernetes dengan KServe

KServe adalah platform native Kubernetes untuk menyajikan model machine learning, yang dibangun di sekitar satu custom resource bernama InferenceService. Jika Anda sudah menjalankan workload di Kubernetes dan menginginkan model serving yang bisa scale ke nol, mendukung canary rollout, serta mengekspos protokol inferensi standar, KServe menyatu dengan klaster yang sudah ada tanpa memaksa Anda mengadopsi stack terpisah. Tutorial ini membahas arsitektur, instalasi, dan pola sehari-hari yang akan Anda gunakan di produksi.

Apa Itu KServe

KServe (sebelumnya KFServing) adalah platform inferensi model CNCF yang berjalan di atas Kubernetes. Ide intinya adalah menyembunyikan boilerplate pembuatan deployment serving di balik sebuah CRD deklaratif. Alih-alih menulis Deployment, Service, HorizontalPodAutoscaler, dan rute ingress secara manual, Anda cukup mendeskripsikan satu InferenceService dan KServe yang merekonsiliasi sisanya.

Beberapa karakteristik membedakan KServe dari sekadar kontainer serving biasa:

  • KServe memahami protokol. KServe menstandarkan endpoint prediksi di sekitar data plane V1 dan Open Inference Protocol (V2), sehingga klien yang ditulis sesuai spesifikasi tetap bekerja lintas framework model.
  • KServe memahami runtime. Definisi ServingRuntime bawaan mencakup framework umum (scikit-learn, XGBoost, PyTorch, TensorFlow, Triton, MLServer, Hugging Face) sehingga Anda cukup menunjuk ke artefak model.
  • KServe bersifat komposabel. Sebuah request dapat mengalir melalui transformer (pra/pascapemrosesan) lalu ke predictor, dan secara opsional disertai explainer.

Berbeda dengan framework serving serbaguna yang Anda host sendiri, KServe adalah sebuah control plane: ia bersandar pada primitif Kubernetes dan, dalam mode serverless, pada Knative serta Istio.

Hubungan dengan Knative dan Istio

KServe mendukung dua mode deployment, dan perbedaannya terletak pada dependensi yang Anda instal.

Mode serverless

Dalam mode serverless, KServe menggunakan Knative Serving untuk mengelola siklus hidup pod predictor dan Istio (atau layer jaringan lain yang didukung Knative) untuk ingress serta routing trafik. Knative memberikan dua hal yang sulit dibangun sendiri: autoscaling berbasis request hingga nol replika, dan pembagian trafik berbasis revision yang menjadi dasar canary rollout. Ini adalah mode default dan paling lengkap fiturnya.

Mode standar (raw)

Mode standar (kadang disebut deployment "raw Kubernetes") melewati Knative dan Istio. KServe membuat objek Deployment, Service, dan HorizontalPodAutoscaler Kubernetes biasa secara langsung. Anda kehilangan scale-to-zero dan canary berbasis revision, tetapi juga membuang dua dependensi besar. Mode standar cocok ketika tim platform tidak ingin ada Istio di klaster, atau ketika trafik yang stabil membuat scale-to-zero kurang bermanfaat.

Anda memilih mode per layanan dengan sebuah anotasi:

metadata:

annotations:

serving.kserve.io/deploymentMode: "RawDeployment" # atau "Serverless"

Prasyarat

Anda membutuhkan:

  • Klaster Kubernetes, versi 1.28 atau lebih baru. Klaster lokal kind atau minikube cocok untuk belajar; klaster terkelola (GKE, EKS, AKS) cocok untuk produksi.
  • kubectl yang sudah dikonfigurasi untuk berkomunikasi dengan klaster tersebut.
  • Izin cluster-admin untuk menginstal CRD dan webhook.
  • Untuk mode serverless, kapasitas cadangan yang cukup untuk pod control-plane Knative dan Istio (kira-kira 2 vCPU dan 4 GB kapasitas bebas di luar workload Anda).

Pastikan context Anda sebelum menginstal:

kubectl config current-context

kubectl get nodes

kubectl version --short

Instalasi

Skrip quickstart (pengembangan)

Untuk klaster pengembangan, skrip quickstart menginstal KServe bersama Knative, Istio, dan cert-manager dalam satu langkah. Selalu baca skrip sebelum menyalurkannya ke shell.

# Periksa terlebih dahulu

curl -sL "https://raw.githubusercontent.com/kserve/kserve/release-0.14/hack/quickinstall.sh" -o quickinstall.sh

less quickinstall.sh

Lalu jalankan

bash quickinstall.sh

Setelah selesai, verifikasi control plane dalam keadaan sehat:

kubectl get pods -n kserve

kubectl get pods -n knative-serving

kubectl get pods -n istio-system

Manifest (produksi)

Untuk produksi, Anda biasanya menginstal setiap dependensi dari manifest yang dipin sendiri agar dapat mengontrol versi dan mengaudit perubahan. Urutan umumnya: cert-manager, lalu Istio, kemudian Knative Serving, dan terakhir KServe.

# Inti KServe, setelah dependensi terpasang

kubectl apply --server-side -f \

"https://github.com/kserve/kserve/releases/download/v0.14.0/kserve.yaml"

Serving runtime bawaan (objek ClusterServingRuntime)

kubectl apply --server-side -f \

"https://github.com/kserve/kserve/releases/download/v0.14.0/kserve-cluster-resources.yaml"

Pastikan CRD telah terdaftar:

kubectl get crd | grep kserve.io

kubectl get clusterservingruntimes

CRD InferenceService

InferenceService (sering disingkat isvc) adalah resource yang akan paling banyak Anda gunakan. Minimal, ia menamai sebuah predictor dan menunjuk ke sebuah model.

Men-deploy model dari object storage

Contoh di bawah menyajikan model scikit-learn yang disimpan di object storage, menggunakan runtime sklearn bawaan. KServe mengunduh artefak di storageUri ke storage initializer model server, sehingga Anda tidak perlu menanamkan model ke dalam image.

apiVersion: serving.kserve.io/v1beta1

kind: InferenceService

metadata:

name: sklearn-iris

namespace: ml-serving

spec:

predictor:

model:

modelFormat:

name: sklearn

storageUri: "gs://kfserving-examples/models/sklearn/1.0/model"

resources:

requests:

cpu: "100m"

memory: "256Mi"

limits:

cpu: "1"

memory: "1Gi"

Terapkan dan tunggu hingga layanan siap:

kubectl create namespace ml-serving

kubectl apply -f sklearn-iris.yaml

kubectl get isvc sklearn-iris -n ml-serving -w

Kolom READY berubah menjadi True dan kolom URL menampilkan endpoint eksternal begitu Knative selesai menyediakan revision.

storageUri mendukung beberapa skema: gs:// (GCS), s3:// (S3 dan penyimpanan kompatibel S3), https://, pvc://, serta hdfs://. Untuk bucket privat, Anda melampirkan kredensial melalui sebuah Secret dan ServiceAccount yang dirujuk KServe. Untuk S3:
apiVersion: v1

kind: Secret

metadata:

name: s3-creds

namespace: ml-serving

annotations:

serving.kserve.io/s3-endpoint: "s3.amazonaws.com"

serving.kserve.io/s3-region: "us-east-1"

serving.kserve.io/s3-usehttps: "1"

type: Opaque

stringData:

AWSACCESSKEYID: ""

AWSSECRETACCESSKEY: ""


apiVersion: v1

kind: ServiceAccount

metadata:

name: sa-s3

namespace: ml-serving

secrets:

  • name: s3-creds

Lalu rujuk service account tersebut di predictor:

spec:

predictor:

serviceAccountName: sa-s3

model:

modelFormat:

name: sklearn

storageUri: "s3://my-bucket/models/iris"

Endpoint Prediksi: V1 dan Open Inference Protocol (V2)

KServe mengekspos dua protokol data-plane, dan runtime menentukan mana yang tersedia.

Protokol V1

Protokol V1 sederhana dan berbasis JSON. Path prediksinya adalah /v1/models/:predict. Body request membungkus input Anda dalam sebuah array instances.

Pertama, temukan alamat ingress. Pada klaster lokal dengan Istio, Anda biasanya melakukan port-forward ke ingress gateway:

kubectl port-forward -n istio-system svc/istio-ingressgateway 8080:80

SERVICEHOST=$(kubectl get isvc sklearn-iris -n ml-serving \

-o jsonpath='{.status.url}' | cut -d "/" -f3)

Lalu kirim sebuah request. Header Host membawa virtual host model agar gateway merutekan dengan benar:

curl -v -H "Host: ${SERVICEHOST}" \

-H "Content-Type: application/json" \

http://localhost:8080/v1/models/sklearn-iris:predict \

-d '{"instances": [[6.8, 2.8, 4.8, 1.4], [6.0, 3.4, 4.5, 1.6]]}'

Respons yang umum:

{"predictions": [1, 1]}

Open Inference Protocol (V2)

Protokol V2 (Open Inference Protocol, awalnya protokol KFServing/Triton) lebih terstruktur dan mendukung tensor bertipe melalui HTTP maupun gRPC. Path prediksinya adalah /v2/models//infer. Runtime seperti MLServer dan Triton berbicara dalam V2.

curl -H "Host: ${SERVICEHOST}" \

-H "Content-Type: application/json" \

http://localhost:8080/v2/models/sklearn-iris/infer \

-d '{

"inputs": [

{

"name": "input-0",

"shape": [2, 4],

"datatype": "FP32",

"data": [6.8, 2.8, 4.8, 1.4, 6.0, 3.4, 4.5, 1.6]

}

]

}'

V2 juga mendefinisikan endpoint health dan metadata (/v2/health/ready, /v2/models/) yang berguna untuk readiness check dan validasi di sisi klien.

Autoscaling dan Scale-to-Zero

Dalam mode serverless, autoscaling ditangani oleh Knative Pod Autoscaler (KPA), yang secara default melakukan scaling berdasarkan concurrency, bukan CPU.

Target concurrency

containerConcurrency dan target concurrency lunak mengendalikan kapan Knative menambah replika. Target 10 berarti Knative berupaya menjaga sekitar sepuluh request yang sedang berjalan per pod sebelum melakukan scale out.
apiVersion: serving.kserve.io/v1beta1

kind: InferenceService

metadata:

name: sklearn-iris

namespace: ml-serving

annotations:

autoscaling.knative.dev/target: "10"

spec:

predictor:

minReplicas: 0

maxReplicas: 5

model:

modelFormat:

name: sklearn

storageUri: "gs://kfserving-examples/models/sklearn/1.0/model"

Scale-to-zero

Menetapkan minReplicas: 0 mengaktifkan scale-to-zero. Ketika tidak ada request yang datang selama periode tenggang yang dikonfigurasi, Knative menghapus semua pod; request pertama setelahnya memicu cold start ketika pod baru memuat model. Scale-to-zero berharga untuk model dengan trafik rendah atau sporadis, tetapi pertimbangkan latensi cold start. Untuk endpoint yang sensitif terhadap latensi, pertahankan minReplicas: 1 agar setidaknya satu pod selalu hangat.

Anda dapat menyetel seberapa agresif Knative melakukan scale down:

metadata:

annotations:

autoscaling.knative.dev/scaleToZeroPodRetentionPeriod: "60s"

autoscaling.knative.dev/window: "60s"

Canary Rollout

KServe membangun deployment canary di atas revision Knative. Ketika Anda memperbarui sebuah InferenceService (misalnya storageUri atau image baru), Knative membuat revision baru. Field canaryTrafficPercent mengendalikan berapa banyak trafik live yang diterima revision baru.

apiVersion: serving.kserve.io/v1beta1

kind: InferenceService

metadata:

name: sklearn-iris

namespace: ml-serving

spec:

predictor:

canaryTrafficPercent: 10

model:

modelFormat:

name: sklearn

storageUri: "gs://kfserving-examples/models/sklearn/1.0/model-v2"

Dengan canaryTrafficPercent: 10, revision baru melayani 10 persen request sementara revision sebelumnya melayani sisanya. Anda memantau tingkat error dan latensi pada canary, lalu menaikkan persentasenya. Menghapus field ini (atau menyetelnya ke 100) mempromosikan canary untuk menerima seluruh trafik. Karena routing berbasis revision, rollback semudah mengarahkan trafik kembali ke revision sebelumnya.

# Periksa revision dan pembagian trafiknya

kubectl get isvc sklearn-iris -n ml-serving -o yaml | grep -A20 "status:"

kubectl get revisions -n ml-serving

Custom Predictor dengan KServe Python SDK

Runtime bawaan mencakup framework umum, tetapi cepat atau lambat Anda akan menyajikan model yang membutuhkan logika kustom. KServe menyediakan Python SDK dengan kelas dasar Model. Anda mengimplementasikan load (dipanggil sekali saat startup) dan predict (dipanggil per request), dan SDK membungkus kode Anda dalam server yang sudah berbicara dalam data plane KServe.

# model.py

import joblib

import numpy as np

from kserve import Model, ModelServer, modelserver

class CustomSklearnModel(Model):

def init(self, name: str):

super().init(name)

self.name = name

self.model = None

self.ready = False

def load(self) -> bool:

# Berjalan sekali ketika pod dimulai.

self.model = joblib.load("/mnt/models/model.joblib")

self.ready = True

return self.ready

def predict(self, payload: dict, headers: dict = None) -> dict:

instances = payload["instances"]

inputs = np.asarray(instances)

predictions = self.model.predict(inputs)

return {"predictions": predictions.tolist()}

if name == "main":

model = CustomSklearnModel("custom-sklearn")

model.load()

ModelServer().start([model])

Mengemas dengan Dockerfile

FROM python:3.11-slim

WORKDIR /app

COPY requirements.txt .

RUN pip install --no-cache-dir -r requirements.txt

COPY model.py .

KServe default menggunakan port 8080 untuk HTTP

EXPOSE 8080

ENTRYPOINT ["python", "model.py"]

requirements.txt minimal:
kserve>=0.14.0

scikit-learn

joblib

numpy

Build dan push image, lalu rujuk image tersebut di sebuah InferenceService menggunakan spec container mentah, bukan blok model:

docker build -t registry.example.com/ml/custom-sklearn:1.0 .

docker push registry.example.com/ml/custom-sklearn:1.0

apiVersion: serving.kserve.io/v1beta1

kind: InferenceService

metadata:

name: custom-sklearn

namespace: ml-serving

spec:

predictor:

containers:

  • name: kserve-container
image: registry.example.com/ml/custom-sklearn:1.0

args:

  • --modelname=custom-sklearn
resources:

requests:

cpu: "200m"

memory: "512Mi"

Transformer: Pra dan Pascapemrosesan

Transformer adalah komponen terpisah yang berjalan sebelum dan sesudah predictor. Pola umumnya: transformer mengubah input mentah (URL gambar, sepotong teks) menjadi format tensor yang diharapkan predictor, lalu mengubah output mentah predictor kembali menjadi respons yang ramah. Ini menjaga predictor tetap fokus pada inferensi dan memungkinkan Anda menskalakan kedua tahap secara independen.

Anda mengimplementasikan transformer dengan SDK yang sama, dengan mengoverride preprocess dan postprocess:

# transformer.py

from typing import Dict

from kserve import Model, ModelServer

class ImageTransformer(Model):

def init(self, name: str, predictorhost: str):

super().init(name)

self.predictorhost = predictorhost

self.ready = True

def preprocess(self, request: Dict, headers: Dict = None) -> Dict:

# Decode/normalisasi sebelum dikirim ke predictor.

raw = request["instances"]

normalized = [[float(v) / 255.0 for v in row] for row in raw]

return {"instances": normalized}

def postprocess(self, response: Dict, headers: Dict = None) -> Dict:

# Petakan indeks kelas ke label setelah predictor merespons.

labels = ["cat", "dog"]

return {"predictions": [labels[i] for i in response["predictions"]]}

if name == "main":

ModelServer().start([ImageTransformer("custom-model", predictor_host="")])

Pasang transformer dalam InferenceService yang sama dengan predictor. KServe secara otomatis merangkai alur request klien -> transformer -> predictor -> transformer -> klien:

apiVersion: serving.kserve.io/v1beta1

kind: InferenceService

metadata:

name: custom-model

namespace: ml-serving

spec:

transformer:

containers:

  • name: kserve-container
image: registry.example.com/ml/image-transformer:1.0

predictor:

model:

modelFormat:

name: pytorch

storageUri: "gs://my-bucket/models/resnet"

ModelMesh untuk Multi-Model Serving

InferenceService standar memberikan pod tersendiri bagi setiap model, yang efisien ketika Anda memiliki segelintir model bertrafik tinggi. Ketika Anda perlu menyajikan ratusan hingga ribuan model, mendedikasikan pod per model memboroskan memori. Untuk kasus ini KServe terintegrasi dengan ModelMesh: sebuah pool pod serving runtime yang dibagikan memuat dan mengeluarkan model sesuai permintaan, menjaga model panas tetap di memori dan memuat model dingin secara lazy. Anda mengaktifkan ModelMesh saat instalasi, lalu men-deploy model dengan resource InferenceService yang dianotasi untuk runtime ModelMesh. Pendekatan ini menukar risiko pemuatan kecil per request dengan densitas model yang jauh lebih tinggi per node.

Catatan Serving GPU

Untuk menyajikan di GPU, minta resource GPU dan pastikan node pool memiliki NVIDIA device plugin terpasang.

spec:

predictor:

model:

modelFormat:

name: pytorch

storageUri: "gs://my-bucket/models/llm"

resources:

limits:

nvidia.com/gpu: "1"

requests:

cpu: "2"

memory: "8Gi"

Beberapa poin praktis:

  • Jadwalkan workload GPU ke node GPU dengan nodeSelector atau taint/toleration agar pod CPU tidak menghabiskan node GPU.
  • Scale-to-zero pada GPU mengembalikan perangkat keras yang mahal di antara lonjakan trafik, tetapi cold start lebih lama karena model besar butuh waktu untuk dimuat. Ukur dulu sebelum mengaktifkannya.
  • Untuk large language model, runtime serving Hugging Face dan Triton (dengan backend TensorRT-LLM atau vLLM) adalah pilihan umum, dikonfigurasi melalui spec model atau containers yang sama.

Monitoring dan Logging

Model server KServe mengekspos metrik Prometheus pada sebuah port metrik, termasuk jumlah request, histogram latensi, dan (untuk runtime V2) statistik per-model. Scrape metrik tersebut dengan ServiceMonitor jika Anda menjalankan Prometheus Operator, dan bangun dashboard untuk request rate, latensi p95/p99, serta rasio error per InferenceService.

KServe juga mendukung payload logger yang secara asinkron meneruskan body request dan response ke sebuah sink (misalnya broker atau collector) untuk audit, deteksi drift, atau membangun dataset pelatihan:

spec:

predictor:

logger:

mode: all # request, response, atau all

url: http://logging-collector.monitoring.svc.cluster.local

Untuk debugging level pod, perkakas Kubernetes biasa tetap berlaku:

kubectl logs -n ml-serving -l serving.kserve.io/inferenceservice=sklearn-iris -c kserve-container

kubectl describe isvc sklearn-iris -n ml-serving

Praktik Terbaik

  • Pin versi. Pin KServe, Knative, dan Istio ke rilis yang diketahui kompatibel, serta pin image runtime. Perlakukan control plane seperti dependensi lainnya.
  • Tetapkan resource request dan limit. Tanpa keduanya, autoscaler tidak punya acuan penjadwalan dan tetangga berisik dapat membuat predictor kelaparan.
  • Pilih mode deployment secara sengaja. Gunakan mode serverless ketika scale-to-zero dan canary berbasis revision benar-benar bermanfaat; gunakan mode standar untuk trafik stabil atau klaster tanpa Istio.
  • Jauhkan model dari image. Gunakan storageUri agar Anda dapat mengganti versi model tanpa membangun ulang kontainer, dan agar image yang sama dapat menyajikan banyak model.
  • Rilis dengan canary. Jangan pernah mengirim 100 persen trafik ke revision baru. Mulai kecil, pantau latensi dan tingkat error, lalu promosikan.
  • Perhatikan cold start. Sisakan minReplicas: 0 untuk endpoint trafik rendah yang toleran. Pertahankan minimal satu replika hangat untuk jalur yang sensitif terhadap latensi.
  • Pisahkan transformer dari predictor ketika pra/pascapemrosesan berat, agar transformer yang terikat CPU dan predictor yang terikat GPU dapat diskalakan secara independen.
  • Monitor sejak hari pertama. Siapkan Prometheus dan logging terstruktur sebelum terjadi insiden, bukan saat insiden berlangsung.

Kesimpulan

KServe mengubah model serving di Kubernetes menjadi operasi deklaratif: Anda mendeskripsikan sebuah InferenceService, dan platform menangani deployment, jaringan, scaling, serta manajemen trafik. Mode serverless, yang didukung Knative dan Istio, menghadirkan scale-to-zero dan canary rollout berbasis revision; mode standar menjaga semuanya tetap ringan untuk workload stabil. Runtime bawaan mencakup framework umum, Python SDK menangani sisanya melalui kelas Model, dan transformer memungkinkan Anda memisahkan pembentukan data dari inferensi. Bagi tim yang sudah berinvestasi di Kubernetes, KServe menawarkan jalur yang konsisten dan berbasis protokol dari artefak terlatih menuju endpoint produksi.

Poin Penting

  • CRD InferenceService adalah satu-satunya abstraksi; selebihnya direkonsiliasi untuk Anda.
  • Mode serverless (Knative + Istio) mengaktifkan scale-to-zero dan canary; mode standar menukar keduanya dengan dependensi yang lebih sedikit.
  • V1 adalah protokol JSON sederhana; Open Inference Protocol (V2) bertipe dan mendukung HTTP serta gRPC.
  • minReplicas: 0 ditambah target concurrency mengendalikan autoscaling; canaryTrafficPercent mengendalikan rollout bertahap.
  • Custom predictor dan transformer adalah kelas Python yang dikemas sebagai image; ModelMesh menangani multi-model serving berdensitas tinggi.

Artikel Terkait

Tutorial Lengkap Kubeflow: MLOps di Kubernetes

Tutorial Lengkap Kubeflow: MLOps di Kubernetes Kubeflow adalah platform open-source untuk deploy, mengelola, dan scaling...

Tutorial Lengkap BentoML: Packaging dan Serving ML Models ke Production

Tutorial Lengkap BentoML: Packaging dan Serving ML Models ke Production BentoML adalah framework open-source untuk build...

Tutorial Flyte: Workflow Orchestration untuk Machine Learning dan Data Engineering

Tutorial Flyte: Workflow Orchestration untuk Machine Learning dan Data Engineering Flyte adalah platform workflow orches...

Tutorial LitServe: Framework Serving Model AI yang Cepat dan Mudah

Tutorial LitServe: Framework Serving Model AI yang Cepat dan Mudah Pendahuluan LitServe adalah framework open-source dar...