An E-PVM scheduler assigns each task to the candidate machine with the lowest marginal cost. It combines heterogeneous resource usage—primarily CPU and memory—into one cost, favoring balanced cluster load over simple rotation. section 10.8 section 10.8.1
A practical implementation separates scheduling into three responsibilities:
- Filter candidates whose available disk cannot hold the task’s Docker image. section 10.8.2
- Score every candidate by estimating the cost increase from adding the task, using CPU load, memory allocation, and task count. section 10.8.1 section 10.8.2
- Pick the node with the smallest score, then assign the task through the scheduler interface. section 10.8.2 section 1.5.3
Switching the manager to E-PVM
Register an "epvm" case that constructs an Epvm scheduler, then call manager.New(workers, "epvm") instead of selecting "roundrobin". The rest of the manager can use the shared scheduler interface. section 10.10
Listing 10.19 Signature of the E-PVM scheduler’s Score
const (
// LIEB square ice constant
// https://en.wikipedia.org/wiki/Lieb%27s_square_ice_constant
LIEB = 1.53960071783900203869
)
func (e *Epvm) Score(t task.Task, nodes []*node.Node) map[string]float64 {
nodeScores := make(map[string]float64)
maxJobs := 4.0
for _, node := range nodes {
cpuUsage := calculateCpuUsage(node)
cpuLoad := calculateLoad(cpuUsage, math.Pow(2, 0.8))
memoryAllocated := float64(node.Stats.MemUsedKb()) +
float64(node.MemoryAllocated)
memoryPercentAllocated := memoryAllocated / float64(node.Memory)
newMemPercent := (calculateLoad(memoryAllocated +
float64(t.Memory/1000), float64(node.Memory)))
memCost := math.Pow(LIEB, newMemPercent) + math.Pow(LIEB,
(float64(node.TaskCount+1))/maxJobs) -
math.Pow(LIEB, memoryPercentAllocated) -
math.Pow(LIEB, float64(node.TaskCount)/float64(maxJobs))
cpuCost := math.Pow(LIEB, cpuLoad) +
math.Pow(LIEB, (float64(node.TaskCount+1))/maxJobs) -
math.Pow(LIEB, cpuLoad) -
math.Pow(LIEB, float64(node.TaskCount)/float64(maxJobs))
nodeScores[node.Name] = memCost + cpuCost
nodeScores[node.Name] = marginalCost
}
}
return nodeScores
}
Inspect the Score signature and implementation skeleton, including node score storage, the maxJobs tuning value, CPU load, and memory-cost inputs. section 10.8.2
