Skip to content

Commit

Permalink
Common flags across VPA components
Browse files Browse the repository at this point in the history
Signed-off-by: Omer Aplatony <[email protected]>
  • Loading branch information
omerap12 committed Oct 2, 2024
1 parent 63926b3 commit 2f60bad
Show file tree
Hide file tree
Showing 7 changed files with 125 additions and 77 deletions.
62 changes: 62 additions & 0 deletions vertical-pod-autoscaler/common/flags.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
Copyright 2024 The Kubernetes 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 flags - common code for flags of all 3 VPA components

package common

import (
"flag"

apiv1 "k8s.io/api/core/v1"
"k8s.io/klog/v2"
)

// CommonFlags contains flag definitions common to all VPA components
type CommonFlags struct {
KubeConfig string
KubeApiQps float64
KubeApiBurst float64
EnableProfiling bool
VpaObjectNamespace string
IgnoredVpaObjectNamespaces string
}

// InitCommonFlags initializes the common flags
func InitCommonFlags() *CommonFlags {
cf := &CommonFlags{}
flag.StringVar(&cf.KubeConfig, "kubeconfig", "", "Path to a kubeconfig. Only required if out-of-cluster.")
flag.Float64Var(&cf.KubeApiQps, "kube-api-qps", 5.0, "QPS limit when making requests to Kubernetes apiserver")
flag.Float64Var(&cf.KubeApiBurst, "kube-api-burst", 10.0, "QPS burst limit when making requests to Kubernetes apiserver")
flag.BoolVar(&cf.EnableProfiling, "profiling", false, "Is debug/pprof endpoint enabled")
flag.StringVar(&cf.VpaObjectNamespace, "vpa-object-namespace", apiv1.NamespaceAll, "Namespace to search for VPA objects. Empty means all namespaces will be used.")
flag.StringVar(&cf.IgnoredVpaObjectNamespaces, "ignored-vpa-object-namespaces", "", "Comma separated list of namespaces to ignore when searching for VPA objects. Empty means no namespaces will be ignored.")
return cf
}

