Skip to content

Commit 7f5809c

Browse files
committed
support region spread constraint
Signed-off-by: huone1 <[email protected]>
1 parent 8125a01 commit 7f5809c

File tree

6 files changed

+453
-20
lines changed

6 files changed

+453
-20
lines changed

pkg/scheduler/core/spreadconstraint/group_clusters.go

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
package spreadconstraint
22

33
import (
4-
"sort"
5-
64
clusterv1alpha1 "github.com/karmada-io/karmada/pkg/apis/cluster/v1alpha1"
75
policyv1alpha1 "github.com/karmada-io/karmada/pkg/apis/policy/v1alpha1"
86
workv1alpha2 "github.com/karmada-io/karmada/pkg/apis/work/v1alpha2"
@@ -22,6 +20,7 @@ type GroupClustersInfo struct {
2220
// ProviderInfo indicate the provider information
2321
type ProviderInfo struct {
2422
Name string
23+
Score int64 // the highest score in all clusters of the provider
2524
AvailableReplicas int64
2625

2726
// Regions under this provider
@@ -35,6 +34,7 @@ type ProviderInfo struct {
3534
// RegionInfo indicate the region information
3635
type RegionInfo struct {
3736
Name string
37+
Score int64 // the highest score in all clusters of the region
3838
AvailableReplicas int64
3939

4040
// Zones under this provider
@@ -46,6 +46,7 @@ type RegionInfo struct {
4646
// ZoneInfo indicate the zone information
4747
type ZoneInfo struct {
4848
Name string
49+
Score int64 // the highest score in all clusters of the zone
4950
AvailableReplicas int64
5051

5152
// Clusters under this zone, sorted by cluster.Score descending.
@@ -146,6 +147,11 @@ func (info *GroupClustersInfo) generateZoneInfo(spreadConstraints []policyv1alph
146147
zoneInfo.AvailableReplicas += clusterInfo.AvailableReplicas
147148
info.Zones[zone] = zoneInfo
148149
}
150+
151+
for zone, zoneInfo := range info.Zones {
152+
zoneInfo.Score = zoneInfo.Clusters[0].Score
153+
info.Zones[zone] = zoneInfo
154+
}
149155
}
150156

151157
func (info *GroupClustersInfo) generateRegionInfo(spreadConstraints []policyv1alpha1.SpreadConstraint) {
@@ -175,6 +181,11 @@ func (info *GroupClustersInfo) generateRegionInfo(spreadConstraints []policyv1al
175181
regionInfo.AvailableReplicas += clusterInfo.AvailableReplicas
176182
info.Regions[region] = regionInfo
177183
}
184+
185+
for region, regionInfo := range info.Regions {
186+
regionInfo.Score = regionInfo.Clusters[0].Score
187+
info.Regions[region] = regionInfo
188+
}
178189
}
179190

180191
func (info *GroupClustersInfo) generateProviderInfo(spreadConstraints []policyv1alpha1.SpreadConstraint) {
@@ -210,6 +221,11 @@ func (info *GroupClustersInfo) generateProviderInfo(spreadConstraints []policyv1
210221
providerInfo.AvailableReplicas += clusterInfo.AvailableReplicas
211222
info.Providers[provider] = providerInfo
212223
}
224+
225+
for provider, providerInfo := range info.Providers {
226+
providerInfo.Score = providerInfo.Clusters[0].Score
227+
info.Providers[provider] = providerInfo
228+
}
213229
}
214230

215231
func isTopologyIgnored(placement *policyv1alpha1.Placement) bool {
@@ -229,13 +245,3 @@ func isTopologyIgnored(placement *policyv1alpha1.Placement) bool {
229245

230246
return false
231247
}
232-
233-
func sortClusters(infos []ClusterDetailInfo) {
234-
sort.Slice(infos, func(i, j int) bool {
235-
if infos[i].Score != infos[j].Score {
236-
return infos[i].Score > infos[j].Score
237-
}
238-
239-
return infos[i].Name < infos[j].Name
240-
})
241-
}

pkg/scheduler/core/spreadconstraint/select_clusters.go

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,18 @@ func SelectBestClusters(placement *policyv1alpha1.Placement, groupClustersInfo *
2929

3030
func selectBestClustersBySpreadConstraints(spreadConstraints []policyv1alpha1.SpreadConstraint,
3131
groupClustersInfo *GroupClustersInfo, needReplicas int32) ([]*clusterv1alpha1.Cluster, error) {
32-
if len(spreadConstraints) > 1 {
33-
return nil, fmt.Errorf("just support single spread constraint")
32+
spreadConstraintMap := make(map[policyv1alpha1.SpreadFieldValue]policyv1alpha1.SpreadConstraint)
33+
for i := range spreadConstraints {
34+
spreadConstraintMap[spreadConstraints[i].SpreadByField] = spreadConstraints[i]
3435
}
3536

36-
spreadConstraint := spreadConstraints[0]
37-
if spreadConstraint.SpreadByField == policyv1alpha1.SpreadByFieldCluster {
38-
return selectBestClustersByCluster(spreadConstraint, groupClustersInfo, needReplicas)
37+
if _, exist := spreadConstraintMap[policyv1alpha1.SpreadByFieldRegion]; exist {
38+
return selectBestClustersByRegion(spreadConstraintMap, groupClustersInfo)
39+
} else if _, exist := spreadConstraintMap[policyv1alpha1.SpreadByFieldCluster]; exist {
40+
return selectBestClustersByCluster(spreadConstraintMap[policyv1alpha1.SpreadByFieldCluster], groupClustersInfo, needReplicas)
41+
} else {
42+
return nil, fmt.Errorf("just support cluster and region spread constraint")
3943
}
40-
41-
return nil, fmt.Errorf("just support cluster spread constraint")
4244
}
4345

4446
func shouldIgnoreSpreadConstraint(placement *policyv1alpha1.Placement) bool {

pkg/scheduler/core/spreadconstraint/select_clusters_by_cluster.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ func selectClustersByAvailableResource(candidateClusters []ClusterDetailInfo, ne
6666
updateClusterID--
6767
}
6868

69-
if updateClusterID < 0 {
69+
if !checkAvailableResource(retClusters, needReplicas) {
7070
return nil
7171
}
7272
return retClusters
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
package spreadconstraint
2+
3+
import (
4+
"fmt"
5+
"sort"
6+
7+
clusterv1alpha1 "github.com/karmada-io/karmada/pkg/apis/cluster/v1alpha1"
8+
policyv1alpha1 "github.com/karmada-io/karmada/pkg/apis/policy/v1alpha1"
9+
)
10+
11+
func selectBestClustersByRegion(spreadConstraintMap map[policyv1alpha1.SpreadFieldValue]policyv1alpha1.SpreadConstraint,
12+
groupClustersInfo *GroupClustersInfo) ([]*clusterv1alpha1.Cluster, error) {
13+
var clusters []*clusterv1alpha1.Cluster
14+
var candidateClusters []ClusterDetailInfo
15+
16+
if len(groupClustersInfo.Regions) < spreadConstraintMap[policyv1alpha1.SpreadByFieldRegion].MinGroups {
17+
return nil, fmt.Errorf("the number of feasible region is less than spreadConstraint.MinGroups")
18+
}
19+
20+
// firstly, select regions which have enough clusters to satisfy the cluster and region propagation constraints
21+
regions := selectRegions(groupClustersInfo.Regions, spreadConstraintMap[policyv1alpha1.SpreadByFieldRegion], spreadConstraintMap[policyv1alpha1.SpreadByFieldCluster])
22+
if len(regions) == 0 {
23+
return nil, fmt.Errorf("the number of clusters is less than the cluster spreadConstraint.MinGroups")
24+
}
25+
26+
// secondly, select the clusters with the highest score in per region,
27+
for i := range regions {
28+
clusters = append(clusters, regions[i].Clusters[0].Cluster)
29+
candidateClusters = append(candidateClusters, regions[i].Clusters[1:]...)
30+
}
31+
32+
needCnt := len(candidateClusters) + len(clusters)
33+
if needCnt > spreadConstraintMap[policyv1alpha1.SpreadByFieldCluster].MaxGroups {
34+
needCnt = spreadConstraintMap[policyv1alpha1.SpreadByFieldCluster].MaxGroups
35+
}
36+
37+
// thirdly, select the remaining Clusters based cluster.Score
38+
sortClusters(candidateClusters)
39+
restCnt := needCnt - len(clusters)
40+
for i := 0; i < restCnt; i++ {
41+
clusters = append(clusters, candidateClusters[i].Cluster)
42+
}
43+
44+
return clusters, nil
45+
}
46+
47+
func selectRegions(RegionInfos map[string]RegionInfo, regionConstraint, clusterConstraint policyv1alpha1.SpreadConstraint) []RegionInfo {
48+
var regions []RegionInfo
49+
for i := range RegionInfos {
50+
regions = append(regions, RegionInfos[i])
51+
}
52+
53+
sort.Slice(regions, func(i, j int) bool {
54+
if regions[i].Score != regions[j].Score {
55+
return regions[i].Score > regions[j].Score
56+
}
57+
58+
return regions[i].Name < regions[j].Name
59+
})
60+
61+
retRegions := regions[:regionConstraint.MinGroups]
62+
candidateRegions := regions[regionConstraint.MinGroups:]
63+
var replaceID = len(retRegions) - 1
64+
for !checkClusterTotalForRegion(retRegions, clusterConstraint) && replaceID >= 0 {
65+
regionID := getRegionWithMaxClusters(candidateRegions, len(retRegions[replaceID].Clusters))
66+
if regionID == InvalidRegionID {
67+
replaceID--
68+
continue
69+
}
70+
71+
retRegions[replaceID], candidateRegions[regionID] = candidateRegions[regionID], retRegions[replaceID]
72+
replaceID--
73+
}
74+
75+
if !checkClusterTotalForRegion(retRegions, clusterConstraint) {
76+
return nil
77+
}
78+
79+
return retRegions
80+
}
81+
82+
func checkClusterTotalForRegion(regions []RegionInfo, clusterConstraint policyv1alpha1.SpreadConstraint) bool {
83+
var sum int
84+
for i := range regions {
85+
sum += len(regions[i].Clusters)
86+
}
87+
88+
return sum >= clusterConstraint.MinGroups
89+
}
90+
91+
func getRegionWithMaxClusters(candidateRegions []RegionInfo, originClusters int) int {
92+
var maxClusters = originClusters
93+
var regionID = -1
94+
for i := range candidateRegions {
95+
if maxClusters < len(candidateRegions[i].Clusters) {
96+
regionID = i
97+
maxClusters = len(candidateRegions[i].Clusters)
98+
}
99+
}
100+
101+
return regionID
102+
}

0 commit comments

Comments
 (0)