Skip to content

Commit

Permalink
scaleutils: utilise nodeselector when identifying scale-in nodes.
Browse files Browse the repository at this point in the history
  • Loading branch information
jrasell committed Mar 29, 2021
1 parent 2f1bbba commit 8a3f13d
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 19 deletions.
39 changes: 20 additions & 19 deletions sdk/helper/scaleutils/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
multierror "github.com/hashicorp/go-multierror"
"github.com/hashicorp/nomad-autoscaler/sdk"
"github.com/hashicorp/nomad-autoscaler/sdk/helper/scaleutils/nodepool"
"github.com/hashicorp/nomad-autoscaler/sdk/helper/scaleutils/nodeselector"
"github.com/hashicorp/nomad/api"
)

Expand Down Expand Up @@ -99,8 +100,9 @@ func (c *ClusterScaleUtils) IdentifyScaleInNodes(cfg map[string]string, num int)
return nil, err
}

// Pull a current list of Nomad nodes from the API.
nodes, _, err := c.client.Nodes().List(nil)
// Pull a current list of Nomad nodes from the API including populated
// resource fields.
nodes, _, err := c.client.Nodes().List(&api.QueryOptions{Params: map[string]string{"resources": "true"}})
if err != nil {
return nil, fmt.Errorf("failed to list Nomad nodes from API: %v", err)
}
Expand Down Expand Up @@ -128,27 +130,26 @@ func (c *ClusterScaleUtils) IdentifyScaleInNodes(cfg map[string]string, num int)
num = len(filteredNodes)
}

var out []*api.NodeListStub

// We currently only support a single ClusterScaleInNodeIDStrategy. If we
// expand this in the future, this will likely be pulled from the config
// map.
strategy := NewestCreateIndexClusterScaleInNodeIDStrategy

// Depending on which strategy has been chosen, sort the node listing.
switch strategy {
case NewestCreateIndexClusterScaleInNodeIDStrategy:
default:
return nil, fmt.Errorf("unsupported scale in node identification strategy: %s", strategy)
// Setup the node selector used to identify suitable nodes for termination.
selector, err := nodeselector.NewSelector(cfg, c.client, c.log)
if err != nil {
return nil, err
}
c.log.Debug("performing node selection", "selector_strategy", selector.Name())

// Iterate through our filtered and sorted list of nodes, selecting the
// required number of nodes to scale in.
for i := 0; i <= num-1; i++ {
c.log.Debug("identified Nomad node for removal", "node_id", filteredNodes[i].ID)
out = append(out, filteredNodes[i])
// It is possible the selector is unable to identify suitable nodes and so
// we should return an error to stop additional execution.
out := selector.Select(filteredNodes, num)
if len(out) < 1 {
return nil, fmt.Errorf("no nodes selected using strategy %s", selector.Name())
}

// Selection can filter most of the nodes in the filtered list, we should
// log about this, but using certain strategies this is to be expected.
if len(out) < num {
c.log.Info("identified portion of requested nodes for removal",
"requested", num, "identified", len(out))
}
return out, nil
}

Expand Down
26 changes: 26 additions & 0 deletions sdk/target.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,30 @@ const (
// Nomad clients are purged from Nomad once they have been terminated
// within their provider.
TargetConfigKeyNodePurge = "node_purge"

// TargetConfigNodeSelectorStrategy is the optional node target config
// option which dictates how the Nomad Autoscaler selects nodes when
// scaling in.
TargetConfigNodeSelectorStrategy = "node_selector_strategy"
)

const (
// TargetNodeSelectorStrategyLeastBusyNodes is the cluster scale-in node
// selection strategy that identifies nodes based on their CPU and Memory
// resource allocation. It picks those with lowest values and is the
// default strategy.
TargetNodeSelectorStrategyLeastBusyNodes = "least_busy_nodes"

// TargetNodeSelectorStrategyNewestCreateIndex is the cluster scale-in node
// selection strategy that uses the sorting perform by the SDK. This
// sorting is based on the nodes CreateIndex, where nodes with higher
// indexes are selected. This is the fastest of the strategies and should
// be used if you have performance concerns or do not care about node
// selection.
TargetNodeSelectorStrategyNewestCreateIndex = "newest_create_index"

// TargetNodeSelectorStrategyEmptyNodes is the cluster scale-in node
// selection strategy that only picks nodes without non-terminal
// allocations.
TargetNodeSelectorStrategyEmptyNodes = "empty_nodes"
)

0 comments on commit 8a3f13d

Please sign in to comment.