k8s.io/kubernetes@v1.29.3/pkg/kubelet/kuberuntime/kuberuntime_manager.go (about) 1 /* 2 Copyright 2016 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 kuberuntime 18 19 import ( 20 "context" 21 "errors" 22 "fmt" 23 "os" 24 "path/filepath" 25 "sort" 26 "time" 27 28 cadvisorapi "github.com/google/cadvisor/info/v1" 29 "github.com/google/go-cmp/cmp" 30 "go.opentelemetry.io/otel/trace" 31 crierror "k8s.io/cri-api/pkg/errors" 32 "k8s.io/klog/v2" 33 34 v1 "k8s.io/api/core/v1" 35 "k8s.io/apimachinery/pkg/api/resource" 36 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 37 kubetypes "k8s.io/apimachinery/pkg/types" 38 utilruntime "k8s.io/apimachinery/pkg/util/runtime" 39 utilversion "k8s.io/apimachinery/pkg/util/version" 40 utilfeature "k8s.io/apiserver/pkg/util/feature" 41 "k8s.io/client-go/tools/record" 42 ref "k8s.io/client-go/tools/reference" 43 "k8s.io/client-go/util/flowcontrol" 44 "k8s.io/component-base/logs/logreduction" 45 internalapi "k8s.io/cri-api/pkg/apis" 46 runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1" 47 48 "k8s.io/kubernetes/pkg/api/legacyscheme" 49 podutil "k8s.io/kubernetes/pkg/api/v1/pod" 50 "k8s.io/kubernetes/pkg/credentialprovider" 51 "k8s.io/kubernetes/pkg/credentialprovider/plugin" 52 "k8s.io/kubernetes/pkg/features" 53 "k8s.io/kubernetes/pkg/kubelet/cm" 54 kubecontainer "k8s.io/kubernetes/pkg/kubelet/container" 55 "k8s.io/kubernetes/pkg/kubelet/events" 56 "k8s.io/kubernetes/pkg/kubelet/images" 57 runtimeutil "k8s.io/kubernetes/pkg/kubelet/kuberuntime/util" 58 "k8s.io/kubernetes/pkg/kubelet/lifecycle" 59 "k8s.io/kubernetes/pkg/kubelet/logs" 60 "k8s.io/kubernetes/pkg/kubelet/metrics" 61 proberesults "k8s.io/kubernetes/pkg/kubelet/prober/results" 62 "k8s.io/kubernetes/pkg/kubelet/runtimeclass" 63 "k8s.io/kubernetes/pkg/kubelet/sysctl" 64 "k8s.io/kubernetes/pkg/kubelet/types" 65 "k8s.io/kubernetes/pkg/kubelet/util/cache" 66 "k8s.io/kubernetes/pkg/kubelet/util/format" 67 sc "k8s.io/kubernetes/pkg/securitycontext" 68 ) 69 70 const ( 71 // The api version of kubelet runtime api 72 kubeRuntimeAPIVersion = "0.1.0" 73 // The root directory for pod logs 74 podLogsRootDirectory = "/var/log/pods" 75 // A minimal shutdown window for avoiding unnecessary SIGKILLs 76 minimumGracePeriodInSeconds = 2 77 78 // The expiration time of version cache. 79 versionCacheTTL = 60 * time.Second 80 // How frequently to report identical errors 81 identicalErrorDelay = 1 * time.Minute 82 // OpenTelemetry instrumentation scope name 83 instrumentationScope = "k8s.io/kubernetes/pkg/kubelet/kuberuntime" 84 ) 85 86 var ( 87 // ErrVersionNotSupported is returned when the api version of runtime interface is not supported 88 ErrVersionNotSupported = errors.New("runtime api version is not supported") 89 ) 90 91 // podStateProvider can determine if none of the elements are necessary to retain (pod content) 92 // or if none of the runtime elements are necessary to retain (containers) 93 type podStateProvider interface { 94 IsPodTerminationRequested(kubetypes.UID) bool 95 ShouldPodContentBeRemoved(kubetypes.UID) bool 96 ShouldPodRuntimeBeRemoved(kubetypes.UID) bool 97 } 98 99 type kubeGenericRuntimeManager struct { 100 runtimeName string 101 recorder record.EventRecorder 102 osInterface kubecontainer.OSInterface 103 104 // machineInfo contains the machine information. 105 machineInfo *cadvisorapi.MachineInfo 106 107 // Container GC manager 108 containerGC *containerGC 109 110 // Keyring for pulling images 111 keyring credentialprovider.DockerKeyring 112 113 // Runner of lifecycle events. 114 runner kubecontainer.HandlerRunner 115 116 // RuntimeHelper that wraps kubelet to generate runtime container options. 117 runtimeHelper kubecontainer.RuntimeHelper 118 119 // Health check results. 120 livenessManager proberesults.Manager 121 readinessManager proberesults.Manager 122 startupManager proberesults.Manager 123 124 // If true, enforce container cpu limits with CFS quota support 125 cpuCFSQuota bool 126 127 // CPUCFSQuotaPeriod sets the CPU CFS quota period value, cpu.cfs_period_us, defaults to 100ms 128 cpuCFSQuotaPeriod metav1.Duration 129 130 // wrapped image puller. 131 imagePuller images.ImageManager 132 133 // gRPC service clients 134 runtimeService internalapi.RuntimeService 135 imageService internalapi.ImageManagerService 136 137 // The version cache of runtime daemon. 138 versionCache *cache.ObjectCache 139 140 // The directory path for seccomp profiles. 141 seccompProfileRoot string 142 143 // Container management interface for pod container. 144 containerManager cm.ContainerManager 145 146 // Internal lifecycle event handlers for container resource management. 147 internalLifecycle cm.InternalContainerLifecycle 148 149 // Manage container logs. 150 logManager logs.ContainerLogManager 151 152 // Manage RuntimeClass resources. 153 runtimeClassManager *runtimeclass.Manager 154 155 // Cache last per-container error message to reduce log spam 156 logReduction *logreduction.LogReduction 157 158 // PodState provider instance 159 podStateProvider podStateProvider 160 161 // Use RuntimeDefault as the default seccomp profile for all workloads. 162 seccompDefault bool 163 164 // MemorySwapBehavior defines how swap is used 165 memorySwapBehavior string 166 167 //Function to get node allocatable resources 168 getNodeAllocatable func() v1.ResourceList 169 170 // Memory throttling factor for MemoryQoS 171 memoryThrottlingFactor float64 172 } 173 174 // KubeGenericRuntime is a interface contains interfaces for container runtime and command. 175 type KubeGenericRuntime interface { 176 kubecontainer.Runtime 177 kubecontainer.StreamingRuntime 178 kubecontainer.CommandRunner 179 } 180 181 // NewKubeGenericRuntimeManager creates a new kubeGenericRuntimeManager 182 func NewKubeGenericRuntimeManager( 183 recorder record.EventRecorder, 184 livenessManager proberesults.Manager, 185 readinessManager proberesults.Manager, 186 startupManager proberesults.Manager, 187 rootDirectory string, 188 machineInfo *cadvisorapi.MachineInfo, 189 podStateProvider podStateProvider, 190 osInterface kubecontainer.OSInterface, 191 runtimeHelper kubecontainer.RuntimeHelper, 192 insecureContainerLifecycleHTTPClient types.HTTPDoer, 193 imageBackOff *flowcontrol.Backoff, 194 serializeImagePulls bool, 195 maxParallelImagePulls *int32, 196 imagePullQPS float32, 197 imagePullBurst int, 198 imageCredentialProviderConfigFile string, 199 imageCredentialProviderBinDir string, 200 cpuCFSQuota bool, 201 cpuCFSQuotaPeriod metav1.Duration, 202 runtimeService internalapi.RuntimeService, 203 imageService internalapi.ImageManagerService, 204 containerManager cm.ContainerManager, 205 logManager logs.ContainerLogManager, 206 runtimeClassManager *runtimeclass.Manager, 207 seccompDefault bool, 208 memorySwapBehavior string, 209 getNodeAllocatable func() v1.ResourceList, 210 memoryThrottlingFactor float64, 211 podPullingTimeRecorder images.ImagePodPullingTimeRecorder, 212 tracerProvider trace.TracerProvider, 213 ) (KubeGenericRuntime, error) { 214 ctx := context.Background() 215 runtimeService = newInstrumentedRuntimeService(runtimeService) 216 imageService = newInstrumentedImageManagerService(imageService) 217 tracer := tracerProvider.Tracer(instrumentationScope) 218 kubeRuntimeManager := &kubeGenericRuntimeManager{ 219 recorder: recorder, 220 cpuCFSQuota: cpuCFSQuota, 221 cpuCFSQuotaPeriod: cpuCFSQuotaPeriod, 222 seccompProfileRoot: filepath.Join(rootDirectory, "seccomp"), 223 livenessManager: livenessManager, 224 readinessManager: readinessManager, 225 startupManager: startupManager, 226 machineInfo: machineInfo, 227 osInterface: osInterface, 228 runtimeHelper: runtimeHelper, 229 runtimeService: runtimeService, 230 imageService: imageService, 231 containerManager: containerManager, 232 internalLifecycle: containerManager.InternalContainerLifecycle(), 233 logManager: logManager, 234 runtimeClassManager: runtimeClassManager, 235 logReduction: logreduction.NewLogReduction(identicalErrorDelay), 236 seccompDefault: seccompDefault, 237 memorySwapBehavior: memorySwapBehavior, 238 getNodeAllocatable: getNodeAllocatable, 239 memoryThrottlingFactor: memoryThrottlingFactor, 240 } 241 242 typedVersion, err := kubeRuntimeManager.getTypedVersion(ctx) 243 if err != nil { 244 klog.ErrorS(err, "Get runtime version failed") 245 return nil, err 246 } 247 248 // Only matching kubeRuntimeAPIVersion is supported now 249 // TODO: Runtime API machinery is under discussion at https://github.com/kubernetes/kubernetes/issues/28642 250 if typedVersion.Version != kubeRuntimeAPIVersion { 251 klog.ErrorS(err, "This runtime api version is not supported", 252 "apiVersion", typedVersion.Version, 253 "supportedAPIVersion", kubeRuntimeAPIVersion) 254 return nil, ErrVersionNotSupported 255 } 256 257 kubeRuntimeManager.runtimeName = typedVersion.RuntimeName 258 klog.InfoS("Container runtime initialized", 259 "containerRuntime", typedVersion.RuntimeName, 260 "version", typedVersion.RuntimeVersion, 261 "apiVersion", typedVersion.RuntimeApiVersion) 262 263 // If the container logs directory does not exist, create it. 264 // TODO: create podLogsRootDirectory at kubelet.go when kubelet is refactored to 265 // new runtime interface 266 if _, err := osInterface.Stat(podLogsRootDirectory); os.IsNotExist(err) { 267 if err := osInterface.MkdirAll(podLogsRootDirectory, 0755); err != nil { 268 klog.ErrorS(err, "Failed to create pod log directory", "path", podLogsRootDirectory) 269 } 270 } 271 272 if imageCredentialProviderConfigFile != "" || imageCredentialProviderBinDir != "" { 273 if err := plugin.RegisterCredentialProviderPlugins(imageCredentialProviderConfigFile, imageCredentialProviderBinDir); err != nil { 274 klog.ErrorS(err, "Failed to register CRI auth plugins") 275 os.Exit(1) 276 } 277 } 278 kubeRuntimeManager.keyring = credentialprovider.NewDockerKeyring() 279 280 kubeRuntimeManager.imagePuller = images.NewImageManager( 281 kubecontainer.FilterEventRecorder(recorder), 282 kubeRuntimeManager, 283 imageBackOff, 284 serializeImagePulls, 285 maxParallelImagePulls, 286 imagePullQPS, 287 imagePullBurst, 288 podPullingTimeRecorder) 289 kubeRuntimeManager.runner = lifecycle.NewHandlerRunner(insecureContainerLifecycleHTTPClient, kubeRuntimeManager, kubeRuntimeManager, recorder) 290 kubeRuntimeManager.containerGC = newContainerGC(runtimeService, podStateProvider, kubeRuntimeManager, tracer) 291 kubeRuntimeManager.podStateProvider = podStateProvider 292 293 kubeRuntimeManager.versionCache = cache.NewObjectCache( 294 func() (interface{}, error) { 295 return kubeRuntimeManager.getTypedVersion(ctx) 296 }, 297 versionCacheTTL, 298 ) 299 300 return kubeRuntimeManager, nil 301 } 302 303 // Type returns the type of the container runtime. 304 func (m *kubeGenericRuntimeManager) Type() string { 305 return m.runtimeName 306 } 307 308 func newRuntimeVersion(version string) (*utilversion.Version, error) { 309 if ver, err := utilversion.ParseSemantic(version); err == nil { 310 return ver, err 311 } 312 return utilversion.ParseGeneric(version) 313 } 314 315 func (m *kubeGenericRuntimeManager) getTypedVersion(ctx context.Context) (*runtimeapi.VersionResponse, error) { 316 typedVersion, err := m.runtimeService.Version(ctx, kubeRuntimeAPIVersion) 317 if err != nil { 318 return nil, fmt.Errorf("get remote runtime typed version failed: %v", err) 319 } 320 return typedVersion, nil 321 } 322 323 // Version returns the version information of the container runtime. 324 func (m *kubeGenericRuntimeManager) Version(ctx context.Context) (kubecontainer.Version, error) { 325 typedVersion, err := m.getTypedVersion(ctx) 326 if err != nil { 327 return nil, err 328 } 329 330 return newRuntimeVersion(typedVersion.RuntimeVersion) 331 } 332 333 // APIVersion returns the cached API version information of the container 334 // runtime. Implementation is expected to update this cache periodically. 335 // This may be different from the runtime engine's version. 336 func (m *kubeGenericRuntimeManager) APIVersion() (kubecontainer.Version, error) { 337 versionObject, err := m.versionCache.Get(m.machineInfo.MachineID) 338 if err != nil { 339 return nil, err 340 } 341 typedVersion := versionObject.(*runtimeapi.VersionResponse) 342 343 return newRuntimeVersion(typedVersion.RuntimeApiVersion) 344 } 345 346 // Status returns the status of the runtime. An error is returned if the Status 347 // function itself fails, nil otherwise. 348 func (m *kubeGenericRuntimeManager) Status(ctx context.Context) (*kubecontainer.RuntimeStatus, error) { 349 resp, err := m.runtimeService.Status(ctx, false) 350 if err != nil { 351 return nil, err 352 } 353 if resp.GetStatus() == nil { 354 return nil, errors.New("runtime status is nil") 355 } 356 return toKubeRuntimeStatus(resp.GetStatus()), nil 357 } 358 359 // GetPods returns a list of containers grouped by pods. The boolean parameter 360 // specifies whether the runtime returns all containers including those already 361 // exited and dead containers (used for garbage collection). 362 func (m *kubeGenericRuntimeManager) GetPods(ctx context.Context, all bool) ([]*kubecontainer.Pod, error) { 363 pods := make(map[kubetypes.UID]*kubecontainer.Pod) 364 sandboxes, err := m.getKubeletSandboxes(ctx, all) 365 if err != nil { 366 return nil, err 367 } 368 for i := range sandboxes { 369 s := sandboxes[i] 370 if s.Metadata == nil { 371 klog.V(4).InfoS("Sandbox does not have metadata", "sandbox", s) 372 continue 373 } 374 podUID := kubetypes.UID(s.Metadata.Uid) 375 if _, ok := pods[podUID]; !ok { 376 pods[podUID] = &kubecontainer.Pod{ 377 ID: podUID, 378 Name: s.Metadata.Name, 379 Namespace: s.Metadata.Namespace, 380 } 381 } 382 p := pods[podUID] 383 converted, err := m.sandboxToKubeContainer(s) 384 if err != nil { 385 klog.V(4).InfoS("Convert sandbox of pod failed", "runtimeName", m.runtimeName, "sandbox", s, "podUID", podUID, "err", err) 386 continue 387 } 388 p.Sandboxes = append(p.Sandboxes, converted) 389 p.CreatedAt = uint64(s.GetCreatedAt()) 390 } 391 392 containers, err := m.getKubeletContainers(ctx, all) 393 if err != nil { 394 return nil, err 395 } 396 for i := range containers { 397 c := containers[i] 398 if c.Metadata == nil { 399 klog.V(4).InfoS("Container does not have metadata", "container", c) 400 continue 401 } 402 403 labelledInfo := getContainerInfoFromLabels(c.Labels) 404 pod, found := pods[labelledInfo.PodUID] 405 if !found { 406 pod = &kubecontainer.Pod{ 407 ID: labelledInfo.PodUID, 408 Name: labelledInfo.PodName, 409 Namespace: labelledInfo.PodNamespace, 410 } 411 pods[labelledInfo.PodUID] = pod 412 } 413 414 converted, err := m.toKubeContainer(c) 415 if err != nil { 416 klog.V(4).InfoS("Convert container of pod failed", "runtimeName", m.runtimeName, "container", c, "podUID", labelledInfo.PodUID, "err", err) 417 continue 418 } 419 420 pod.Containers = append(pod.Containers, converted) 421 } 422 423 // Convert map to list. 424 var result []*kubecontainer.Pod 425 for _, pod := range pods { 426 result = append(result, pod) 427 } 428 429 // There are scenarios where multiple pods are running in parallel having 430 // the same name, because one of them have not been fully terminated yet. 431 // To avoid unexpected behavior on container name based search (for example 432 // by calling *Kubelet.findContainer() without specifying a pod ID), we now 433 // return the list of pods ordered by their creation time. 434 sort.SliceStable(result, func(i, j int) bool { 435 return result[i].CreatedAt > result[j].CreatedAt 436 }) 437 klog.V(4).InfoS("Retrieved pods from runtime", "all", all) 438 return result, nil 439 } 440 441 // containerKillReason explains what killed a given container 442 type containerKillReason string 443 444 const ( 445 reasonStartupProbe containerKillReason = "StartupProbe" 446 reasonLivenessProbe containerKillReason = "LivenessProbe" 447 reasonFailedPostStartHook containerKillReason = "FailedPostStartHook" 448 reasonUnknown containerKillReason = "Unknown" 449 ) 450 451 // containerToKillInfo contains necessary information to kill a container. 452 type containerToKillInfo struct { 453 // The spec of the container. 454 container *v1.Container 455 // The name of the container. 456 name string 457 // The message indicates why the container will be killed. 458 message string 459 // The reason is a clearer source of info on why a container will be killed 460 // TODO: replace message with reason? 461 reason containerKillReason 462 } 463 464 // containerResources holds the set of resources applicable to the running container 465 type containerResources struct { 466 memoryLimit int64 467 memoryRequest int64 468 cpuLimit int64 469 cpuRequest int64 470 } 471 472 // containerToUpdateInfo contains necessary information to update a container's resources. 473 type containerToUpdateInfo struct { 474 // Index of the container in pod.Spec.Containers that needs resource update 475 apiContainerIdx int 476 // ID of the runtime container that needs resource update 477 kubeContainerID kubecontainer.ContainerID 478 // Desired resources for the running container 479 desiredContainerResources containerResources 480 // Most recently configured resources on the running container 481 currentContainerResources *containerResources 482 } 483 484 // podActions keeps information what to do for a pod. 485 type podActions struct { 486 // Stop all running (regular, init and ephemeral) containers and the sandbox for the pod. 487 KillPod bool 488 // Whether need to create a new sandbox. If needed to kill pod and create 489 // a new pod sandbox, all init containers need to be purged (i.e., removed). 490 CreateSandbox bool 491 // The id of existing sandbox. It is used for starting containers in ContainersToStart. 492 SandboxID string 493 // The attempt number of creating sandboxes for the pod. 494 Attempt uint32 495 496 // The next init container to start. 497 NextInitContainerToStart *v1.Container 498 // InitContainersToStart keeps a list of indexes for the init containers to 499 // start, where the index is the index of the specific init container in the 500 // pod spec (pod.Spec.InitContainers). 501 // NOTE: This is a field for SidecarContainers feature. Either this or 502 // NextInitContainerToStart will be set. 503 InitContainersToStart []int 504 // ContainersToStart keeps a list of indexes for the containers to start, 505 // where the index is the index of the specific container in the pod spec ( 506 // pod.Spec.Containers). 507 ContainersToStart []int 508 // ContainersToKill keeps a map of containers that need to be killed, note that 509 // the key is the container ID of the container, while 510 // the value contains necessary information to kill a container. 511 ContainersToKill map[kubecontainer.ContainerID]containerToKillInfo 512 // EphemeralContainersToStart is a list of indexes for the ephemeral containers to start, 513 // where the index is the index of the specific container in pod.Spec.EphemeralContainers. 514 EphemeralContainersToStart []int 515 // ContainersToUpdate keeps a list of containers needing resource update. 516 // Container resource update is applicable only for CPU and memory. 517 ContainersToUpdate map[v1.ResourceName][]containerToUpdateInfo 518 // UpdatePodResources is true if container(s) need resource update with restart 519 UpdatePodResources bool 520 } 521 522 func (p podActions) String() string { 523 return fmt.Sprintf("KillPod: %t, CreateSandbox: %t, UpdatePodResources: %t, Attempt: %d, InitContainersToStart: %v, ContainersToStart: %v, EphemeralContainersToStart: %v,ContainersToUpdate: %v, ContainersToKill: %v", 524 p.KillPod, p.CreateSandbox, p.UpdatePodResources, p.Attempt, p.InitContainersToStart, p.ContainersToStart, p.EphemeralContainersToStart, p.ContainersToUpdate, p.ContainersToKill) 525 } 526 527 func containerChanged(container *v1.Container, containerStatus *kubecontainer.Status) (uint64, uint64, bool) { 528 expectedHash := kubecontainer.HashContainer(container) 529 return expectedHash, containerStatus.Hash, containerStatus.Hash != expectedHash 530 } 531 532 func shouldRestartOnFailure(pod *v1.Pod) bool { 533 return pod.Spec.RestartPolicy != v1.RestartPolicyNever 534 } 535 536 func containerSucceeded(c *v1.Container, podStatus *kubecontainer.PodStatus) bool { 537 cStatus := podStatus.FindContainerStatusByName(c.Name) 538 if cStatus == nil || cStatus.State == kubecontainer.ContainerStateRunning { 539 return false 540 } 541 return cStatus.ExitCode == 0 542 } 543 544 func isInPlacePodVerticalScalingAllowed(pod *v1.Pod) bool { 545 if !utilfeature.DefaultFeatureGate.Enabled(features.InPlacePodVerticalScaling) { 546 return false 547 } 548 if types.IsStaticPod(pod) { 549 return false 550 } 551 return true 552 } 553 554 func (m *kubeGenericRuntimeManager) computePodResizeAction(pod *v1.Pod, containerIdx int, kubeContainerStatus *kubecontainer.Status, changes *podActions) bool { 555 container := pod.Spec.Containers[containerIdx] 556 if container.Resources.Limits == nil || len(pod.Status.ContainerStatuses) == 0 { 557 return true 558 } 559 560 // Determine if the *running* container needs resource update by comparing v1.Spec.Resources (desired) 561 // with v1.Status.Resources / runtime.Status.Resources (last known actual). 562 // Proceed only when kubelet has accepted the resize a.k.a v1.Spec.Resources.Requests == v1.Status.AllocatedResources. 563 // Skip if runtime containerID doesn't match pod.Status containerID (container is restarting) 564 apiContainerStatus, exists := podutil.GetContainerStatus(pod.Status.ContainerStatuses, container.Name) 565 if !exists || apiContainerStatus.State.Running == nil || apiContainerStatus.Resources == nil || 566 kubeContainerStatus.State != kubecontainer.ContainerStateRunning || 567 kubeContainerStatus.ID.String() != apiContainerStatus.ContainerID || 568 !cmp.Equal(container.Resources.Requests, apiContainerStatus.AllocatedResources) { 569 return true 570 } 571 572 desiredMemoryLimit := container.Resources.Limits.Memory().Value() 573 desiredCPULimit := container.Resources.Limits.Cpu().MilliValue() 574 desiredCPURequest := container.Resources.Requests.Cpu().MilliValue() 575 currentMemoryLimit := apiContainerStatus.Resources.Limits.Memory().Value() 576 currentCPULimit := apiContainerStatus.Resources.Limits.Cpu().MilliValue() 577 currentCPURequest := apiContainerStatus.Resources.Requests.Cpu().MilliValue() 578 // Runtime container status resources (from CRI), if set, supercedes v1(api) container status resrouces. 579 if kubeContainerStatus.Resources != nil { 580 if kubeContainerStatus.Resources.MemoryLimit != nil { 581 currentMemoryLimit = kubeContainerStatus.Resources.MemoryLimit.Value() 582 } 583 if kubeContainerStatus.Resources.CPULimit != nil { 584 currentCPULimit = kubeContainerStatus.Resources.CPULimit.MilliValue() 585 } 586 if kubeContainerStatus.Resources.CPURequest != nil { 587 currentCPURequest = kubeContainerStatus.Resources.CPURequest.MilliValue() 588 } 589 } 590 591 // Note: cgroup doesn't support memory request today, so we don't compare that. If canAdmitPod called during 592 // handlePodResourcesResize finds 'fit', then desiredMemoryRequest == currentMemoryRequest. 593 if desiredMemoryLimit == currentMemoryLimit && desiredCPULimit == currentCPULimit && desiredCPURequest == currentCPURequest { 594 return true 595 } 596 597 desiredResources := containerResources{ 598 memoryLimit: desiredMemoryLimit, 599 memoryRequest: apiContainerStatus.AllocatedResources.Memory().Value(), 600 cpuLimit: desiredCPULimit, 601 cpuRequest: desiredCPURequest, 602 } 603 currentResources := containerResources{ 604 memoryLimit: currentMemoryLimit, 605 memoryRequest: apiContainerStatus.Resources.Requests.Memory().Value(), 606 cpuLimit: currentCPULimit, 607 cpuRequest: currentCPURequest, 608 } 609 610 resizePolicy := make(map[v1.ResourceName]v1.ResourceResizeRestartPolicy) 611 for _, pol := range container.ResizePolicy { 612 resizePolicy[pol.ResourceName] = pol.RestartPolicy 613 } 614 determineContainerResize := func(rName v1.ResourceName, specValue, statusValue int64) (resize, restart bool) { 615 if specValue == statusValue { 616 return false, false 617 } 618 if resizePolicy[rName] == v1.RestartContainer { 619 return true, true 620 } 621 return true, false 622 } 623 markContainerForUpdate := func(rName v1.ResourceName, specValue, statusValue int64) { 624 cUpdateInfo := containerToUpdateInfo{ 625 apiContainerIdx: containerIdx, 626 kubeContainerID: kubeContainerStatus.ID, 627 desiredContainerResources: desiredResources, 628 currentContainerResources: ¤tResources, 629 } 630 // Order the container updates such that resource decreases are applied before increases 631 switch { 632 case specValue > statusValue: // append 633 changes.ContainersToUpdate[rName] = append(changes.ContainersToUpdate[rName], cUpdateInfo) 634 case specValue < statusValue: // prepend 635 changes.ContainersToUpdate[rName] = append(changes.ContainersToUpdate[rName], containerToUpdateInfo{}) 636 copy(changes.ContainersToUpdate[rName][1:], changes.ContainersToUpdate[rName]) 637 changes.ContainersToUpdate[rName][0] = cUpdateInfo 638 } 639 } 640 resizeMemLim, restartMemLim := determineContainerResize(v1.ResourceMemory, desiredMemoryLimit, currentMemoryLimit) 641 resizeCPULim, restartCPULim := determineContainerResize(v1.ResourceCPU, desiredCPULimit, currentCPULimit) 642 resizeCPUReq, restartCPUReq := determineContainerResize(v1.ResourceCPU, desiredCPURequest, currentCPURequest) 643 if restartCPULim || restartCPUReq || restartMemLim { 644 // resize policy requires this container to restart 645 changes.ContainersToKill[kubeContainerStatus.ID] = containerToKillInfo{ 646 name: kubeContainerStatus.Name, 647 container: &pod.Spec.Containers[containerIdx], 648 message: fmt.Sprintf("Container %s resize requires restart", container.Name), 649 } 650 changes.ContainersToStart = append(changes.ContainersToStart, containerIdx) 651 changes.UpdatePodResources = true 652 return false 653 } else { 654 if resizeMemLim { 655 markContainerForUpdate(v1.ResourceMemory, desiredMemoryLimit, currentMemoryLimit) 656 } 657 if resizeCPULim { 658 markContainerForUpdate(v1.ResourceCPU, desiredCPULimit, currentCPULimit) 659 } else if resizeCPUReq { 660 markContainerForUpdate(v1.ResourceCPU, desiredCPURequest, currentCPURequest) 661 } 662 } 663 return true 664 } 665 666 func (m *kubeGenericRuntimeManager) doPodResizeAction(pod *v1.Pod, podStatus *kubecontainer.PodStatus, podContainerChanges podActions, result kubecontainer.PodSyncResult) { 667 pcm := m.containerManager.NewPodContainerManager() 668 //TODO(vinaykul,InPlacePodVerticalScaling): Figure out best way to get enforceMemoryQoS value (parameter #4 below) in platform-agnostic way 669 podResources := cm.ResourceConfigForPod(pod, m.cpuCFSQuota, uint64((m.cpuCFSQuotaPeriod.Duration)/time.Microsecond), false) 670 if podResources == nil { 671 klog.ErrorS(nil, "Unable to get resource configuration", "pod", pod.Name) 672 result.Fail(fmt.Errorf("Unable to get resource configuration processing resize for pod %s", pod.Name)) 673 return 674 } 675 setPodCgroupConfig := func(rName v1.ResourceName, setLimitValue bool) error { 676 var err error 677 switch rName { 678 case v1.ResourceCPU: 679 podCpuResources := &cm.ResourceConfig{CPUPeriod: podResources.CPUPeriod} 680 if setLimitValue { 681 podCpuResources.CPUQuota = podResources.CPUQuota 682 } else { 683 podCpuResources.CPUShares = podResources.CPUShares 684 } 685 err = pcm.SetPodCgroupConfig(pod, rName, podCpuResources) 686 case v1.ResourceMemory: 687 err = pcm.SetPodCgroupConfig(pod, rName, podResources) 688 } 689 if err != nil { 690 klog.ErrorS(err, "Failed to set cgroup config", "resource", rName, "pod", pod.Name) 691 } 692 return err 693 } 694 // Memory and CPU are updated separately because memory resizes may be ordered differently than CPU resizes. 695 // If resize results in net pod resource increase, set pod cgroup config before resizing containers. 696 // If resize results in net pod resource decrease, set pod cgroup config after resizing containers. 697 // If an error occurs at any point, abort. Let future syncpod iterations retry the unfinished stuff. 698 resizeContainers := func(rName v1.ResourceName, currPodCgLimValue, newPodCgLimValue, currPodCgReqValue, newPodCgReqValue int64) error { 699 var err error 700 if newPodCgLimValue > currPodCgLimValue { 701 if err = setPodCgroupConfig(rName, true); err != nil { 702 return err 703 } 704 } 705 if newPodCgReqValue > currPodCgReqValue { 706 if err = setPodCgroupConfig(rName, false); err != nil { 707 return err 708 } 709 } 710 if len(podContainerChanges.ContainersToUpdate[rName]) > 0 { 711 if err = m.updatePodContainerResources(pod, rName, podContainerChanges.ContainersToUpdate[rName]); err != nil { 712 klog.ErrorS(err, "updatePodContainerResources failed", "pod", format.Pod(pod), "resource", rName) 713 return err 714 } 715 } 716 if newPodCgLimValue < currPodCgLimValue { 717 err = setPodCgroupConfig(rName, true) 718 } 719 if newPodCgReqValue < currPodCgReqValue { 720 if err = setPodCgroupConfig(rName, false); err != nil { 721 return err 722 } 723 } 724 return err 725 } 726 if len(podContainerChanges.ContainersToUpdate[v1.ResourceMemory]) > 0 || podContainerChanges.UpdatePodResources { 727 if podResources.Memory == nil { 728 klog.ErrorS(nil, "podResources.Memory is nil", "pod", pod.Name) 729 result.Fail(fmt.Errorf("podResources.Memory is nil for pod %s", pod.Name)) 730 return 731 } 732 currentPodMemoryConfig, err := pcm.GetPodCgroupConfig(pod, v1.ResourceMemory) 733 if err != nil { 734 klog.ErrorS(err, "GetPodCgroupConfig for memory failed", "pod", pod.Name) 735 result.Fail(err) 736 return 737 } 738 currentPodMemoryUsage, err := pcm.GetPodCgroupMemoryUsage(pod) 739 if err != nil { 740 klog.ErrorS(err, "GetPodCgroupMemoryUsage failed", "pod", pod.Name) 741 result.Fail(err) 742 return 743 } 744 if currentPodMemoryUsage >= uint64(*podResources.Memory) { 745 klog.ErrorS(nil, "Aborting attempt to set pod memory limit less than current memory usage", "pod", pod.Name) 746 result.Fail(fmt.Errorf("Aborting attempt to set pod memory limit less than current memory usage for pod %s", pod.Name)) 747 return 748 } 749 if errResize := resizeContainers(v1.ResourceMemory, int64(*currentPodMemoryConfig.Memory), *podResources.Memory, 0, 0); errResize != nil { 750 result.Fail(errResize) 751 return 752 } 753 } 754 if len(podContainerChanges.ContainersToUpdate[v1.ResourceCPU]) > 0 || podContainerChanges.UpdatePodResources { 755 if podResources.CPUQuota == nil || podResources.CPUShares == nil { 756 klog.ErrorS(nil, "podResources.CPUQuota or podResources.CPUShares is nil", "pod", pod.Name) 757 result.Fail(fmt.Errorf("podResources.CPUQuota or podResources.CPUShares is nil for pod %s", pod.Name)) 758 return 759 } 760 currentPodCpuConfig, err := pcm.GetPodCgroupConfig(pod, v1.ResourceCPU) 761 if err != nil { 762 klog.ErrorS(err, "GetPodCgroupConfig for CPU failed", "pod", pod.Name) 763 result.Fail(err) 764 return 765 } 766 if errResize := resizeContainers(v1.ResourceCPU, *currentPodCpuConfig.CPUQuota, *podResources.CPUQuota, 767 int64(*currentPodCpuConfig.CPUShares), int64(*podResources.CPUShares)); errResize != nil { 768 result.Fail(errResize) 769 return 770 } 771 } 772 } 773 774 func (m *kubeGenericRuntimeManager) updatePodContainerResources(pod *v1.Pod, resourceName v1.ResourceName, containersToUpdate []containerToUpdateInfo) error { 775 klog.V(5).InfoS("Updating container resources", "pod", klog.KObj(pod)) 776 777 for _, cInfo := range containersToUpdate { 778 container := pod.Spec.Containers[cInfo.apiContainerIdx].DeepCopy() 779 // If updating memory limit, use most recently configured CPU request and limit values. 780 // If updating CPU request and limit, use most recently configured memory request and limit values. 781 switch resourceName { 782 case v1.ResourceMemory: 783 container.Resources.Limits = v1.ResourceList{ 784 v1.ResourceCPU: *resource.NewMilliQuantity(cInfo.currentContainerResources.cpuLimit, resource.DecimalSI), 785 v1.ResourceMemory: *resource.NewQuantity(cInfo.desiredContainerResources.memoryLimit, resource.BinarySI), 786 } 787 container.Resources.Requests = v1.ResourceList{ 788 v1.ResourceCPU: *resource.NewMilliQuantity(cInfo.currentContainerResources.cpuRequest, resource.DecimalSI), 789 v1.ResourceMemory: *resource.NewQuantity(cInfo.desiredContainerResources.memoryRequest, resource.BinarySI), 790 } 791 case v1.ResourceCPU: 792 container.Resources.Limits = v1.ResourceList{ 793 v1.ResourceCPU: *resource.NewMilliQuantity(cInfo.desiredContainerResources.cpuLimit, resource.DecimalSI), 794 v1.ResourceMemory: *resource.NewQuantity(cInfo.currentContainerResources.memoryLimit, resource.BinarySI), 795 } 796 container.Resources.Requests = v1.ResourceList{ 797 v1.ResourceCPU: *resource.NewMilliQuantity(cInfo.desiredContainerResources.cpuRequest, resource.DecimalSI), 798 v1.ResourceMemory: *resource.NewQuantity(cInfo.currentContainerResources.memoryRequest, resource.BinarySI), 799 } 800 } 801 if err := m.updateContainerResources(pod, container, cInfo.kubeContainerID); err != nil { 802 // Log error and abort as container updates need to succeed in the order determined by computePodResizeAction. 803 // The recovery path is for SyncPod to keep retrying at later times until it succeeds. 804 klog.ErrorS(err, "updateContainerResources failed", "container", container.Name, "cID", cInfo.kubeContainerID, 805 "pod", format.Pod(pod), "resourceName", resourceName) 806 return err 807 } 808 // If UpdateContainerResources is error-free, it means desired values for 'resourceName' was accepted by runtime. 809 // So we update currentContainerResources for 'resourceName', which is our view of most recently configured resources. 810 // Note: We can't rely on GetPodStatus as runtime may lag in actuating the resource values it just accepted. 811 switch resourceName { 812 case v1.ResourceMemory: 813 cInfo.currentContainerResources.memoryLimit = cInfo.desiredContainerResources.memoryLimit 814 cInfo.currentContainerResources.memoryRequest = cInfo.desiredContainerResources.memoryRequest 815 case v1.ResourceCPU: 816 cInfo.currentContainerResources.cpuLimit = cInfo.desiredContainerResources.cpuLimit 817 cInfo.currentContainerResources.cpuRequest = cInfo.desiredContainerResources.cpuRequest 818 } 819 } 820 return nil 821 } 822 823 // computePodActions checks whether the pod spec has changed and returns the changes if true. 824 func (m *kubeGenericRuntimeManager) computePodActions(ctx context.Context, pod *v1.Pod, podStatus *kubecontainer.PodStatus) podActions { 825 klog.V(5).InfoS("Syncing Pod", "pod", klog.KObj(pod)) 826 827 createPodSandbox, attempt, sandboxID := runtimeutil.PodSandboxChanged(pod, podStatus) 828 changes := podActions{ 829 KillPod: createPodSandbox, 830 CreateSandbox: createPodSandbox, 831 SandboxID: sandboxID, 832 Attempt: attempt, 833 ContainersToStart: []int{}, 834 ContainersToKill: make(map[kubecontainer.ContainerID]containerToKillInfo), 835 } 836 837 // If we need to (re-)create the pod sandbox, everything will need to be 838 // killed and recreated, and init containers should be purged. 839 if createPodSandbox { 840 if !shouldRestartOnFailure(pod) && attempt != 0 && len(podStatus.ContainerStatuses) != 0 { 841 // Should not restart the pod, just return. 842 // we should not create a sandbox, and just kill the pod if it is already done. 843 // if all containers are done and should not be started, there is no need to create a new sandbox. 844 // this stops confusing logs on pods whose containers all have exit codes, but we recreate a sandbox before terminating it. 845 // 846 // If ContainerStatuses is empty, we assume that we've never 847 // successfully created any containers. In this case, we should 848 // retry creating the sandbox. 849 changes.CreateSandbox = false 850 return changes 851 } 852 853 // Get the containers to start, excluding the ones that succeeded if RestartPolicy is OnFailure. 854 var containersToStart []int 855 for idx, c := range pod.Spec.Containers { 856 if pod.Spec.RestartPolicy == v1.RestartPolicyOnFailure && containerSucceeded(&c, podStatus) { 857 continue 858 } 859 containersToStart = append(containersToStart, idx) 860 } 861 862 // We should not create a sandbox, and just kill the pod if initialization 863 // is done and there is no container to start. 864 if len(containersToStart) == 0 { 865 hasInitialized := false 866 if !utilfeature.DefaultFeatureGate.Enabled(features.SidecarContainers) { 867 _, _, hasInitialized = findNextInitContainerToRun(pod, podStatus) 868 } else { 869 // If there is any regular container, it means all init containers have 870 // been initialized. 871 hasInitialized = hasAnyRegularContainerCreated(pod, podStatus) 872 } 873 874 if hasInitialized { 875 changes.CreateSandbox = false 876 return changes 877 } 878 } 879 880 // If we are creating a pod sandbox, we should restart from the initial 881 // state. 882 if len(pod.Spec.InitContainers) != 0 { 883 // Pod has init containers, return the first one. 884 if !utilfeature.DefaultFeatureGate.Enabled(features.SidecarContainers) { 885 changes.NextInitContainerToStart = &pod.Spec.InitContainers[0] 886 } else { 887 changes.InitContainersToStart = []int{0} 888 } 889 890 return changes 891 } 892 changes.ContainersToStart = containersToStart 893 return changes 894 } 895 896 // Ephemeral containers may be started even if initialization is not yet complete. 897 for i := range pod.Spec.EphemeralContainers { 898 c := (*v1.Container)(&pod.Spec.EphemeralContainers[i].EphemeralContainerCommon) 899 900 // Ephemeral Containers are never restarted 901 if podStatus.FindContainerStatusByName(c.Name) == nil { 902 changes.EphemeralContainersToStart = append(changes.EphemeralContainersToStart, i) 903 } 904 } 905 906 // Check initialization progress. 907 if !utilfeature.DefaultFeatureGate.Enabled(features.SidecarContainers) { 908 initLastStatus, next, done := findNextInitContainerToRun(pod, podStatus) 909 if !done { 910 if next != nil { 911 initFailed := initLastStatus != nil && isInitContainerFailed(initLastStatus) 912 if initFailed && !shouldRestartOnFailure(pod) { 913 changes.KillPod = true 914 } else { 915 // Always try to stop containers in unknown state first. 916 if initLastStatus != nil && initLastStatus.State == kubecontainer.ContainerStateUnknown { 917 changes.ContainersToKill[initLastStatus.ID] = containerToKillInfo{ 918 name: next.Name, 919 container: next, 920 message: fmt.Sprintf("Init container is in %q state, try killing it before restart", 921 initLastStatus.State), 922 reason: reasonUnknown, 923 } 924 } 925 changes.NextInitContainerToStart = next 926 } 927 } 928 // Initialization failed or still in progress. Skip inspecting non-init 929 // containers. 930 return changes 931 } 932 } else { 933 hasInitialized := m.computeInitContainerActions(pod, podStatus, &changes) 934 if changes.KillPod || !hasInitialized { 935 // Initialization failed or still in progress. Skip inspecting non-init 936 // containers. 937 return changes 938 } 939 } 940 941 if isInPlacePodVerticalScalingAllowed(pod) { 942 changes.ContainersToUpdate = make(map[v1.ResourceName][]containerToUpdateInfo) 943 latestPodStatus, err := m.GetPodStatus(ctx, podStatus.ID, pod.Name, pod.Namespace) 944 if err == nil { 945 podStatus = latestPodStatus 946 } 947 } 948 949 // Number of running containers to keep. 950 keepCount := 0 951 // check the status of containers. 952 for idx, container := range pod.Spec.Containers { 953 containerStatus := podStatus.FindContainerStatusByName(container.Name) 954 955 // Call internal container post-stop lifecycle hook for any non-running container so that any 956 // allocated cpus are released immediately. If the container is restarted, cpus will be re-allocated 957 // to it. 958 if containerStatus != nil && containerStatus.State != kubecontainer.ContainerStateRunning { 959 if err := m.internalLifecycle.PostStopContainer(containerStatus.ID.ID); err != nil { 960 klog.ErrorS(err, "Internal container post-stop lifecycle hook failed for container in pod with error", 961 "containerName", container.Name, "pod", klog.KObj(pod)) 962 } 963 } 964 965 // If container does not exist, or is not running, check whether we 966 // need to restart it. 967 if containerStatus == nil || containerStatus.State != kubecontainer.ContainerStateRunning { 968 if kubecontainer.ShouldContainerBeRestarted(&container, pod, podStatus) { 969 klog.V(3).InfoS("Container of pod is not in the desired state and shall be started", "containerName", container.Name, "pod", klog.KObj(pod)) 970 changes.ContainersToStart = append(changes.ContainersToStart, idx) 971 if containerStatus != nil && containerStatus.State == kubecontainer.ContainerStateUnknown { 972 // If container is in unknown state, we don't know whether it 973 // is actually running or not, always try killing it before 974 // restart to avoid having 2 running instances of the same container. 975 changes.ContainersToKill[containerStatus.ID] = containerToKillInfo{ 976 name: containerStatus.Name, 977 container: &pod.Spec.Containers[idx], 978 message: fmt.Sprintf("Container is in %q state, try killing it before restart", 979 containerStatus.State), 980 reason: reasonUnknown, 981 } 982 } 983 } 984 continue 985 } 986 // The container is running, but kill the container if any of the following condition is met. 987 var message string 988 var reason containerKillReason 989 restart := shouldRestartOnFailure(pod) 990 // Do not restart if only the Resources field has changed with InPlacePodVerticalScaling enabled 991 if _, _, changed := containerChanged(&container, containerStatus); changed && 992 (!isInPlacePodVerticalScalingAllowed(pod) || 993 kubecontainer.HashContainerWithoutResources(&container) != containerStatus.HashWithoutResources) { 994 message = fmt.Sprintf("Container %s definition changed", container.Name) 995 // Restart regardless of the restart policy because the container 996 // spec changed. 997 restart = true 998 } else if liveness, found := m.livenessManager.Get(containerStatus.ID); found && liveness == proberesults.Failure { 999 // If the container failed the liveness probe, we should kill it. 1000 message = fmt.Sprintf("Container %s failed liveness probe", container.Name) 1001 reason = reasonLivenessProbe 1002 } else if startup, found := m.startupManager.Get(containerStatus.ID); found && startup == proberesults.Failure { 1003 // If the container failed the startup probe, we should kill it. 1004 message = fmt.Sprintf("Container %s failed startup probe", container.Name) 1005 reason = reasonStartupProbe 1006 } else if isInPlacePodVerticalScalingAllowed(pod) && !m.computePodResizeAction(pod, idx, containerStatus, &changes) { 1007 // computePodResizeAction updates 'changes' if resize policy requires restarting this container 1008 continue 1009 } else { 1010 // Keep the container. 1011 keepCount++ 1012 continue 1013 } 1014 1015 // We need to kill the container, but if we also want to restart the 1016 // container afterwards, make the intent clear in the message. Also do 1017 // not kill the entire pod since we expect container to be running eventually. 1018 if restart { 1019 message = fmt.Sprintf("%s, will be restarted", message) 1020 changes.ContainersToStart = append(changes.ContainersToStart, idx) 1021 } 1022 1023 changes.ContainersToKill[containerStatus.ID] = containerToKillInfo{ 1024 name: containerStatus.Name, 1025 container: &pod.Spec.Containers[idx], 1026 message: message, 1027 reason: reason, 1028 } 1029 klog.V(2).InfoS("Message for Container of pod", "containerName", container.Name, "containerStatusID", containerStatus.ID, "pod", klog.KObj(pod), "containerMessage", message) 1030 } 1031 1032 if keepCount == 0 && len(changes.ContainersToStart) == 0 { 1033 changes.KillPod = true 1034 if utilfeature.DefaultFeatureGate.Enabled(features.SidecarContainers) { 1035 // To prevent the restartable init containers to keep pod alive, we should 1036 // not restart them. 1037 changes.InitContainersToStart = nil 1038 } 1039 } 1040 1041 return changes 1042 } 1043 1044 // SyncPod syncs the running pod into the desired pod by executing following steps: 1045 // 1046 // 1. Compute sandbox and container changes. 1047 // 2. Kill pod sandbox if necessary. 1048 // 3. Kill any containers that should not be running. 1049 // 4. Create sandbox if necessary. 1050 // 5. Create ephemeral containers. 1051 // 6. Create init containers. 1052 // 7. Resize running containers (if InPlacePodVerticalScaling==true) 1053 // 8. Create normal containers. 1054 func (m *kubeGenericRuntimeManager) SyncPod(ctx context.Context, pod *v1.Pod, podStatus *kubecontainer.PodStatus, pullSecrets []v1.Secret, backOff *flowcontrol.Backoff) (result kubecontainer.PodSyncResult) { 1055 // Step 1: Compute sandbox and container changes. 1056 podContainerChanges := m.computePodActions(ctx, pod, podStatus) 1057 klog.V(3).InfoS("computePodActions got for pod", "podActions", podContainerChanges, "pod", klog.KObj(pod)) 1058 if podContainerChanges.CreateSandbox { 1059 ref, err := ref.GetReference(legacyscheme.Scheme, pod) 1060 if err != nil { 1061 klog.ErrorS(err, "Couldn't make a ref to pod", "pod", klog.KObj(pod)) 1062 } 1063 if podContainerChanges.SandboxID != "" { 1064 m.recorder.Eventf(ref, v1.EventTypeNormal, events.SandboxChanged, "Pod sandbox changed, it will be killed and re-created.") 1065 } else { 1066 klog.V(4).InfoS("SyncPod received new pod, will create a sandbox for it", "pod", klog.KObj(pod)) 1067 } 1068 } 1069 1070 // Step 2: Kill the pod if the sandbox has changed. 1071 if podContainerChanges.KillPod { 1072 if podContainerChanges.CreateSandbox { 1073 klog.V(4).InfoS("Stopping PodSandbox for pod, will start new one", "pod", klog.KObj(pod)) 1074 } else { 1075 klog.V(4).InfoS("Stopping PodSandbox for pod, because all other containers are dead", "pod", klog.KObj(pod)) 1076 } 1077 1078 killResult := m.killPodWithSyncResult(ctx, pod, kubecontainer.ConvertPodStatusToRunningPod(m.runtimeName, podStatus), nil) 1079 result.AddPodSyncResult(killResult) 1080 if killResult.Error() != nil { 1081 klog.ErrorS(killResult.Error(), "killPodWithSyncResult failed") 1082 return 1083 } 1084 1085 if podContainerChanges.CreateSandbox { 1086 m.purgeInitContainers(ctx, pod, podStatus) 1087 } 1088 } else { 1089 // Step 3: kill any running containers in this pod which are not to keep. 1090 for containerID, containerInfo := range podContainerChanges.ContainersToKill { 1091 klog.V(3).InfoS("Killing unwanted container for pod", "containerName", containerInfo.name, "containerID", containerID, "pod", klog.KObj(pod)) 1092 killContainerResult := kubecontainer.NewSyncResult(kubecontainer.KillContainer, containerInfo.name) 1093 result.AddSyncResult(killContainerResult) 1094 if err := m.killContainer(ctx, pod, containerID, containerInfo.name, containerInfo.message, containerInfo.reason, nil, nil); err != nil { 1095 killContainerResult.Fail(kubecontainer.ErrKillContainer, err.Error()) 1096 klog.ErrorS(err, "killContainer for pod failed", "containerName", containerInfo.name, "containerID", containerID, "pod", klog.KObj(pod)) 1097 return 1098 } 1099 } 1100 } 1101 1102 // Keep terminated init containers fairly aggressively controlled 1103 // This is an optimization because container removals are typically handled 1104 // by container garbage collector. 1105 m.pruneInitContainersBeforeStart(ctx, pod, podStatus) 1106 1107 // We pass the value of the PRIMARY podIP and list of podIPs down to 1108 // generatePodSandboxConfig and generateContainerConfig, which in turn 1109 // passes it to various other functions, in order to facilitate functionality 1110 // that requires this value (hosts file and downward API) and avoid races determining 1111 // the pod IP in cases where a container requires restart but the 1112 // podIP isn't in the status manager yet. The list of podIPs is used to 1113 // generate the hosts file. 1114 // 1115 // We default to the IPs in the passed-in pod status, and overwrite them if the 1116 // sandbox needs to be (re)started. 1117 var podIPs []string 1118 if podStatus != nil { 1119 podIPs = podStatus.IPs 1120 } 1121 1122 // Step 4: Create a sandbox for the pod if necessary. 1123 podSandboxID := podContainerChanges.SandboxID 1124 if podContainerChanges.CreateSandbox { 1125 var msg string 1126 var err error 1127 1128 klog.V(4).InfoS("Creating PodSandbox for pod", "pod", klog.KObj(pod)) 1129 metrics.StartedPodsTotal.Inc() 1130 createSandboxResult := kubecontainer.NewSyncResult(kubecontainer.CreatePodSandbox, format.Pod(pod)) 1131 result.AddSyncResult(createSandboxResult) 1132 1133 // ConvertPodSysctlsVariableToDotsSeparator converts sysctl variable 1134 // in the Pod.Spec.SecurityContext.Sysctls slice into a dot as a separator. 1135 // runc uses the dot as the separator to verify whether the sysctl variable 1136 // is correct in a separate namespace, so when using the slash as the sysctl 1137 // variable separator, runc returns an error: "sysctl is not in a separate kernel namespace" 1138 // and the podSandBox cannot be successfully created. Therefore, before calling runc, 1139 // we need to convert the sysctl variable, the dot is used as a separator to separate the kernel namespace. 1140 // When runc supports slash as sysctl separator, this function can no longer be used. 1141 sysctl.ConvertPodSysctlsVariableToDotsSeparator(pod.Spec.SecurityContext) 1142 1143 // Prepare resources allocated by the Dynammic Resource Allocation feature for the pod 1144 if utilfeature.DefaultFeatureGate.Enabled(features.DynamicResourceAllocation) { 1145 if err := m.runtimeHelper.PrepareDynamicResources(pod); err != nil { 1146 ref, referr := ref.GetReference(legacyscheme.Scheme, pod) 1147 if referr != nil { 1148 klog.ErrorS(referr, "Couldn't make a ref to pod", "pod", klog.KObj(pod)) 1149 return 1150 } 1151 m.recorder.Eventf(ref, v1.EventTypeWarning, events.FailedPrepareDynamicResources, "Failed to prepare dynamic resources: %v", err) 1152 klog.ErrorS(err, "Failed to prepare dynamic resources", "pod", klog.KObj(pod)) 1153 return 1154 } 1155 } 1156 1157 podSandboxID, msg, err = m.createPodSandbox(ctx, pod, podContainerChanges.Attempt) 1158 if err != nil { 1159 // createPodSandbox can return an error from CNI, CSI, 1160 // or CRI if the Pod has been deleted while the POD is 1161 // being created. If the pod has been deleted then it's 1162 // not a real error. 1163 // 1164 // SyncPod can still be running when we get here, which 1165 // means the PodWorker has not acked the deletion. 1166 if m.podStateProvider.IsPodTerminationRequested(pod.UID) { 1167 klog.V(4).InfoS("Pod was deleted and sandbox failed to be created", "pod", klog.KObj(pod), "podUID", pod.UID) 1168 return 1169 } 1170 metrics.StartedPodsErrorsTotal.Inc() 1171 createSandboxResult.Fail(kubecontainer.ErrCreatePodSandbox, msg) 1172 klog.ErrorS(err, "CreatePodSandbox for pod failed", "pod", klog.KObj(pod)) 1173 ref, referr := ref.GetReference(legacyscheme.Scheme, pod) 1174 if referr != nil { 1175 klog.ErrorS(referr, "Couldn't make a ref to pod", "pod", klog.KObj(pod)) 1176 } 1177 m.recorder.Eventf(ref, v1.EventTypeWarning, events.FailedCreatePodSandBox, "Failed to create pod sandbox: %v", err) 1178 return 1179 } 1180 klog.V(4).InfoS("Created PodSandbox for pod", "podSandboxID", podSandboxID, "pod", klog.KObj(pod)) 1181 1182 resp, err := m.runtimeService.PodSandboxStatus(ctx, podSandboxID, false) 1183 if err != nil { 1184 ref, referr := ref.GetReference(legacyscheme.Scheme, pod) 1185 if referr != nil { 1186 klog.ErrorS(referr, "Couldn't make a ref to pod", "pod", klog.KObj(pod)) 1187 } 1188 m.recorder.Eventf(ref, v1.EventTypeWarning, events.FailedStatusPodSandBox, "Unable to get pod sandbox status: %v", err) 1189 klog.ErrorS(err, "Failed to get pod sandbox status; Skipping pod", "pod", klog.KObj(pod)) 1190 result.Fail(err) 1191 return 1192 } 1193 if resp.GetStatus() == nil { 1194 result.Fail(errors.New("pod sandbox status is nil")) 1195 return 1196 } 1197 1198 // If we ever allow updating a pod from non-host-network to 1199 // host-network, we may use a stale IP. 1200 if !kubecontainer.IsHostNetworkPod(pod) { 1201 // Overwrite the podIPs passed in the pod status, since we just started the pod sandbox. 1202 podIPs = m.determinePodSandboxIPs(pod.Namespace, pod.Name, resp.GetStatus()) 1203 klog.V(4).InfoS("Determined the ip for pod after sandbox changed", "IPs", podIPs, "pod", klog.KObj(pod)) 1204 } 1205 } 1206 1207 // the start containers routines depend on pod ip(as in primary pod ip) 1208 // instead of trying to figure out if we have 0 < len(podIPs) 1209 // everytime, we short circuit it here 1210 podIP := "" 1211 if len(podIPs) != 0 { 1212 podIP = podIPs[0] 1213 } 1214 1215 // Get podSandboxConfig for containers to start. 1216 configPodSandboxResult := kubecontainer.NewSyncResult(kubecontainer.ConfigPodSandbox, podSandboxID) 1217 result.AddSyncResult(configPodSandboxResult) 1218 podSandboxConfig, err := m.generatePodSandboxConfig(pod, podContainerChanges.Attempt) 1219 if err != nil { 1220 message := fmt.Sprintf("GeneratePodSandboxConfig for pod %q failed: %v", format.Pod(pod), err) 1221 klog.ErrorS(err, "GeneratePodSandboxConfig for pod failed", "pod", klog.KObj(pod)) 1222 configPodSandboxResult.Fail(kubecontainer.ErrConfigPodSandbox, message) 1223 return 1224 } 1225 1226 // Helper containing boilerplate common to starting all types of containers. 1227 // typeName is a description used to describe this type of container in log messages, 1228 // currently: "container", "init container" or "ephemeral container" 1229 // metricLabel is the label used to describe this type of container in monitoring metrics. 1230 // currently: "container", "init_container" or "ephemeral_container" 1231 start := func(ctx context.Context, typeName, metricLabel string, spec *startSpec) error { 1232 startContainerResult := kubecontainer.NewSyncResult(kubecontainer.StartContainer, spec.container.Name) 1233 result.AddSyncResult(startContainerResult) 1234 1235 isInBackOff, msg, err := m.doBackOff(pod, spec.container, podStatus, backOff) 1236 if isInBackOff { 1237 startContainerResult.Fail(err, msg) 1238 klog.V(4).InfoS("Backing Off restarting container in pod", "containerType", typeName, "container", spec.container, "pod", klog.KObj(pod)) 1239 return err 1240 } 1241 1242 metrics.StartedContainersTotal.WithLabelValues(metricLabel).Inc() 1243 if sc.HasWindowsHostProcessRequest(pod, spec.container) { 1244 metrics.StartedHostProcessContainersTotal.WithLabelValues(metricLabel).Inc() 1245 } 1246 klog.V(4).InfoS("Creating container in pod", "containerType", typeName, "container", spec.container, "pod", klog.KObj(pod)) 1247 // NOTE (aramase) podIPs are populated for single stack and dual stack clusters. Send only podIPs. 1248 if msg, err := m.startContainer(ctx, podSandboxID, podSandboxConfig, spec, pod, podStatus, pullSecrets, podIP, podIPs); err != nil { 1249 // startContainer() returns well-defined error codes that have reasonable cardinality for metrics and are 1250 // useful to cluster administrators to distinguish "server errors" from "user errors". 1251 metrics.StartedContainersErrorsTotal.WithLabelValues(metricLabel, err.Error()).Inc() 1252 if sc.HasWindowsHostProcessRequest(pod, spec.container) { 1253 metrics.StartedHostProcessContainersErrorsTotal.WithLabelValues(metricLabel, err.Error()).Inc() 1254 } 1255 startContainerResult.Fail(err, msg) 1256 // known errors that are logged in other places are logged at higher levels here to avoid 1257 // repetitive log spam 1258 switch { 1259 case err == images.ErrImagePullBackOff: 1260 klog.V(3).InfoS("Container start failed in pod", "containerType", typeName, "container", spec.container, "pod", klog.KObj(pod), "containerMessage", msg, "err", err) 1261 default: 1262 utilruntime.HandleError(fmt.Errorf("%v %+v start failed in pod %v: %v: %s", typeName, spec.container, format.Pod(pod), err, msg)) 1263 } 1264 return err 1265 } 1266 1267 return nil 1268 } 1269 1270 // Step 5: start ephemeral containers 1271 // These are started "prior" to init containers to allow running ephemeral containers even when there 1272 // are errors starting an init container. In practice init containers will start first since ephemeral 1273 // containers cannot be specified on pod creation. 1274 for _, idx := range podContainerChanges.EphemeralContainersToStart { 1275 start(ctx, "ephemeral container", metrics.EphemeralContainer, ephemeralContainerStartSpec(&pod.Spec.EphemeralContainers[idx])) 1276 } 1277 1278 if !utilfeature.DefaultFeatureGate.Enabled(features.SidecarContainers) { 1279 // Step 6: start the init container. 1280 if container := podContainerChanges.NextInitContainerToStart; container != nil { 1281 // Start the next init container. 1282 if err := start(ctx, "init container", metrics.InitContainer, containerStartSpec(container)); err != nil { 1283 return 1284 } 1285 1286 // Successfully started the container; clear the entry in the failure 1287 klog.V(4).InfoS("Completed init container for pod", "containerName", container.Name, "pod", klog.KObj(pod)) 1288 } 1289 } else { 1290 // Step 6: start init containers. 1291 for _, idx := range podContainerChanges.InitContainersToStart { 1292 container := &pod.Spec.InitContainers[idx] 1293 // Start the next init container. 1294 if err := start(ctx, "init container", metrics.InitContainer, containerStartSpec(container)); err != nil { 1295 if types.IsRestartableInitContainer(container) { 1296 klog.V(4).InfoS("Failed to start the restartable init container for the pod, skipping", "initContainerName", container.Name, "pod", klog.KObj(pod)) 1297 continue 1298 } 1299 klog.V(4).InfoS("Failed to initialize the pod, as the init container failed to start, aborting", "initContainerName", container.Name, "pod", klog.KObj(pod)) 1300 return 1301 } 1302 1303 // Successfully started the container; clear the entry in the failure 1304 klog.V(4).InfoS("Completed init container for pod", "containerName", container.Name, "pod", klog.KObj(pod)) 1305 } 1306 } 1307 1308 // Step 7: For containers in podContainerChanges.ContainersToUpdate[CPU,Memory] list, invoke UpdateContainerResources 1309 if isInPlacePodVerticalScalingAllowed(pod) { 1310 if len(podContainerChanges.ContainersToUpdate) > 0 || podContainerChanges.UpdatePodResources { 1311 m.doPodResizeAction(pod, podStatus, podContainerChanges, result) 1312 } 1313 } 1314 1315 // Step 8: start containers in podContainerChanges.ContainersToStart. 1316 for _, idx := range podContainerChanges.ContainersToStart { 1317 start(ctx, "container", metrics.Container, containerStartSpec(&pod.Spec.Containers[idx])) 1318 } 1319 1320 return 1321 } 1322 1323 // If a container is still in backoff, the function will return a brief backoff error and 1324 // a detailed error message. 1325 func (m *kubeGenericRuntimeManager) doBackOff(pod *v1.Pod, container *v1.Container, podStatus *kubecontainer.PodStatus, backOff *flowcontrol.Backoff) (bool, string, error) { 1326 var cStatus *kubecontainer.Status 1327 for _, c := range podStatus.ContainerStatuses { 1328 if c.Name == container.Name && c.State == kubecontainer.ContainerStateExited { 1329 cStatus = c 1330 break 1331 } 1332 } 1333 1334 if cStatus == nil { 1335 return false, "", nil 1336 } 1337 1338 klog.V(3).InfoS("Checking backoff for container in pod", "containerName", container.Name, "pod", klog.KObj(pod)) 1339 // Use the finished time of the latest exited container as the start point to calculate whether to do back-off. 1340 ts := cStatus.FinishedAt 1341 // backOff requires a unique key to identify the container. 1342 key := getStableKey(pod, container) 1343 if backOff.IsInBackOffSince(key, ts) { 1344 if containerRef, err := kubecontainer.GenerateContainerRef(pod, container); err == nil { 1345 m.recorder.Eventf(containerRef, v1.EventTypeWarning, events.BackOffStartContainer, 1346 fmt.Sprintf("Back-off restarting failed container %s in pod %s", container.Name, format.Pod(pod))) 1347 } 1348 err := fmt.Errorf("back-off %s restarting failed container=%s pod=%s", backOff.Get(key), container.Name, format.Pod(pod)) 1349 klog.V(3).InfoS("Back-off restarting failed container", "err", err.Error()) 1350 return true, err.Error(), kubecontainer.ErrCrashLoopBackOff 1351 } 1352 1353 backOff.Next(key, ts) 1354 return false, "", nil 1355 } 1356 1357 // KillPod kills all the containers of a pod. Pod may be nil, running pod must not be. 1358 // gracePeriodOverride if specified allows the caller to override the pod default grace period. 1359 // only hard kill paths are allowed to specify a gracePeriodOverride in the kubelet in order to not corrupt user data. 1360 // it is useful when doing SIGKILL for hard eviction scenarios, or max grace period during soft eviction scenarios. 1361 func (m *kubeGenericRuntimeManager) KillPod(ctx context.Context, pod *v1.Pod, runningPod kubecontainer.Pod, gracePeriodOverride *int64) error { 1362 err := m.killPodWithSyncResult(ctx, pod, runningPod, gracePeriodOverride) 1363 return err.Error() 1364 } 1365 1366 // killPodWithSyncResult kills a runningPod and returns SyncResult. 1367 // Note: The pod passed in could be *nil* when kubelet restarted. 1368 func (m *kubeGenericRuntimeManager) killPodWithSyncResult(ctx context.Context, pod *v1.Pod, runningPod kubecontainer.Pod, gracePeriodOverride *int64) (result kubecontainer.PodSyncResult) { 1369 killContainerResults := m.killContainersWithSyncResult(ctx, pod, runningPod, gracePeriodOverride) 1370 for _, containerResult := range killContainerResults { 1371 result.AddSyncResult(containerResult) 1372 } 1373 1374 // stop sandbox, the sandbox will be removed in GarbageCollect 1375 killSandboxResult := kubecontainer.NewSyncResult(kubecontainer.KillPodSandbox, runningPod.ID) 1376 result.AddSyncResult(killSandboxResult) 1377 // Stop all sandboxes belongs to same pod 1378 for _, podSandbox := range runningPod.Sandboxes { 1379 if err := m.runtimeService.StopPodSandbox(ctx, podSandbox.ID.ID); err != nil && !crierror.IsNotFound(err) { 1380 killSandboxResult.Fail(kubecontainer.ErrKillPodSandbox, err.Error()) 1381 klog.ErrorS(nil, "Failed to stop sandbox", "podSandboxID", podSandbox.ID) 1382 } 1383 } 1384 1385 return 1386 } 1387 1388 func (m *kubeGenericRuntimeManager) GeneratePodStatus(event *runtimeapi.ContainerEventResponse) (*kubecontainer.PodStatus, error) { 1389 podIPs := m.determinePodSandboxIPs(event.PodSandboxStatus.Metadata.Namespace, event.PodSandboxStatus.Metadata.Name, event.PodSandboxStatus) 1390 1391 kubeContainerStatuses := []*kubecontainer.Status{} 1392 for _, status := range event.ContainersStatuses { 1393 kubeContainerStatuses = append(kubeContainerStatuses, m.convertToKubeContainerStatus(status)) 1394 } 1395 1396 sort.Sort(containerStatusByCreated(kubeContainerStatuses)) 1397 1398 return &kubecontainer.PodStatus{ 1399 ID: kubetypes.UID(event.PodSandboxStatus.Metadata.Uid), 1400 Name: event.PodSandboxStatus.Metadata.Name, 1401 Namespace: event.PodSandboxStatus.Metadata.Namespace, 1402 IPs: podIPs, 1403 SandboxStatuses: []*runtimeapi.PodSandboxStatus{event.PodSandboxStatus}, 1404 ContainerStatuses: kubeContainerStatuses, 1405 }, nil 1406 } 1407 1408 // GetPodStatus retrieves the status of the pod, including the 1409 // information of all containers in the pod that are visible in Runtime. 1410 func (m *kubeGenericRuntimeManager) GetPodStatus(ctx context.Context, uid kubetypes.UID, name, namespace string) (*kubecontainer.PodStatus, error) { 1411 // Now we retain restart count of container as a container label. Each time a container 1412 // restarts, pod will read the restart count from the registered dead container, increment 1413 // it to get the new restart count, and then add a label with the new restart count on 1414 // the newly started container. 1415 // However, there are some limitations of this method: 1416 // 1. When all dead containers were garbage collected, the container status could 1417 // not get the historical value and would be *inaccurate*. Fortunately, the chance 1418 // is really slim. 1419 // 2. When working with old version containers which have no restart count label, 1420 // we can only assume their restart count is 0. 1421 // Anyhow, we only promised "best-effort" restart count reporting, we can just ignore 1422 // these limitations now. 1423 // TODO: move this comment to SyncPod. 1424 podSandboxIDs, err := m.getSandboxIDByPodUID(ctx, uid, nil) 1425 if err != nil { 1426 return nil, err 1427 } 1428 1429 pod := &v1.Pod{ 1430 ObjectMeta: metav1.ObjectMeta{ 1431 Name: name, 1432 Namespace: namespace, 1433 UID: uid, 1434 }, 1435 } 1436 1437 podFullName := format.Pod(pod) 1438 1439 klog.V(4).InfoS("getSandboxIDByPodUID got sandbox IDs for pod", "podSandboxID", podSandboxIDs, "pod", klog.KObj(pod)) 1440 1441 sandboxStatuses := []*runtimeapi.PodSandboxStatus{} 1442 containerStatuses := []*kubecontainer.Status{} 1443 var timestamp time.Time 1444 1445 podIPs := []string{} 1446 for idx, podSandboxID := range podSandboxIDs { 1447 resp, err := m.runtimeService.PodSandboxStatus(ctx, podSandboxID, false) 1448 // Between List (getSandboxIDByPodUID) and check (PodSandboxStatus) another thread might remove a container, and that is normal. 1449 // The previous call (getSandboxIDByPodUID) never fails due to a pod sandbox not existing. 1450 // Therefore, this method should not either, but instead act as if the previous call failed, 1451 // which means the error should be ignored. 1452 if crierror.IsNotFound(err) { 1453 continue 1454 } 1455 if err != nil { 1456 klog.ErrorS(err, "PodSandboxStatus of sandbox for pod", "podSandboxID", podSandboxID, "pod", klog.KObj(pod)) 1457 return nil, err 1458 } 1459 if resp.GetStatus() == nil { 1460 return nil, errors.New("pod sandbox status is nil") 1461 1462 } 1463 sandboxStatuses = append(sandboxStatuses, resp.Status) 1464 // Only get pod IP from latest sandbox 1465 if idx == 0 && resp.Status.State == runtimeapi.PodSandboxState_SANDBOX_READY { 1466 podIPs = m.determinePodSandboxIPs(namespace, name, resp.Status) 1467 } 1468 1469 if idx == 0 && utilfeature.DefaultFeatureGate.Enabled(features.EventedPLEG) { 1470 if resp.Timestamp == 0 { 1471 // If the Evented PLEG is enabled in the kubelet, but not in the runtime 1472 // then the pod status we get will not have the timestamp set. 1473 // e.g. CI job 'pull-kubernetes-e2e-gce-alpha-features' will runs with 1474 // features gate enabled, which includes Evented PLEG, but uses the 1475 // runtime without Evented PLEG support. 1476 klog.V(4).InfoS("Runtime does not set pod status timestamp", "pod", klog.KObj(pod)) 1477 containerStatuses, err = m.getPodContainerStatuses(ctx, uid, name, namespace) 1478 if err != nil { 1479 if m.logReduction.ShouldMessageBePrinted(err.Error(), podFullName) { 1480 klog.ErrorS(err, "getPodContainerStatuses for pod failed", "pod", klog.KObj(pod)) 1481 } 1482 return nil, err 1483 } 1484 } else { 1485 // Get the statuses of all containers visible to the pod and 1486 // timestamp from sandboxStatus. 1487 timestamp = time.Unix(resp.Timestamp, 0) 1488 for _, cs := range resp.ContainersStatuses { 1489 cStatus := m.convertToKubeContainerStatus(cs) 1490 containerStatuses = append(containerStatuses, cStatus) 1491 } 1492 } 1493 } 1494 } 1495 1496 if !utilfeature.DefaultFeatureGate.Enabled(features.EventedPLEG) { 1497 // Get statuses of all containers visible in the pod. 1498 containerStatuses, err = m.getPodContainerStatuses(ctx, uid, name, namespace) 1499 if err != nil { 1500 if m.logReduction.ShouldMessageBePrinted(err.Error(), podFullName) { 1501 klog.ErrorS(err, "getPodContainerStatuses for pod failed", "pod", klog.KObj(pod)) 1502 } 1503 return nil, err 1504 } 1505 } 1506 1507 m.logReduction.ClearID(podFullName) 1508 return &kubecontainer.PodStatus{ 1509 ID: uid, 1510 Name: name, 1511 Namespace: namespace, 1512 IPs: podIPs, 1513 SandboxStatuses: sandboxStatuses, 1514 ContainerStatuses: containerStatuses, 1515 TimeStamp: timestamp, 1516 }, nil 1517 } 1518 1519 // GarbageCollect removes dead containers using the specified container gc policy. 1520 func (m *kubeGenericRuntimeManager) GarbageCollect(ctx context.Context, gcPolicy kubecontainer.GCPolicy, allSourcesReady bool, evictNonDeletedPods bool) error { 1521 return m.containerGC.GarbageCollect(ctx, gcPolicy, allSourcesReady, evictNonDeletedPods) 1522 } 1523 1524 // UpdatePodCIDR is just a passthrough method to update the runtimeConfig of the shim 1525 // with the podCIDR supplied by the kubelet. 1526 func (m *kubeGenericRuntimeManager) UpdatePodCIDR(ctx context.Context, podCIDR string) error { 1527 // TODO(#35531): do we really want to write a method on this manager for each 1528 // field of the config? 1529 klog.InfoS("Updating runtime config through cri with podcidr", "CIDR", podCIDR) 1530 return m.runtimeService.UpdateRuntimeConfig(ctx, 1531 &runtimeapi.RuntimeConfig{ 1532 NetworkConfig: &runtimeapi.NetworkConfig{ 1533 PodCidr: podCIDR, 1534 }, 1535 }) 1536 } 1537 1538 func (m *kubeGenericRuntimeManager) CheckpointContainer(ctx context.Context, options *runtimeapi.CheckpointContainerRequest) error { 1539 return m.runtimeService.CheckpointContainer(ctx, options) 1540 } 1541 1542 func (m *kubeGenericRuntimeManager) ListMetricDescriptors(ctx context.Context) ([]*runtimeapi.MetricDescriptor, error) { 1543 return m.runtimeService.ListMetricDescriptors(ctx) 1544 } 1545 1546 func (m *kubeGenericRuntimeManager) ListPodSandboxMetrics(ctx context.Context) ([]*runtimeapi.PodSandboxMetrics, error) { 1547 return m.runtimeService.ListPodSandboxMetrics(ctx) 1548 }