Skip to content

Commit

Permalink
Merge pull request #4 from meezaan/selective
Browse files Browse the repository at this point in the history
WIP
  • Loading branch information
meezaan authored Apr 14, 2021
2 parents e5c5c9c + 2d42ece commit 37b67a7
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 17 deletions.
4 changes: 3 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ ENV LINODE_LKE_CLUSTER_POOL_ID "567890"
ENV LINODE_LKE_CLUSTER_POOL_MINIMUM_NODES "3"

ENV AUTOSCALE_TRIGGER "memory"
# used or requested
ENV AUTOSCALE_TRIGGER_TYPE "requested"
ENV AUTOSCALE_UP_PERCENTAGE "60"
ENV AUTOSCALE_DOWN_PERCENTAGE "40"
ENV AUTOSCALE_RESOURCE_REQUEST_UP_PERCENTAGE "80"
Expand All @@ -19,4 +21,4 @@ ENV AUTOSCALE_THRESHOLD_COUNT "3"
ENV AUTOSCALE_NUMBER_OF_NODES "1"
ENV AUTOSCALE_WAIT_TIME_AFTER_SCALING "180"

CMD ["php", "/autoscaler/bin/autoscale"]
CMD ["php", "/autoscaler/bin/autoscale"]
32 changes: 27 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ The docker container takes all its configuration via environment variables. Here
| LINODE_LKE_CLUSTER_ID | The ID of the LKE Cluster to Autoscale |
| LINODE_LKE_CLUSTER_POOL_ID | The Node Pool ID within the LKE Cluster to Autoscale |
| LINODE_LKE_CLUSTER_POOL_MINIMUM_NODES | The minimum nodes to keep in the cluster. The cluster won't be scaled down below this.|
| AUTOSCALE_TRIGGER | 'cpu' or 'memory'
| AUTOSCALE_TRIGGER | 'cpu' or 'memory'. Defaults to memmory.
| AUTOSCALE_TRIGGER_TYPE | 'requested' or 'used'. Defaults to requested. This tells the autoscaler to use either the *requested* or the *currently used* memory or cpu to scale up or down if it breaches the threshhold.
| AUTOSCALE_UP_PERCENTAGE | At what percentage of 'cpu' or 'memory' to scale up the node pool. Example: 65
| AUTOSCALE_DOWN_PERCENTAGE | At what percentage of 'cpu' or 'memory' to scale down the node pool. Example: 40
| AUTOSCALE_RESOURCE_REQUEST_UP_PERCENTAGE | At what percentage of 'cpu' or 'memory' of the requested / available to scale up the cluster. Default: 80
Expand All @@ -55,23 +56,44 @@ The docker container takes all its configuration via environment variables. Here

To understand the above assuming we have set the following values.
* AUTOSCALE_TRIGGER=memory
* AUTOSCALE_TRIGGER_TYPE=requested
* AUTOSCALE_UP_PERCENTAGE=65
* AUTOSCALE_UP_PERCENTAGE=30
* AUTOSCALE_DOWN_PERCENTAGE=30
* AUTOSCALE_RESOURCE_REQUEST_UP_PERCENTAGE=80
* AUTOSCALE_RESOURCE_REQUEST_DOWN_PERCENTAGE=80
* AUTOSCALE_RESOURCE_REQUEST_DOWN_PERCENTAGE=70
* AUTOSCALE_QUERY_INTERVAL=10
* AUTOSCALE_THRESHOLD_COUNT=3
* AUTOSCALE_NUMBER_OF_NODES=2
* AUTOSCALE_WAIT_TIME_AFTER_SCALING=180

With this setup, the autoscaler utility will query the Kuberenetes API every 10 seconds. If with 3 consecutive calls
to the API (effectively meaning over 30 seconds), the memory usage is higher than 65% or the requested memory exceeds 80% of the total memory available on the cluster, 2 more nodes will be added to the
to the API (effectively meaning over 30 seconds), the requested memory exceeds 80% of the total memory available on the cluster, 2 more nodes will be added to the
specified node pool. The utility will wait for 180 seconds and then start querying the API every 10 seconds again.

If with 3 consecutive calls to the API (effectively meaning over 30 seconds), the memory usage is lower than 30% or the requested memory is below 80% of the total memory available on the cluster,
If with 3 consecutive calls to the API (effectively meaning over 30 seconds), the requested memory is below 70% of the total memory available on the cluster,
1 node will be removed (**nodes are always removed one at a time to ensure you don't run out of capacity all of a sudden**) from the specified node pool. The utility will wait for 180 seconds and then start
querying the API every 10 seconds again.

Same example, with a different trigger type.
* AUTOSCALE_TRIGGER=memory
* AUTOSCALE_TRIGGER_TYPE=used
* AUTOSCALE_UP_PERCENTAGE=65
* AUTOSCALE_DOWN_PERCENTAGE=30
* AUTOSCALE_RESOURCE_REQUEST_UP_PERCENTAGE=80
* AUTOSCALE_RESOURCE_REQUEST_DOWN_PERCENTAGE=70
* AUTOSCALE_QUERY_INTERVAL=10
* AUTOSCALE_THRESHOLD_COUNT=3
* AUTOSCALE_NUMBER_OF_NODES=2
* AUTOSCALE_WAIT_TIME_AFTER_SCALING=180

With this setup, the autoscaler utility will query the Kuberenetes API every 10 seconds. If with 3 consecutive calls
to the API (effectively meaning over 30 seconds), the memory usage is higher than 65% of the total memory available on the cluster, 2 more nodes will be added to the
specified node pool. The utility will wait for 180 seconds and then start querying the API every 10 seconds again.

If with 3 consecutive calls to the API (effectively meaning over 30 seconds), the memory usage is below 30% of the total memory available on the cluster,
1 node will be removed (**nodes are always removed one at a time to ensure you don't run out of capacity all of a sudden**) from the specified node pool. The utility will wait for 180 seconds and then start
querying the API every 10 seconds again.

## Usage

You'll need to configure the Docker image with env variables and the kubectl config.
Expand Down
8 changes: 5 additions & 3 deletions bin/autoscale
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ pcntl_signal(SIGINT, function () {

/** Config **/
$autoscaleTrigger = getenv('AUTOSCALE_TRIGGER'); // memory or cpu for memory percentage or CPU percentage
$autoscaleTriggerType = getenv('AUTOSCALE_TRIGGER_TYPE'); // requested or used
$autoscaleUpAtUtilisationPercent = getenv('AUTOSCALE_UP_PERCENTAGE');
$autoscaleDownAtUtilisationPercent = getenv('AUTOSCALE_DOWN_PERCENTAGE');
$autoscaleUpRequestPercentage = getenv('AUTOSCALE_RESOURCE_REQUEST_UP_PERCENTAGE');
Expand Down Expand Up @@ -47,6 +48,7 @@ $linode = new Pool($linodePAT, $linodeLkeClusterId, $linodeLkeClusterPoolId);

$logger->info('Start Monitoring Cluster...');
$logger->info('Autoscale Trigger is set to: ' . $autoscaleTrigger);
$logger->info('Autoscale Trigger Type is set to: ' . $autoscaleTriggerType);
$logger->info('Create Counter...');
$counter = new Counter($autoscaleThresholdCount);

Expand All @@ -66,7 +68,7 @@ while (true) {

$usedPercentage = $autoscaleTrigger == 'cpu' ? $nodes->getUsedCpuPercent() : $nodes->getUsedMemoryPercent();
$requestedPercentage = $autoscaleTrigger == 'cpu' ? $nodes->getRequestedCpuPercent() : $nodes->getRequestedMemoryPercent();
$scale = new Scale($autoscaleUpAtUtilisationPercent, $autoscaleDownAtUtilisationPercent, $usedPercentage, $requestedPercentage, $autoscaleUpRequestPercentage, $autoscaleDownRequestPercentage);
$scale = new Scale($autoscaleUpAtUtilisationPercent, $autoscaleDownAtUtilisationPercent, $usedPercentage, $requestedPercentage, $autoscaleUpRequestPercentage, $autoscaleDownRequestPercentage, $autoscaleTriggerType);
$logger->info(strtoupper($autoscaleTrigger) . ' Scale calculated', [
'usedPercentage' => $usedPercentage,
'scaleUpPercentage' => $autoscaleUpAtUtilisationPercent,
Expand Down Expand Up @@ -98,14 +100,14 @@ while (true) {
if ($counter->scaleDownCountBreached()) {
$logger->alert('Counter Scale Down Count Breached. Size DOWN Cluster...', ['count' => $counter->count, 'threshold' => $counter->thresholdCount]);
// Scale down if current nodes are greater than the minimum AND if the current nodes - the scale down number is greater than or equal to the minimum number
if ($currentNodesInPool > $linodeClusterPoolMinimumNodeCount && ($currentNodesInPool - $autoscaleNodesToAddOrRemovePerBreach) >= $linodeClusterPoolMinimumNodeCount) {
if ($currentNodesInPool > $linodeClusterPoolMinimumNodeCount && ($currentNodesInPool - 1) >= $linodeClusterPoolMinimumNodeCount) {
$logger->alert('Current Nodes in LKE Pool: ' . $currentNodesInPool);
$logger->alert("Removing $autoscaleNodesToAddOrRemovePerBreach node(s)...");
// Scale down only 1 node at a time, this is much safer than scaling down multiple nodes
$linode->updateNodeCount($currentNodesInPool - 1);
sleep($autoscaleWaitTimeBetweenScaling);
}
$logger->alert("Skip downsizing cluster because we are already at the minimum number ($linodeClusterPoolMinimumNodeCount) of nodes or scaling down by $autoscaleNodesToAddOrRemovePerBreach will put us at less than the minimum number.");
$logger->alert("Skip downsizing cluster because we are already at the minimum number ($linodeClusterPoolMinimumNodeCount) of nodes or scaling down by 1 will put us at less than the minimum number.");
$counter->reset();
}
} catch (Exception $e) {
Expand Down
26 changes: 18 additions & 8 deletions src/Scale.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,27 +12,37 @@ class Scale
public float $requestedPercentage;
public float $thresholdRequestUpPercentage;
public float $thresholdRequestDownPercentage;
public string $triggerType; // requested or used

public function __construct(float $thresholdUpPercentage, float $thresholdDownPercentage, float $usedPercentage, float $requestedPercentage, float $thresholdRequestUpPercentage, float $thresholdRequestDownPercentage)
public function __construct(float $thresholdUpPercentage, float $thresholdDownPercentage, float $usedPercentage,
float $requestedPercentage, float $thresholdRequestUpPercentage, float $thresholdRequestDownPercentage,
string $triggerType)
{
$this->thresholdUpPercentage = $thresholdUpPercentage;
$this->thresholdDownPercentage = $thresholdDownPercentage;
$this->usedPercentage = $usedPercentage;
$this->requestedPercentage = $requestedPercentage;
$this->thresholdRequestUpPercentage = $thresholdRequestUpPercentage;
$this->thresholdRequestDownPercentage = $thresholdRequestDownPercentage;
$this->triggerType = $triggerType;
}

public function scaleUp()
public function scaleUp(): bool
{
return $this->usedPercentage > $this->thresholdUpPercentage
|| $this->requestedPercentage > $this->thresholdRequestUpPercentage;
if ($this->triggerType == 'requested') {
return $this->requestedPercentage > $this->thresholdRequestUpPercentage;
}

return $this->usedPercentage > $this->thresholdUpPercentage;

}

public function scaleDown()
public function scaleDown(): bool
{
return $this->usedPercentage < $this->thresholdDownPercentage
|| $this->requestedPercentage < $this->thresholdRequestDownPercentage;
if ($this->triggerType == 'requested') {
return $this->requestedPercentage < $this->thresholdRequestDownPercentage;
}

return $this->usedPercentage < $this->thresholdDownPercentage;
}
}
}

0 comments on commit 37b67a7

Please sign in to comment.