Fix volume limit for EBS on m5 and c5 instances

pull/8/head
Hemant Kumar 2018-07-19 15:05:58 -04:00
parent b9bd7f2ffb
commit 45b8107378
3 changed files with 65 additions and 16 deletions

View File

@ -29,6 +29,7 @@ import (
utilfeature "k8s.io/apiserver/pkg/util/feature" utilfeature "k8s.io/apiserver/pkg/util/feature"
utilfeaturetesting "k8s.io/apiserver/pkg/util/feature/testing" utilfeaturetesting "k8s.io/apiserver/pkg/util/feature/testing"
"k8s.io/kubernetes/pkg/features" "k8s.io/kubernetes/pkg/features"
kubeletapis "k8s.io/kubernetes/pkg/kubelet/apis"
"k8s.io/kubernetes/pkg/scheduler/algorithm" "k8s.io/kubernetes/pkg/scheduler/algorithm"
schedulercache "k8s.io/kubernetes/pkg/scheduler/cache" schedulercache "k8s.io/kubernetes/pkg/scheduler/cache"
volumeutil "k8s.io/kubernetes/pkg/volume/util" volumeutil "k8s.io/kubernetes/pkg/volume/util"
@ -826,6 +827,23 @@ func TestVolumeCountConflicts(t *testing.T) {
} }
} }
func TestMaxVolumeFunc(t *testing.T) {
node := &v1.Node{
ObjectMeta: metav1.ObjectMeta{
Name: "node-for-m5-instance",
Labels: map[string]string{
kubeletapis.LabelInstanceType: "m5.large",
},
},
}
os.Unsetenv(KubeMaxPDVols)
maxVolumeFunc := getMaxVolumeFunc(EBSVolumeFilterType)
maxVolume := maxVolumeFunc(node)
if maxVolume != DefaultMaxEBSM5VolumeLimit {
t.Errorf("Expected max volume to be %d got %d", DefaultMaxEBSM5VolumeLimit, maxVolume)
}
}
func getNodeWithPodAndVolumeLimits(pods []*v1.Pod, limit int64, filter string) *schedulercache.NodeInfo { func getNodeWithPodAndVolumeLimits(pods []*v1.Pod, limit int64, filter string) *schedulercache.NodeInfo {
nodeInfo := schedulercache.NewNodeInfo(pods...) nodeInfo := schedulercache.NewNodeInfo(pods...)
node := &v1.Node{ node := &v1.Node{

View File

@ -20,6 +20,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"os" "os"
"regexp"
"strconv" "strconv"
"sync" "sync"
@ -97,6 +98,8 @@ const (
// Amazon recommends no more than 40; the system root volume uses at least one. // Amazon recommends no more than 40; the system root volume uses at least one.
// See http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/volume_limits.html#linux-specific-volume-limits // See http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/volume_limits.html#linux-specific-volume-limits
DefaultMaxEBSVolumes = 39 DefaultMaxEBSVolumes = 39
// DefaultMaxEBSM5VolumeLimit is default EBS volume limit on m5 and c5 instances
DefaultMaxEBSM5VolumeLimit = 25
// DefaultMaxGCEPDVolumes defines the maximum number of PD Volumes for GCE // DefaultMaxGCEPDVolumes defines the maximum number of PD Volumes for GCE
// GCE instances can have up to 16 PD volumes attached. // GCE instances can have up to 16 PD volumes attached.
DefaultMaxGCEPDVolumes = 16 DefaultMaxGCEPDVolumes = 16
@ -291,7 +294,7 @@ func NoDiskConflict(pod *v1.Pod, meta algorithm.PredicateMetadata, nodeInfo *sch
type MaxPDVolumeCountChecker struct { type MaxPDVolumeCountChecker struct {
filter VolumeFilter filter VolumeFilter
volumeLimitKey v1.ResourceName volumeLimitKey v1.ResourceName
maxVolumes int maxVolumeFunc func(node *v1.Node) int
pvInfo PersistentVolumeInfo pvInfo PersistentVolumeInfo
pvcInfo PersistentVolumeClaimInfo pvcInfo PersistentVolumeClaimInfo
@ -317,7 +320,6 @@ type VolumeFilter struct {
func NewMaxPDVolumeCountPredicate( func NewMaxPDVolumeCountPredicate(
filterName string, pvInfo PersistentVolumeInfo, pvcInfo PersistentVolumeClaimInfo) algorithm.FitPredicate { filterName string, pvInfo PersistentVolumeInfo, pvcInfo PersistentVolumeClaimInfo) algorithm.FitPredicate {
var filter VolumeFilter var filter VolumeFilter
var maxVolumes int
var volumeLimitKey v1.ResourceName var volumeLimitKey v1.ResourceName
switch filterName { switch filterName {
@ -325,15 +327,12 @@ func NewMaxPDVolumeCountPredicate(
case EBSVolumeFilterType: case EBSVolumeFilterType:
filter = EBSVolumeFilter filter = EBSVolumeFilter
volumeLimitKey = v1.ResourceName(volumeutil.EBSVolumeLimitKey) volumeLimitKey = v1.ResourceName(volumeutil.EBSVolumeLimitKey)
maxVolumes = getMaxVols(DefaultMaxEBSVolumes)
case GCEPDVolumeFilterType: case GCEPDVolumeFilterType:
filter = GCEPDVolumeFilter filter = GCEPDVolumeFilter
volumeLimitKey = v1.ResourceName(volumeutil.GCEVolumeLimitKey) volumeLimitKey = v1.ResourceName(volumeutil.GCEVolumeLimitKey)
maxVolumes = getMaxVols(DefaultMaxGCEPDVolumes)
case AzureDiskVolumeFilterType: case AzureDiskVolumeFilterType:
filter = AzureDiskVolumeFilter filter = AzureDiskVolumeFilter
volumeLimitKey = v1.ResourceName(volumeutil.AzureVolumeLimitKey) volumeLimitKey = v1.ResourceName(volumeutil.AzureVolumeLimitKey)
maxVolumes = getMaxVols(DefaultMaxAzureDiskVolumes)
default: default:
glog.Fatalf("Wrong filterName, Only Support %v %v %v ", EBSVolumeFilterType, glog.Fatalf("Wrong filterName, Only Support %v %v %v ", EBSVolumeFilterType,
GCEPDVolumeFilterType, AzureDiskVolumeFilterType) GCEPDVolumeFilterType, AzureDiskVolumeFilterType)
@ -343,7 +342,7 @@ func NewMaxPDVolumeCountPredicate(
c := &MaxPDVolumeCountChecker{ c := &MaxPDVolumeCountChecker{
filter: filter, filter: filter,
volumeLimitKey: volumeLimitKey, volumeLimitKey: volumeLimitKey,
maxVolumes: maxVolumes, maxVolumeFunc: getMaxVolumeFunc(filterName),
pvInfo: pvInfo, pvInfo: pvInfo,
pvcInfo: pvcInfo, pvcInfo: pvcInfo,
randomVolumeIDPrefix: rand.String(32), randomVolumeIDPrefix: rand.String(32),
@ -352,19 +351,52 @@ func NewMaxPDVolumeCountPredicate(
return c.predicate return c.predicate
} }
// getMaxVols checks the max PD volumes environment variable, otherwise returning a default value func getMaxVolumeFunc(filterName string) func(node *v1.Node) int {
func getMaxVols(defaultVal int) int { return func(node *v1.Node) int {
maxVolumesFromEnv := getMaxVolLimitFromEnv()
if maxVolumesFromEnv > 0 {
return maxVolumesFromEnv
}
var nodeInstanceType string
for k, v := range node.ObjectMeta.Labels {
if k == kubeletapis.LabelInstanceType {
nodeInstanceType = v
}
}
switch filterName {
case EBSVolumeFilterType:
return getMaxEBSVolume(nodeInstanceType)
case GCEPDVolumeFilterType:
return DefaultMaxGCEPDVolumes
case AzureDiskVolumeFilterType:
return DefaultMaxAzureDiskVolumes
default:
return -1
}
}
}
func getMaxEBSVolume(nodeInstanceType string) int {
if ok, _ := regexp.MatchString("^[cm]5.*", nodeInstanceType); ok {
return DefaultMaxEBSM5VolumeLimit
}
return DefaultMaxEBSVolumes
}
// getMaxVolLimitFromEnv checks the max PD volumes environment variable, otherwise returning a default value
func getMaxVolLimitFromEnv() int {
if rawMaxVols := os.Getenv(KubeMaxPDVols); rawMaxVols != "" { if rawMaxVols := os.Getenv(KubeMaxPDVols); rawMaxVols != "" {
if parsedMaxVols, err := strconv.Atoi(rawMaxVols); err != nil { if parsedMaxVols, err := strconv.Atoi(rawMaxVols); err != nil {
glog.Errorf("Unable to parse maximum PD volumes value, using default of %v: %v", defaultVal, err) glog.Errorf("Unable to parse maximum PD volumes value, using default: %v", err)
} else if parsedMaxVols <= 0 { } else if parsedMaxVols <= 0 {
glog.Errorf("Maximum PD volumes must be a positive value, using default of %v", defaultVal) glog.Errorf("Maximum PD volumes must be a positive value, using default ")
} else { } else {
return parsedMaxVols return parsedMaxVols
} }
} }
return defaultVal return -1
} }
func (c *MaxPDVolumeCountChecker) filterVolumes(volumes []v1.Volume, namespace string, filteredVolumes map[string]bool) error { func (c *MaxPDVolumeCountChecker) filterVolumes(volumes []v1.Volume, namespace string, filteredVolumes map[string]bool) error {
@ -454,7 +486,7 @@ func (c *MaxPDVolumeCountChecker) predicate(pod *v1.Pod, meta algorithm.Predicat
} }
numNewVolumes := len(newVolumes) numNewVolumes := len(newVolumes)
maxAttachLimit := c.maxVolumes maxAttachLimit := c.maxVolumeFunc(nodeInfo.Node())
if utilfeature.DefaultFeatureGate.Enabled(features.AttachVolumeLimit) { if utilfeature.DefaultFeatureGate.Enabled(features.AttachVolumeLimit) {
volumeLimits := nodeInfo.VolumeLimits() volumeLimits := nodeInfo.VolumeLimits()

View File

@ -3913,7 +3913,6 @@ func TestVolumeZonePredicateWithVolumeBinding(t *testing.T) {
func TestGetMaxVols(t *testing.T) { func TestGetMaxVols(t *testing.T) {
previousValue := os.Getenv(KubeMaxPDVols) previousValue := os.Getenv(KubeMaxPDVols)
defaultValue := 39
tests := []struct { tests := []struct {
rawMaxVols string rawMaxVols string
@ -3922,12 +3921,12 @@ func TestGetMaxVols(t *testing.T) {
}{ }{
{ {
rawMaxVols: "invalid", rawMaxVols: "invalid",
expected: defaultValue, expected: -1,
name: "Unable to parse maximum PD volumes value, using default value", name: "Unable to parse maximum PD volumes value, using default value",
}, },
{ {
rawMaxVols: "-2", rawMaxVols: "-2",
expected: defaultValue, expected: -1,
name: "Maximum PD volumes must be a positive value, using default value", name: "Maximum PD volumes must be a positive value, using default value",
}, },
{ {
@ -3940,7 +3939,7 @@ func TestGetMaxVols(t *testing.T) {
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.name, func(t *testing.T) {
os.Setenv(KubeMaxPDVols, test.rawMaxVols) os.Setenv(KubeMaxPDVols, test.rawMaxVols)
result := getMaxVols(defaultValue) result := getMaxVolLimitFromEnv()
if result != test.expected { if result != test.expected {
t.Errorf("expected %v got %v", test.expected, result) t.Errorf("expected %v got %v", test.expected, result)
} }