mirror of
https://github.com/kubernetes/kubernetes.git
synced 2026-05-28 04:04:39 -04:00
Fix ImageLocality scoring for image volumes
Include image volumes in the image source count used by calculatePriority, so the raw image score and max threshold are based on the same image sources. Update ImageLocality tests to cover ImageVolume scoring against equivalent regular container images. Signed-off-by: Joshua Su <i@joshua.su>
This commit is contained in:
parent
63b36867dd
commit
b96072e372
2 changed files with 50 additions and 17 deletions
|
|
@ -74,8 +74,8 @@ func (pl *ImageLocality) Score(ctx context.Context, state fwk.CycleState, pod *v
|
|||
}
|
||||
totalNumNodes := len(nodeInfos)
|
||||
|
||||
imageScores := sumImageScores(nodeInfo, pod, totalNumNodes)
|
||||
score := calculatePriority(imageScores, len(pod.Spec.InitContainers)+len(pod.Spec.Containers))
|
||||
imageScores, imageCount := sumImageScores(nodeInfo, pod, totalNumNodes)
|
||||
score := calculatePriority(imageScores, imageCount)
|
||||
|
||||
return score, nil
|
||||
}
|
||||
|
|
@ -90,35 +90,41 @@ func New(_ context.Context, _ runtime.Object, h fwk.Handle) (fwk.Plugin, error)
|
|||
return &ImageLocality{handle: h}, nil
|
||||
}
|
||||
|
||||
// calculatePriority returns the priority of a node. Given the sumScores of requested images on the node, the node's
|
||||
// priority is obtained by scaling the maximum priority value with a ratio proportional to the sumScores.
|
||||
func calculatePriority(sumScores int64, numContainers int) int64 {
|
||||
maxThreshold := maxContainerThreshold * int64(numContainers)
|
||||
// calculatePriority returns the priority of a node. Given the sumScores of requested
|
||||
// images on the node and the Pod's image count, the node's priority is obtained by
|
||||
// scaling the maximum priority value with a ratio derived from the clamped sumScores.
|
||||
func calculatePriority(sumScores int64, imageCount int) int64 {
|
||||
maxThreshold := maxContainerThreshold * int64(imageCount)
|
||||
if sumScores < minThreshold {
|
||||
sumScores = minThreshold
|
||||
} else if sumScores > maxThreshold {
|
||||
sumScores = maxThreshold
|
||||
}
|
||||
|
||||
return fwk.MaxNodeScore * (sumScores - minThreshold) / (maxThreshold - minThreshold)
|
||||
return fwk.MaxScore * (sumScores - minThreshold) / (maxThreshold - minThreshold)
|
||||
}
|
||||
|
||||
// sumImageScores returns the total image score for all container images in the Pod spec,
|
||||
// including regular containers, init containers, and image volumes, that already exist on the node.
|
||||
// Each image receives a raw score of its size, scaled by scaledImageScore. The raw scores are later used to calculate
|
||||
// the final score.
|
||||
func sumImageScores(nodeInfo fwk.NodeInfo, pod *v1.Pod, totalNumNodes int) int64 {
|
||||
// sumImageScores returns the total image score and the number of image sources in the Pod spec,
|
||||
// including regular containers, init containers, and image volumes. Images that already exist on the node
|
||||
// receive a raw score of their size, scaled by scaledImageScore.
|
||||
// The raw score and image count are later used to calculate the final score.
|
||||
func sumImageScores(nodeInfo fwk.NodeInfo, pod *v1.Pod, totalNumNodes int) (int64, int) {
|
||||
var sum int64
|
||||
|
||||
for _, container := range pod.Spec.InitContainers {
|
||||
if state, ok := nodeInfo.GetImageStates()[normalizedImageName(container.Image)]; ok {
|
||||
sum += scaledImageScore(state, totalNumNodes)
|
||||
}
|
||||
}
|
||||
|
||||
for _, container := range pod.Spec.Containers {
|
||||
if state, ok := nodeInfo.GetImageStates()[normalizedImageName(container.Image)]; ok {
|
||||
sum += scaledImageScore(state, totalNumNodes)
|
||||
}
|
||||
}
|
||||
|
||||
imageCount := len(pod.Spec.InitContainers) + len(pod.Spec.Containers)
|
||||
|
||||
for _, volume := range pod.Spec.Volumes {
|
||||
if volume.Image == nil {
|
||||
continue
|
||||
|
|
@ -126,8 +132,9 @@ func sumImageScores(nodeInfo fwk.NodeInfo, pod *v1.Pod, totalNumNodes int) int64
|
|||
if state, ok := nodeInfo.GetImageStates()[normalizedImageName(volume.Image.Reference)]; ok {
|
||||
sum += scaledImageScore(state, totalNumNodes)
|
||||
}
|
||||
imageCount++
|
||||
}
|
||||
return sum
|
||||
return sum, imageCount
|
||||
}
|
||||
|
||||
// scaledImageScore returns an adaptively scaled score for the given state of an image.
|
||||
|
|
|
|||
|
|
@ -111,6 +111,17 @@ func TestImageLocalityPriority(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
test30300AsContainers := v1.PodSpec{
|
||||
Containers: []v1.Container{
|
||||
{
|
||||
Image: "gcr.io/30",
|
||||
},
|
||||
{
|
||||
Image: "gcr.io/300",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
test30Init300 := v1.PodSpec{
|
||||
Containers: []v1.Container{
|
||||
{
|
||||
|
|
@ -360,20 +371,35 @@ func TestImageLocalityPriority(t *testing.T) {
|
|||
name: "pod with multiple small images",
|
||||
},
|
||||
{
|
||||
// Pod: gcr.io/300 gcr.io/30
|
||||
// Pod: gcr.io/30 ImageVolume: gcr.io/300
|
||||
|
||||
// Node1
|
||||
// Image: gcr.io/300:latest 300MB
|
||||
// Score: 100 * (300M * 1/2 - 23M) / (1000M - 23M) = 12
|
||||
// Score: 100 * (300M * 1/2 - 23M) / (1000M * 2 - 23M) = 6
|
||||
|
||||
// Node2
|
||||
// Image: gcr.io/30:latest 30MB
|
||||
// Score: 100 * (30M - 23M) / (1000M - 23M) = 0
|
||||
// Score: 0 (30M * 1/2 < 23M, min-threshold)
|
||||
pod: &v1.Pod{Spec: testImageVolume},
|
||||
nodes: []*v1.Node{makeImageNode("node1", node300600900), makeImageNode("node2", node400030)},
|
||||
expectedList: []fwk.NodeScore{{Name: "node1", Score: 12}, {Name: "node2", Score: 0}},
|
||||
expectedList: []fwk.NodeScore{{Name: "node1", Score: 6}, {Name: "node2", Score: 0}},
|
||||
name: "pod with ImageVolume",
|
||||
},
|
||||
{
|
||||
// Pod: gcr.io/30 gcr.io/300
|
||||
|
||||
// Node1
|
||||
// Image: gcr.io/300:latest 300MB
|
||||
// Score: 100 * (300M * 1/2 - 23M) / (1000M * 2 - 23M) = 6
|
||||
|
||||
// Node2
|
||||
// Image: gcr.io/30:latest 30MB
|
||||
// Score: 0 (30M * 1/2 < 23M, min-threshold)
|
||||
pod: &v1.Pod{Spec: test30300AsContainers},
|
||||
nodes: []*v1.Node{makeImageNode("node1", node300600900), makeImageNode("node2", node400030)},
|
||||
expectedList: []fwk.NodeScore{{Name: "node1", Score: 6}, {Name: "node2", Score: 0}},
|
||||
name: "same images as ImageVolume pod but as regular container images",
|
||||
},
|
||||
{
|
||||
// Pod: gcr.io/30 InitContainers: gcr.io/300
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue