mirror of
https://github.com/hanzo-ml/kserve.git
synced 2026-10-30 20:53:49 +00:00
291 lines
11 KiB
Go
291 lines
11 KiB
Go
/*
|
|
Copyright 2023 The KServe Authors.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"flag"
|
|
"net/http"
|
|
"os"
|
|
|
|
istio_networking "istio.io/api/networking/v1alpha3"
|
|
corev1 "k8s.io/api/core/v1"
|
|
"k8s.io/client-go/kubernetes"
|
|
typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1"
|
|
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
|
|
"k8s.io/client-go/tools/record"
|
|
ctrl "sigs.k8s.io/controller-runtime"
|
|
"sigs.k8s.io/controller-runtime/pkg/client/config"
|
|
"sigs.k8s.io/controller-runtime/pkg/log/zap"
|
|
"sigs.k8s.io/controller-runtime/pkg/manager"
|
|
"sigs.k8s.io/controller-runtime/pkg/manager/signals"
|
|
metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
|
|
"sigs.k8s.io/controller-runtime/pkg/webhook"
|
|
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
|
|
|
|
"github.com/kserve/kserve/pkg/apis/serving/v1alpha1"
|
|
"github.com/kserve/kserve/pkg/apis/serving/v1beta1"
|
|
"github.com/kserve/kserve/pkg/constants"
|
|
graphcontroller "github.com/kserve/kserve/pkg/controller/v1alpha1/inferencegraph"
|
|
trainedmodelcontroller "github.com/kserve/kserve/pkg/controller/v1alpha1/trainedmodel"
|
|
"github.com/kserve/kserve/pkg/controller/v1alpha1/trainedmodel/reconcilers/modelconfig"
|
|
v1beta1controller "github.com/kserve/kserve/pkg/controller/v1beta1/inferenceservice"
|
|
kservescheme "github.com/kserve/kserve/pkg/scheme"
|
|
kserveutils "github.com/kserve/kserve/pkg/utils"
|
|
"github.com/kserve/kserve/pkg/webhook/admission/pod"
|
|
"github.com/kserve/kserve/pkg/webhook/admission/servingruntime"
|
|
)
|
|
|
|
var setupLog = ctrl.Log.WithName("setup")
|
|
|
|
const (
|
|
LeaderLockName = "kserve-controller-manager-leader-lock"
|
|
)
|
|
|
|
// Options defines the program configurable options that may be passed on the command line.
|
|
type Options struct {
|
|
metricsAddr string
|
|
webhookPort int
|
|
enableLeaderElection bool
|
|
probeAddr string
|
|
zapOpts zap.Options
|
|
}
|
|
|
|
// DefaultOptions returns the default values for the program options.
|
|
func DefaultOptions() Options {
|
|
return Options{
|
|
metricsAddr: ":8080",
|
|
webhookPort: 9443,
|
|
enableLeaderElection: false,
|
|
probeAddr: ":8081",
|
|
zapOpts: zap.Options{},
|
|
}
|
|
}
|
|
|
|
// GetOptions parses the program flags and returns them as Options.
|
|
func GetOptions() Options {
|
|
opts := DefaultOptions()
|
|
flag.StringVar(&opts.metricsAddr, "metrics-addr", opts.metricsAddr, "The address the metric endpoint binds to.")
|
|
flag.IntVar(&opts.webhookPort, "webhook-port", opts.webhookPort, "The port that the webhook server binds to.")
|
|
flag.BoolVar(&opts.enableLeaderElection, "leader-elect", opts.enableLeaderElection,
|
|
"Enable leader election for kserve controller manager. "+
|
|
"Enabling this will ensure there is only one active kserve controller manager.")
|
|
flag.StringVar(&opts.probeAddr, "health-probe-addr", opts.probeAddr, "The address the probe endpoint binds to.")
|
|
opts.zapOpts.BindFlags(flag.CommandLine)
|
|
flag.Parse()
|
|
return opts
|
|
}
|
|
|
|
func init() {
|
|
// Allow unknown fields in Istio API client for backwards compatibility if cluster has existing vs with deprecated fields.
|
|
istio_networking.VirtualServiceUnmarshaler.AllowUnknownFields = true
|
|
istio_networking.GatewayUnmarshaler.AllowUnknownFields = true
|
|
}
|
|
|
|
func main() {
|
|
options := GetOptions()
|
|
ctrl.SetLogger(zap.New(zap.UseFlagOptions(&options.zapOpts)))
|
|
|
|
// Get a config to talk to the apiserver
|
|
setupLog.Info("Setting up client for manager")
|
|
cfg, err := config.GetConfig()
|
|
if err != nil {
|
|
setupLog.Error(err, "unable to set up client config")
|
|
os.Exit(1)
|
|
}
|
|
|
|
// Setup clientset to directly talk to the api server
|
|
clientSet, err := kubernetes.NewForConfig(cfg)
|
|
if err != nil {
|
|
setupLog.Error(err, "unable to create clientSet")
|
|
os.Exit(1)
|
|
}
|
|
|
|
// Create a new Cmd to provide shared dependencies and start components
|
|
setupLog.Info("Setting up manager")
|
|
|
|
cacheOpts, err := v1beta1controller.NewCacheOptions()
|
|
if err != nil {
|
|
setupLog.Error(err, "unable to create cache options")
|
|
os.Exit(1)
|
|
}
|
|
|
|
mgr, err := manager.New(cfg, manager.Options{
|
|
Metrics: metricsserver.Options{
|
|
BindAddress: options.metricsAddr,
|
|
},
|
|
WebhookServer: webhook.NewServer(webhook.Options{
|
|
Port: options.webhookPort,
|
|
}),
|
|
LeaderElection: options.enableLeaderElection,
|
|
LeaderElectionID: LeaderLockName,
|
|
HealthProbeBindAddress: options.probeAddr,
|
|
Cache: cacheOpts,
|
|
})
|
|
if err != nil {
|
|
setupLog.Error(err, "unable to set up overall controller manager")
|
|
os.Exit(1)
|
|
}
|
|
|
|
setupLog.Info("Registering Components.")
|
|
|
|
setupLog.Info("Setting up controller schemes")
|
|
if err := kservescheme.AddAll(mgr.GetScheme()); err != nil {
|
|
setupLog.Error(err, "unable to register API schemes")
|
|
os.Exit(1)
|
|
}
|
|
|
|
isvcConfigMap, err := v1beta1.GetInferenceServiceConfigMap(context.Background(), clientSet)
|
|
if err != nil {
|
|
setupLog.Error(err, "unable to get configmap", "name", constants.InferenceServiceConfigMapName, "namespace", constants.KServeNamespace)
|
|
os.Exit(1)
|
|
}
|
|
deployConfig, err := v1beta1.NewDeployConfig(isvcConfigMap)
|
|
if err != nil {
|
|
setupLog.Error(err, "unable to get deploy config.")
|
|
os.Exit(1)
|
|
}
|
|
ingressConfig, err := v1beta1.NewIngressConfig(isvcConfigMap)
|
|
if err != nil {
|
|
setupLog.Error(err, "unable to get ingress config.")
|
|
os.Exit(1)
|
|
}
|
|
|
|
// Update Global GPU Resource Type List when custom GPU resource types are provided
|
|
_, err = v1beta1.NewMultiNodeConfig(isvcConfigMap)
|
|
if err != nil {
|
|
setupLog.Error(err, "unable to get multiNode config.")
|
|
os.Exit(1)
|
|
}
|
|
|
|
// Setup all Controllers
|
|
setupLog.Info("Setting up v1beta1 controller")
|
|
eventBroadcaster := record.NewBroadcaster()
|
|
eventBroadcaster.StartRecordingToSink(&typedcorev1.EventSinkImpl{Interface: clientSet.CoreV1().Events("")})
|
|
if err = (&v1beta1controller.InferenceServiceReconciler{
|
|
Client: mgr.GetClient(),
|
|
Clientset: clientSet,
|
|
Log: ctrl.Log.WithName("v1beta1Controllers").WithName("InferenceService"),
|
|
Scheme: mgr.GetScheme(),
|
|
Recorder: eventBroadcaster.NewRecorder(
|
|
mgr.GetScheme(), corev1.EventSource{Component: "v1beta1Controllers"}),
|
|
}).SetupWithManager(mgr, deployConfig, ingressConfig); err != nil {
|
|
setupLog.Error(err, "unable to create controller", "v1beta1Controller", "InferenceService")
|
|
os.Exit(1)
|
|
}
|
|
|
|
// Setup TrainedModel controller
|
|
trainedModelEventBroadcaster := record.NewBroadcaster()
|
|
setupLog.Info("Setting up v1beta1 TrainedModel controller")
|
|
trainedModelEventBroadcaster.StartRecordingToSink(&typedcorev1.EventSinkImpl{Interface: clientSet.CoreV1().Events("")})
|
|
if err = (&trainedmodelcontroller.TrainedModelReconciler{
|
|
Client: mgr.GetClient(),
|
|
Log: ctrl.Log.WithName("v1beta1Controllers").WithName("TrainedModel"),
|
|
Scheme: mgr.GetScheme(),
|
|
Recorder: eventBroadcaster.NewRecorder(mgr.GetScheme(), corev1.EventSource{Component: "v1beta1Controllers"}),
|
|
ModelConfigReconciler: modelconfig.NewModelConfigReconciler(mgr.GetClient(), clientSet, mgr.GetScheme()),
|
|
}).SetupWithManager(mgr); err != nil {
|
|
setupLog.Error(err, "unable to create controller", "v1beta1Controllers", "TrainedModel")
|
|
os.Exit(1)
|
|
}
|
|
|
|
// Setup Inference graph controller
|
|
inferenceGraphEventBroadcaster := record.NewBroadcaster()
|
|
setupLog.Info("Setting up InferenceGraph controller")
|
|
inferenceGraphEventBroadcaster.StartRecordingToSink(&typedcorev1.EventSinkImpl{Interface: clientSet.CoreV1().Events("")})
|
|
if err = (&graphcontroller.InferenceGraphReconciler{
|
|
Client: mgr.GetClient(),
|
|
Clientset: clientSet,
|
|
Log: ctrl.Log.WithName("v1alpha1Controllers").WithName("InferenceGraph"),
|
|
Scheme: mgr.GetScheme(),
|
|
Recorder: eventBroadcaster.NewRecorder(mgr.GetScheme(), corev1.EventSource{Component: "InferenceGraphController"}),
|
|
}).SetupWithManager(mgr, deployConfig); err != nil {
|
|
setupLog.Error(err, "unable to create controller", "v1alpha1Controllers", "InferenceGraph")
|
|
os.Exit(1)
|
|
}
|
|
|
|
setupLog.Info("setting up webhook server")
|
|
hookServer := mgr.GetWebhookServer()
|
|
|
|
setupLog.Info("registering webhooks to the webhook server")
|
|
hookServer.Register("/mutate-pods", &webhook.Admission{
|
|
Handler: &pod.Mutator{Client: mgr.GetClient(), Clientset: clientSet, Decoder: admission.NewDecoder(mgr.GetScheme())},
|
|
})
|
|
|
|
csrAvailable, err := kserveutils.IsCrdAvailable(mgr.GetConfig(), v1alpha1.SchemeGroupVersion.String(), "ClusterServingRuntime")
|
|
if err != nil {
|
|
setupLog.Error(err, "failed to check ClusterServingRuntime CRD availability")
|
|
os.Exit(1)
|
|
}
|
|
if csrAvailable {
|
|
setupLog.Info("registering cluster serving runtime validator webhook to the webhook server")
|
|
hookServer.Register("/validate-serving-kserve-io-v1alpha1-clusterservingruntime", &webhook.Admission{
|
|
Handler: &servingruntime.ClusterServingRuntimeValidator{Client: mgr.GetClient(), Decoder: admission.NewDecoder(mgr.GetScheme())},
|
|
})
|
|
}
|
|
|
|
setupLog.Info("registering serving runtime validator webhook to the webhook server")
|
|
hookServer.Register("/validate-serving-kserve-io-v1alpha1-servingruntime", &webhook.Admission{
|
|
Handler: &servingruntime.ServingRuntimeValidator{Client: mgr.GetClient(), Decoder: admission.NewDecoder(mgr.GetScheme())},
|
|
})
|
|
|
|
if err = ctrl.NewWebhookManagedBy(mgr).
|
|
For(&v1alpha1.TrainedModel{}).
|
|
WithValidator(&v1alpha1.TrainedModelValidator{}).
|
|
Complete(); err != nil {
|
|
setupLog.Error(err, "unable to create webhook", "webhook", "v1alpha1")
|
|
os.Exit(1)
|
|
}
|
|
|
|
if err = ctrl.NewWebhookManagedBy(mgr).
|
|
For(&v1alpha1.InferenceGraph{}).
|
|
WithValidator(&v1alpha1.InferenceGraphValidator{}).
|
|
Complete(); err != nil {
|
|
setupLog.Error(err, "unable to create webhook", "webhook", "v1alpha1")
|
|
os.Exit(1)
|
|
}
|
|
|
|
if err = ctrl.NewWebhookManagedBy(mgr).
|
|
For(&v1beta1.InferenceService{}).
|
|
WithDefaulter(&v1beta1.InferenceServiceDefaulter{}).
|
|
WithValidator(&v1beta1.InferenceServiceValidator{}).
|
|
Complete(); err != nil {
|
|
setupLog.Error(err, "unable to create webhook", "webhook", "v1beta1")
|
|
os.Exit(1)
|
|
}
|
|
|
|
if err := mgr.AddHealthzCheck("healthz", func(req *http.Request) error {
|
|
return mgr.GetWebhookServer().StartedChecker()(req)
|
|
}); err != nil {
|
|
setupLog.Error(err, "Unable to set up health check")
|
|
os.Exit(1)
|
|
}
|
|
if err := mgr.AddReadyzCheck("readyz", func(req *http.Request) error {
|
|
return mgr.GetWebhookServer().StartedChecker()(req)
|
|
}); err != nil {
|
|
setupLog.Error(err, "Unable to set up ready check")
|
|
os.Exit(1)
|
|
}
|
|
|
|
// Start the Cmd
|
|
setupLog.Info("Starting the Cmd.")
|
|
if err := mgr.Start(signals.SetupSignalHandler()); err != nil {
|
|
setupLog.Error(err, "unable to run the manager")
|
|
os.Exit(1)
|
|
}
|
|
}
|