Restart containers in right order after the podSandboxChanged

This is a workaround for the issue that the kubelet cannot differentiate
the container statuses of the previous podSandbox from the current one.

If the node is rebooted, all containers will be in the exited state and
the kubelet will try to recreate a new podSandbox. In this case, the
kubelet should not mistakenly think that the newly created podSandbox
has been initialized.
This commit is contained in:
Gunju Kim 2023-08-31 01:17:37 +09:00
parent 86676bce04
commit ca6fda05ce
No known key found for this signature in database
GPG Key ID: 9300A528F3F0DAB7
2 changed files with 166 additions and 1 deletions

View File

@ -949,7 +949,31 @@ func (m *kubeGenericRuntimeManager) computeInitContainerActions(pod *v1.Pod, pod
// have been executed at some point in the past. However, they could have been removed
// from the container runtime now, and if we proceed, it would appear as if they
// never ran and will re-execute improperly except for the restartable init containers.
podHasInitialized := hasAnyRegularContainerCreated(pod, podStatus)
podHasInitialized := false
for _, container := range pod.Spec.Containers {
status := podStatus.FindContainerStatusByName(container.Name)
if status == nil {
continue
}
switch status.State {
case kubecontainer.ContainerStateCreated,
kubecontainer.ContainerStateRunning:
podHasInitialized = true
case kubecontainer.ContainerStateExited:
// This is a workaround for the issue that the kubelet cannot
// differentiate the container statuses of the previous podSandbox
// from the current one.
// If the node is rebooted, all containers will be in the exited
// state and the kubelet will try to recreate a new podSandbox.
// In this case, the kubelet should not mistakenly think that
// the newly created podSandbox has been initialized.
default:
// Ignore other states
}
if podHasInitialized {
break
}
}
// isPreviouslyInitialized indicates if the current init container is
// previously initialized.

View File

@ -2506,3 +2506,144 @@ var _ = SIGDescribe("[NodeAlphaFeature:SidecarContainers] Containers Lifecycle",
framework.ExpectNoError(results.Starts(regular1))
})
})
var _ = SIGDescribe("[NodeAlphaFeature:SidecarContainers][Serial] Containers Lifecycle", func() {
f := framework.NewDefaultFramework("containers-lifecycle-test-serial")
f.NamespacePodSecurityEnforceLevel = admissionapi.LevelPrivileged
ginkgo.It("should restart the containers in right order after the node reboot", func(ctx context.Context) {
init1 := "init-1"
restartableInit2 := "restartable-init-2"
init3 := "init-3"
regular1 := "regular-1"
podLabels := map[string]string{
"test": "containers-lifecycle-test-serial",
"namespace": f.Namespace.Name,
}
pod := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "initialized-pod",
Labels: podLabels,
},
Spec: v1.PodSpec{
RestartPolicy: v1.RestartPolicyAlways,
InitContainers: []v1.Container{
{
Name: init1,
Image: busyboxImage,
Command: ExecCommand(init1, execCommand{
Delay: 5,
ExitCode: 0,
}),
},
{
Name: restartableInit2,
Image: busyboxImage,
Command: ExecCommand(restartableInit2, execCommand{
Delay: 300,
ExitCode: 0,
}),
RestartPolicy: &containerRestartPolicyAlways,
},
{
Name: init3,
Image: busyboxImage,
Command: ExecCommand(init3, execCommand{
Delay: 5,
ExitCode: 0,
}),
},
},
Containers: []v1.Container{
{
Name: regular1,
Image: busyboxImage,
Command: ExecCommand(regular1, execCommand{
Delay: 300,
ExitCode: 0,
}),
},
},
},
}
preparePod(pod)
client := e2epod.NewPodClient(f)
pod = client.Create(ctx, pod)
ginkgo.By("Waiting for the pod to be initialized and run")
err := e2epod.WaitForPodRunningInNamespace(ctx, f.ClientSet, pod)
framework.ExpectNoError(err)
ginkgo.By("Getting the current pod sandbox ID")
rs, _, err := getCRIClient()
framework.ExpectNoError(err)
sandboxes, err := rs.ListPodSandbox(ctx, &runtimeapi.PodSandboxFilter{
LabelSelector: podLabels,
})
framework.ExpectNoError(err)
gomega.Expect(sandboxes).To(gomega.HaveLen(1))
podSandboxID := sandboxes[0].Id
ginkgo.By("Stopping the kubelet")
restartKubelet := stopKubelet()
gomega.Eventually(ctx, func() bool {
return kubeletHealthCheck(kubeletHealthCheckURL)
}, f.Timeouts.PodStart, f.Timeouts.Poll).Should(gomega.BeFalse())
ginkgo.By("Stopping the pod sandbox to simulate the node reboot")
err = rs.StopPodSandbox(ctx, podSandboxID)
framework.ExpectNoError(err)
ginkgo.By("Restarting the kubelet")
restartKubelet()
gomega.Eventually(ctx, func() bool {
return kubeletHealthCheck(kubeletHealthCheckURL)
}, f.Timeouts.PodStart, f.Timeouts.Poll).Should(gomega.BeTrue())
ginkgo.By("Waiting for the pod to be re-initialized and run")
err = e2epod.WaitForPodCondition(ctx, f.ClientSet, pod.Namespace, pod.Name, "re-initialized", f.Timeouts.PodStart, func(pod *v1.Pod) (bool, error) {
if pod.Status.ContainerStatuses[0].RestartCount < 1 {
return false, nil
}
if pod.Status.Phase != v1.PodRunning {
return false, nil
}
return true, nil
})
framework.ExpectNoError(err)
ginkgo.By("Parsing results")
pod, err = client.Get(ctx, pod.Name, metav1.GetOptions{})
framework.ExpectNoError(err)
results := parseOutput(context.TODO(), f, pod)
ginkgo.By("Analyzing results")
init1Started, err := results.FindIndex(init1, "Started", 0)
framework.ExpectNoError(err)
restartableInit2Started, err := results.FindIndex(restartableInit2, "Started", 0)
framework.ExpectNoError(err)
init3Started, err := results.FindIndex(init3, "Started", 0)
framework.ExpectNoError(err)
regular1Started, err := results.FindIndex(regular1, "Started", 0)
framework.ExpectNoError(err)
init1Restarted, err := results.FindIndex(init1, "Started", init1Started+1)
framework.ExpectNoError(err)
restartableInit2Restarted, err := results.FindIndex(restartableInit2, "Started", restartableInit2Started+1)
framework.ExpectNoError(err)
init3Restarted, err := results.FindIndex(init3, "Started", init3Started+1)
framework.ExpectNoError(err)
regular1Restarted, err := results.FindIndex(regular1, "Started", regular1Started+1)
framework.ExpectNoError(err)
framework.ExpectNoError(init1Started.IsBefore(restartableInit2Started))
framework.ExpectNoError(restartableInit2Started.IsBefore(init3Started))
framework.ExpectNoError(init3Started.IsBefore(regular1Started))
framework.ExpectNoError(init1Restarted.IsBefore(restartableInit2Restarted))
framework.ExpectNoError(restartableInit2Restarted.IsBefore(init3Restarted))
framework.ExpectNoError(init3Restarted.IsBefore(regular1Restarted))
})
})