Skip to content

Commit

Permalink
Merge branch 'kubernetes:master' into fix-deleteNodesFromCloudProvide…
Browse files Browse the repository at this point in the history
…r-panic
  • Loading branch information
daimaxiaxie authored Feb 6, 2024
2 parents f22c4b0 + 131d385 commit bb8d2fe
Show file tree
Hide file tree
Showing 28 changed files with 336 additions and 62 deletions.
4 changes: 2 additions & 2 deletions charts/cluster-autoscaler/Chart.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
apiVersion: v2
appVersion: 1.28.2
appVersion: 1.29.0
description: Scales Kubernetes worker nodes within autoscaling groups.
engine: gotpl
home: https://github.com/kubernetes/autoscaler
Expand All @@ -11,4 +11,4 @@ name: cluster-autoscaler
sources:
- https://github.com/kubernetes/autoscaler/tree/master/cluster-autoscaler
type: application
version: 9.34.1
version: 9.35.0
2 changes: 1 addition & 1 deletion charts/cluster-autoscaler/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,7 @@ vpa:
| image.pullPolicy | string | `"IfNotPresent"` | Image pull policy |
| image.pullSecrets | list | `[]` | Image pull secrets |
| image.repository | string | `"registry.k8s.io/autoscaling/cluster-autoscaler"` | Image repository |
| image.tag | string | `"v1.28.2"` | Image tag |
| image.tag | string | `"v1.29.0"` | Image tag |
| kubeTargetVersionOverride | string | `""` | Allow overriding the `.Capabilities.KubeVersion.GitVersion` check. Useful for `helm template` commands. |
| kwokConfigMapName | string | `"kwok-provider-config"` | configmap for configuring kwok provider |
| magnumCABundlePath | string | `"/etc/kubernetes/ca-bundle.crt"` | Path to the host's CA bundle, from `ca-file` in the cloud-config file. |
Expand Down
2 changes: 1 addition & 1 deletion charts/cluster-autoscaler/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ image:
# image.repository -- Image repository
repository: registry.k8s.io/autoscaling/cluster-autoscaler
# image.tag -- Image tag
tag: v1.28.2
tag: v1.29.0
# image.pullPolicy -- Image pull policy
pullPolicy: IfNotPresent
## Optionally specify an array of imagePullSecrets.
Expand Down
1 change: 1 addition & 0 deletions cluster-autoscaler/OWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ approvers:
reviewers:
- BigDarkClown
- feiskyer
- vadasambar
- x13n
emeritus_approvers:
- aleksandra-malinowska # 2022-09-30
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ rules:
resources: ["daemonsets", "replicasets", "statefulsets"]
verbs: ["watch", "list", "get"]
- apiGroups: ["storage.k8s.io"]
resources: ["storageclasses", "csinodes"]
resources: ["storageclasses", "csinodes", "csidrivers", "csistoragecapacities"]
verbs: ["watch", "list", "get"]
- apiGroups: [""]
resources: ["configmaps"]
Expand Down
21 changes: 18 additions & 3 deletions cluster-autoscaler/cloudprovider/oci/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,29 @@ We recommend setting up and configuring the Cluster Autoscaler to use
[Instance Principals](https://docs.oracle.com/en-us/iaas/Content/Identity/Tasks/callingservicesfrominstances.htm)
to authenticate to the OCI APIs.

The following policy provides the minimum privileges necessary for Cluster Autoscaler to run:
The following policy provides the privileges necessary for Cluster Autoscaler to run:

1: Create a compartment-level dynamic group containing the nodes (compute instances) in the cluster:

```
All {instance.compartment.id = 'ocid1.compartment.oc1..aaaaaaaa7ey4sg3a6b5wnv5hlkjlkjadslkfjalskfjalsadfadsf'}
```

Note: the matching rule in the dynamic group above includes all instances
in the specified compartment. If this is too broad for your requirements,
you can add more conditions for example

```
All {instance.compartment.id = '...', tag.MyTagNamespace.MyNodeRole = 'MyTagValue'}
```

here `MyTagValue` is the defined-tag assigned to all nodes where `cluster-autoscaler` pods will be scheduled
(for example, with `nodeSeletor`).
See [node-pool](https://docs.oracle.com/en-us/iaas/Content/ContEng/Tasks/contengtaggingclusterresources_tagging-oke-resources_node-tags.htm)
or [instance-pool](https://docs.oracle.com/en-us/iaas/Content/Compute/Tasks/creatinginstanceconfig.htm)
and also [managing dynamic groups](https://docs.oracle.com/en-us/iaas/Content/Identity/Tasks/managingdynamicgroups.htm).


2: Create a *tenancy-level* policy to allow nodes to manage node-pools and/or instance-pools:

```
Expand All @@ -51,7 +66,7 @@ Allow dynamic-group acme-oci-cluster-autoscaler-dyn-grp to inspect compartments

### If using Workload Identity

Note: This is available to use with OKE Node Pools or OCI Managed Instance Pools with OKE Enhanced Clusters only.
Note: This is available to use with OKE Node Pools or OCI Managed Instance Pools with OKE Enhanced Clusters only.

See the [documentation](https://docs.oracle.com/en-us/iaas/Content/ContEng/Tasks/contenggrantingworkloadaccesstoresources.htm) for more details

Expand Down Expand Up @@ -235,7 +250,7 @@ OCI config file based authentication deployment:
kubectl apply -f ./cloudprovider/oci/examples/oci-ip-cluster-autoscaler-w-config.yaml
```
OCI with node pool yamls:
OCI with node pool yamls:
```
# First substitute any values mentioned in the file and then apply
Expand Down
28 changes: 26 additions & 2 deletions cluster-autoscaler/core/scaledown/planner/planner.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (

apiv1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/autoscaler/cluster-autoscaler/cloudprovider"
"k8s.io/autoscaler/cluster-autoscaler/context"
"k8s.io/autoscaler/cluster-autoscaler/core/scaledown"
"k8s.io/autoscaler/cluster-autoscaler/core/scaledown/eligibility"
Expand Down Expand Up @@ -261,6 +262,7 @@ func (p *Planner) categorizeNodes(podDestinations map[string]bool, scaleDownCand
unremovableTimeout := p.latestUpdate.Add(p.context.AutoscalingOptions.UnremovableNodeRecheckTimeout)
unremovableCount := 0
var removableList []simulator.NodeToBeRemoved
atomicScaleDownNodesCount := 0
p.unremovableNodes.Update(p.context.ClusterSnapshot.NodeInfos(), p.latestUpdate)
currentlyUnneededNodeNames, utilizationMap, ineligible := p.eligibilityChecker.FilterOutUnremovable(p.context, scaleDownCandidates, p.latestUpdate, p.unremovableNodes)
for _, n := range ineligible {
Expand All @@ -274,8 +276,8 @@ func (p *Planner) categorizeNodes(podDestinations map[string]bool, scaleDownCand
klog.Warningf("%d out of %d nodes skipped in scale down simulation due to timeout.", len(currentlyUnneededNodeNames)-i, len(currentlyUnneededNodeNames))
break
}
if len(removableList) >= p.unneededNodesLimit() {
klog.V(4).Infof("%d out of %d nodes skipped in scale down simulation: there are already %d unneeded nodes so no point in looking for more.", len(currentlyUnneededNodeNames)-i, len(currentlyUnneededNodeNames), len(removableList))
if len(removableList)-atomicScaleDownNodesCount >= p.unneededNodesLimit() {
klog.V(4).Infof("%d out of %d nodes skipped in scale down simulation: there are already %d unneeded nodes so no point in looking for more. Total atomic scale down nodes: %d", len(currentlyUnneededNodeNames)-i, len(currentlyUnneededNodeNames), len(removableList), atomicScaleDownNodesCount)
break
}
removable, unremovable := p.rs.SimulateNodeRemoval(node, podDestinations, p.latestUpdate, p.context.RemainingPdbTracker)
Expand All @@ -287,6 +289,10 @@ func (p *Planner) categorizeNodes(podDestinations map[string]bool, scaleDownCand
delete(podDestinations, removable.Node.Name)
p.context.RemainingPdbTracker.RemovePods(removable.PodsToReschedule)
removableList = append(removableList, *removable)
if p.atomicScaleDownNode(removable) {
atomicScaleDownNodesCount++
klog.V(2).Infof("Considering node %s for atomic scale down. Total atomic scale down nodes count: %d", removable.Node.Name, atomicScaleDownNodesCount)
}
}
if unremovable != nil {
unremovableCount += 1
Expand All @@ -299,6 +305,24 @@ func (p *Planner) categorizeNodes(podDestinations map[string]bool, scaleDownCand
}
}

// atomicScaleDownNode checks if the removable node would be considered for atomic scale down.
func (p *Planner) atomicScaleDownNode(node *simulator.NodeToBeRemoved) bool {
nodeGroup, err := p.context.CloudProvider.NodeGroupForNode(node.Node)
if err != nil {
klog.Errorf("failed to get node info for %v: %s", node.Node.Name, err)
return false
}
autoscalingOptions, err := nodeGroup.GetOptions(p.context.NodeGroupDefaults)
if err != nil && err != cloudprovider.ErrNotImplemented {
klog.Errorf("Failed to get autoscaling options for node group %s: %v", nodeGroup.Id(), err)
return false
}
if autoscalingOptions != nil && autoscalingOptions.ZeroOrMaxNodeScaling {
return true
}
return false
}

// unneededNodesLimit returns the number of nodes after which calculating more
// unneeded nodes is a waste of time. The reasoning behind it is essentially as
// follows.
Expand Down
81 changes: 81 additions & 0 deletions cluster-autoscaler/core/scaledown/planner/planner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,10 @@ func TestUpdateClusterState(t *testing.T) {
assert.NoError(t, err)
registry := kube_util.NewListerRegistry(nil, nil, nil, nil, nil, nil, nil, rsLister, nil)
provider := testprovider.NewTestCloudProvider(nil, nil)
provider.AddNodeGroup("ng1", 0, 0, 0)
for _, node := range tc.nodes {
provider.AddNode("ng1", node)
}
context, err := NewScaleTestAutoscalingContext(config.AutoscalingOptions{
NodeGroupDefaults: config.NodeGroupAutoscalingOptions{
ScaleDownUnneededTime: 10 * time.Minute,
Expand Down Expand Up @@ -521,6 +525,7 @@ func TestUpdateClusterStatUnneededNodesLimit(t *testing.T) {
name string
previouslyUnneeded int
nodes int
opts *config.NodeGroupAutoscalingOptions
maxParallelism int
maxUnneededTime time.Duration
updateInterval time.Duration
Expand Down Expand Up @@ -589,6 +594,78 @@ func TestUpdateClusterStatUnneededNodesLimit(t *testing.T) {
updateInterval: 30 * time.Second,
wantUnneeded: 30,
},
{
name: "atomic sclale down - default settings",
previouslyUnneeded: 5,
nodes: 100,
maxParallelism: 10,
maxUnneededTime: 1 * time.Minute,
updateInterval: 10 * time.Second,
wantUnneeded: 100,
opts: &config.NodeGroupAutoscalingOptions{
ZeroOrMaxNodeScaling: true,
},
},
{
name: "atomic sclale down - quick loops",
previouslyUnneeded: 5,
nodes: 100,
maxParallelism: 10,
maxUnneededTime: 1 * time.Minute,
updateInterval: 1 * time.Second,
wantUnneeded: 100,
opts: &config.NodeGroupAutoscalingOptions{
ZeroOrMaxNodeScaling: true,
},
},
{
name: "atomic sclale down - slow loops",
previouslyUnneeded: 5,
nodes: 100,
maxParallelism: 10,
maxUnneededTime: 1 * time.Minute,
updateInterval: 30 * time.Second,
wantUnneeded: 100,
opts: &config.NodeGroupAutoscalingOptions{
ZeroOrMaxNodeScaling: true,
},
},
{
name: "atomic sclale down - too many unneeded",
previouslyUnneeded: 77,
nodes: 100,
maxParallelism: 10,
maxUnneededTime: 1 * time.Minute,
updateInterval: 30 * time.Second,
wantUnneeded: 100,
opts: &config.NodeGroupAutoscalingOptions{
ZeroOrMaxNodeScaling: true,
},
},
{
name: "atomic sclale down - no uneeded",
previouslyUnneeded: 0,
nodes: 100,
maxParallelism: 10,
maxUnneededTime: 1 * time.Minute,
updateInterval: 30 * time.Second,
wantUnneeded: 100,
opts: &config.NodeGroupAutoscalingOptions{
ZeroOrMaxNodeScaling: true,
},
},
{
name: "atomic sclale down - short uneeded time and short update interval",
previouslyUnneeded: 0,
nodes: 500,
maxParallelism: 1,
maxUnneededTime: 1 * time.Second,
updateInterval: 1 * time.Second,
wantUnneeded: 500,
opts: &config.NodeGroupAutoscalingOptions{
ZeroOrMaxNodeScaling: true,
},
},
}
for _, tc := range testCases {
tc := tc
Expand All @@ -603,6 +680,10 @@ func TestUpdateClusterStatUnneededNodesLimit(t *testing.T) {
previouslyUnneeded[i] = simulator.NodeToBeRemoved{Node: nodes[i]}
}
provider := testprovider.NewTestCloudProvider(nil, nil)
provider.AddNodeGroupWithCustomOptions("ng1", 0, 0, 0, tc.opts)
for _, node := range nodes {
provider.AddNode("ng1", node)
}
context, err := NewScaleTestAutoscalingContext(config.AutoscalingOptions{
NodeGroupDefaults: config.NodeGroupAutoscalingOptions{
ScaleDownUnneededTime: tc.maxUnneededTime,
Expand Down
2 changes: 1 addition & 1 deletion vertical-pod-autoscaler/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ procedure described below.

# Installation

The current default version is Vertical Pod Autoscaler 0.14.0
The current default version is Vertical Pod Autoscaler 1.0.0

### Compatibility

Expand Down
20 changes: 13 additions & 7 deletions vertical-pod-autoscaler/pkg/admission-controller/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ import (
"time"

apiv1 "k8s.io/api/core/v1"
"k8s.io/client-go/informers"
kube_client "k8s.io/client-go/kubernetes"
kube_flag "k8s.io/component-base/cli/flag"
"k8s.io/klog/v2"

"k8s.io/autoscaler/vertical-pod-autoscaler/common"
"k8s.io/autoscaler/vertical-pod-autoscaler/pkg/admission-controller/logic"
"k8s.io/autoscaler/vertical-pod-autoscaler/pkg/admission-controller/resource/pod"
Expand All @@ -32,20 +37,20 @@ import (
"k8s.io/autoscaler/vertical-pod-autoscaler/pkg/admission-controller/resource/vpa"
vpa_clientset "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/client/clientset/versioned"
"k8s.io/autoscaler/vertical-pod-autoscaler/pkg/target"
controllerfetcher "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/target/controller_fetcher"
"k8s.io/autoscaler/vertical-pod-autoscaler/pkg/utils/limitrange"
"k8s.io/autoscaler/vertical-pod-autoscaler/pkg/utils/metrics"
metrics_admission "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/utils/metrics/admission"
"k8s.io/autoscaler/vertical-pod-autoscaler/pkg/utils/status"
vpa_api_util "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/utils/vpa"
"k8s.io/client-go/informers"
kube_client "k8s.io/client-go/kubernetes"
kube_flag "k8s.io/component-base/cli/flag"
"k8s.io/klog/v2"
)

const (
defaultResyncPeriod = 10 * time.Minute
statusUpdateInterval = 10 * time.Second
defaultResyncPeriod = 10 * time.Minute
statusUpdateInterval = 10 * time.Second
scaleCacheEntryLifetime time.Duration = time.Hour
scaleCacheEntryFreshnessTime time.Duration = 10 * time.Minute
scaleCacheEntryJitterFactor float64 = 1.
)

var (
Expand Down Expand Up @@ -87,6 +92,7 @@ func main() {
kubeClient := kube_client.NewForConfigOrDie(config)
factory := informers.NewSharedInformerFactory(kubeClient, defaultResyncPeriod)
targetSelectorFetcher := target.NewVpaTargetSelectorFetcher(config, kubeClient, factory)
controllerFetcher := controllerfetcher.NewControllerFetcher(config, kubeClient, factory, scaleCacheEntryFreshnessTime, scaleCacheEntryLifetime, scaleCacheEntryJitterFactor)
podPreprocessor := pod.NewDefaultPreProcessor()
vpaPreprocessor := vpa.NewDefaultPreProcessor()
var limitRangeCalculator limitrange.LimitRangeCalculator
Expand All @@ -96,7 +102,7 @@ func main() {
limitRangeCalculator = limitrange.NewNoopLimitsCalculator()
}
recommendationProvider := recommendation.NewProvider(limitRangeCalculator, vpa_api_util.NewCappingRecommendationProcessor(limitRangeCalculator))
vpaMatcher := vpa.NewMatcher(vpaLister, targetSelectorFetcher)
vpaMatcher := vpa.NewMatcher(vpaLister, targetSelectorFetcher, controllerFetcher)

hostname, err := os.Hostname()
if err != nil {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@ package vpa
import (
core "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/klog/v2"

vpa_types "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1"
vpa_lister "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/client/listers/autoscaling.k8s.io/v1"
"k8s.io/autoscaler/vertical-pod-autoscaler/pkg/target"
controllerfetcher "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/target/controller_fetcher"
vpa_api_util "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/utils/vpa"
"k8s.io/klog/v2"
)

// Matcher is capable of returning a single matching VPA object
Expand All @@ -33,15 +35,18 @@ type Matcher interface {
}

type matcher struct {
vpaLister vpa_lister.VerticalPodAutoscalerLister
selectorFetcher target.VpaTargetSelectorFetcher
vpaLister vpa_lister.VerticalPodAutoscalerLister
selectorFetcher target.VpaTargetSelectorFetcher
controllerFetcher controllerfetcher.ControllerFetcher
}

// NewMatcher returns a new VPA matcher.
func NewMatcher(vpaLister vpa_lister.VerticalPodAutoscalerLister,
selectorFetcher target.VpaTargetSelectorFetcher) Matcher {
selectorFetcher target.VpaTargetSelectorFetcher,
controllerFetcher controllerfetcher.ControllerFetcher) Matcher {
return &matcher{vpaLister: vpaLister,
selectorFetcher: selectorFetcher}
selectorFetcher: selectorFetcher,
controllerFetcher: controllerFetcher}
}

func (m *matcher) GetMatchingVPA(pod *core.Pod) *vpa_types.VerticalPodAutoscaler {
Expand All @@ -66,7 +71,7 @@ func (m *matcher) GetMatchingVPA(pod *core.Pod) *vpa_types.VerticalPodAutoscaler
})
}
klog.V(2).Infof("Let's choose from %d configs for pod %s/%s", len(onConfigs), pod.Namespace, pod.Name)
result := vpa_api_util.GetControllingVPAForPod(pod, onConfigs)
result := vpa_api_util.GetControllingVPAForPod(pod, onConfigs, m.controllerFetcher)
if result != nil {
return result.Vpa
}
Expand Down
Loading

0 comments on commit bb8d2fe

Please sign in to comment.