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