// InitLoggingFlags initializes the logging flags
func InitLoggingFlags() {
flag.Lookup("v").Usage = "set the log level verbosity (default: 2)"
flag.Lookup("stderrthreshold").Usage = "set the log level threshold for writing to standard error (default: info)"
err := flag.Set("v", "2")
if err != nil {
klog.Fatalf("Unable to set log level verbosity: %v", err)
}
err = flag.Set("stderrthreshold", "0")
if err != nil {
klog.Fatalf("Unable to set log level threshold for writing to standard error: %v", err)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,3 @@ ARG TARGETARCH
COPY --from=builder /gopath/src/k8s.io/autoscaler/vertical-pod-autoscaler/pkg/admission-controller/admission-controller-$TARGETARCH /admission-controller

ENTRYPOINT ["/admission-controller"]
CMD ["--v=4", "--stderrthreshold=info"]
42 changes: 19 additions & 23 deletions vertical-pod-autoscaler/pkg/admission-controller/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import (
"strings"
"time"

apiv1 "k8s.io/api/core/v1"
"k8s.io/client-go/informers"
kube_client "k8s.io/client-go/kubernetes"
kube_flag "k8s.io/component-base/cli/flag"
Expand Down Expand Up @@ -66,41 +65,38 @@ var (
ciphers = flag.String("tls-ciphers", "", "A comma-separated or colon-separated list of ciphers to accept. Only works when min-tls-version is set to tls1_2.")
minTlsVersion = flag.String("min-tls-version", "tls1_2", "The minimum TLS version to accept. Must be set to either tls1_2 (default) or tls1_3.")

port = flag.Int("port", 8000, "The port to listen on.")
address = flag.String("address", ":8944", "The address to expose Prometheus metrics.")
kubeconfig = flag.String("kubeconfig", "", "Path to a kubeconfig. Only required if out-of-cluster.")
kubeApiQps = flag.Float64("kube-api-qps", 5.0, `QPS limit when making requests to Kubernetes apiserver`)
kubeApiBurst = flag.Float64("kube-api-burst", 10.0, `QPS burst limit when making requests to Kubernetes apiserver`)
enableProfiling = flag.Bool("profiling", false, "Is debug/pprof endpoint enabled")
namespace = os.Getenv("NAMESPACE")
serviceName = flag.String("webhook-service", "vpa-webhook", "Kubernetes service under which webhook is registered. Used when registerByURL is set to false.")
webhookAddress = flag.String("webhook-address", "", "Address under which webhook is registered. Used when registerByURL is set to true.")
webhookPort = flag.String("webhook-port", "", "Server Port for Webhook")
webhookTimeout = flag.Int("webhook-timeout-seconds", 30, "Timeout in seconds that the API server should wait for this webhook to respond before failing.")
webHookFailurePolicy = flag.Bool("webhook-failure-policy-fail", false, "If set to true, will configure the admission webhook failurePolicy to \"Fail\". Use with caution.")
registerWebhook = flag.Bool("register-webhook", true, "If set to true, admission webhook object will be created on start up to register with the API server.")
registerByURL = flag.Bool("register-by-url", false, "If set to true, admission webhook will be registered by URL (webhookAddress:webhookPort) instead of by service name")
vpaObjectNamespace = flag.String("vpa-object-namespace", apiv1.NamespaceAll, "Namespace to search for VPA objects. Empty means all namespaces will be used. Must not be used if ignored-vpa-object-namespaces is set.")
ignoredVpaObjectNamespaces = flag.String("ignored-vpa-object-namespaces", "", "Comma separated list of namespaces to ignore. Must not be used if vpa-object-namespace is used.")
port = flag.Int("port", 8000, "The port to listen on.")
address = flag.String("address", ":8944", "The address to expose Prometheus metrics.")
namespace = os.Getenv("NAMESPACE")
serviceName = flag.String("webhook-service", "vpa-webhook", "Kubernetes service under which webhook is registered. Used when registerByURL is set to false.")
webhookAddress = flag.String("webhook-address", "", "Address under which webhook is registered. Used when registerByURL is set to true.")
webhookPort = flag.String("webhook-port", "", "Server Port for Webhook")
webhookTimeout = flag.Int("webhook-timeout-seconds", 30, "Timeout in seconds that the API server should wait for this webhook to respond before failing.")
webHookFailurePolicy = flag.Bool("webhook-failure-policy-fail", false, "If set to true, will configure the admission webhook failurePolicy to \"Fail\". Use with caution.")
registerWebhook = flag.Bool("register-webhook", true, "If set to true, admission webhook object will be created on start up to register with the API server.")
registerByURL = flag.Bool("register-by-url", false, "If set to true, admission webhook will be registered by URL (webhookAddress:webhookPort) instead of by service name")
)

func main() {
commonFlags := common.InitCommonFlags()
klog.InitFlags(nil)
common.InitLoggingFlags()
flag.Parse()
kube_flag.InitFlags()
klog.V(1).Infof("Vertical Pod Autoscaler %s Admission Controller", common.VerticalPodAutoscalerVersion)

if len(*vpaObjectNamespace) > 0 && len(*ignoredVpaObjectNamespaces) > 0 {
if len(commonFlags.VpaObjectNamespace) > 0 && len(commonFlags.IgnoredVpaObjectNamespaces) > 0 {
klog.Fatalf("--vpa-object-namespace and --ignored-vpa-object-namespaces are mutually exclusive and can't be set together.")
}

healthCheck := metrics.NewHealthCheck(time.Minute)
metrics_admission.Register()
server.Initialize(enableProfiling, healthCheck, address)
server.Initialize(&commonFlags.EnableProfiling, healthCheck, address)

config := common.CreateKubeConfigOrDie(*kubeconfig, float32(*kubeApiQps), int(*kubeApiBurst))
config := common.CreateKubeConfigOrDie(commonFlags.KubeConfig, float32(commonFlags.KubeApiQps), int(commonFlags.KubeApiBurst))

vpaClient := vpa_clientset.NewForConfigOrDie(config)
vpaLister := vpa_api_util.NewVpasLister(vpaClient, make(chan struct{}), *vpaObjectNamespace)
vpaLister := vpa_api_util.NewVpasLister(vpaClient, make(chan struct{}), commonFlags.VpaObjectNamespace)
kubeClient := kube_client.NewForConfigOrDie(config)
factory := informers.NewSharedInformerFactory(kubeClient, defaultResyncPeriod)
targetSelectorFetcher := target.NewVpaTargetSelectorFetcher(config, kubeClient, factory)
Expand Down Expand Up @@ -146,10 +142,10 @@ func main() {
TLSConfig: configTLS(*certsConfiguration, *minTlsVersion, *ciphers, stopCh),
}
url := fmt.Sprintf("%v:%v", *webhookAddress, *webhookPort)
ignoredNamespaces := strings.Split(*ignoredVpaObjectNamespaces, ",")
ignoredNamespaces := strings.Split(commonFlags.IgnoredVpaObjectNamespaces, ",")
go func() {
if *registerWebhook {
selfRegistration(kubeClient, readFile(*certsConfiguration.clientCaFile), webHookDelay, namespace, *serviceName, url, *registerByURL, int32(*webhookTimeout), *vpaObjectNamespace, ignoredNamespaces, *webHookFailurePolicy)
selfRegistration(kubeClient, readFile(*certsConfiguration.clientCaFile), webHookDelay, namespace, *serviceName, url, *registerByURL, int32(*webhookTimeout), commonFlags.VpaObjectNamespace, ignoredNamespaces, *webHookFailurePolicy)
}
// Start status updates after the webhook is initialized.
statusUpdater.Run(stopCh)
Expand Down
1 change: 0 additions & 1 deletion vertical-pod-autoscaler/pkg/recommender/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,3 @@ ARG TARGETARCH
COPY --from=builder /gopath/src/k8s.io/autoscaler/vertical-pod-autoscaler/pkg/recommender/recommender-$TARGETARCH /recommender

ENTRYPOINT ["/recommender"]
CMD ["--v=4", "--stderrthreshold=info", "--prometheus-address=http://prometheus.monitoring.svc"]
63 changes: 30 additions & 33 deletions vertical-pod-autoscaler/pkg/recommender/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,31 +59,25 @@ var (
recommenderName = flag.String("recommender-name", input.DefaultRecommenderName, "Set the recommender name. Recommender will generate recommendations for VPAs that configure the same recommender name. If the recommender name is left as default it will also generate recommendations that don't explicitly specify recommender. You shouldn't run two recommenders with the same name in a cluster.")
metricsFetcherInterval = flag.Duration("recommender-interval", 1*time.Minute, `How often metrics should be fetched`)
checkpointsGCInterval = flag.Duration("checkpoints-gc-interval", 10*time.Minute, `How often orphaned checkpoints should be garbage collected`)
prometheusAddress = flag.String("prometheus-address", "", `Where to reach for Prometheus metrics`)
prometheusAddress = flag.String("prometheus-address", "http://prometheus.monitoring.svc", `Where to reach for Prometheus metrics`)
prometheusJobName = flag.String("prometheus-cadvisor-job-name", "kubernetes-cadvisor", `Name of the prometheus job name which scrapes the cAdvisor metrics`)
address = flag.String("address", ":8942", "The address to expose Prometheus metrics.")
kubeconfig = flag.String("kubeconfig", "", "Path to a kubeconfig. Only required if out-of-cluster.")
kubeApiQps = flag.Float64("kube-api-qps", 5.0, `QPS limit when making requests to Kubernetes apiserver`)
kubeApiBurst = flag.Float64("kube-api-burst", 10.0, `QPS burst limit when making requests to Kubernetes apiserver`)
enableProfiling = flag.Bool("profiling", false, "Is debug/pprof endpoint enabled")

storage = flag.String("storage", "", `Specifies storage mode. Supported values: prometheus, checkpoint (default)`)
// prometheus history provider configs
historyLength = flag.String("history-length", "8d", `How much time back prometheus have to be queried to get historical metrics`)
historyResolution = flag.String("history-resolution", "1h", `Resolution at which Prometheus is queried for historical metrics`)
queryTimeout = flag.String("prometheus-query-timeout", "5m", `How long to wait before killing long queries`)
podLabelPrefix = flag.String("pod-label-prefix", "pod_label_", `Which prefix to look for pod labels in metrics`)
podLabelsMetricName = flag.String("metric-for-pod-labels", "up{job=\"kubernetes-pods\"}", `Which metric to look for pod labels in metrics`)
podNamespaceLabel = flag.String("pod-namespace-label", "kubernetes_namespace", `Label name to look for pod namespaces`)
podNameLabel = flag.String("pod-name-label", "kubernetes_pod_name", `Label name to look for pod names`)
ctrNamespaceLabel = flag.String("container-namespace-label", "namespace", `Label name to look for container namespaces`)
ctrPodNameLabel = flag.String("container-pod-name-label", "pod_name", `Label name to look for container pod names`)
ctrNameLabel = flag.String("container-name-label", "name", `Label name to look for container names`)
vpaObjectNamespace = flag.String("vpa-object-namespace", apiv1.NamespaceAll, "Namespace to search for VPA objects and pod stats. Empty means all namespaces will be used. Must not be used if ignored-vpa-object-namespaces is set.")
ignoredVpaObjectNamespaces = flag.String("ignored-vpa-object-namespaces", "", "Comma separated list of namespaces to ignore. Must not be used if vpa-object-namespace is used.")
username = flag.String("username", "", "The username used in the prometheus server basic auth")
password = flag.String("password", "", "The password used in the prometheus server basic auth")
memorySaver = flag.Bool("memory-saver", false, `If true, only track pods which have an associated VPA`)
historyLength = flag.String("history-length", "8d", `How much time back prometheus have to be queried to get historical metrics`)
historyResolution = flag.String("history-resolution", "1h", `Resolution at which Prometheus is queried for historical metrics`)
queryTimeout = flag.String("prometheus-query-timeout", "5m", `How long to wait before killing long queries`)
podLabelPrefix = flag.String("pod-label-prefix", "pod_label_", `Which prefix to look for pod labels in metrics`)
podLabelsMetricName = flag.String("metric-for-pod-labels", "up{job=\"kubernetes-pods\"}", `Which metric to look for pod labels in metrics`)
podNamespaceLabel = flag.String("pod-namespace-label", "kubernetes_namespace", `Label name to look for pod namespaces`)
podNameLabel = flag.String("pod-name-label", "kubernetes_pod_name", `Label name to look for pod names`)
ctrNamespaceLabel = flag.String("container-namespace-label", "namespace", `Label name to look for container namespaces`)
ctrPodNameLabel = flag.String("container-pod-name-label", "pod_name", `Label name to look for container pod names`)
ctrNameLabel = flag.String("container-name-label", "name", `Label name to look for container names`)
username = flag.String("username", "", "The username used in the prometheus server basic auth")
password = flag.String("password", "", "The password used in the prometheus server basic auth")
memorySaver = flag.Bool("memory-saver", false, `If true, only track pods which have an associated VPA`)
// external metrics provider config
useExternalMetrics = flag.Bool("use-external-metrics", false, "ALPHA. Use an external metrics provider instead of metrics_server.")
externalCpuMetric = flag.String("external-metrics-cpu-metric", "", "ALPHA. Metric to use with external metrics provider for CPU usage.")
Expand Down Expand Up @@ -117,33 +111,36 @@ const (
)

func main() {
commonFlags := common.InitCommonFlags()
klog.InitFlags(nil)
common.InitLoggingFlags()
flag.Parse()

leaderElection := defaultLeaderElectionConfiguration()
componentbaseoptions.BindLeaderElectionFlags(&leaderElection, pflag.CommandLine)

kube_flag.InitFlags()
klog.V(1).Infof("Vertical Pod Autoscaler %s Recommender: %v", common.VerticalPodAutoscalerVersion, *recommenderName)

if len(*vpaObjectNamespace) > 0 && len(*ignoredVpaObjectNamespaces) > 0 {
if len(commonFlags.VpaObjectNamespace) > 0 && len(commonFlags.IgnoredVpaObjectNamespaces) > 0 {
klog.Fatalf("--vpa-object-namespace and --ignored-vpa-object-namespaces are mutually exclusive and can't be set together.")
}

healthCheck := metrics.NewHealthCheck(*metricsFetcherInterval * 5)
metrics_recommender.Register()
metrics_quality.Register()
server.Initialize(enableProfiling, healthCheck, address)
server.Initialize(&commonFlags.EnableProfiling, healthCheck, address)

if !leaderElection.LeaderElect {
run(healthCheck)
run(healthCheck, commonFlags)
} else {
id, err := os.Hostname()
if err != nil {
klog.Fatalf("Unable to get hostname: %v", err)
}
id = id + "_" + string(uuid.NewUUID())

config := common.CreateKubeConfigOrDie(*kubeconfig, float32(*kubeApiQps), int(*kubeApiBurst))
config := common.CreateKubeConfigOrDie(commonFlags.KubeConfig, float32(commonFlags.KubeApiQps), int(commonFlags.KubeApiBurst))
kubeClient := kube_client.NewForConfigOrDie(config)

lock, err := resourcelock.New(
Expand All @@ -168,7 +165,7 @@ func main() {
ReleaseOnCancel: true,
Callbacks: leaderelection.LeaderCallbacks{
OnStartedLeading: func(_ context.Context) {
run(healthCheck)
run(healthCheck, commonFlags)
},
OnStoppedLeading: func() {
klog.Fatal("lost master")
Expand Down Expand Up @@ -196,13 +193,13 @@ func defaultLeaderElectionConfiguration() componentbaseconfig.LeaderElectionConf
}
}

func run(healthCheck *metrics.HealthCheck) {
config := common.CreateKubeConfigOrDie(*kubeconfig, float32(*kubeApiQps), int(*kubeApiBurst))
func run(healthCheck *metrics.HealthCheck, commonFlag *common.CommonFlags) {
config := common.CreateKubeConfigOrDie(commonFlag.KubeConfig, float32(commonFlag.KubeApiQps), int(commonFlag.KubeApiBurst))
kubeClient := kube_client.NewForConfigOrDie(config)
clusterState := model.NewClusterState(aggregateContainerStateGCInterval)
factory := informers.NewSharedInformerFactoryWithOptions(kubeClient, defaultResyncPeriod, informers.WithNamespace(*vpaObjectNamespace))
factory := informers.NewSharedInformerFactoryWithOptions(kubeClient, defaultResyncPeriod, informers.WithNamespace(commonFlag.IgnoredVpaObjectNamespaces))
controllerFetcher := controllerfetcher.NewControllerFetcher(config, kubeClient, factory, scaleCacheEntryFreshnessTime, scaleCacheEntryLifetime, scaleCacheEntryJitterFactor)
podLister, oomObserver := input.NewPodListerAndOOMObserver(kubeClient, *vpaObjectNamespace)
podLister, oomObserver := input.NewPodListerAndOOMObserver(kubeClient, commonFlag.IgnoredVpaObjectNamespaces)

model.InitializeAggregationsConfig(model.NewAggregationsConfig(*memoryAggregationInterval, *memoryAggregationIntervalCount, *memoryHistogramDecayHalfLife, *cpuHistogramDecayHalfLife, *oomBumpUpRatio, *oomMinBumpUp))

Expand Down Expand Up @@ -232,15 +229,15 @@ func run(healthCheck *metrics.HealthCheck) {
source = input_metrics.NewPodMetricsesSource(resourceclient.NewForConfigOrDie(config))
}

ignoredNamespaces := strings.Split(*ignoredVpaObjectNamespaces, ",")
ignoredNamespaces := strings.Split(commonFlag.IgnoredVpaObjectNamespaces, ",")

clusterStateFeeder := input.ClusterStateFeederFactory{
PodLister: podLister,
OOMObserver: oomObserver,
KubeClient: kubeClient,
MetricsClient: input_metrics.NewMetricsClient(source, *vpaObjectNamespace, "default-metrics-client"),
MetricsClient: input_metrics.NewMetricsClient(source, commonFlag.VpaObjectNamespace, "default-metrics-client"),
VpaCheckpointClient: vpa_clientset.NewForConfigOrDie(config).AutoscalingV1(),
VpaLister: vpa_api_util.NewVpasLister(vpa_clientset.NewForConfigOrDie(config), make(chan struct{}), *vpaObjectNamespace),
VpaLister: vpa_api_util.NewVpasLister(vpa_clientset.NewForConfigOrDie(config), make(chan struct{}), commonFlag.VpaObjectNamespace),
ClusterState: clusterState,
SelectorFetcher: target.NewVpaTargetSelectorFetcher(config, kubeClient, factory),
MemorySaveMode: *memorySaver,
Expand Down Expand Up @@ -283,7 +280,7 @@ func run(healthCheck *metrics.HealthCheck) {
CtrPodNameLabel: *ctrPodNameLabel,
CtrNameLabel: *ctrNameLabel,
CadvisorMetricsJobName: *prometheusJobName,
Namespace: *vpaObjectNamespace,
Namespace: commonFlag.VpaObjectNamespace,
PrometheusBasicAuthTransport: history.PrometheusBasicAuthTransport{
Username: *username,
Password: *password,
Expand Down
Loading

0 comments on commit 2f60bad

Please sign in to comment.