Merge pull request #2008 from Poor12/add-pprof
Add pprof for karmada components
This commit is contained in:
commit
5ecb4ba1bb
|
@ -34,6 +34,7 @@ import (
|
||||||
"github.com/karmada-io/karmada/pkg/resourceinterpreter"
|
"github.com/karmada-io/karmada/pkg/resourceinterpreter"
|
||||||
"github.com/karmada-io/karmada/pkg/sharedcli"
|
"github.com/karmada-io/karmada/pkg/sharedcli"
|
||||||
"github.com/karmada-io/karmada/pkg/sharedcli/klogflag"
|
"github.com/karmada-io/karmada/pkg/sharedcli/klogflag"
|
||||||
|
"github.com/karmada-io/karmada/pkg/sharedcli/profileflag"
|
||||||
"github.com/karmada-io/karmada/pkg/util"
|
"github.com/karmada-io/karmada/pkg/util"
|
||||||
"github.com/karmada-io/karmada/pkg/util/gclient"
|
"github.com/karmada-io/karmada/pkg/util/gclient"
|
||||||
"github.com/karmada-io/karmada/pkg/util/helper"
|
"github.com/karmada-io/karmada/pkg/util/helper"
|
||||||
|
@ -104,6 +105,9 @@ func init() {
|
||||||
|
|
||||||
func run(ctx context.Context, karmadaConfig karmadactl.KarmadaConfig, opts *options.Options) error {
|
func run(ctx context.Context, karmadaConfig karmadactl.KarmadaConfig, opts *options.Options) error {
|
||||||
klog.Infof("karmada-agent version: %s", version.Get())
|
klog.Infof("karmada-agent version: %s", version.Get())
|
||||||
|
|
||||||
|
profileflag.ListenAndServe(opts.ProfileOpts)
|
||||||
|
|
||||||
controlPlaneRestConfig, err := karmadaConfig.GetRestConfig(opts.KarmadaContext, opts.KarmadaKubeConfig)
|
controlPlaneRestConfig, err := karmadaConfig.GetRestConfig(opts.KarmadaContext, opts.KarmadaKubeConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error building kubeconfig of karmada control plane: %w", err)
|
return fmt.Errorf("error building kubeconfig of karmada control plane: %w", err)
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"k8s.io/client-go/tools/leaderelection/resourcelock"
|
"k8s.io/client-go/tools/leaderelection/resourcelock"
|
||||||
componentbaseconfig "k8s.io/component-base/config"
|
componentbaseconfig "k8s.io/component-base/config"
|
||||||
|
|
||||||
|
"github.com/karmada-io/karmada/pkg/sharedcli/profileflag"
|
||||||
"github.com/karmada-io/karmada/pkg/sharedcli/ratelimiterflag"
|
"github.com/karmada-io/karmada/pkg/sharedcli/ratelimiterflag"
|
||||||
"github.com/karmada-io/karmada/pkg/util"
|
"github.com/karmada-io/karmada/pkg/util"
|
||||||
)
|
)
|
||||||
|
@ -88,6 +89,7 @@ type Options struct {
|
||||||
MetricsBindAddress string
|
MetricsBindAddress string
|
||||||
|
|
||||||
RateLimiterOpts ratelimiterflag.Options
|
RateLimiterOpts ratelimiterflag.Options
|
||||||
|
ProfileOpts profileflag.Options
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewOptions builds an default scheduler options.
|
// NewOptions builds an default scheduler options.
|
||||||
|
@ -153,4 +155,5 @@ func (o *Options) AddFlags(fs *pflag.FlagSet, allControllers []string) {
|
||||||
fs.IntVar(&o.ConcurrentWorkSyncs, "concurrent-work-syncs", 5, "The number of Works that are allowed to sync concurrently.")
|
fs.IntVar(&o.ConcurrentWorkSyncs, "concurrent-work-syncs", 5, "The number of Works that are allowed to sync concurrently.")
|
||||||
fs.StringVar(&o.MetricsBindAddress, "metrics-bind-address", ":8080", "The TCP address that the controller should bind to for serving prometheus metrics(e.g. 127.0.0.1:8088, :8088)")
|
fs.StringVar(&o.MetricsBindAddress, "metrics-bind-address", ":8080", "The TCP address that the controller should bind to for serving prometheus metrics(e.g. 127.0.0.1:8088, :8088)")
|
||||||
o.RateLimiterOpts.AddFlags(fs)
|
o.RateLimiterOpts.AddFlags(fs)
|
||||||
|
o.ProfileOpts.AddFlags(fs)
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,6 @@ import (
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
|
||||||
"k8s.io/apimachinery/pkg/util/sets"
|
"k8s.io/apimachinery/pkg/util/sets"
|
||||||
"k8s.io/apiserver/pkg/admission"
|
"k8s.io/apiserver/pkg/admission"
|
||||||
"k8s.io/apiserver/pkg/endpoints/openapi"
|
"k8s.io/apiserver/pkg/endpoints/openapi"
|
||||||
|
@ -21,6 +20,7 @@ import (
|
||||||
genericoptions "k8s.io/apiserver/pkg/server/options"
|
genericoptions "k8s.io/apiserver/pkg/server/options"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
"k8s.io/client-go/kubernetes"
|
"k8s.io/client-go/kubernetes"
|
||||||
|
"k8s.io/klog/v2"
|
||||||
netutils "k8s.io/utils/net"
|
netutils "k8s.io/utils/net"
|
||||||
|
|
||||||
"github.com/karmada-io/karmada/pkg/aggregatedapiserver"
|
"github.com/karmada-io/karmada/pkg/aggregatedapiserver"
|
||||||
|
@ -28,7 +28,9 @@ import (
|
||||||
clientset "github.com/karmada-io/karmada/pkg/generated/clientset/versioned"
|
clientset "github.com/karmada-io/karmada/pkg/generated/clientset/versioned"
|
||||||
informers "github.com/karmada-io/karmada/pkg/generated/informers/externalversions"
|
informers "github.com/karmada-io/karmada/pkg/generated/informers/externalversions"
|
||||||
generatedopenapi "github.com/karmada-io/karmada/pkg/generated/openapi"
|
generatedopenapi "github.com/karmada-io/karmada/pkg/generated/openapi"
|
||||||
|
"github.com/karmada-io/karmada/pkg/sharedcli/profileflag"
|
||||||
"github.com/karmada-io/karmada/pkg/util/lifted"
|
"github.com/karmada-io/karmada/pkg/util/lifted"
|
||||||
|
"github.com/karmada-io/karmada/pkg/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
const defaultEtcdPathPrefix = "/registry"
|
const defaultEtcdPathPrefix = "/registry"
|
||||||
|
@ -42,6 +44,8 @@ type Options struct {
|
||||||
KubeAPIQPS float32
|
KubeAPIQPS float32
|
||||||
// KubeAPIBurst is the burst to allow while talking with karmada-apiserver.
|
// KubeAPIBurst is the burst to allow while talking with karmada-apiserver.
|
||||||
KubeAPIBurst int
|
KubeAPIBurst int
|
||||||
|
|
||||||
|
ProfileOpts profileflag.Options
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewOptions returns a new Options.
|
// NewOptions returns a new Options.
|
||||||
|
@ -63,6 +67,7 @@ func (o *Options) AddFlags(flags *pflag.FlagSet) {
|
||||||
flags.Float32Var(&o.KubeAPIQPS, "kube-api-qps", 40.0, "QPS to use while talking with karmada-apiserver. Doesn't cover events and node heartbeat apis which rate limiting is controlled by a different set of flags.")
|
flags.Float32Var(&o.KubeAPIQPS, "kube-api-qps", 40.0, "QPS to use while talking with karmada-apiserver. Doesn't cover events and node heartbeat apis which rate limiting is controlled by a different set of flags.")
|
||||||
flags.IntVar(&o.KubeAPIBurst, "kube-api-burst", 60, "Burst to use while talking with karmada-apiserver. Doesn't cover events and node heartbeat apis which rate limiting is controlled by a different set of flags.")
|
flags.IntVar(&o.KubeAPIBurst, "kube-api-burst", 60, "Burst to use while talking with karmada-apiserver. Doesn't cover events and node heartbeat apis which rate limiting is controlled by a different set of flags.")
|
||||||
utilfeature.DefaultMutableFeatureGate.AddFlag(flags)
|
utilfeature.DefaultMutableFeatureGate.AddFlag(flags)
|
||||||
|
o.ProfileOpts.AddFlags(flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Complete fills in fields required to have valid data.
|
// Complete fills in fields required to have valid data.
|
||||||
|
@ -70,15 +75,12 @@ func (o *Options) Complete() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates Options.
|
|
||||||
func (o *Options) Validate() error {
|
|
||||||
var errs []error
|
|
||||||
errs = append(errs, o.RecommendedOptions.Validate()...)
|
|
||||||
return utilerrors.NewAggregate(errs)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run runs the aggregated-apiserver with options. This should never exit.
|
// Run runs the aggregated-apiserver with options. This should never exit.
|
||||||
func (o *Options) Run(ctx context.Context) error {
|
func (o *Options) Run(ctx context.Context) error {
|
||||||
|
klog.Infof("karmada-aggregated-apiserver version: %s", version.Get())
|
||||||
|
|
||||||
|
profileflag.ListenAndServe(o.ProfileOpts)
|
||||||
|
|
||||||
config, err := o.Config()
|
config, err := o.Config()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -120,6 +122,7 @@ func (o *Options) Config() (*aggregatedapiserver.Config, error) {
|
||||||
o.SharedInformerFactory = informerFactory
|
o.SharedInformerFactory = informerFactory
|
||||||
return []admission.PluginInitializer{}, nil
|
return []admission.PluginInitializer{}, nil
|
||||||
}
|
}
|
||||||
|
o.RecommendedOptions.Features = &genericoptions.FeatureOptions{EnableProfiling: false}
|
||||||
|
|
||||||
serverConfig := genericapiserver.NewRecommendedConfig(aggregatedapiserver.Codecs)
|
serverConfig := genericapiserver.NewRecommendedConfig(aggregatedapiserver.Codecs)
|
||||||
serverConfig.LongRunningFunc = customLongRunningRequestCheck(sets.NewString("watch", "proxy"),
|
serverConfig.LongRunningFunc = customLongRunningRequestCheck(sets.NewString("watch", "proxy"),
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
package options
|
||||||
|
|
||||||
|
import (
|
||||||
|
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Validate validates Options.
|
||||||
|
func (o *Options) Validate() error {
|
||||||
|
var errs []error
|
||||||
|
errs = append(errs, o.RecommendedOptions.Validate()...)
|
||||||
|
|
||||||
|
return utilerrors.NewAggregate(errs)
|
||||||
|
}
|
|
@ -44,6 +44,7 @@ import (
|
||||||
"github.com/karmada-io/karmada/pkg/resourceinterpreter"
|
"github.com/karmada-io/karmada/pkg/resourceinterpreter"
|
||||||
"github.com/karmada-io/karmada/pkg/sharedcli"
|
"github.com/karmada-io/karmada/pkg/sharedcli"
|
||||||
"github.com/karmada-io/karmada/pkg/sharedcli/klogflag"
|
"github.com/karmada-io/karmada/pkg/sharedcli/klogflag"
|
||||||
|
"github.com/karmada-io/karmada/pkg/sharedcli/profileflag"
|
||||||
"github.com/karmada-io/karmada/pkg/util"
|
"github.com/karmada-io/karmada/pkg/util"
|
||||||
"github.com/karmada-io/karmada/pkg/util/gclient"
|
"github.com/karmada-io/karmada/pkg/util/gclient"
|
||||||
"github.com/karmada-io/karmada/pkg/util/helper"
|
"github.com/karmada-io/karmada/pkg/util/helper"
|
||||||
|
@ -97,6 +98,9 @@ func NewControllerManagerCommand(ctx context.Context) *cobra.Command {
|
||||||
// Run runs the controller-manager with options. This should never exit.
|
// Run runs the controller-manager with options. This should never exit.
|
||||||
func Run(ctx context.Context, opts *options.Options) error {
|
func Run(ctx context.Context, opts *options.Options) error {
|
||||||
klog.Infof("karmada-controller-manager version: %s", version.Get())
|
klog.Infof("karmada-controller-manager version: %s", version.Get())
|
||||||
|
|
||||||
|
profileflag.ListenAndServe(opts.ProfileOpts)
|
||||||
|
|
||||||
config, err := controllerruntime.GetConfig()
|
config, err := controllerruntime.GetConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
componentbaseconfig "k8s.io/component-base/config"
|
componentbaseconfig "k8s.io/component-base/config"
|
||||||
|
|
||||||
"github.com/karmada-io/karmada/pkg/features"
|
"github.com/karmada-io/karmada/pkg/features"
|
||||||
|
"github.com/karmada-io/karmada/pkg/sharedcli/profileflag"
|
||||||
"github.com/karmada-io/karmada/pkg/sharedcli/ratelimiterflag"
|
"github.com/karmada-io/karmada/pkg/sharedcli/ratelimiterflag"
|
||||||
"github.com/karmada-io/karmada/pkg/util"
|
"github.com/karmada-io/karmada/pkg/util"
|
||||||
)
|
)
|
||||||
|
@ -112,6 +113,7 @@ type Options struct {
|
||||||
ConcurrentResourceTemplateSyncs int
|
ConcurrentResourceTemplateSyncs int
|
||||||
|
|
||||||
RateLimiterOpts ratelimiterflag.Options
|
RateLimiterOpts ratelimiterflag.Options
|
||||||
|
ProfileOpts profileflag.Options
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewOptions builds an empty options.
|
// NewOptions builds an empty options.
|
||||||
|
@ -190,5 +192,6 @@ func (o *Options) AddFlags(flags *pflag.FlagSet, allControllers, disabledByDefau
|
||||||
flags.IntVar(&o.ConcurrentResourceTemplateSyncs, "concurrent-resource-template-syncs", 5, "The number of resource templates that are allowed to sync concurrently.")
|
flags.IntVar(&o.ConcurrentResourceTemplateSyncs, "concurrent-resource-template-syncs", 5, "The number of resource templates that are allowed to sync concurrently.")
|
||||||
|
|
||||||
o.RateLimiterOpts.AddFlags(flags)
|
o.RateLimiterOpts.AddFlags(flags)
|
||||||
|
o.ProfileOpts.AddFlags(flags)
|
||||||
features.FeatureGate.AddFlag(flags)
|
features.FeatureGate.AddFlag(flags)
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ import (
|
||||||
karmadaclientset "github.com/karmada-io/karmada/pkg/generated/clientset/versioned"
|
karmadaclientset "github.com/karmada-io/karmada/pkg/generated/clientset/versioned"
|
||||||
"github.com/karmada-io/karmada/pkg/sharedcli"
|
"github.com/karmada-io/karmada/pkg/sharedcli"
|
||||||
"github.com/karmada-io/karmada/pkg/sharedcli/klogflag"
|
"github.com/karmada-io/karmada/pkg/sharedcli/klogflag"
|
||||||
|
"github.com/karmada-io/karmada/pkg/sharedcli/profileflag"
|
||||||
"github.com/karmada-io/karmada/pkg/version"
|
"github.com/karmada-io/karmada/pkg/version"
|
||||||
"github.com/karmada-io/karmada/pkg/version/sharedcommand"
|
"github.com/karmada-io/karmada/pkg/version/sharedcommand"
|
||||||
)
|
)
|
||||||
|
@ -81,6 +82,8 @@ func run(opts *options.Options, stopChan <-chan struct{}) error {
|
||||||
klog.Infof("Please make sure the karmada-scheduler-estimator of all member clusters has been deployed")
|
klog.Infof("Please make sure the karmada-scheduler-estimator of all member clusters has been deployed")
|
||||||
go serveHealthzAndMetrics(net.JoinHostPort(opts.BindAddress, strconv.Itoa(opts.SecurePort)))
|
go serveHealthzAndMetrics(net.JoinHostPort(opts.BindAddress, strconv.Itoa(opts.SecurePort)))
|
||||||
|
|
||||||
|
profileflag.ListenAndServe(opts.ProfileOpts)
|
||||||
|
|
||||||
restConfig, err := clientcmd.BuildConfigFromFlags(opts.Master, opts.KubeConfig)
|
restConfig, err := clientcmd.BuildConfigFromFlags(opts.Master, opts.KubeConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error building kubeconfig: %s", err.Error())
|
return fmt.Errorf("error building kubeconfig: %s", err.Error())
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"k8s.io/client-go/tools/leaderelection/resourcelock"
|
"k8s.io/client-go/tools/leaderelection/resourcelock"
|
||||||
componentbaseconfig "k8s.io/component-base/config"
|
componentbaseconfig "k8s.io/component-base/config"
|
||||||
|
|
||||||
|
"github.com/karmada-io/karmada/pkg/sharedcli/profileflag"
|
||||||
"github.com/karmada-io/karmada/pkg/util"
|
"github.com/karmada-io/karmada/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -47,6 +48,7 @@ type Options struct {
|
||||||
DeschedulingInterval metav1.Duration
|
DeschedulingInterval metav1.Duration
|
||||||
// UnschedulableThreshold specifies the period of pod unschedulable condition.
|
// UnschedulableThreshold specifies the period of pod unschedulable condition.
|
||||||
UnschedulableThreshold metav1.Duration
|
UnschedulableThreshold metav1.Duration
|
||||||
|
ProfileOpts profileflag.Options
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewOptions builds a default descheduler options.
|
// NewOptions builds a default descheduler options.
|
||||||
|
@ -81,4 +83,5 @@ func (o *Options) AddFlags(fs *pflag.FlagSet) {
|
||||||
fs.IntVar(&o.SchedulerEstimatorPort, "scheduler-estimator-port", defaultEstimatorPort, "The secure port on which to connect the accurate scheduler estimator.")
|
fs.IntVar(&o.SchedulerEstimatorPort, "scheduler-estimator-port", defaultEstimatorPort, "The secure port on which to connect the accurate scheduler estimator.")
|
||||||
fs.DurationVar(&o.DeschedulingInterval.Duration, "descheduling-interval", defaultDeschedulingInterval, "Time interval between two consecutive descheduler executions. Setting this value instructs the descheduler to run in a continuous loop at the interval specified.")
|
fs.DurationVar(&o.DeschedulingInterval.Duration, "descheduling-interval", defaultDeschedulingInterval, "Time interval between two consecutive descheduler executions. Setting this value instructs the descheduler to run in a continuous loop at the interval specified.")
|
||||||
fs.DurationVar(&o.UnschedulableThreshold.Duration, "unschedulable-threshold", defaultUnschedulableThreshold, "The period of pod unschedulable condition. This value is considered as a classification standard of unschedulable replicas.")
|
fs.DurationVar(&o.UnschedulableThreshold.Duration, "unschedulable-threshold", defaultUnschedulableThreshold, "The period of pod unschedulable condition. This value is considered as a classification standard of unschedulable replicas.")
|
||||||
|
o.ProfileOpts.AddFlags(fs)
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,34 +9,39 @@ import (
|
||||||
componentbaseconfig "k8s.io/component-base/config"
|
componentbaseconfig "k8s.io/component-base/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// a callback function to modify options
|
||||||
|
type ModifyOptions func(option *Options)
|
||||||
|
|
||||||
|
// New an Options with default parameters
|
||||||
|
func New(modifyOptions ModifyOptions) Options {
|
||||||
|
option := Options{
|
||||||
|
LeaderElection: componentbaseconfig.LeaderElectionConfiguration{
|
||||||
|
LeaderElect: false,
|
||||||
|
},
|
||||||
|
BindAddress: "127.0.0.1",
|
||||||
|
SecurePort: 9000,
|
||||||
|
KubeAPIQPS: 40,
|
||||||
|
KubeAPIBurst: 30,
|
||||||
|
SchedulerEstimatorTimeout: metav1.Duration{Duration: 1 * time.Second},
|
||||||
|
SchedulerEstimatorPort: 9001,
|
||||||
|
DeschedulingInterval: metav1.Duration{Duration: 1 * time.Second},
|
||||||
|
UnschedulableThreshold: metav1.Duration{Duration: 1 * time.Second},
|
||||||
|
}
|
||||||
|
|
||||||
|
if modifyOptions != nil {
|
||||||
|
modifyOptions(&option)
|
||||||
|
}
|
||||||
|
return option
|
||||||
|
}
|
||||||
|
|
||||||
func TestValidateKarmadaDescheduler(t *testing.T) {
|
func TestValidateKarmadaDescheduler(t *testing.T) {
|
||||||
successCases := []Options{
|
successCases := []Options{
|
||||||
{
|
New(nil),
|
||||||
LeaderElection: componentbaseconfig.LeaderElectionConfiguration{
|
New(func(option *Options) {
|
||||||
LeaderElect: false,
|
option.LeaderElection = componentbaseconfig.LeaderElectionConfiguration{
|
||||||
},
|
|
||||||
BindAddress: "127.0.0.1",
|
|
||||||
SecurePort: 9000,
|
|
||||||
KubeAPIQPS: 40,
|
|
||||||
KubeAPIBurst: 30,
|
|
||||||
SchedulerEstimatorTimeout: metav1.Duration{Duration: 1 * time.Second},
|
|
||||||
SchedulerEstimatorPort: 9001,
|
|
||||||
DeschedulingInterval: metav1.Duration{Duration: 1 * time.Second},
|
|
||||||
UnschedulableThreshold: metav1.Duration{Duration: 1 * time.Second},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
LeaderElection: componentbaseconfig.LeaderElectionConfiguration{
|
|
||||||
LeaderElect: true,
|
LeaderElect: true,
|
||||||
},
|
}
|
||||||
BindAddress: "127.0.0.1",
|
}), {
|
||||||
SecurePort: 9000,
|
|
||||||
KubeAPIQPS: 40,
|
|
||||||
KubeAPIBurst: 30,
|
|
||||||
SchedulerEstimatorTimeout: metav1.Duration{Duration: 1 * time.Second},
|
|
||||||
SchedulerEstimatorPort: 9001,
|
|
||||||
DeschedulingInterval: metav1.Duration{Duration: 1 * time.Second},
|
|
||||||
UnschedulableThreshold: metav1.Duration{Duration: 1 * time.Second},
|
|
||||||
}, {
|
|
||||||
LeaderElection: componentbaseconfig.LeaderElectionConfiguration{
|
LeaderElection: componentbaseconfig.LeaderElectionConfiguration{
|
||||||
LeaderElect: false,
|
LeaderElect: false,
|
||||||
},
|
},
|
||||||
|
@ -58,99 +63,39 @@ func TestValidateKarmadaDescheduler(t *testing.T) {
|
||||||
expectedErrs field.ErrorList
|
expectedErrs field.ErrorList
|
||||||
}{
|
}{
|
||||||
"invalid BindAddress": {
|
"invalid BindAddress": {
|
||||||
opt: Options{
|
opt: New(func(option *Options) {
|
||||||
LeaderElection: componentbaseconfig.LeaderElectionConfiguration{
|
option.BindAddress = "127.0.0.1:8080"
|
||||||
LeaderElect: false,
|
}),
|
||||||
},
|
|
||||||
BindAddress: "127.0.0.1:8080",
|
|
||||||
SecurePort: 9000,
|
|
||||||
KubeAPIQPS: 40,
|
|
||||||
KubeAPIBurst: 30,
|
|
||||||
SchedulerEstimatorTimeout: metav1.Duration{Duration: 1 * time.Second},
|
|
||||||
SchedulerEstimatorPort: 9001,
|
|
||||||
DeschedulingInterval: metav1.Duration{Duration: 1 * time.Second},
|
|
||||||
UnschedulableThreshold: metav1.Duration{Duration: 1 * time.Second},
|
|
||||||
},
|
|
||||||
expectedErrs: field.ErrorList{field.Invalid(newPath.Child("BindAddress"), "127.0.0.1:8080", "not a valid textual representation of an IP address")},
|
expectedErrs: field.ErrorList{field.Invalid(newPath.Child("BindAddress"), "127.0.0.1:8080", "not a valid textual representation of an IP address")},
|
||||||
},
|
},
|
||||||
"invalid SecurePort": {
|
"invalid SecurePort": {
|
||||||
opt: Options{
|
opt: New(func(option *Options) {
|
||||||
LeaderElection: componentbaseconfig.LeaderElectionConfiguration{
|
option.SecurePort = 90000
|
||||||
LeaderElect: false,
|
}),
|
||||||
},
|
|
||||||
BindAddress: "127.0.0.1",
|
|
||||||
SecurePort: 90000,
|
|
||||||
KubeAPIQPS: 40,
|
|
||||||
KubeAPIBurst: 30,
|
|
||||||
SchedulerEstimatorTimeout: metav1.Duration{Duration: 1 * time.Second},
|
|
||||||
SchedulerEstimatorPort: 9001,
|
|
||||||
DeschedulingInterval: metav1.Duration{Duration: 1 * time.Second},
|
|
||||||
UnschedulableThreshold: metav1.Duration{Duration: 1 * time.Second},
|
|
||||||
},
|
|
||||||
expectedErrs: field.ErrorList{field.Invalid(newPath.Child("SecurePort"), 90000, "must be a valid port between 0 and 65535 inclusive")},
|
expectedErrs: field.ErrorList{field.Invalid(newPath.Child("SecurePort"), 90000, "must be a valid port between 0 and 65535 inclusive")},
|
||||||
},
|
},
|
||||||
"invalid SchedulerEstimatorPort": {
|
"invalid SchedulerEstimatorPort": {
|
||||||
opt: Options{
|
opt: New(func(option *Options) {
|
||||||
LeaderElection: componentbaseconfig.LeaderElectionConfiguration{
|
option.SchedulerEstimatorPort = 90000
|
||||||
LeaderElect: false,
|
}),
|
||||||
},
|
|
||||||
BindAddress: "127.0.0.1",
|
|
||||||
SecurePort: 9000,
|
|
||||||
KubeAPIQPS: 40,
|
|
||||||
KubeAPIBurst: 30,
|
|
||||||
SchedulerEstimatorTimeout: metav1.Duration{Duration: 1 * time.Second},
|
|
||||||
SchedulerEstimatorPort: 90000,
|
|
||||||
DeschedulingInterval: metav1.Duration{Duration: 1 * time.Second},
|
|
||||||
UnschedulableThreshold: metav1.Duration{Duration: 1 * time.Second},
|
|
||||||
},
|
|
||||||
expectedErrs: field.ErrorList{field.Invalid(newPath.Child("SchedulerEstimatorPort"), 90000, "must be a valid port between 0 and 65535 inclusive")},
|
expectedErrs: field.ErrorList{field.Invalid(newPath.Child("SchedulerEstimatorPort"), 90000, "must be a valid port between 0 and 65535 inclusive")},
|
||||||
},
|
},
|
||||||
"invalid SchedulerEstimatorTimeout": {
|
"invalid SchedulerEstimatorTimeout": {
|
||||||
opt: Options{
|
opt: New(func(option *Options) {
|
||||||
LeaderElection: componentbaseconfig.LeaderElectionConfiguration{
|
option.SchedulerEstimatorTimeout = metav1.Duration{Duration: -1 * time.Second}
|
||||||
LeaderElect: false,
|
}),
|
||||||
},
|
|
||||||
BindAddress: "127.0.0.1",
|
|
||||||
SecurePort: 9000,
|
|
||||||
KubeAPIQPS: 40,
|
|
||||||
KubeAPIBurst: 30,
|
|
||||||
SchedulerEstimatorTimeout: metav1.Duration{Duration: -1 * time.Second},
|
|
||||||
SchedulerEstimatorPort: 9000,
|
|
||||||
DeschedulingInterval: metav1.Duration{Duration: 1 * time.Second},
|
|
||||||
UnschedulableThreshold: metav1.Duration{Duration: 1 * time.Second},
|
|
||||||
},
|
|
||||||
expectedErrs: field.ErrorList{field.Invalid(newPath.Child("SchedulerEstimatorTimeout"), metav1.Duration{Duration: -1 * time.Second}, "must be greater than or equal to 0")},
|
expectedErrs: field.ErrorList{field.Invalid(newPath.Child("SchedulerEstimatorTimeout"), metav1.Duration{Duration: -1 * time.Second}, "must be greater than or equal to 0")},
|
||||||
},
|
},
|
||||||
"invalid DeschedulingInterval": {
|
"invalid DeschedulingInterval": {
|
||||||
opt: Options{
|
opt: New(func(option *Options) {
|
||||||
LeaderElection: componentbaseconfig.LeaderElectionConfiguration{
|
option.DeschedulingInterval = metav1.Duration{Duration: -1 * time.Second}
|
||||||
LeaderElect: false,
|
}),
|
||||||
},
|
|
||||||
BindAddress: "127.0.0.1",
|
|
||||||
SecurePort: 9000,
|
|
||||||
KubeAPIQPS: 40,
|
|
||||||
KubeAPIBurst: 30,
|
|
||||||
SchedulerEstimatorTimeout: metav1.Duration{Duration: 1 * time.Second},
|
|
||||||
SchedulerEstimatorPort: 9000,
|
|
||||||
DeschedulingInterval: metav1.Duration{Duration: -1 * time.Second},
|
|
||||||
UnschedulableThreshold: metav1.Duration{Duration: 1 * time.Second},
|
|
||||||
},
|
|
||||||
expectedErrs: field.ErrorList{field.Invalid(newPath.Child("DeschedulingInterval"), metav1.Duration{Duration: -1 * time.Second}, "must be greater than or equal to 0")},
|
expectedErrs: field.ErrorList{field.Invalid(newPath.Child("DeschedulingInterval"), metav1.Duration{Duration: -1 * time.Second}, "must be greater than or equal to 0")},
|
||||||
},
|
},
|
||||||
"invalid UnschedulableThreshold": {
|
"invalid UnschedulableThreshold": {
|
||||||
opt: Options{
|
opt: New(func(option *Options) {
|
||||||
LeaderElection: componentbaseconfig.LeaderElectionConfiguration{
|
option.UnschedulableThreshold = metav1.Duration{Duration: -1 * time.Second}
|
||||||
LeaderElect: false,
|
}),
|
||||||
},
|
|
||||||
BindAddress: "127.0.0.1",
|
|
||||||
SecurePort: 9000,
|
|
||||||
KubeAPIQPS: 40,
|
|
||||||
KubeAPIBurst: 30,
|
|
||||||
SchedulerEstimatorTimeout: metav1.Duration{Duration: 1 * time.Second},
|
|
||||||
SchedulerEstimatorPort: 9000,
|
|
||||||
DeschedulingInterval: metav1.Duration{Duration: 1 * time.Second},
|
|
||||||
UnschedulableThreshold: metav1.Duration{Duration: -1 * time.Second},
|
|
||||||
},
|
|
||||||
expectedErrs: field.ErrorList{field.Invalid(newPath.Child("UnschedulableThreshold"), metav1.Duration{Duration: -1 * time.Second}, "must be greater than or equal to 0")},
|
expectedErrs: field.ErrorList{field.Invalid(newPath.Child("UnschedulableThreshold"), metav1.Duration{Duration: -1 * time.Second}, "must be greater than or equal to 0")},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ import (
|
||||||
searchv1alpha1 "github.com/karmada-io/karmada/pkg/apis/search/v1alpha1"
|
searchv1alpha1 "github.com/karmada-io/karmada/pkg/apis/search/v1alpha1"
|
||||||
generatedopenapi "github.com/karmada-io/karmada/pkg/generated/openapi"
|
generatedopenapi "github.com/karmada-io/karmada/pkg/generated/openapi"
|
||||||
"github.com/karmada-io/karmada/pkg/search"
|
"github.com/karmada-io/karmada/pkg/search"
|
||||||
|
"github.com/karmada-io/karmada/pkg/sharedcli/profileflag"
|
||||||
"github.com/karmada-io/karmada/pkg/version"
|
"github.com/karmada-io/karmada/pkg/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -33,6 +34,8 @@ type Options struct {
|
||||||
KubeAPIQPS float32
|
KubeAPIQPS float32
|
||||||
// KubeAPIBurst is the burst to allow while talking with karmada-search.
|
// KubeAPIBurst is the burst to allow while talking with karmada-search.
|
||||||
KubeAPIBurst int
|
KubeAPIBurst int
|
||||||
|
|
||||||
|
ProfileOpts profileflag.Options
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewOptions returns a new Options.
|
// NewOptions returns a new Options.
|
||||||
|
@ -57,6 +60,7 @@ func (o *Options) AddFlags(flags *pflag.FlagSet) {
|
||||||
flags.IntVar(&o.KubeAPIBurst, "kube-api-burst", 60, "Burst to use while talking with karmada-apiserver. Doesn't cover events and node heartbeat apis which rate limiting is controlled by a different set of flags.")
|
flags.IntVar(&o.KubeAPIBurst, "kube-api-burst", 60, "Burst to use while talking with karmada-apiserver. Doesn't cover events and node heartbeat apis which rate limiting is controlled by a different set of flags.")
|
||||||
|
|
||||||
utilfeature.DefaultMutableFeatureGate.AddFlag(flags)
|
utilfeature.DefaultMutableFeatureGate.AddFlag(flags)
|
||||||
|
o.ProfileOpts.AddFlags(flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Complete fills in fields required to have valid data.
|
// Complete fills in fields required to have valid data.
|
||||||
|
@ -68,6 +72,8 @@ func (o *Options) Complete() error {
|
||||||
func (o *Options) Run(ctx context.Context) error {
|
func (o *Options) Run(ctx context.Context) error {
|
||||||
klog.Infof("karmada-search version: %s", version.Get())
|
klog.Infof("karmada-search version: %s", version.Get())
|
||||||
|
|
||||||
|
profileflag.ListenAndServe(o.ProfileOpts)
|
||||||
|
|
||||||
config, err := o.Config()
|
config, err := o.Config()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -99,6 +105,8 @@ func (o *Options) Config() (*search.Config, error) {
|
||||||
return nil, fmt.Errorf("error creating self-signed certificates: %v", err)
|
return nil, fmt.Errorf("error creating self-signed certificates: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
o.RecommendedOptions.Features = &genericoptions.FeatureOptions{EnableProfiling: false}
|
||||||
|
|
||||||
serverConfig := genericapiserver.NewRecommendedConfig(searchscheme.Codecs)
|
serverConfig := genericapiserver.NewRecommendedConfig(searchscheme.Codecs)
|
||||||
serverConfig.OpenAPIConfig = genericapiserver.DefaultOpenAPIConfig(generatedopenapi.GetOpenAPIDefinitions, openapi.NewDefinitionNamer(searchscheme.Scheme))
|
serverConfig.OpenAPIConfig = genericapiserver.DefaultOpenAPIConfig(generatedopenapi.GetOpenAPIDefinitions, openapi.NewDefinitionNamer(searchscheme.Scheme))
|
||||||
serverConfig.OpenAPIConfig.Info.Title = "karmada-search"
|
serverConfig.OpenAPIConfig.Info.Title = "karmada-search"
|
||||||
|
|
|
@ -2,6 +2,8 @@ package options
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
|
|
||||||
|
"github.com/karmada-io/karmada/pkg/sharedcli/profileflag"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -27,6 +29,7 @@ type Options struct {
|
||||||
ClusterAPIBurst int
|
ClusterAPIBurst int
|
||||||
// Parallelism defines the amount of parallelism in algorithms for estimating. Must be greater than 0. Defaults to 16.
|
// Parallelism defines the amount of parallelism in algorithms for estimating. Must be greater than 0. Defaults to 16.
|
||||||
Parallelism int
|
Parallelism int
|
||||||
|
ProfileOpts profileflag.Options
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewOptions builds an empty options.
|
// NewOptions builds an empty options.
|
||||||
|
@ -48,4 +51,5 @@ func (o *Options) AddFlags(fs *pflag.FlagSet) {
|
||||||
fs.Float32Var(&o.ClusterAPIQPS, "kube-api-qps", 20.0, "QPS to use while talking with apiserver. Doesn't cover events and node heartbeat apis which rate limiting is controlled by a different set of flags.")
|
fs.Float32Var(&o.ClusterAPIQPS, "kube-api-qps", 20.0, "QPS to use while talking with apiserver. Doesn't cover events and node heartbeat apis which rate limiting is controlled by a different set of flags.")
|
||||||
fs.IntVar(&o.ClusterAPIBurst, "kube-api-burst", 30, "Burst to use while talking with apiserver. Doesn't cover events and node heartbeat apis which rate limiting is controlled by a different set of flags.")
|
fs.IntVar(&o.ClusterAPIBurst, "kube-api-burst", 30, "Burst to use while talking with apiserver. Doesn't cover events and node heartbeat apis which rate limiting is controlled by a different set of flags.")
|
||||||
fs.IntVar(&o.Parallelism, "parallelism", o.Parallelism, "Parallelism defines the amount of parallelism in algorithms for estimating. Must be greater than 0. Defaults to 16.")
|
fs.IntVar(&o.Parallelism, "parallelism", o.Parallelism, "Parallelism defines the amount of parallelism in algorithms for estimating. Must be greater than 0. Defaults to 16.")
|
||||||
|
o.ProfileOpts.AddFlags(fs)
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,14 +6,27 @@ import (
|
||||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// a callback function to modify options
|
||||||
|
type ModifyOptions func(option *Options)
|
||||||
|
|
||||||
|
// New an Options with default parameters
|
||||||
|
func New(modifyOptions ModifyOptions) Options {
|
||||||
|
option := Options{
|
||||||
|
ClusterName: "testCluster",
|
||||||
|
BindAddress: "0.0.0.0",
|
||||||
|
SecurePort: 10100,
|
||||||
|
ServerPort: 8088,
|
||||||
|
}
|
||||||
|
|
||||||
|
if modifyOptions != nil {
|
||||||
|
modifyOptions(&option)
|
||||||
|
}
|
||||||
|
return option
|
||||||
|
}
|
||||||
|
|
||||||
func TestValidateKarmadaSchedulerEstimator(t *testing.T) {
|
func TestValidateKarmadaSchedulerEstimator(t *testing.T) {
|
||||||
successCases := []Options{
|
successCases := []Options{
|
||||||
{
|
New(nil),
|
||||||
ClusterName: "testCluster",
|
|
||||||
BindAddress: "0.0.0.0",
|
|
||||||
SecurePort: 10100,
|
|
||||||
ServerPort: 8088,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
for _, successCase := range successCases {
|
for _, successCase := range successCases {
|
||||||
if errs := successCase.Validate(); len(errs) != 0 {
|
if errs := successCase.Validate(); len(errs) != 0 {
|
||||||
|
@ -27,39 +40,27 @@ func TestValidateKarmadaSchedulerEstimator(t *testing.T) {
|
||||||
expectedErrs field.ErrorList
|
expectedErrs field.ErrorList
|
||||||
}{
|
}{
|
||||||
"invalid ClusterName": {
|
"invalid ClusterName": {
|
||||||
opt: Options{
|
opt: New(func(option *Options) {
|
||||||
ClusterName: "",
|
option.ClusterName = ""
|
||||||
BindAddress: "127.0.0.1",
|
}),
|
||||||
SecurePort: 10100,
|
|
||||||
ServerPort: 8088,
|
|
||||||
},
|
|
||||||
expectedErrs: field.ErrorList{field.Invalid(newPath.Child("ClusterName"), "", "clusterName cannot be empty")},
|
expectedErrs: field.ErrorList{field.Invalid(newPath.Child("ClusterName"), "", "clusterName cannot be empty")},
|
||||||
},
|
},
|
||||||
"invalid BindAddress": {
|
"invalid BindAddress": {
|
||||||
opt: Options{
|
opt: New(func(option *Options) {
|
||||||
ClusterName: "testCluster",
|
option.BindAddress = "127.0.0.1:8082"
|
||||||
BindAddress: "127.0.0.1:8082",
|
}),
|
||||||
SecurePort: 10100,
|
|
||||||
ServerPort: 8088,
|
|
||||||
},
|
|
||||||
expectedErrs: field.ErrorList{field.Invalid(newPath.Child("BindAddress"), "127.0.0.1:8082", "not a valid textual representation of an IP address")},
|
expectedErrs: field.ErrorList{field.Invalid(newPath.Child("BindAddress"), "127.0.0.1:8082", "not a valid textual representation of an IP address")},
|
||||||
},
|
},
|
||||||
"invalid SecurePort": {
|
"invalid SecurePort": {
|
||||||
opt: Options{
|
opt: New(func(option *Options) {
|
||||||
ClusterName: "testCluster",
|
option.SecurePort = 908188
|
||||||
BindAddress: "127.0.0.1",
|
}),
|
||||||
SecurePort: 908188,
|
|
||||||
ServerPort: 8088,
|
|
||||||
},
|
|
||||||
expectedErrs: field.ErrorList{field.Invalid(newPath.Child("SecurePort"), 908188, "must be a valid port between 0 and 65535 inclusive")},
|
expectedErrs: field.ErrorList{field.Invalid(newPath.Child("SecurePort"), 908188, "must be a valid port between 0 and 65535 inclusive")},
|
||||||
},
|
},
|
||||||
"invalid ServerPort": {
|
"invalid ServerPort": {
|
||||||
opt: Options{
|
opt: New(func(option *Options) {
|
||||||
ClusterName: "testCluster",
|
option.ServerPort = 80888
|
||||||
BindAddress: "127.0.0.1",
|
}),
|
||||||
SecurePort: 9089,
|
|
||||||
ServerPort: 80888,
|
|
||||||
},
|
|
||||||
expectedErrs: field.ErrorList{field.Invalid(newPath.Child("ServerPort"), 80888, "must be a valid port between 0 and 65535 inclusive")},
|
expectedErrs: field.ErrorList{field.Invalid(newPath.Child("ServerPort"), 80888, "must be a valid port between 0 and 65535 inclusive")},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ import (
|
||||||
"github.com/karmada-io/karmada/pkg/estimator/server"
|
"github.com/karmada-io/karmada/pkg/estimator/server"
|
||||||
"github.com/karmada-io/karmada/pkg/sharedcli"
|
"github.com/karmada-io/karmada/pkg/sharedcli"
|
||||||
"github.com/karmada-io/karmada/pkg/sharedcli/klogflag"
|
"github.com/karmada-io/karmada/pkg/sharedcli/klogflag"
|
||||||
|
"github.com/karmada-io/karmada/pkg/sharedcli/profileflag"
|
||||||
"github.com/karmada-io/karmada/pkg/version"
|
"github.com/karmada-io/karmada/pkg/version"
|
||||||
"github.com/karmada-io/karmada/pkg/version/sharedcommand"
|
"github.com/karmada-io/karmada/pkg/version/sharedcommand"
|
||||||
)
|
)
|
||||||
|
@ -66,6 +67,8 @@ func run(ctx context.Context, opts *options.Options) error {
|
||||||
klog.Infof("karmada-scheduler-estimator version: %s", version.Get())
|
klog.Infof("karmada-scheduler-estimator version: %s", version.Get())
|
||||||
go serveHealthzAndMetrics(net.JoinHostPort(opts.BindAddress, strconv.Itoa(opts.SecurePort)))
|
go serveHealthzAndMetrics(net.JoinHostPort(opts.BindAddress, strconv.Itoa(opts.SecurePort)))
|
||||||
|
|
||||||
|
profileflag.ListenAndServe(opts.ProfileOpts)
|
||||||
|
|
||||||
restConfig, err := clientcmd.BuildConfigFromFlags(opts.Master, opts.KubeConfig)
|
restConfig, err := clientcmd.BuildConfigFromFlags(opts.Master, opts.KubeConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error building kubeconfig: %s", err.Error())
|
return fmt.Errorf("error building kubeconfig: %s", err.Error())
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
componentbaseconfig "k8s.io/component-base/config"
|
componentbaseconfig "k8s.io/component-base/config"
|
||||||
|
|
||||||
"github.com/karmada-io/karmada/pkg/features"
|
"github.com/karmada-io/karmada/pkg/features"
|
||||||
|
"github.com/karmada-io/karmada/pkg/sharedcli/profileflag"
|
||||||
"github.com/karmada-io/karmada/pkg/util"
|
"github.com/karmada-io/karmada/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -50,6 +51,7 @@ type Options struct {
|
||||||
|
|
||||||
// EnableEmptyWorkloadPropagation represents whether workload with 0 replicas could be propagated to member clusters.
|
// EnableEmptyWorkloadPropagation represents whether workload with 0 replicas could be propagated to member clusters.
|
||||||
EnableEmptyWorkloadPropagation bool
|
EnableEmptyWorkloadPropagation bool
|
||||||
|
ProfileOpts profileflag.Options
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewOptions builds an default scheduler options.
|
// NewOptions builds an default scheduler options.
|
||||||
|
@ -87,4 +89,5 @@ func (o *Options) AddFlags(fs *pflag.FlagSet) {
|
||||||
fs.IntVar(&o.SchedulerEstimatorPort, "scheduler-estimator-port", defaultEstimatorPort, "The secure port on which to connect the accurate scheduler estimator.")
|
fs.IntVar(&o.SchedulerEstimatorPort, "scheduler-estimator-port", defaultEstimatorPort, "The secure port on which to connect the accurate scheduler estimator.")
|
||||||
fs.BoolVar(&o.EnableEmptyWorkloadPropagation, "enable-empty-workload-propagation", false, "Enable workload with replicas 0 to be propagated to member clusters.")
|
fs.BoolVar(&o.EnableEmptyWorkloadPropagation, "enable-empty-workload-propagation", false, "Enable workload with replicas 0 to be propagated to member clusters.")
|
||||||
features.FeatureGate.AddFlag(fs)
|
features.FeatureGate.AddFlag(fs)
|
||||||
|
o.ProfileOpts.AddFlags(fs)
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,32 +9,39 @@ import (
|
||||||
componentbaseconfig "k8s.io/component-base/config"
|
componentbaseconfig "k8s.io/component-base/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// a callback function to modify options
|
||||||
|
type ModifyOptions func(option *Options)
|
||||||
|
|
||||||
|
// New an Options with default parameters
|
||||||
|
func New(modifyOptions ModifyOptions) Options {
|
||||||
|
option := Options{
|
||||||
|
LeaderElection: componentbaseconfig.LeaderElectionConfiguration{
|
||||||
|
LeaderElect: false,
|
||||||
|
},
|
||||||
|
BindAddress: "127.0.0.1",
|
||||||
|
SecurePort: 9000,
|
||||||
|
KubeAPIQPS: 40,
|
||||||
|
KubeAPIBurst: 30,
|
||||||
|
EnableSchedulerEstimator: false,
|
||||||
|
SchedulerEstimatorTimeout: metav1.Duration{Duration: 1 * time.Second},
|
||||||
|
SchedulerEstimatorPort: 9001,
|
||||||
|
}
|
||||||
|
|
||||||
|
if modifyOptions != nil {
|
||||||
|
modifyOptions(&option)
|
||||||
|
}
|
||||||
|
return option
|
||||||
|
}
|
||||||
|
|
||||||
func TestValidateKarmadaSchedulerConfiguration(t *testing.T) {
|
func TestValidateKarmadaSchedulerConfiguration(t *testing.T) {
|
||||||
successCases := []Options{
|
successCases := []Options{
|
||||||
{
|
New(nil),
|
||||||
LeaderElection: componentbaseconfig.LeaderElectionConfiguration{
|
New(func(option *Options) {
|
||||||
LeaderElect: false,
|
option.LeaderElection = componentbaseconfig.LeaderElectionConfiguration{
|
||||||
},
|
|
||||||
BindAddress: "127.0.0.1",
|
|
||||||
SecurePort: 9000,
|
|
||||||
KubeAPIQPS: 40,
|
|
||||||
KubeAPIBurst: 30,
|
|
||||||
EnableSchedulerEstimator: false,
|
|
||||||
SchedulerEstimatorTimeout: metav1.Duration{Duration: 1 * time.Second},
|
|
||||||
SchedulerEstimatorPort: 9001,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
LeaderElection: componentbaseconfig.LeaderElectionConfiguration{
|
|
||||||
LeaderElect: true,
|
LeaderElect: true,
|
||||||
},
|
}
|
||||||
BindAddress: "127.0.0.1",
|
}),
|
||||||
SecurePort: 9000,
|
{
|
||||||
KubeAPIQPS: 40,
|
|
||||||
KubeAPIBurst: 30,
|
|
||||||
EnableSchedulerEstimator: false,
|
|
||||||
SchedulerEstimatorTimeout: metav1.Duration{Duration: 1 * time.Second},
|
|
||||||
SchedulerEstimatorPort: 9001,
|
|
||||||
}, {
|
|
||||||
LeaderElection: componentbaseconfig.LeaderElectionConfiguration{
|
LeaderElection: componentbaseconfig.LeaderElectionConfiguration{
|
||||||
LeaderElect: false,
|
LeaderElect: false,
|
||||||
},
|
},
|
||||||
|
@ -42,7 +49,8 @@ func TestValidateKarmadaSchedulerConfiguration(t *testing.T) {
|
||||||
SecurePort: 9000,
|
SecurePort: 9000,
|
||||||
KubeAPIQPS: 40,
|
KubeAPIQPS: 40,
|
||||||
KubeAPIBurst: 30,
|
KubeAPIBurst: 30,
|
||||||
}}
|
},
|
||||||
|
}
|
||||||
|
|
||||||
for _, successCase := range successCases {
|
for _, successCase := range successCases {
|
||||||
if errs := successCase.Validate(); len(errs) != 0 {
|
if errs := successCase.Validate(); len(errs) != 0 {
|
||||||
|
@ -56,63 +64,27 @@ func TestValidateKarmadaSchedulerConfiguration(t *testing.T) {
|
||||||
expectedErrs field.ErrorList
|
expectedErrs field.ErrorList
|
||||||
}{
|
}{
|
||||||
"invalid BindAddress": {
|
"invalid BindAddress": {
|
||||||
opt: Options{
|
opt: New(func(option *Options) {
|
||||||
LeaderElection: componentbaseconfig.LeaderElectionConfiguration{
|
option.BindAddress = "127.0.0.1:8080"
|
||||||
LeaderElect: false,
|
}),
|
||||||
},
|
|
||||||
BindAddress: "127.0.0.1:8080",
|
|
||||||
SecurePort: 9000,
|
|
||||||
KubeAPIQPS: 40,
|
|
||||||
KubeAPIBurst: 30,
|
|
||||||
EnableSchedulerEstimator: false,
|
|
||||||
SchedulerEstimatorTimeout: metav1.Duration{Duration: 1 * time.Second},
|
|
||||||
SchedulerEstimatorPort: 9001,
|
|
||||||
},
|
|
||||||
expectedErrs: field.ErrorList{field.Invalid(newPath.Child("BindAddress"), "127.0.0.1:8080", "not a valid textual representation of an IP address")},
|
expectedErrs: field.ErrorList{field.Invalid(newPath.Child("BindAddress"), "127.0.0.1:8080", "not a valid textual representation of an IP address")},
|
||||||
},
|
},
|
||||||
"invalid SecurePort": {
|
"invalid SecurePort": {
|
||||||
opt: Options{
|
opt: New(func(option *Options) {
|
||||||
LeaderElection: componentbaseconfig.LeaderElectionConfiguration{
|
option.SecurePort = 90000
|
||||||
LeaderElect: false,
|
}),
|
||||||
},
|
|
||||||
BindAddress: "127.0.0.1",
|
|
||||||
SecurePort: 90000,
|
|
||||||
KubeAPIQPS: 40,
|
|
||||||
KubeAPIBurst: 30,
|
|
||||||
EnableSchedulerEstimator: false,
|
|
||||||
SchedulerEstimatorTimeout: metav1.Duration{Duration: 1 * time.Second},
|
|
||||||
SchedulerEstimatorPort: 9001,
|
|
||||||
},
|
|
||||||
expectedErrs: field.ErrorList{field.Invalid(newPath.Child("SecurePort"), 90000, "must be a valid port between 0 and 65535 inclusive")},
|
expectedErrs: field.ErrorList{field.Invalid(newPath.Child("SecurePort"), 90000, "must be a valid port between 0 and 65535 inclusive")},
|
||||||
},
|
},
|
||||||
"invalid SchedulerEstimatorPort": {
|
"invalid SchedulerEstimatorPort": {
|
||||||
opt: Options{
|
opt: New(func(option *Options) {
|
||||||
LeaderElection: componentbaseconfig.LeaderElectionConfiguration{
|
option.SchedulerEstimatorPort = 90000
|
||||||
LeaderElect: false,
|
}),
|
||||||
},
|
|
||||||
BindAddress: "127.0.0.1",
|
|
||||||
SecurePort: 9000,
|
|
||||||
KubeAPIQPS: 40,
|
|
||||||
KubeAPIBurst: 30,
|
|
||||||
EnableSchedulerEstimator: false,
|
|
||||||
SchedulerEstimatorTimeout: metav1.Duration{Duration: 1 * time.Second},
|
|
||||||
SchedulerEstimatorPort: 90000,
|
|
||||||
},
|
|
||||||
expectedErrs: field.ErrorList{field.Invalid(newPath.Child("SchedulerEstimatorPort"), 90000, "must be a valid port between 0 and 65535 inclusive")},
|
expectedErrs: field.ErrorList{field.Invalid(newPath.Child("SchedulerEstimatorPort"), 90000, "must be a valid port between 0 and 65535 inclusive")},
|
||||||
},
|
},
|
||||||
"invalid SchedulerEstimatorTimeout": {
|
"invalid SchedulerEstimatorTimeout": {
|
||||||
opt: Options{
|
opt: New(func(option *Options) {
|
||||||
LeaderElection: componentbaseconfig.LeaderElectionConfiguration{
|
option.SchedulerEstimatorTimeout = metav1.Duration{Duration: -1 * time.Second}
|
||||||
LeaderElect: false,
|
}),
|
||||||
},
|
|
||||||
BindAddress: "127.0.0.1",
|
|
||||||
SecurePort: 9000,
|
|
||||||
KubeAPIQPS: 40,
|
|
||||||
KubeAPIBurst: 30,
|
|
||||||
EnableSchedulerEstimator: false,
|
|
||||||
SchedulerEstimatorTimeout: metav1.Duration{Duration: -1 * time.Second},
|
|
||||||
SchedulerEstimatorPort: 9000,
|
|
||||||
},
|
|
||||||
expectedErrs: field.ErrorList{field.Invalid(newPath.Child("SchedulerEstimatorTimeout"), metav1.Duration{Duration: -1 * time.Second}, "must be greater than or equal to 0")},
|
expectedErrs: field.ErrorList{field.Invalid(newPath.Child("SchedulerEstimatorTimeout"), metav1.Duration{Duration: -1 * time.Second}, "must be greater than or equal to 0")},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ import (
|
||||||
"github.com/karmada-io/karmada/pkg/scheduler/framework/runtime"
|
"github.com/karmada-io/karmada/pkg/scheduler/framework/runtime"
|
||||||
"github.com/karmada-io/karmada/pkg/sharedcli"
|
"github.com/karmada-io/karmada/pkg/sharedcli"
|
||||||
"github.com/karmada-io/karmada/pkg/sharedcli/klogflag"
|
"github.com/karmada-io/karmada/pkg/sharedcli/klogflag"
|
||||||
|
"github.com/karmada-io/karmada/pkg/sharedcli/profileflag"
|
||||||
"github.com/karmada-io/karmada/pkg/version"
|
"github.com/karmada-io/karmada/pkg/version"
|
||||||
"github.com/karmada-io/karmada/pkg/version/sharedcommand"
|
"github.com/karmada-io/karmada/pkg/version/sharedcommand"
|
||||||
)
|
)
|
||||||
|
@ -90,6 +91,8 @@ func run(opts *options.Options, stopChan <-chan struct{}, registryOptions ...Opt
|
||||||
klog.Infof("karmada-scheduler version: %s", version.Get())
|
klog.Infof("karmada-scheduler version: %s", version.Get())
|
||||||
go serveHealthzAndMetrics(net.JoinHostPort(opts.BindAddress, strconv.Itoa(opts.SecurePort)))
|
go serveHealthzAndMetrics(net.JoinHostPort(opts.BindAddress, strconv.Itoa(opts.SecurePort)))
|
||||||
|
|
||||||
|
profileflag.ListenAndServe(opts.ProfileOpts)
|
||||||
|
|
||||||
restConfig, err := clientcmd.BuildConfigFromFlags(opts.Master, opts.KubeConfig)
|
restConfig, err := clientcmd.BuildConfigFromFlags(opts.Master, opts.KubeConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error building kubeconfig: %s", err.Error())
|
return fmt.Errorf("error building kubeconfig: %s", err.Error())
|
||||||
|
|
|
@ -2,6 +2,8 @@ package options
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
|
|
||||||
|
"github.com/karmada-io/karmada/pkg/sharedcli/profileflag"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -44,6 +46,8 @@ type Options struct {
|
||||||
// for serving health probes
|
// for serving health probes
|
||||||
// Defaults to ":8000".
|
// Defaults to ":8000".
|
||||||
HealthProbeBindAddress string
|
HealthProbeBindAddress string
|
||||||
|
|
||||||
|
ProfileOpts profileflag.Options
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewOptions builds an empty options.
|
// NewOptions builds an empty options.
|
||||||
|
@ -68,4 +72,6 @@ func (o *Options) AddFlags(flags *pflag.FlagSet) {
|
||||||
flags.IntVar(&o.KubeAPIBurst, "kube-api-burst", 60, "Burst to use while talking with karmada-apiserver. Doesn't cover events and node heartbeat apis which rate limiting is controlled by a different set of flags.")
|
flags.IntVar(&o.KubeAPIBurst, "kube-api-burst", 60, "Burst to use while talking with karmada-apiserver. Doesn't cover events and node heartbeat apis which rate limiting is controlled by a different set of flags.")
|
||||||
flags.StringVar(&o.MetricsBindAddress, "metrics-bind-address", ":8080", "The TCP address that the controller should bind to for serving prometheus metrics(e.g. 127.0.0.1:8088, :8088)")
|
flags.StringVar(&o.MetricsBindAddress, "metrics-bind-address", ":8080", "The TCP address that the controller should bind to for serving prometheus metrics(e.g. 127.0.0.1:8088, :8088)")
|
||||||
flags.StringVar(&o.HealthProbeBindAddress, "health-probe-bind-address", ":8000", "The TCP address that the controller should bind to for serving health probes(e.g. 127.0.0.1:8000, :8000)")
|
flags.StringVar(&o.HealthProbeBindAddress, "health-probe-bind-address", ":8000", "The TCP address that the controller should bind to for serving health probes(e.g. 127.0.0.1:8000, :8000)")
|
||||||
|
|
||||||
|
o.ProfileOpts.AddFlags(flags)
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,14 +6,27 @@ import (
|
||||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// a callback function to modify options
|
||||||
|
type ModifyOptions func(option *Options)
|
||||||
|
|
||||||
|
// New an Options with default parameters
|
||||||
|
func New(modifyOptions ModifyOptions) Options {
|
||||||
|
option := Options{
|
||||||
|
BindAddress: "127.0.0.1",
|
||||||
|
SecurePort: 9000,
|
||||||
|
KubeAPIQPS: 40,
|
||||||
|
KubeAPIBurst: 30,
|
||||||
|
}
|
||||||
|
|
||||||
|
if modifyOptions != nil {
|
||||||
|
modifyOptions(&option)
|
||||||
|
}
|
||||||
|
return option
|
||||||
|
}
|
||||||
|
|
||||||
func TestValidateKarmadaWebhookConfiguration(t *testing.T) {
|
func TestValidateKarmadaWebhookConfiguration(t *testing.T) {
|
||||||
successCases := []Options{
|
successCases := []Options{
|
||||||
{
|
New(nil),
|
||||||
BindAddress: "127.0.0.1",
|
|
||||||
SecurePort: 9000,
|
|
||||||
KubeAPIQPS: 40,
|
|
||||||
KubeAPIBurst: 30,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
for _, successCases := range successCases {
|
for _, successCases := range successCases {
|
||||||
if errs := successCases.Validate(); len(errs) != 0 {
|
if errs := successCases.Validate(); len(errs) != 0 {
|
||||||
|
@ -26,21 +39,15 @@ func TestValidateKarmadaWebhookConfiguration(t *testing.T) {
|
||||||
expectedErrs field.ErrorList
|
expectedErrs field.ErrorList
|
||||||
}{
|
}{
|
||||||
"invalid BindAddress": {
|
"invalid BindAddress": {
|
||||||
opt: Options{
|
opt: New(func(option *Options) {
|
||||||
BindAddress: "127.0.0.1:8080",
|
option.BindAddress = "127.0.0.1:8080"
|
||||||
SecurePort: 9000,
|
}),
|
||||||
KubeAPIQPS: 40,
|
|
||||||
KubeAPIBurst: 30,
|
|
||||||
},
|
|
||||||
expectedErrs: field.ErrorList{field.Invalid(newPath.Child("BindAddress"), "127.0.0.1:8080", "not a valid textual representation of an IP address")},
|
expectedErrs: field.ErrorList{field.Invalid(newPath.Child("BindAddress"), "127.0.0.1:8080", "not a valid textual representation of an IP address")},
|
||||||
},
|
},
|
||||||
"invalid SecurePort": {
|
"invalid SecurePort": {
|
||||||
opt: Options{
|
opt: New(func(option *Options) {
|
||||||
BindAddress: "127.0.0.1",
|
option.SecurePort = 900000
|
||||||
SecurePort: 900000,
|
}),
|
||||||
KubeAPIQPS: 40,
|
|
||||||
KubeAPIBurst: 30,
|
|
||||||
},
|
|
||||||
expectedErrs: field.ErrorList{field.Invalid(newPath.Child("SecurePort"), 900000, "must be a valid port between 0 and 65535 inclusive")},
|
expectedErrs: field.ErrorList{field.Invalid(newPath.Child("SecurePort"), 900000, "must be a valid port between 0 and 65535 inclusive")},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ import (
|
||||||
"github.com/karmada-io/karmada/cmd/webhook/app/options"
|
"github.com/karmada-io/karmada/cmd/webhook/app/options"
|
||||||
"github.com/karmada-io/karmada/pkg/sharedcli"
|
"github.com/karmada-io/karmada/pkg/sharedcli"
|
||||||
"github.com/karmada-io/karmada/pkg/sharedcli/klogflag"
|
"github.com/karmada-io/karmada/pkg/sharedcli/klogflag"
|
||||||
|
"github.com/karmada-io/karmada/pkg/sharedcli/profileflag"
|
||||||
"github.com/karmada-io/karmada/pkg/util/gclient"
|
"github.com/karmada-io/karmada/pkg/util/gclient"
|
||||||
"github.com/karmada-io/karmada/pkg/version"
|
"github.com/karmada-io/karmada/pkg/version"
|
||||||
"github.com/karmada-io/karmada/pkg/version/sharedcommand"
|
"github.com/karmada-io/karmada/pkg/version/sharedcommand"
|
||||||
|
@ -79,6 +80,9 @@ func NewWebhookCommand(ctx context.Context) *cobra.Command {
|
||||||
// Run runs the webhook server with options. This should never exit.
|
// Run runs the webhook server with options. This should never exit.
|
||||||
func Run(ctx context.Context, opts *options.Options) error {
|
func Run(ctx context.Context, opts *options.Options) error {
|
||||||
klog.Infof("karmada-webhook version: %s", version.Get())
|
klog.Infof("karmada-webhook version: %s", version.Get())
|
||||||
|
|
||||||
|
profileflag.ListenAndServe(opts.ProfileOpts)
|
||||||
|
|
||||||
config, err := controllerruntime.GetConfig()
|
config, err := controllerruntime.GetConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
package profileflag
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"net/http/pprof"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/spf13/pflag"
|
||||||
|
"k8s.io/klog/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Options are options for pprof.
|
||||||
|
type Options struct {
|
||||||
|
// EnableProfile is the flag about whether to enable pprof profiling.
|
||||||
|
EnableProfile bool
|
||||||
|
// ProfilePort is the TCP address for pprof profiling.
|
||||||
|
// Defaults to 127.0.0.1:6060 if unspecified.
|
||||||
|
ProfilingBindAddress string
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddFlags adds flags to the specified FlagSet.
|
||||||
|
func (o *Options) AddFlags(fs *pflag.FlagSet) {
|
||||||
|
fs.BoolVar(&o.EnableProfile, "enable-pprof", false, "Enable profiling via web interface host:port/debug/pprof/.")
|
||||||
|
fs.StringVar(&o.ProfilingBindAddress, "profiling-bind-address", ":6060", "The TCP address for serving profiling(e.g. 127.0.0.1:6060, :6060). This is only applicable if profiling is enabled.")
|
||||||
|
}
|
||||||
|
|
||||||
|
func installHandlerForPProf(mux *http.ServeMux) {
|
||||||
|
mux.HandleFunc("/debug/pprof/", pprof.Index)
|
||||||
|
mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
|
||||||
|
mux.HandleFunc("/debug/pprof/profile", pprof.Profile)
|
||||||
|
mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
|
||||||
|
mux.HandleFunc("/debug/pprof/trace", pprof.Trace)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListenAndServe start a http server to enable pprof.
|
||||||
|
func ListenAndServe(opts Options) {
|
||||||
|
if opts.EnableProfile {
|
||||||
|
mux := http.NewServeMux()
|
||||||
|
installHandlerForPProf(mux)
|
||||||
|
klog.Infof("Starting profiling on port %s", opts.ProfilingBindAddress)
|
||||||
|
go func() {
|
||||||
|
if err := http.ListenAndServe(opts.ProfilingBindAddress, mux); err != nil {
|
||||||
|
klog.Errorf("Failed to enable profiling: %v", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue