k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/cmd/kubemark/app/hollow_node.go (about) 1 /* 2 Copyright 2023 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package app 18 19 import ( 20 "context" 21 goflag "flag" 22 "fmt" 23 "time" 24 25 "github.com/spf13/cobra" 26 "github.com/spf13/pflag" 27 "go.opentelemetry.io/otel/trace/noop" 28 internalapi "k8s.io/cri-api/pkg/apis" 29 "k8s.io/klog/v2" 30 31 v1 "k8s.io/api/core/v1" 32 "k8s.io/apimachinery/pkg/api/resource" 33 "k8s.io/apimachinery/pkg/types" 34 "k8s.io/apimachinery/pkg/util/sets" 35 clientset "k8s.io/client-go/kubernetes" 36 restclient "k8s.io/client-go/rest" 37 "k8s.io/client-go/tools/clientcmd" 38 "k8s.io/client-go/tools/events" 39 cliflag "k8s.io/component-base/cli/flag" 40 _ "k8s.io/component-base/metrics/prometheus/restclient" // for client metric registration 41 _ "k8s.io/component-base/metrics/prometheus/version" // for version metric registration 42 "k8s.io/component-base/version" 43 "k8s.io/component-base/version/verflag" 44 remote "k8s.io/cri-client/pkg" 45 fakeremote "k8s.io/cri-client/pkg/fake" 46 "k8s.io/kubernetes/pkg/api/legacyscheme" 47 "k8s.io/kubernetes/pkg/cluster/ports" 48 cadvisortest "k8s.io/kubernetes/pkg/kubelet/cadvisor/testing" 49 "k8s.io/kubernetes/pkg/kubelet/certificate/bootstrap" 50 "k8s.io/kubernetes/pkg/kubelet/cm" 51 "k8s.io/kubernetes/pkg/kubemark" 52 kubemarkproxy "k8s.io/kubernetes/pkg/proxy/kubemark" 53 utilflag "k8s.io/kubernetes/pkg/util/flag" 54 ) 55 56 type hollowNodeConfig struct { 57 KubeconfigPath string 58 BootstrapKubeconfigPath string 59 CertDirectory string 60 KubeletPort int 61 KubeletReadOnlyPort int 62 Morph string 63 NodeName string 64 ServerPort int 65 ContentType string 66 QPS float32 67 Burst int 68 NodeLabels map[string]string 69 RegisterWithTaints []v1.Taint 70 MaxPods int 71 ExtendedResources map[string]string 72 UseHostImageService bool 73 74 // Deprecated config; remove these with the corresponding flags 75 UseRealProxier bool 76 ProxierSyncPeriod time.Duration 77 ProxierMinSyncPeriod time.Duration 78 } 79 80 const ( 81 maxPods = 110 82 podsPerCore = 0 83 ) 84 85 // TODO(#45650): Refactor hollow-node into hollow-kubelet and hollow-proxy 86 // and make the config driven. 87 var knownMorphs = sets.NewString("kubelet", "proxy") 88 89 func (c *hollowNodeConfig) addFlags(fs *pflag.FlagSet) { 90 fs.StringVar(&c.KubeconfigPath, "kubeconfig", "/kubeconfig/kubeconfig", "Path to kubeconfig file.") 91 fs.StringVar(&c.BootstrapKubeconfigPath, "bootstrap-kubeconfig", "", "Path to bootstrap kubeconfig file.") 92 fs.StringVar(&c.CertDirectory, "cert-dir", "/etc/srv/", "Path to cert directory for bootstraping.") 93 fs.IntVar(&c.KubeletPort, "kubelet-port", ports.KubeletPort, "Port on which HollowKubelet should be listening.") 94 fs.IntVar(&c.KubeletReadOnlyPort, "kubelet-read-only-port", ports.KubeletReadOnlyPort, "Read-only port on which Kubelet is listening.") 95 fs.StringVar(&c.NodeName, "name", "fake-node", "Name of this Hollow Node.") 96 fs.IntVar(&c.ServerPort, "api-server-port", 443, "Port on which API server is listening.") 97 fs.StringVar(&c.Morph, "morph", "", fmt.Sprintf("Specifies into which Hollow component this binary should morph. Allowed values: %v", knownMorphs.List())) 98 fs.StringVar(&c.ContentType, "kube-api-content-type", "application/vnd.kubernetes.protobuf", "ContentType of requests sent to apiserver.") 99 fs.Float32Var(&c.QPS, "kube-api-qps", 10, "QPS indicates the maximum QPS to the apiserver.") 100 fs.IntVar(&c.Burst, "kube-api-burst", 20, "Burst indicates maximum burst for throttle to the apiserver.") 101 102 bindableNodeLabels := cliflag.ConfigurationMap(c.NodeLabels) 103 fs.Var(&bindableNodeLabels, "node-labels", "Additional node labels") 104 fs.Var(utilflag.RegisterWithTaintsVar{Value: &c.RegisterWithTaints}, "register-with-taints", "Register the node with the given list of taints (comma separated \"<key>=<value>:<effect>\"). No-op if register-node is false.") 105 fs.IntVar(&c.MaxPods, "max-pods", maxPods, "Number of pods that can run on this Kubelet.") 106 bindableExtendedResources := cliflag.ConfigurationMap(c.ExtendedResources) 107 fs.Var(&bindableExtendedResources, "extended-resources", "Register the node with extended resources (comma separated \"<name>=<quantity>\")") 108 fs.BoolVar(&c.UseHostImageService, "use-host-image-service", true, "Set to true if the hollow-kubelet should use the host image service. If set to false the fake image service will be used") 109 110 fs.BoolVar(&c.UseRealProxier, "use-real-proxier", true, "Has no effect.") 111 _ = fs.MarkDeprecated("use-real-proxier", "This flag is deprecated and will be removed in a future release.") 112 fs.DurationVar(&c.ProxierSyncPeriod, "proxier-sync-period", 30*time.Second, "Has no effect.") 113 _ = fs.MarkDeprecated("proxier-sync-period", "This flag is deprecated and will be removed in a future release.") 114 fs.DurationVar(&c.ProxierMinSyncPeriod, "proxier-min-sync-period", 0, "Has no effect.") 115 _ = fs.MarkDeprecated("proxier-min-sync-period", "This flag is deprecated and will be removed in a future release.") 116 } 117 118 func (c *hollowNodeConfig) createClientConfigFromFile() (*restclient.Config, error) { 119 clientConfig, err := clientcmd.LoadFromFile(c.KubeconfigPath) 120 if err != nil { 121 return nil, fmt.Errorf("error while loading kubeconfig from file %v: %v", c.KubeconfigPath, err) 122 } 123 config, err := clientcmd.NewDefaultClientConfig(*clientConfig, &clientcmd.ConfigOverrides{}).ClientConfig() 124 if err != nil { 125 return nil, fmt.Errorf("error while creating kubeconfig: %v", err) 126 } 127 config.ContentType = c.ContentType 128 config.QPS = c.QPS 129 config.Burst = c.Burst 130 return config, nil 131 } 132 133 func (c *hollowNodeConfig) bootstrapClientConfig() error { 134 if c.BootstrapKubeconfigPath != "" { 135 return bootstrap.LoadClientCert(context.TODO(), c.KubeconfigPath, c.BootstrapKubeconfigPath, c.CertDirectory, types.NodeName(c.NodeName)) 136 } 137 return nil 138 } 139 140 func (c *hollowNodeConfig) createHollowKubeletOptions() *kubemark.HollowKubeletOptions { 141 return &kubemark.HollowKubeletOptions{ 142 NodeName: c.NodeName, 143 KubeletPort: c.KubeletPort, 144 KubeletReadOnlyPort: c.KubeletReadOnlyPort, 145 MaxPods: c.MaxPods, 146 PodsPerCore: podsPerCore, 147 NodeLabels: c.NodeLabels, 148 RegisterWithTaints: c.RegisterWithTaints, 149 } 150 } 151 152 // NewHollowNodeCommand creates a *cobra.Command object with default parameters 153 func NewHollowNodeCommand() *cobra.Command { 154 s := &hollowNodeConfig{ 155 NodeLabels: make(map[string]string), 156 ExtendedResources: make(map[string]string), 157 } 158 159 cmd := &cobra.Command{ 160 Use: "kubemark", 161 Long: "kubemark", 162 RunE: func(cmd *cobra.Command, args []string) error { 163 verflag.PrintAndExitIfRequested() 164 cliflag.PrintFlags(cmd.Flags()) 165 return run(cmd.Context(), s) 166 }, 167 Args: func(cmd *cobra.Command, args []string) error { 168 for _, arg := range args { 169 if len(arg) > 0 { 170 return fmt.Errorf("%q does not take any arguments, got %q", cmd.CommandPath(), args) 171 } 172 } 173 return nil 174 }, 175 } 176 177 fs := cmd.Flags() 178 fs.AddGoFlagSet(goflag.CommandLine) // for flags like --docker-only 179 s.addFlags(fs) 180 181 return cmd 182 } 183 184 func run(ctx context.Context, config *hollowNodeConfig) error { 185 // To help debugging, immediately log version and print flags. 186 klog.Infof("Version: %+v", version.Get()) 187 188 if !knownMorphs.Has(config.Morph) { 189 return fmt.Errorf("Unknown morph: %v. allowed values: %v", config.Morph, knownMorphs.List()) 190 } 191 192 // create a client to communicate with API server. 193 err := config.bootstrapClientConfig() 194 if err != nil { 195 return fmt.Errorf("Failed to bootstrap, error: %w. Exiting", err) 196 } 197 clientConfig, err := config.createClientConfigFromFile() 198 if err != nil { 199 return fmt.Errorf("Failed to create a ClientConfig, error: %w. Exiting", err) 200 } 201 202 if config.Morph == "kubelet" { 203 clientConfig.UserAgent = "hollow-kubelet" 204 client, err := clientset.NewForConfig(clientConfig) 205 if err != nil { 206 return fmt.Errorf("Failed to create a ClientSet, error: %w. Exiting", err) 207 } 208 209 f, c := kubemark.GetHollowKubeletConfig(config.createHollowKubeletOptions()) 210 211 heartbeatClientConfig := *clientConfig 212 heartbeatClientConfig.Timeout = c.NodeStatusUpdateFrequency.Duration 213 // The timeout is the minimum of the lease duration and status update frequency 214 leaseTimeout := time.Duration(c.NodeLeaseDurationSeconds) * time.Second 215 if heartbeatClientConfig.Timeout > leaseTimeout { 216 heartbeatClientConfig.Timeout = leaseTimeout 217 } 218 219 heartbeatClientConfig.QPS = float32(-1) 220 heartbeatClient, err := clientset.NewForConfig(&heartbeatClientConfig) 221 if err != nil { 222 return fmt.Errorf("Failed to create a ClientSet, error: %w. Exiting", err) 223 } 224 225 cadvisorInterface := &cadvisortest.Fake{ 226 NodeName: config.NodeName, 227 } 228 229 var containerManager cm.ContainerManager 230 if config.ExtendedResources != nil { 231 extendedResources := v1.ResourceList{} 232 for k, v := range config.ExtendedResources { 233 extendedResources[v1.ResourceName(k)] = resource.MustParse(v) 234 } 235 236 containerManager = cm.NewStubContainerManagerWithDevicePluginResource(extendedResources) 237 } else { 238 containerManager = cm.NewStubContainerManager() 239 } 240 241 endpoint, err := fakeremote.GenerateEndpoint() 242 if err != nil { 243 return fmt.Errorf("Failed to generate fake endpoint, error: %w", err) 244 } 245 fakeRemoteRuntime := fakeremote.NewFakeRemoteRuntime() 246 if err = fakeRemoteRuntime.Start(endpoint); err != nil { 247 return fmt.Errorf("Failed to start fake runtime, error: %w", err) 248 } 249 defer fakeRemoteRuntime.Stop() 250 logger := klog.Background() 251 runtimeService, err := remote.NewRemoteRuntimeService(endpoint, 15*time.Second, noop.NewTracerProvider(), &logger) 252 if err != nil { 253 return fmt.Errorf("Failed to init runtime service, error: %w", err) 254 } 255 256 var imageService internalapi.ImageManagerService = fakeRemoteRuntime.ImageService 257 if config.UseHostImageService { 258 imageService, err = remote.NewRemoteImageService(c.ImageServiceEndpoint, 15*time.Second, noop.NewTracerProvider(), &logger) 259 if err != nil { 260 return fmt.Errorf("Failed to init image service, error: %w", err) 261 } 262 } 263 264 hollowKubelet := kubemark.NewHollowKubelet( 265 f, c, 266 client, 267 heartbeatClient, 268 cadvisorInterface, 269 imageService, 270 runtimeService, 271 containerManager, 272 ) 273 hollowKubelet.Run(ctx) 274 } 275 276 if config.Morph == "proxy" { 277 clientConfig.UserAgent = "hollow-proxy" 278 279 client, err := clientset.NewForConfig(clientConfig) 280 if err != nil { 281 return fmt.Errorf("Failed to create API Server client, error: %w", err) 282 } 283 eventBroadcaster := events.NewBroadcaster(&events.EventSinkImpl{Interface: client.EventsV1()}) 284 recorder := eventBroadcaster.NewRecorder(legacyscheme.Scheme, "kube-proxy") 285 286 hollowProxy := kubemarkproxy.NewHollowProxy( 287 config.NodeName, 288 client, 289 client.CoreV1(), 290 eventBroadcaster, 291 recorder, 292 ) 293 return hollowProxy.Run() 294 } 295 296 return nil 297 }