Skip to content

Commit d7abedd

Browse files
committed
add qos controller
1 parent 18758f5 commit d7abedd

File tree

10 files changed

+1838
-0
lines changed

10 files changed

+1838
-0
lines changed
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
Copyright 2017 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package qoscontroller
18+
19+
// Disk I/O Controller
20+
type DiskIOController struct {
21+
}
22+
23+
// initialize of QosController is implemented by DiskIOController and does all the initialization works
24+
func (dc *DiskIOController) initialize(qosResourceStatus *QosResourceStatus) error {
25+
return nil
26+
}
27+
28+
//process of QosController is implemented by DiskIOController and does all what a disk I/O controller has to do
29+
func (dc *DiskIOController) process(qosResourceStatus *QosResourceStatus) error {
30+
return nil
31+
}

pkg/kubelet/qoscontroller/doc.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
Copyright 2017 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
// package qoscontroller guarantees the resource availability for the primary application. It constantly watches the node
18+
// and application status. In case of performance drop, correction actions like freeze or kill are triggered
19+
// to ensure primary applications' stability.
20+
package qoscontroller
Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
/*
2+
Copyright 2017 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package qoscontroller
18+
19+
import (
20+
"github.com/golang/glog"
21+
"k8s.io/api/core/v1"
22+
"math"
23+
"sort"
24+
)
25+
26+
// Memory Controller
27+
type MemController struct {
28+
}
29+
30+
type By func(p1, p2 *v1.Pod) bool
31+
32+
func (by By) Sort(pods []*v1.Pod) {
33+
ps := &podSorter{
34+
pods: pods,
35+
by: by,
36+
}
37+
sort.Sort(ps)
38+
}
39+
40+
type podSorter struct {
41+
pods []*v1.Pod
42+
by func(p1, p2 *v1.Pod) bool
43+
}
44+
45+
func (s *podSorter) Len() int {
46+
return len(s.pods)
47+
}
48+
49+
func (s *podSorter) Swap(i, j int) {
50+
s.pods[i], s.pods[j] = s.pods[j], s.pods[i]
51+
}
52+
53+
func (s *podSorter) Less(i, j int) bool {
54+
return s.by(s.pods[i], s.pods[j])
55+
}
56+
57+
//Function to sort secondary/best effort pod list based on memory Usage
58+
func sortPodListMemoryUsage(qosResourceStatus *QosResourceStatus, pods []*v1.Pod) {
59+
By(func(p1, p2 *v1.Pod) bool {
60+
p1ID := p1.UID
61+
_, ok1 := qosResourceStatus.podResourceSummary[p1ID]
62+
p2ID := p2.UID
63+
_, ok2 := qosResourceStatus.podResourceSummary[p2ID]
64+
if !ok1 || !ok2 {
65+
glog.Errorf("Cannot obtain pod IDs during pod sorting")
66+
return false
67+
}
68+
p1MemoryUsage := qosResourceStatus.podResourceSummary[p1ID].memoryResourceUsage.currentUsage
69+
p2MemoryUsage := qosResourceStatus.podResourceSummary[p2ID].memoryResourceUsage.currentUsage
70+
return p1MemoryUsage > p2MemoryUsage
71+
}).Sort(pods)
72+
return
73+
}
74+
75+
// initialize of QosController is implemented by MemController and does all the initialization works
76+
func (mc *MemController) initialize(qosResourceStatus *QosResourceStatus) *QosResourceStatus {
77+
return qosResourceStatus
78+
}
79+
80+
// process of QosController is implemented by MemController and does all what a memory controller has to do
81+
func (mc *MemController) process(qosResourceStatus *QosResourceStatus) *QosResourceStatus {
82+
83+
var podRequestedMemory uint64
84+
var podMemoryThreshold float64
85+
86+
sortedSecondaryList := false
87+
secondaryPods := qosResourceStatus.secondaryPodList
88+
//Check the memory usage for each primary pod
89+
for _, pod := range qosResourceStatus.primaryPodList {
90+
podID := (*pod).UID
91+
_, ok := qosResourceStatus.podResourceSummary[podID]
92+
if !ok {
93+
continue
94+
}
95+
podRequestedMemory = 0
96+
97+
//Calculate the pod requested memory using the requested memory for each container
98+
for _, container := range pod.Spec.Containers {
99+
podRequestedMemory += uint64(container.Resources.Requests.Memory().Value())
100+
}
101+
102+
//Calculate the pod memory threshold based on the configured threshold rate
103+
thresholdRate := 1 - qosResourceStatus.QosConfig.MemoryConfig.PodMemoryThresholdRate
104+
podMemoryThreshold = float64(podRequestedMemory) * thresholdRate
105+
106+
//Get the pod ID and use it to obtain the acquired memory statistics for last N samples
107+
podMemoryUsage := qosResourceStatus.podResourceSummary[podID].memoryResourceUsage.currentUsage
108+
podMemoryUsageSamples := qosResourceStatus.podResourceSummary[podID].memoryResourceUsage.samples
109+
monitoringInterval := float64(qosResourceStatus.QosConfig.MonitoringInterval)
110+
111+
//Calculate predicted memory usage
112+
predictedMemoryUsage := calculatePredictedUsage(podMemoryUsage, podMemoryUsageSamples, monitoringInterval)
113+
glog.Infof("pod=%v Current usage = %v predicted usage =%v threshold=%v", pod.Name, podMemoryUsage, predictedMemoryUsage, podMemoryThreshold)
114+
//Check if the current pod memory usage is greater than the pod memory threshold
115+
if float64(podMemoryUsage) > podMemoryThreshold && predictedMemoryUsage > podMemoryThreshold {
116+
117+
if sortedSecondaryList == false {
118+
//Sort the secondary pod list based on decreasing usage of memory
119+
sortPodListMemoryUsage(qosResourceStatus, secondaryPods)
120+
sortedSecondaryList = true
121+
}
122+
//Update the action list with the secondary pods to be killed
123+
secondaryPods = updateActionList(podMemoryUsage,
124+
podRequestedMemory,
125+
&(qosResourceStatus.ActionList),
126+
secondaryPods,
127+
qosResourceStatus.QosConfig.MemoryConfig.ProcessMultiPod)
128+
}
129+
}
130+
return qosResourceStatus
131+
}
132+
133+
//Function to calculate the predicted usage for the pod based on the rate of increase/decrease using N samples
134+
func calculatePredictedUsage(currentUsage uint64, usageSamples []uint64, monitoringInterval float64) (predictedUsage float64) {
135+
136+
var aggregateRate, averageRate, actualSamples, actualUsage float64
137+
var currentSample, previousSample float64
138+
139+
currentSample = float64(currentUsage)
140+
aggregateRate = 0
141+
actualSamples = 0
142+
actualUsage = currentSample
143+
144+
//Calculate the rate of increase for last N samples
145+
for i := len(usageSamples); i > 1; i-- {
146+
previousSample = float64(usageSamples[i-2])
147+
actualUsage += previousSample
148+
if currentSample > previousSample {
149+
if previousSample > 0 {
150+
aggregateRate += (currentSample - previousSample) / previousSample
151+
actualSamples++
152+
}
153+
} else {
154+
if currentSample > 0 {
155+
aggregateRate -= (previousSample - currentSample) / currentSample
156+
actualSamples++
157+
}
158+
}
159+
currentSample = previousSample
160+
}
161+
162+
//Calculate the average Usage and rate of increase
163+
averageRate = aggregateRate / actualSamples
164+
actualUsage = actualUsage / actualSamples
165+
//Calculate the predicted usage in the next monitoring interval based on the increase/decrease rate
166+
rate := math.Pow((1 + averageRate), monitoringInterval)
167+
predictedUsage = actualUsage * rate
168+
169+
return predictedUsage
170+
171+
}
172+
173+
func updateActionList(podMemoryUsage uint64,
174+
primaryPodRequestedMemory uint64,
175+
actionList *[]*Action,
176+
secondaryPods []*v1.Pod,
177+
processMultiPod bool) []*v1.Pod {
178+
179+
var secondaryPodRequestedMemory uint64
180+
var revocableMemory uint64
181+
revocableMemory = 0
182+
183+
i := 0
184+
//Check the secondary pods to be killed
185+
for _, pod := range secondaryPods {
186+
//Populate the action list with the secondary pod to be killed
187+
var action Action
188+
action.Target = pod
189+
action.ActionType = KillPod
190+
*actionList = append(*actionList, &action)
191+
i++
192+
glog.Infof("Secondary Pod %v added to action list", pod.Name)
193+
//Check if the option of killing multiple secondary pods is enabled
194+
if processMultiPod == false {
195+
return secondaryPods[i:]
196+
}
197+
198+
//Consider the memory that will be released by killing the secondary pod
199+
secondaryPodRequestedMemory = 0
200+
for _, container := range pod.Spec.Containers {
201+
secondaryPodRequestedMemory += uint64(container.Resources.Requests.Memory().Value())
202+
}
203+
204+
//Calculate the total revocable memory corresponding to secondary pods
205+
revocableMemory += secondaryPodRequestedMemory
206+
207+
//Check if the memory revoked meets the primary pods requested memory
208+
//Some threshold of requested memory like 95% can be considered if required
209+
if (podMemoryUsage + revocableMemory) > primaryPodRequestedMemory {
210+
return secondaryPods[i:]
211+
}
212+
}
213+
return secondaryPods[i:]
214+
}

0 commit comments

Comments
 (0)