Skip to content

Commit dad8fe7

Browse files
authored
Merge pull request kubernetes#124220 from HirazawaUi/fix-pod-restarted
[kubelet]: fixed container restart due to pod spec field changes
2 parents 5cdab88 + 3ec13c5 commit dad8fe7

File tree

10 files changed

+50
-88
lines changed

10 files changed

+50
-88
lines changed

pkg/kubelet/container/container_hash_test.go

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,7 @@ var (
6868
}
6969
`
7070

71-
sampleV115HashValue = uint64(0x311670a)
72-
sampleV116HashValue = sampleV115HashValue
71+
sampleV131HashValue = uint64(0x8e45cbd0)
7372
)
7473

7574
func TestConsistentHashContainer(t *testing.T) {
@@ -79,11 +78,7 @@ func TestConsistentHashContainer(t *testing.T) {
7978
}
8079

8180
currentHash := HashContainer(container)
82-
if currentHash != sampleV116HashValue {
83-
t.Errorf("mismatched hash value with v1.16")
84-
}
85-
86-
if currentHash != sampleV115HashValue {
87-
t.Errorf("mismatched hash value with v1.15")
81+
if currentHash != sampleV131HashValue {
82+
t.Errorf("mismatched hash value with v1.31")
8883
}
8984
}

pkg/kubelet/container/helpers.go

Lines changed: 18 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -110,28 +110,20 @@ func ShouldContainerBeRestarted(container *v1.Container, pod *v1.Pod, podStatus
110110
// Note: remember to update hashValues in container_hash_test.go as well.
111111
func HashContainer(container *v1.Container) uint64 {
112112
hash := fnv.New32a()
113-
// Omit nil or empty field when calculating hash value
114-
// Please see https://github.com/kubernetes/kubernetes/issues/53644
115-
containerJSON, _ := json.Marshal(container)
113+
containerJSON, _ := json.Marshal(pickFieldsToHash(container))
116114
hashutil.DeepHashObject(hash, containerJSON)
117115
return uint64(hash.Sum32())
118116
}
119117

120-
// HashContainerWithoutResources returns the hash of the container with Resources field zero'd out.
121-
func HashContainerWithoutResources(container *v1.Container) uint64 {
122-
// InPlacePodVerticalScaling enables mutable Resources field.
123-
// Changes to this field may not require container restart depending on policy.
124-
// Compute hash over fields besides the Resources field
125-
// NOTE: This is needed during alpha and beta so that containers using Resources but
126-
// not subject to In-place resize are not unexpectedly restarted when
127-
// InPlacePodVerticalScaling feature-gate is toggled.
128-
//TODO(vinaykul,InPlacePodVerticalScaling): Remove this in GA+1 and make HashContainerWithoutResources to become Hash.
129-
hashWithoutResources := fnv.New32a()
130-
containerCopy := container.DeepCopy()
131-
containerCopy.Resources = v1.ResourceRequirements{}
132-
containerJSON, _ := json.Marshal(containerCopy)
133-
hashutil.DeepHashObject(hashWithoutResources, containerJSON)
134-
return uint64(hashWithoutResources.Sum32())
118+
// pickFieldsToHash pick fields that will affect the running status of the container for hash,
119+
// currently this field range only contains `image` and `name`.
120+
// Note: this list must be updated if ever kubelet wants to allow mutations to other fields.
121+
func pickFieldsToHash(container *v1.Container) map[string]string {
122+
retval := map[string]string{
123+
"name": container.Name,
124+
"image": container.Image,
125+
}
126+
return retval
135127
}
136128

137129
// envVarsToMap constructs a map of environment name to value from a slice
@@ -269,15 +261,14 @@ func ConvertPodStatusToRunningPod(runtimeName string, podStatus *PodStatus) Pod
269261
continue
270262
}
271263
container := &Container{
272-
ID: containerStatus.ID,
273-
Name: containerStatus.Name,
274-
Image: containerStatus.Image,
275-
ImageID: containerStatus.ImageID,
276-
ImageRef: containerStatus.ImageRef,
277-
ImageRuntimeHandler: containerStatus.ImageRuntimeHandler,
278-
Hash: containerStatus.Hash,
279-
HashWithoutResources: containerStatus.HashWithoutResources,
280-
State: containerStatus.State,
264+
ID: containerStatus.ID,
265+
Name: containerStatus.Name,
266+
Image: containerStatus.Image,
267+
ImageID: containerStatus.ImageID,
268+
ImageRef: containerStatus.ImageRef,
269+
ImageRuntimeHandler: containerStatus.ImageRuntimeHandler,
270+
Hash: containerStatus.Hash,
271+
State: containerStatus.State,
281272
}
282273
runningPod.Containers = append(runningPod.Containers, container)
283274
}

pkg/kubelet/container/helpers_test.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -657,7 +657,7 @@ func TestHashContainer(t *testing.T) {
657657
"echo abc",
658658
},
659659
containerPort: int32(8001),
660-
expectedHash: uint64(0x3c42280f),
660+
expectedHash: uint64(0x8e45cbd0),
661661
},
662662
}
663663

@@ -938,7 +938,7 @@ func TestHashContainerWithoutResources(t *testing.T) {
938938
},
939939
ResizePolicy: []v1.ContainerResizePolicy{cpuPolicyRestartRequired, memPolicyRestartNotRequired},
940940
},
941-
0x5f62cb4c,
941+
0x11a6d6d6,
942942
},
943943
{
944944
"Burstable pod with memory policy restart required",
@@ -951,7 +951,7 @@ func TestHashContainerWithoutResources(t *testing.T) {
951951
},
952952
ResizePolicy: []v1.ContainerResizePolicy{cpuPolicyRestartNotRequired, memPolicyRestartRequired},
953953
},
954-
0xcdab9e00,
954+
0x11a6d6d6,
955955
},
956956
{
957957
"Guaranteed pod with CPU policy restart required",
@@ -964,7 +964,7 @@ func TestHashContainerWithoutResources(t *testing.T) {
964964
},
965965
ResizePolicy: []v1.ContainerResizePolicy{cpuPolicyRestartRequired, memPolicyRestartNotRequired},
966966
},
967-
0x5f62cb4c,
967+
0x11a6d6d6,
968968
},
969969
{
970970
"Guaranteed pod with memory policy restart required",
@@ -977,13 +977,13 @@ func TestHashContainerWithoutResources(t *testing.T) {
977977
},
978978
ResizePolicy: []v1.ContainerResizePolicy{cpuPolicyRestartNotRequired, memPolicyRestartRequired},
979979
},
980-
0xcdab9e00,
980+
0x11a6d6d6,
981981
},
982982
}
983983
for _, tc := range tests {
984984
t.Run(tc.name, func(t *testing.T) {
985985
containerCopy := tc.container.DeepCopy()
986-
hash := HashContainerWithoutResources(tc.container)
986+
hash := HashContainer(tc.container)
987987
assert.Equal(t, tc.expectedHash, hash, "[%s]", tc.name)
988988
assert.Equal(t, containerCopy, tc.container, "[%s]", tc.name)
989989
})

pkg/kubelet/container/runtime.go

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -295,11 +295,6 @@ type Container struct {
295295
// Hash of the container, used for comparison. Optional for containers
296296
// not managed by kubelet.
297297
Hash uint64
298-
// Hash of the container over fields with Resources field zero'd out.
299-
// NOTE: This is needed during alpha and beta so that containers using Resources are
300-
// not unexpectedly restarted when InPlacePodVerticalScaling feature-gate is toggled.
301-
//TODO(vinaykul,InPlacePodVerticalScaling): Remove this in GA+1 and make HashWithoutResources to become Hash.
302-
HashWithoutResources uint64
303298
// State is the state of the container.
304299
State State
305300
}
@@ -365,8 +360,6 @@ type Status struct {
365360
ImageRuntimeHandler string
366361
// Hash of the container, used for comparison.
367362
Hash uint64
368-
// Hash of the container over fields with Resources field zero'd out.
369-
HashWithoutResources uint64
370363
// Number of times that the container has been restarted.
371364
RestartCount int
372365
// A string explains why container is in such a status.

pkg/kubelet/kuberuntime/helpers.go

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -102,15 +102,14 @@ func (m *kubeGenericRuntimeManager) toKubeContainer(c *runtimeapi.Container) (*k
102102

103103
annotatedInfo := getContainerInfoFromAnnotations(c.Annotations)
104104
return &kubecontainer.Container{
105-
ID: kubecontainer.ContainerID{Type: m.runtimeName, ID: c.Id},
106-
Name: c.GetMetadata().GetName(),
107-
ImageID: imageID,
108-
ImageRef: c.ImageRef,
109-
ImageRuntimeHandler: c.Image.RuntimeHandler,
110-
Image: c.Image.Image,
111-
Hash: annotatedInfo.Hash,
112-
HashWithoutResources: annotatedInfo.HashWithoutResources,
113-
State: toKubeContainerState(c.State),
105+
ID: kubecontainer.ContainerID{Type: m.runtimeName, ID: c.Id},
106+
Name: c.GetMetadata().GetName(),
107+
ImageID: imageID,
108+
ImageRef: c.ImageRef,
109+
ImageRuntimeHandler: c.Image.RuntimeHandler,
110+
Image: c.Image.Image,
111+
Hash: annotatedInfo.Hash,
112+
State: toKubeContainerState(c.State),
114113
}, nil
115114
}
116115

pkg/kubelet/kuberuntime/kuberuntime_container.go

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -622,17 +622,16 @@ func toKubeContainerStatus(status *runtimeapi.ContainerStatus, runtimeName strin
622622
Type: runtimeName,
623623
ID: status.Id,
624624
},
625-
Name: labeledInfo.ContainerName,
626-
Image: status.Image.Image,
627-
ImageID: imageID,
628-
ImageRef: status.ImageRef,
629-
ImageRuntimeHandler: status.Image.RuntimeHandler,
630-
Hash: annotatedInfo.Hash,
631-
HashWithoutResources: annotatedInfo.HashWithoutResources,
632-
RestartCount: annotatedInfo.RestartCount,
633-
State: toKubeContainerState(status.State),
634-
CreatedAt: time.Unix(0, status.CreatedAt),
635-
Resources: cStatusResources,
625+
Name: labeledInfo.ContainerName,
626+
Image: status.Image.Image,
627+
ImageID: imageID,
628+
ImageRef: status.ImageRef,
629+
ImageRuntimeHandler: status.Image.RuntimeHandler,
630+
Hash: annotatedInfo.Hash,
631+
RestartCount: annotatedInfo.RestartCount,
632+
State: toKubeContainerState(status.State),
633+
CreatedAt: time.Unix(0, status.CreatedAt),
634+
Resources: cStatusResources,
636635
}
637636

638637
if status.State != runtimeapi.ContainerState_CONTAINER_CREATED {

pkg/kubelet/kuberuntime/kuberuntime_manager.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,10 @@ func (p podActions) String() string {
518518
p.KillPod, p.CreateSandbox, p.UpdatePodResources, p.Attempt, p.InitContainersToStart, p.ContainersToStart, p.EphemeralContainersToStart, p.ContainersToUpdate, p.ContainersToKill)
519519
}
520520

521+
// containerChanged will determine whether the container has changed based on the fields that will affect the running of the container.
522+
// Currently, there are only `image` and `name` fields.
523+
// we don't need to consider the pod UID here, because we find the containerStatus through the pod UID.
524+
// If the pod UID changes, we will not be able to find the containerStatus to compare against.
521525
func containerChanged(container *v1.Container, containerStatus *kubecontainer.Status) (uint64, uint64, bool) {
522526
expectedHash := kubecontainer.HashContainer(container)
523527
return expectedHash, containerStatus.Hash, containerStatus.Hash != expectedHash
@@ -981,10 +985,7 @@ func (m *kubeGenericRuntimeManager) computePodActions(ctx context.Context, pod *
981985
var message string
982986
var reason containerKillReason
983987
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+
if _, _, changed := containerChanged(&container, containerStatus); changed {
988989
message = fmt.Sprintf("Container %s definition changed", container.Name)
989990
// Restart regardless of the restart policy because the container
990991
// spec changed.

pkg/kubelet/kuberuntime/kuberuntime_manager_test.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2436,7 +2436,6 @@ func TestComputePodActionsForPodResize(t *testing.T) {
24362436
// compute hash
24372437
if kcs := kps.FindContainerStatusByName(pod.Spec.Containers[idx].Name); kcs != nil {
24382438
kcs.Hash = kubecontainer.HashContainer(&pod.Spec.Containers[idx])
2439-
kcs.HashWithoutResources = kubecontainer.HashContainerWithoutResources(&pod.Spec.Containers[idx])
24402439
}
24412440
}
24422441
makeAndSetFakePod(t, m, fakeRuntime, pod)
@@ -2452,7 +2451,6 @@ func TestComputePodActionsForPodResize(t *testing.T) {
24522451
for idx := range pod.Spec.Containers {
24532452
if kcs := kps.FindContainerStatusByName(pod.Spec.Containers[idx].Name); kcs != nil {
24542453
kcs.Hash = kubecontainer.HashContainer(&pod.Spec.Containers[idx])
2455-
kcs.HashWithoutResources = kubecontainer.HashContainerWithoutResources(&pod.Spec.Containers[idx])
24562454
}
24572455
}
24582456
if test.mutatePodFn != nil {

pkg/kubelet/kuberuntime/labels.go

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,8 @@ import (
2222

2323
v1 "k8s.io/api/core/v1"
2424
kubetypes "k8s.io/apimachinery/pkg/types"
25-
utilfeature "k8s.io/apiserver/pkg/util/feature"
2625
"k8s.io/klog/v2"
2726
"k8s.io/kubelet/pkg/types"
28-
"k8s.io/kubernetes/pkg/features"
2927
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
3028
)
3129

@@ -35,7 +33,6 @@ const (
3533
podTerminationGracePeriodLabel = "io.kubernetes.pod.terminationGracePeriod"
3634

3735
containerHashLabel = "io.kubernetes.container.hash"
38-
containerHashWithoutResourcesLabel = "io.kubernetes.container.hashWithoutResources"
3936
containerRestartCountLabel = "io.kubernetes.container.restartCount"
4037
containerTerminationMessagePathLabel = "io.kubernetes.container.terminationMessagePath"
4138
containerTerminationMessagePolicyLabel = "io.kubernetes.container.terminationMessagePolicy"
@@ -65,7 +62,6 @@ type labeledContainerInfo struct {
6562

6663
type annotatedContainerInfo struct {
6764
Hash uint64
68-
HashWithoutResources uint64
6965
RestartCount int
7066
PodDeletionGracePeriod *int64
7167
PodTerminationGracePeriod *int64
@@ -117,9 +113,6 @@ func newContainerAnnotations(container *v1.Container, pod *v1.Pod, restartCount
117113
}
118114

119115
annotations[containerHashLabel] = strconv.FormatUint(kubecontainer.HashContainer(container), 16)
120-
if utilfeature.DefaultFeatureGate.Enabled(features.InPlacePodVerticalScaling) {
121-
annotations[containerHashWithoutResourcesLabel] = strconv.FormatUint(kubecontainer.HashContainerWithoutResources(container), 16)
122-
}
123116
annotations[containerRestartCountLabel] = strconv.Itoa(restartCount)
124117
annotations[containerTerminationMessagePathLabel] = container.TerminationMessagePath
125118
annotations[containerTerminationMessagePolicyLabel] = string(container.TerminationMessagePolicy)
@@ -200,11 +193,6 @@ func getContainerInfoFromAnnotations(annotations map[string]string) *annotatedCo
200193
if containerInfo.Hash, err = getUint64ValueFromLabel(annotations, containerHashLabel); err != nil {
201194
klog.ErrorS(err, "Unable to get label value from annotations", "label", containerHashLabel, "annotations", annotations)
202195
}
203-
if utilfeature.DefaultFeatureGate.Enabled(features.InPlacePodVerticalScaling) {
204-
if containerInfo.HashWithoutResources, err = getUint64ValueFromLabel(annotations, containerHashWithoutResourcesLabel); err != nil {
205-
klog.ErrorS(err, "Unable to get label value from annotations", "label", containerHashWithoutResourcesLabel, "annotations", annotations)
206-
}
207-
}
208196
if containerInfo.RestartCount, err = getIntValueFromLabel(annotations, containerRestartCountLabel); err != nil {
209197
klog.ErrorS(err, "Unable to get label value from annotations", "label", containerRestartCountLabel, "annotations", annotations)
210198
}

pkg/kubelet/kuberuntime/labels_test.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,6 @@ func TestContainerAnnotations(t *testing.T) {
155155
PodDeletionGracePeriod: pod.DeletionGracePeriodSeconds,
156156
PodTerminationGracePeriod: pod.Spec.TerminationGracePeriodSeconds,
157157
Hash: kubecontainer.HashContainer(container),
158-
HashWithoutResources: kubecontainer.HashContainerWithoutResources(container),
159158
RestartCount: restartCount,
160159
TerminationMessagePath: container.TerminationMessagePath,
161160
PreStopHandler: container.Lifecycle.PreStop,
@@ -182,7 +181,6 @@ func TestContainerAnnotations(t *testing.T) {
182181
expected.PreStopHandler = nil
183182
// Because container is changed, the Hash should be updated
184183
expected.Hash = kubecontainer.HashContainer(container)
185-
expected.HashWithoutResources = kubecontainer.HashContainerWithoutResources(container)
186184
annotations = newContainerAnnotations(container, pod, restartCount, opts)
187185
containerInfo = getContainerInfoFromAnnotations(annotations)
188186
if !reflect.DeepEqual(containerInfo, expected) {

0 commit comments

Comments
 (0)