// podSandboxChanged checks whether the spec of the pod is changed and returns // (changed, new attempt, original sandboxID if exist). func (m *kubeGenericRuntimeManager) podSandboxChanged(pod *api.Pod, podStatus *kubecontainer.PodStatus) (changed bool, attempt uint32, sandboxID string) { if len(podStatus.SandboxStatuses) == 0 { glog.V(2).Infof("No sandbox for pod %q can be found. Need to start a new one", format.Pod(pod)) return true, 0, "" } readySandboxCount := 0 for _, s := range podStatus.SandboxStatuses { if s.GetState() == runtimeApi.PodSandBoxState_READY { readySandboxCount++ } } // Needs to create a new sandbox when readySandboxCount > 1 or the ready sandbox is not the latest one. sandboxStatus := podStatus.SandboxStatuses[0] if readySandboxCount > 1 || sandboxStatus.GetState() != runtimeApi.PodSandBoxState_READY { glog.V(2).Infof("No ready sandbox for pod %q can be found. Need to start a new one", format.Pod(pod)) return true, sandboxStatus.Metadata.GetAttempt() + 1, sandboxStatus.GetId() } // Needs to create a new sandbox when network namespace changed. if sandboxStatus.Linux != nil && sandboxStatus.Linux.Namespaces.Options != nil && sandboxStatus.Linux.Namespaces.Options.GetHostNetwork() != kubecontainer.IsHostNetworkPod(pod) { glog.V(2).Infof("Sandbox for pod %q has changed. Need to start a new one", format.Pod(pod)) return true, sandboxStatus.Metadata.GetAttempt() + 1, "" } return false, sandboxStatus.Metadata.GetAttempt(), sandboxStatus.GetId() }
// generatePodSandboxConfig generates pod sandbox config from api.Pod. func (m *kubeGenericRuntimeManager) generatePodSandboxConfig(pod *api.Pod, podIP string) (*runtimeApi.PodSandboxConfig, error) { sandboxName := buildSandboxName(pod) // TODO: deprecating podsandbox resource requirements in favor of the pod level cgroup // Refer https://github.com/kubernetes/kubernetes/issues/29871 podSandboxConfig := &runtimeApi.PodSandboxConfig{ Name: &sandboxName, Labels: newPodLabels(pod), Annotations: newPodAnnotations(pod), } if !kubecontainer.IsHostNetworkPod(pod) { dnsServers, dnsSearches, err := m.runtimeHelper.GetClusterDNS(pod) if err != nil { return nil, err } podSandboxConfig.DnsOptions = &runtimeApi.DNSOption{ Servers: dnsServers, Searches: dnsSearches, } // TODO: Add domain support in new runtime interface hostname, _, err := m.runtimeHelper.GeneratePodHostNameAndDomain(pod) if err != nil { return nil, err } podSandboxConfig.Hostname = &hostname } cgroupParent := "" portMappings := []*runtimeApi.PortMapping{} for _, c := range pod.Spec.Containers { opts, err := m.runtimeHelper.GenerateRunContainerOptions(pod, &c, podIP) if err != nil { return nil, err } for idx := range opts.PortMappings { port := opts.PortMappings[idx] hostPort := int32(port.HostPort) containerPort := int32(port.ContainerPort) protocol := toRuntimeProtocol(port.Protocol) portMappings = append(portMappings, &runtimeApi.PortMapping{ HostIp: &port.HostIP, HostPort: &hostPort, ContainerPort: &containerPort, Protocol: &protocol, Name: &port.Name, }) } // TODO: refactor kubelet to get cgroup parent for pod instead of containers cgroupParent = opts.CgroupParent } podSandboxConfig.Linux = generatePodSandboxLinuxConfig(pod, cgroupParent) if len(portMappings) > 0 { podSandboxConfig.PortMappings = portMappings } return podSandboxConfig, nil }
// isHostNetwork checks whether the pod is running in host-network mode. func (m *kubeGenericRuntimeManager) isHostNetwork(podSandBoxID string, pod *api.Pod) (bool, error) { if pod != nil { return kubecontainer.IsHostNetworkPod(pod), nil } podStatus, err := m.runtimeService.PodSandboxStatus(podSandBoxID) if err != nil { return false, err } if podStatus.Linux != nil && podStatus.Linux.Namespaces != nil && podStatus.Linux.Namespaces.Options != nil { if podStatus.Linux.Namespaces.Options.HostNetwork != nil { return podStatus.Linux.Namespaces.Options.GetHostNetwork(), nil } } return false, nil }
// generatePodSandboxConfig generates pod sandbox config from v1.Pod. func (m *kubeGenericRuntimeManager) generatePodSandboxConfig(pod *v1.Pod, attempt uint32) (*runtimeapi.PodSandboxConfig, error) { // TODO: deprecating podsandbox resource requirements in favor of the pod level cgroup // Refer https://github.com/kubernetes/kubernetes/issues/29871 podUID := string(pod.UID) podSandboxConfig := &runtimeapi.PodSandboxConfig{ Metadata: &runtimeapi.PodSandboxMetadata{ Name: &pod.Name, Namespace: &pod.Namespace, Uid: &podUID, Attempt: &attempt, }, Labels: newPodLabels(pod), Annotations: newPodAnnotations(pod), } if !kubecontainer.IsHostNetworkPod(pod) { dnsServers, dnsSearches, err := m.runtimeHelper.GetClusterDNS(pod) if err != nil { return nil, err } podSandboxConfig.DnsConfig = &runtimeapi.DNSConfig{ Servers: dnsServers, Searches: dnsSearches, Options: defaultDNSOptions, } // TODO: Add domain support in new runtime interface hostname, _, err := m.runtimeHelper.GeneratePodHostNameAndDomain(pod) if err != nil { return nil, err } podSandboxConfig.Hostname = &hostname } logDir := buildPodLogsDirectory(pod.UID) podSandboxConfig.LogDirectory = &logDir cgroupParent := "" portMappings := []*runtimeapi.PortMapping{} for _, c := range pod.Spec.Containers { // TODO: use a separate interface to only generate portmappings opts, err := m.runtimeHelper.GenerateRunContainerOptions(pod, &c, "") if err != nil { return nil, err } for idx := range opts.PortMappings { port := opts.PortMappings[idx] hostPort := int32(port.HostPort) containerPort := int32(port.ContainerPort) protocol := toRuntimeProtocol(port.Protocol) portMappings = append(portMappings, &runtimeapi.PortMapping{ HostIp: &port.HostIP, HostPort: &hostPort, ContainerPort: &containerPort, Protocol: &protocol, }) } // TODO: refactor kubelet to get cgroup parent for pod instead of containers cgroupParent = opts.CgroupParent } podSandboxConfig.Linux = m.generatePodSandboxLinuxConfig(pod, cgroupParent) if len(portMappings) > 0 { podSandboxConfig.PortMappings = portMappings } return podSandboxConfig, nil }
// SyncPod syncs the running pod into the desired pod by executing following steps: // // 1. Compute sandbox and container changes. // 2. Kill pod sandbox if necessary. // 3. Kill any containers that should not be running. // 4. Create sandbox if necessary. // 5. Create init containers. // 6. Create normal containers. func (m *kubeGenericRuntimeManager) SyncPod(pod *api.Pod, _ api.PodStatus, podStatus *kubecontainer.PodStatus, pullSecrets []api.Secret, backOff *flowcontrol.Backoff) (result kubecontainer.PodSyncResult) { // Step 1: Compute sandbox and container changes. podContainerChanges := m.computePodContainerChanges(pod, podStatus) glog.V(3).Infof("computePodContainerChanges got %+v for pod %q", podContainerChanges, format.Pod(pod)) if podContainerChanges.CreateSandbox { ref, err := api.GetReference(pod) if err != nil { glog.Errorf("Couldn't make a ref to pod %q: '%v'", format.Pod(pod), err) } if podContainerChanges.SandboxID != "" { m.recorder.Eventf(ref, api.EventTypeNormal, "SandboxChanged", "Pod sandbox changed, it will be killed and re-created.") } else { m.recorder.Eventf(ref, api.EventTypeNormal, "SandboxReceived", "Pod sandbox received, it will be created.") } } // Step 2: Kill the pod if the sandbox has changed. if podContainerChanges.CreateSandbox || (len(podContainerChanges.ContainersToKeep) == 0 && len(podContainerChanges.ContainersToStart) == 0) { if len(podContainerChanges.ContainersToKeep) == 0 && len(podContainerChanges.ContainersToStart) == 0 { glog.V(4).Infof("Stopping PodSandbox for %q because all other containers are dead.", format.Pod(pod)) } else { glog.V(4).Infof("Stopping PodSandbox for %q, will start new one", format.Pod(pod)) } killResult := m.killPodWithSyncResult(pod, kubecontainer.ConvertPodStatusToRunningPod(m.runtimeName, podStatus), nil) result.AddPodSyncResult(killResult) if killResult.Error() != nil { glog.Errorf("killPodWithSyncResult failed: %v", killResult.Error()) return } } else { // Step 3: kill any running containers in this pod which are not to keep. for containerID, containerInfo := range podContainerChanges.ContainersToKill { glog.V(3).Infof("Killing unwanted container %q(id=%q) for pod %q", containerInfo.name, containerID, format.Pod(pod)) killContainerResult := kubecontainer.NewSyncResult(kubecontainer.KillContainer, containerInfo.name) result.AddSyncResult(killContainerResult) if err := m.killContainer(pod, containerID, containerInfo.name, containerInfo.message, nil); err != nil { killContainerResult.Fail(kubecontainer.ErrKillContainer, err.Error()) glog.Errorf("killContainer %q(id=%q) for pod %q failed: %v", containerInfo.name, containerID, format.Pod(pod), err) return } } } // Keep terminated init containers fairly aggressively controlled m.pruneInitContainersBeforeStart(pod, podStatus, podContainerChanges.InitContainersToKeep) // We pass the value of the podIP down to generatePodSandboxConfig and // generateContainerConfig, which in turn passes it to various other // functions, in order to facilitate functionality that requires this // value (hosts file and downward API) and avoid races determining // the pod IP in cases where a container requires restart but the // podIP isn't in the status manager yet. // // We default to the IP in the passed-in pod status, and overwrite it if the // sandbox needs to be (re)started. podIP := "" if podStatus != nil { podIP = podStatus.IP } // Step 4: Create a sandbox for the pod if necessary. podSandboxID := podContainerChanges.SandboxID if podContainerChanges.CreateSandbox && len(podContainerChanges.ContainersToStart) > 0 { var msg string var err error glog.V(4).Infof("Creating sandbox for pod %q", format.Pod(pod)) createSandboxResult := kubecontainer.NewSyncResult(kubecontainer.CreatePodSandbox, format.Pod(pod)) result.AddSyncResult(createSandboxResult) podSandboxID, msg, err = m.createPodSandbox(pod, podContainerChanges.Attempt) if err != nil { createSandboxResult.Fail(kubecontainer.ErrCreatePodSandbox, msg) glog.Errorf("createPodSandbox for pod %q failed: %v", format.Pod(pod), err) return } setupNetworkResult := kubecontainer.NewSyncResult(kubecontainer.SetupNetwork, podSandboxID) result.AddSyncResult(setupNetworkResult) if !kubecontainer.IsHostNetworkPod(pod) { glog.V(3).Infof("Calling network plugin %s to setup pod for %s", m.networkPlugin.Name(), format.Pod(pod)) // Setup pod network plugin with sandbox id // TODO: rename the last param to sandboxID err = m.networkPlugin.SetUpPod(pod.Namespace, pod.Name, kubecontainer.ContainerID{ Type: m.runtimeName, ID: podSandboxID, }) if err != nil { message := fmt.Sprintf("Failed to setup network for pod %q using network plugins %q: %v", format.Pod(pod), m.networkPlugin.Name(), err) setupNetworkResult.Fail(kubecontainer.ErrSetupNetwork, message) glog.Error(message) killPodSandboxResult := kubecontainer.NewSyncResult(kubecontainer.KillPodSandbox, format.Pod(pod)) result.AddSyncResult(killPodSandboxResult) if err := m.runtimeService.StopPodSandbox(podSandboxID); err != nil { killPodSandboxResult.Fail(kubecontainer.ErrKillPodSandbox, err.Error()) glog.Errorf("Kill sandbox %q failed for pod %q: %v", podSandboxID, format.Pod(pod), err) } return } podSandboxStatus, err := m.runtimeService.PodSandboxStatus(podSandboxID) if err != nil { glog.Errorf("Failed to get pod sandbox status: %v; Skipping pod %q", err, format.Pod(pod)) result.Fail(err) return } // Overwrite the podIP passed in the pod status, since we just started the infra container. podIP = m.determinePodSandboxIP(pod.Namespace, pod.Name, podSandboxStatus) glog.V(4).Infof("Determined the ip %q for pod %q after sandbox changed", podIP, format.Pod(pod)) } } // Get podSandboxConfig for containers to start. configPodSandboxResult := kubecontainer.NewSyncResult(kubecontainer.ConfigPodSandbox, podSandboxID) result.AddSyncResult(configPodSandboxResult) podSandboxConfig, err := m.generatePodSandboxConfig(pod, podContainerChanges.Attempt) if err != nil { message := fmt.Sprintf("GeneratePodSandboxConfig for pod %q failed: %v", format.Pod(pod), err) glog.Error(message) configPodSandboxResult.Fail(kubecontainer.ErrConfigPodSandbox, message) return } // Step 5: start init containers. status, next, done := findNextInitContainerToRun(pod, podStatus) if status != nil && status.ExitCode != 0 { // container initialization has failed, flag the pod as failed initContainerResult := kubecontainer.NewSyncResult(kubecontainer.InitContainer, status.Name) initContainerResult.Fail(kubecontainer.ErrRunInitContainer, fmt.Sprintf("init container %q exited with %d", status.Name, status.ExitCode)) result.AddSyncResult(initContainerResult) if pod.Spec.RestartPolicy == api.RestartPolicyNever { utilruntime.HandleError(fmt.Errorf("error running pod %q init container %q, restart=Never: %#v", format.Pod(pod), status.Name, status)) return } utilruntime.HandleError(fmt.Errorf("Error running pod %q init container %q, restarting: %#v", format.Pod(pod), status.Name, status)) } if next != nil { if len(podContainerChanges.ContainersToStart) == 0 { glog.V(4).Infof("No containers to start, stopping at init container %+v in pod %v", next.Name, format.Pod(pod)) return } // If we need to start the next container, do so now then exit container := next startContainerResult := kubecontainer.NewSyncResult(kubecontainer.StartContainer, container.Name) result.AddSyncResult(startContainerResult) isInBackOff, msg, err := m.doBackOff(pod, container, podStatus, backOff) if isInBackOff { startContainerResult.Fail(err, msg) glog.V(4).Infof("Backing Off restarting init container %+v in pod %v", container, format.Pod(pod)) return } glog.V(4).Infof("Creating init container %+v in pod %v", container, format.Pod(pod)) if msg, err := m.startContainer(podSandboxID, podSandboxConfig, container, pod, podStatus, pullSecrets, podIP); err != nil { startContainerResult.Fail(err, msg) utilruntime.HandleError(fmt.Errorf("init container start failed: %v: %s", err, msg)) return } // Successfully started the container; clear the entry in the failure glog.V(4).Infof("Completed init container %q for pod %q", container.Name, format.Pod(pod)) return } if !done { // init container still running glog.V(4).Infof("An init container is still running in pod %v", format.Pod(pod)) return } if podContainerChanges.InitFailed { glog.V(4).Infof("Not all init containers have succeeded for pod %v", format.Pod(pod)) return } // Step 6: start containers in podContainerChanges.ContainersToStart. for idx := range podContainerChanges.ContainersToStart { container := &pod.Spec.Containers[idx] startContainerResult := kubecontainer.NewSyncResult(kubecontainer.StartContainer, container.Name) result.AddSyncResult(startContainerResult) isInBackOff, msg, err := m.doBackOff(pod, container, podStatus, backOff) if isInBackOff { startContainerResult.Fail(err, msg) glog.V(4).Infof("Backing Off restarting container %+v in pod %v", container, format.Pod(pod)) continue } glog.V(4).Infof("Creating container %+v in pod %v", container, format.Pod(pod)) if msg, err := m.startContainer(podSandboxID, podSandboxConfig, container, pod, podStatus, pullSecrets, podIP); err != nil { startContainerResult.Fail(err, msg) utilruntime.HandleError(fmt.Errorf("container start failed: %v: %s", err, msg)) continue } } return }