Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
K
k3s
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Jacklull
k3s
Commits
5161d169
Commit
5161d169
authored
Dec 07, 2015
by
Jay Vyas
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Parameterization for downscale, upscale, and tolerance with backsolving
unit test for visible testing of tolerance algorithm expectations. Logs for dScale, uScale, tol creation.
parent
dc4bc2a5
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
103 additions
and
23 deletions
+103
-23
controllermanager.go
cmd/kube-controller-manager/app/controllermanager.go
+5
-1
horizontal.go
pkg/controller/podautoscaler/horizontal.go
+29
-19
horizontal_test.go
pkg/controller/podautoscaler/horizontal_test.go
+69
-3
No files found.
cmd/kube-controller-manager/app/controllermanager.go
View file @
5161d169
...
@@ -362,7 +362,11 @@ func (s *CMServer) Run(_ []string) error {
...
@@ -362,7 +362,11 @@ func (s *CMServer) Run(_ []string) error {
metrics
.
DefaultHeapsterService
,
metrics
.
DefaultHeapsterService
,
metrics
.
DefaultHeapsterPort
,
metrics
.
DefaultHeapsterPort
,
)
)
podautoscaler
.
NewHorizontalController
(
hpaClient
,
metricsClient
)
.
// TODO parameterize tolerance/downscale/upscale options.
tolerance
:=
1.0
downScale
:=
time
.
Duration
(
5
)
*
time
.
Second
upScale
:=
time
.
Duration
(
3
)
*
time
.
Second
podautoscaler
.
NewHorizontalController
(
kubeClient
,
metricsClient
,
tolerance
,
downScale
,
upScale
)
.
Run
(
s
.
HorizontalPodAutoscalerSyncPeriod
)
Run
(
s
.
HorizontalPodAutoscalerSyncPeriod
)
}
}
...
...
pkg/controller/podautoscaler/horizontal.go
View file @
5161d169
...
@@ -31,30 +31,40 @@ import (
...
@@ -31,30 +31,40 @@ import (
"k8s.io/kubernetes/pkg/util"
"k8s.io/kubernetes/pkg/util"
)
)
const
(
// Usage shoud exceed the tolerance before we start downscale or upscale the pods.
// TODO: make it a flag or HPA spec element.
tolerance
=
0.1
)
type
HorizontalController
struct
{
type
HorizontalController
struct
{
client
client
.
Interface
client
client
.
Interface
metricsClient
metrics
.
MetricsClient
metricsClient
metrics
.
MetricsClient
eventRecorder
record
.
EventRecorder
eventRecorder
record
.
EventRecorder
tolerance
float64
downscaleForbiddenWindow
time
.
Duration
upscaleForbiddenWindow
time
.
Duration
}
}
var
downscaleForbiddenWindow
=
5
*
time
.
Minute
func
NewHorizontalController
(
client
client
.
Interface
,
metricsClient
metrics
.
MetricsClient
,
tol
float64
,
dScale
,
uScale
time
.
Duration
)
*
HorizontalController
{
var
upscaleForbiddenWindow
=
3
*
time
.
Minute
func
NewHorizontalController
(
client
client
.
Interface
,
metricsClient
metrics
.
MetricsClient
)
*
HorizontalController
{
broadcaster
:=
record
.
NewBroadcaster
()
broadcaster
:=
record
.
NewBroadcaster
()
broadcaster
.
StartRecordingToSink
(
client
.
Events
(
""
))
broadcaster
.
StartRecordingToSink
(
client
.
Events
(
""
))
recorder
:=
broadcaster
.
NewRecorder
(
api
.
EventSource
{
Component
:
"horizontal-pod-autoscaler"
})
recorder
:=
broadcaster
.
NewRecorder
(
api
.
EventSource
{
Component
:
"horizontal-pod-autoscaler"
})
if
tol
<
0
||
tol
>
1
{
glog
.
Warningf
(
"Invalid tolerance provided %v using default."
,
tol
)
tol
=
.1
}
if
uScale
==
0
*
time
.
Second
{
glog
.
Warningf
(
"Invalid upscale value provided, %v using default."
,
uScale
)
uScale
=
3
*
time
.
Minute
}
if
dScale
==
0
*
time
.
Second
{
glog
.
Warningf
(
"Invalid downscale value provided, %v using default."
,
dScale
)
dScale
=
5
*
time
.
Minute
}
glog
.
V
(
2
)
.
Infof
(
"Created Horizontal Controller with downscale %v, upscale %v, and tolerance %v"
,
tol
,
uScale
,
dScale
)
return
&
HorizontalController
{
return
&
HorizontalController
{
client
:
client
,
client
:
client
,
metricsClient
:
metricsClient
,
metricsClient
:
metricsClient
,
eventRecorder
:
recorder
,
eventRecorder
:
recorder
,
tolerance
:
tol
,
downscaleForbiddenWindow
:
dScale
,
upscaleForbiddenWindow
:
uScale
,
}
}
}
}
...
@@ -83,7 +93,7 @@ func (a *HorizontalController) computeReplicasForCPUUtilization(hpa extensions.H
...
@@ -83,7 +93,7 @@ func (a *HorizontalController) computeReplicasForCPUUtilization(hpa extensions.H
}
}
usageRatio
:=
float64
(
*
currentUtilization
)
/
float64
(
hpa
.
Spec
.
CPUUtilization
.
TargetPercentage
)
usageRatio
:=
float64
(
*
currentUtilization
)
/
float64
(
hpa
.
Spec
.
CPUUtilization
.
TargetPercentage
)
if
math
.
Abs
(
1.0
-
usageRatio
)
>
tolerance
{
if
math
.
Abs
(
1.0
-
usageRatio
)
>
a
.
tolerance
{
return
int
(
math
.
Ceil
(
usageRatio
*
float64
(
currentReplicas
))),
currentUtilization
,
timestamp
,
nil
return
int
(
math
.
Ceil
(
usageRatio
*
float64
(
currentReplicas
))),
currentUtilization
,
timestamp
,
nil
}
else
{
}
else
{
return
currentReplicas
,
currentUtilization
,
timestamp
,
nil
return
currentReplicas
,
currentUtilization
,
timestamp
,
nil
...
@@ -125,7 +135,7 @@ func (a *HorizontalController) reconcileAutoscaler(hpa extensions.HorizontalPodA
...
@@ -125,7 +135,7 @@ func (a *HorizontalController) reconcileAutoscaler(hpa extensions.HorizontalPodA
// and there was no rescaling in the last downscaleForbiddenWindow.
// and there was no rescaling in the last downscaleForbiddenWindow.
if
desiredReplicas
<
currentReplicas
&&
if
desiredReplicas
<
currentReplicas
&&
(
hpa
.
Status
.
LastScaleTime
==
nil
||
(
hpa
.
Status
.
LastScaleTime
==
nil
||
hpa
.
Status
.
LastScaleTime
.
Add
(
downscaleForbiddenWindow
)
.
Before
(
timestamp
))
{
hpa
.
Status
.
LastScaleTime
.
Add
(
a
.
downscaleForbiddenWindow
)
.
Before
(
timestamp
))
{
rescale
=
true
rescale
=
true
}
}
...
@@ -133,7 +143,7 @@ func (a *HorizontalController) reconcileAutoscaler(hpa extensions.HorizontalPodA
...
@@ -133,7 +143,7 @@ func (a *HorizontalController) reconcileAutoscaler(hpa extensions.HorizontalPodA
// and there was no rescaling in the last upscaleForbiddenWindow.
// and there was no rescaling in the last upscaleForbiddenWindow.
if
desiredReplicas
>
currentReplicas
&&
if
desiredReplicas
>
currentReplicas
&&
(
hpa
.
Status
.
LastScaleTime
==
nil
||
(
hpa
.
Status
.
LastScaleTime
==
nil
||
hpa
.
Status
.
LastScaleTime
.
Add
(
upscaleForbiddenWindow
)
.
Before
(
timestamp
))
{
hpa
.
Status
.
LastScaleTime
.
Add
(
a
.
upscaleForbiddenWindow
)
.
Before
(
timestamp
))
{
rescale
=
true
rescale
=
true
}
}
}
}
...
...
pkg/controller/podautoscaler/horizontal_test.go
View file @
5161d169
...
@@ -20,6 +20,7 @@ import (
...
@@ -20,6 +20,7 @@ import (
"encoding/json"
"encoding/json"
"fmt"
"fmt"
"io"
"io"
"math"
"testing"
"testing"
"time"
"time"
...
@@ -32,9 +33,14 @@ import (
...
@@ -32,9 +33,14 @@ import (
"k8s.io/kubernetes/pkg/controller/podautoscaler/metrics"
"k8s.io/kubernetes/pkg/controller/podautoscaler/metrics"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/runtime"
glog
"github.com/golang/glog"
"github.com/stretchr/testify/assert"
heapster
"k8s.io/heapster/api/v1/types"
heapster
"k8s.io/heapster/api/v1/types"
)
"github.com/stretchr/testify/assert"
// unit tests need tolerance awareness to calibrate.
const
(
tolerance
=
.1
)
)
func
(
w
fakeResponseWrapper
)
DoRaw
()
([]
byte
,
error
)
{
func
(
w
fakeResponseWrapper
)
DoRaw
()
([]
byte
,
error
)
{
...
@@ -206,7 +212,7 @@ func (tc *testCase) verifyResults(t *testing.T) {
...
@@ -206,7 +212,7 @@ func (tc *testCase) verifyResults(t *testing.T) {
func
(
tc
*
testCase
)
runTest
(
t
*
testing
.
T
)
{
func
(
tc
*
testCase
)
runTest
(
t
*
testing
.
T
)
{
testClient
:=
tc
.
prepareTestClient
(
t
)
testClient
:=
tc
.
prepareTestClient
(
t
)
metricsClient
:=
metrics
.
NewHeapsterMetricsClient
(
testClient
,
metrics
.
DefaultHeapsterNamespace
,
metrics
.
DefaultHeapsterScheme
,
metrics
.
DefaultHeapsterService
,
metrics
.
DefaultHeapsterPort
)
metricsClient
:=
metrics
.
NewHeapsterMetricsClient
(
testClient
,
metrics
.
DefaultHeapsterNamespace
,
metrics
.
DefaultHeapsterScheme
,
metrics
.
DefaultHeapsterService
,
metrics
.
DefaultHeapsterPort
)
hpaController
:=
NewHorizontalController
(
testClient
,
metricsClient
)
hpaController
:=
NewHorizontalController
(
testClient
,
metricsClient
,
tolerance
,
time
.
Second
,
time
.
Second
)
err
:=
hpaController
.
reconcileAutoscalers
()
err
:=
hpaController
.
reconcileAutoscalers
()
assert
.
Equal
(
t
,
nil
,
err
)
assert
.
Equal
(
t
,
nil
,
err
)
if
tc
.
verifyEvents
{
if
tc
.
verifyEvents
{
...
@@ -360,4 +366,64 @@ func TestEventNotCreated(t *testing.T) {
...
@@ -360,4 +366,64 @@ func TestEventNotCreated(t *testing.T) {
tc
.
runTest
(
t
)
tc
.
runTest
(
t
)
}
}
// TODO: add more tests
// TestComputedToleranceAlgImplementation is a regression test which
// back-calculates a minimal percentage for downscaling based on a small percentage
// increase in pod utilization which is calibrated against the tolerance value.
func
TestComputedToleranceAlgImplementation
(
t
*
testing
.
T
)
{
startPods
:=
10
// 150 mCPU per pod.
totalUsedCPUOfAllPods
:=
uint64
(
startPods
*
150
)
// Each pod starts out asking for 2X what is really needed.
// This means we will have a 50% ratio of used/requested
totalRequestedCPUOfAllPods
:=
2
*
totalUsedCPUOfAllPods
requestedToUsed
:=
float64
(
totalRequestedCPUOfAllPods
/
totalUsedCPUOfAllPods
)
// Spread the amount we ask over 10 pods. We can add some jitter later in reportedLevels.
perPodRequested
:=
int
(
totalRequestedCPUOfAllPods
)
/
startPods
// Force a minimal scaling event by satisfying (tolerance < 1 - resourcesUsedRatio).
target
:=
math
.
Abs
(
1
/
(
requestedToUsed
*
(
1
-
tolerance
)))
+
.01
finalCpuPercentTarget
:=
int
(
target
*
100
)
resourcesUsedRatio
:=
float64
(
totalUsedCPUOfAllPods
)
/
float64
(
float64
(
totalRequestedCPUOfAllPods
)
*
target
)
// the autoscaler will compare this vs. tolearnce. Lets calculate the usageRatio, which will be
// compared w tolerance.
usageRatioToleranceValue
:=
float64
(
1
-
resourcesUsedRatio
)
// i.e. .60 * 20 -> scaled down expectation.
finalPods
:=
math
.
Ceil
(
resourcesUsedRatio
*
float64
(
startPods
))
glog
.
Infof
(
"To breach tolerance %f we will create a utilization ratio difference of %f"
,
tolerance
,
usageRatioToleranceValue
)
tc
:=
testCase
{
minReplicas
:
0
,
maxReplicas
:
1000
,
initialReplicas
:
startPods
,
desiredReplicas
:
int
(
finalPods
),
CPUTarget
:
finalCpuPercentTarget
,
reportedLevels
:
[]
uint64
{
totalUsedCPUOfAllPods
/
10
,
totalUsedCPUOfAllPods
/
10
,
totalUsedCPUOfAllPods
/
10
,
totalUsedCPUOfAllPods
/
10
,
totalUsedCPUOfAllPods
/
10
,
totalUsedCPUOfAllPods
/
10
,
totalUsedCPUOfAllPods
/
10
,
totalUsedCPUOfAllPods
/
10
,
totalUsedCPUOfAllPods
/
10
,
totalUsedCPUOfAllPods
/
10
,
},
reportedCPURequests
:
[]
resource
.
Quantity
{
resource
.
MustParse
(
fmt
.
Sprint
(
perPodRequested
+
100
)
+
"m"
),
resource
.
MustParse
(
fmt
.
Sprint
(
perPodRequested
-
100
)
+
"m"
),
resource
.
MustParse
(
fmt
.
Sprint
(
perPodRequested
+
10
)
+
"m"
),
resource
.
MustParse
(
fmt
.
Sprint
(
perPodRequested
-
10
)
+
"m"
),
resource
.
MustParse
(
fmt
.
Sprint
(
perPodRequested
+
2
)
+
"m"
),
resource
.
MustParse
(
fmt
.
Sprint
(
perPodRequested
-
2
)
+
"m"
),
resource
.
MustParse
(
fmt
.
Sprint
(
perPodRequested
+
1
)
+
"m"
),
resource
.
MustParse
(
fmt
.
Sprint
(
perPodRequested
-
1
)
+
"m"
),
resource
.
MustParse
(
fmt
.
Sprint
(
perPodRequested
)
+
"m"
),
resource
.
MustParse
(
fmt
.
Sprint
(
perPodRequested
)
+
"m"
),
},
}
tc
.
runTest
(
t
)
}
// TODO: add more tests, e.g., enforcement of upscal/downscale window.
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment