Skip to content

Commit 7ac9ef8

Browse files
committed
[AKS] Autoscaling support for VMs pool
1 parent 8cb6833 commit 7ac9ef8

6 files changed

Lines changed: 3490 additions & 6 deletions

File tree

src/aks-preview/azext_aks_preview/_help.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2247,6 +2247,9 @@
22472247
- name: --localdns-config
22482248
type: string
22492249
short-summary: Set the localDNS Profile for a nodepool with a JSON config file.
2250+
- name: --node-vm-size -s
2251+
type: string
2252+
short-summary: VM size for Kubernetes nodes. Only configurable when updating the autoscale settings of a VirtualMachines node pool.
22502253
examples:
22512254
- name: Reconcile the nodepool back to its current state.
22522255
text: az aks nodepool update -g MyResourceGroup -n nodepool1 --cluster-name MyManagedCluster
@@ -2258,6 +2261,8 @@
22582261
text: az aks nodepool update --update-cluster-autoscaler --min-count 1 --max-count 10 -g MyResourceGroup -n nodepool1 --cluster-name MyManagedCluster
22592262
- name: Change a node pool to system mode
22602263
text: az aks nodepool update --mode System -g MyResourceGroup -n nodepool1 --cluster-name MyManagedCluster
2264+
- name: Update cluster autoscaler vm size, min-count and max-count for virtual machines node pool
2265+
text: az aks nodepool update -g MyResourceGroup -n nodepool1 --cluster-name MyManagedCluster --update-cluster-autoscaler --node-vm-size "Standard_D2s_v3" --min-count 2 --max-count 4
22612266
"""
22622267

22632268
helps['aks nodepool get-upgrades'] = """

src/aks-preview/azext_aks_preview/_params.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1882,6 +1882,11 @@ def load_arguments(self, _):
18821882
'localdns_config',
18831883
help='Path to a JSON file to configure the local DNS profile for an existing nodepool.',
18841884
)
1885+
c.argument(
1886+
"node_vm_size",
1887+
options_list=["--node-vm-size", "-s"],
1888+
completer=get_vm_size_completion_list,
1889+
)
18851890

18861891
with self.argument_context("aks nodepool upgrade") as c:
18871892
c.argument("max_surge", validator=validate_max_surge)

src/aks-preview/azext_aks_preview/agentpool_decorator.py

Lines changed: 315 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import os
88
from azure.cli.core.util import get_file_json
99
from types import SimpleNamespace
10-
from typing import Dict, TypeVar, Union, List
10+
from typing import Dict, TypeVar, Union, List, Tuple
1111

1212
from azure.cli.command_modules.acs._consts import AgentPoolDecoratorMode, DecoratorMode, DecoratorEarlyExitException
1313
from azure.cli.command_modules.acs.agentpool_decorator import (
@@ -21,6 +21,7 @@
2121
CLIInternalError,
2222
InvalidArgumentValueError,
2323
MutuallyExclusiveArgumentError,
24+
ArgumentUsageError,
2425
)
2526
from azure.cli.core.commands import AzCliCommand
2627
from azure.cli.core.profiles import ResourceType
@@ -839,6 +840,194 @@ def get_localdns_profile(self):
839840
return profile
840841
return None
841842

843+
def get_node_count_and_enable_cluster_autoscaler_min_max_count_vms(
844+
self,
845+
) -> Tuple[int, bool, Union[int, None], Union[int, None]]:
846+
"""Obtain the value of node_count, enable_cluster_autoscaler, min_count and max_count.
847+
848+
This function will verify the parameters through function "__validate_counts_in_autoscaler" by default.
849+
850+
This function is for Virtual Machines nodepool only.
851+
852+
:return: a tuple containing four elements: node_count of int type, enable_cluster_autoscaler of bool type,
853+
min_count of int type or None and max_count of int type or None
854+
"""
855+
# node_count
856+
# read the original value passed by the command
857+
node_count = self.raw_param.get("node_count")
858+
# enable_cluster_autoscaler
859+
# read the original value passed by the command
860+
enable_cluster_autoscaler = self.raw_param.get("enable_cluster_autoscaler", False)
861+
# min_count
862+
# read the original value passed by the command
863+
min_count = self.raw_param.get("min_count")
864+
# max_count
865+
# read the original value passed by the command
866+
max_count = self.raw_param.get("max_count")
867+
# try to read the property value corresponding to the parameter from the `agentpool` object
868+
869+
# validation
870+
self._AKSAgentPoolContext__validate_counts_in_autoscaler(
871+
node_count,
872+
enable_cluster_autoscaler,
873+
min_count,
874+
max_count,
875+
mode=self.get_mode(),
876+
decorator_mode=DecoratorMode.CREATE,
877+
)
878+
return node_count, enable_cluster_autoscaler, min_count, max_count
879+
880+
def get_node_count_from_vms_agentpool(
881+
self, agentpool: AgentPool
882+
) -> Union[int, None]:
883+
"""Get current node count for vms agentpool.
884+
885+
:return: the node count of the vms agentpool
886+
"""
887+
count = 0
888+
if agentpool.virtual_machine_nodes_status:
889+
for node_status in agentpool.virtual_machine_nodes_status:
890+
if node_status.count is not None:
891+
count += node_status.count
892+
if count == 0:
893+
# If no node status is available, return None
894+
return None
895+
return count
896+
897+
def get_update_enable_disable_cluster_autoscaler_and_min_max_count_vmsize_vms(
898+
self,
899+
) -> Tuple[bool, bool, bool, Union[int, None], Union[int, None], str]:
900+
"""Obtain the value of update_cluster_autoscaler, enable_cluster_autoscaler, disable_cluster_autoscaler,
901+
min_count and max_count, and vm size.
902+
903+
This function is for VMs agentpool only.
904+
905+
This function will verify the parameters through function "__validate_counts_in_autoscaler"
906+
by default. Besides if both enable_cluster_autoscaler and update_cluster_autoscaler are specified, a
907+
MutuallyExclusiveArgumentError will be raised. If enable_cluster_autoscaler or update_cluster_autoscaler is
908+
specified and there are multiple agent pool profiles, an ArgumentUsageError will be raised.
909+
If enable_cluster_autoscaler is specified and autoscaler is already enabled in `ap`,
910+
it will output warning messages and exit with code 0.
911+
If update_cluster_autoscaler is specified and autoscaler is not enabled in `ap`, it will raise an
912+
InvalidArgumentValueError.
913+
If disable_cluster_autoscaler is specified and autoscaler is not enabled in `ap`,
914+
it will output warning messages and exit with code 0.
915+
916+
:return: a tuple containing four elements: update_cluster_autoscaler of bool type, enable_cluster_autoscaler
917+
of bool type, disable_cluster_autoscaler of bool type, min_count of int type or None and max_count of int type
918+
or None
919+
"""
920+
update_cluster_autoscaler = self.raw_param.get("update_cluster_autoscaler", False)
921+
922+
# enable_cluster_autoscaler
923+
# read the original value passed by the command
924+
enable_cluster_autoscaler = self.raw_param.get("enable_cluster_autoscaler", False)
925+
926+
# disable_cluster_autoscaler
927+
# read the original value passed by the command
928+
disable_cluster_autoscaler = self.raw_param.get("disable_cluster_autoscaler", False)
929+
930+
# min_count
931+
# read the original value passed by the command
932+
min_count = self.raw_param.get("min_count")
933+
934+
# max_count
935+
# read the original value passed by the command
936+
max_count = self.raw_param.get("max_count")
937+
938+
# vm_size
939+
# read the original value passed by the command
940+
vm_size = self.raw_param.get("node_vm_size")
941+
942+
# validation
943+
if self.agentpool_decorator_mode == AgentPoolDecoratorMode.MANAGED_CLUSTER:
944+
# For multi-agent pool, use the az aks nodepool command
945+
if (enable_cluster_autoscaler or update_cluster_autoscaler) and len(self._agentpools) > 1:
946+
raise ArgumentUsageError(
947+
'There are more than one node pool in the cluster. Please use "az aks nodepool" command '
948+
"to update per node pool auto scaler settings"
949+
)
950+
951+
if enable_cluster_autoscaler + update_cluster_autoscaler + disable_cluster_autoscaler > 1:
952+
raise MutuallyExclusiveArgumentError(
953+
"Can only specify one of --enable-cluster-autoscaler, --update-cluster-autoscaler and "
954+
"--disable-cluster-autoscaler"
955+
)
956+
957+
if not update_cluster_autoscaler and vm_size is not None:
958+
raise MutuallyExclusiveArgumentError(
959+
"--node-vm-size is only applicable when updating cluster autoscaler settings "
960+
"with --updata-cluster-autoscaler"
961+
)
962+
963+
self._AKSAgentPoolContext__validate_counts_in_autoscaler(
964+
None,
965+
enable_cluster_autoscaler or update_cluster_autoscaler,
966+
min_count,
967+
max_count,
968+
mode=self.get_mode(),
969+
decorator_mode=DecoratorMode.UPDATE,
970+
)
971+
972+
autoscale_profile = (
973+
self.agentpool.virtual_machines_profile
974+
and self.agentpool.virtual_machines_profile.scale
975+
and self.agentpool.virtual_machines_profile.scale.autoscale
976+
)
977+
978+
manual_scale_profile = (
979+
self.agentpool.virtual_machines_profile
980+
and self.agentpool.virtual_machines_profile.scale
981+
and self.agentpool.virtual_machines_profile.scale.manual
982+
)
983+
984+
# if enabling cluster autoscaler
985+
if enable_cluster_autoscaler:
986+
if autoscale_profile:
987+
logger.warning(
988+
"Cluster autoscaler is already enabled for this node pool.\n"
989+
'Please run "az aks --update-cluster-autoscaler" '
990+
"if you want to update min-count or max-count."
991+
)
992+
raise DecoratorEarlyExitException()
993+
if manual_scale_profile:
994+
if len(manual_scale_profile) != 1:
995+
raise InvalidArgumentValueError(
996+
"Autoscaler cannot be enabled on node pool with multiple manual scale profiles.\n"
997+
"Please ensure that only one manual scale profile exists before enabling autoscaler."
998+
)
999+
1000+
# if updating cluster autoscaler
1001+
if update_cluster_autoscaler and not autoscale_profile:
1002+
raise InvalidArgumentValueError(
1003+
"Cluster autoscaler is not enabled for this virtual machines node pool.\n"
1004+
'Run "az aks nodepool update --enable-cluster-autoscaler" '
1005+
"to enable cluster with min-count and max-count."
1006+
)
1007+
1008+
# if disabling cluster autoscaler
1009+
if disable_cluster_autoscaler and not autoscale_profile:
1010+
logger.warning(
1011+
"Cluster autoscaler is already disabled for this node pool."
1012+
)
1013+
raise DecoratorEarlyExitException()
1014+
1015+
# if vm_size is not specified, use the size from the existing agentpool profile
1016+
if vm_size is None:
1017+
if autoscale_profile:
1018+
vm_size = autoscale_profile.size
1019+
1020+
if manual_scale_profile:
1021+
vm_size = manual_scale_profile[0].size
1022+
1023+
return (
1024+
update_cluster_autoscaler,
1025+
enable_cluster_autoscaler,
1026+
disable_cluster_autoscaler,
1027+
min_count,
1028+
max_count,
1029+
vm_size,
1030+
)
8421031

8431032
class AKSPreviewAgentPoolAddDecorator(AKSAgentPoolAddDecorator):
8441033
def __init__(
@@ -1090,20 +1279,46 @@ def set_up_virtual_machines_profile(self, agentpool: AgentPool) -> AgentPool:
10901279

10911280
sizes = self.context.get_vm_sizes()
10921281
if len(sizes) != 1:
1093-
raise InvalidArgumentValueError(f"We only accept single sku size for manual profile. {sizes} is invalid.")
1094-
count, _, _, _ = self.context.get_node_count_and_enable_cluster_autoscaler_min_max_count()
1095-
agentpool.virtual_machines_profile = self.models.VirtualMachinesProfile(
1282+
raise InvalidArgumentValueError(f"We only accept single sku size for scale profile. {sizes} is invalid.")
1283+
1284+
(
1285+
node_count,
1286+
enable_auto_scaling,
1287+
min_count,
1288+
max_count,
1289+
) = (
1290+
self.context.get_node_count_and_enable_cluster_autoscaler_min_max_count_vms()
1291+
)
1292+
1293+
if enable_auto_scaling:
1294+
agentpool.virtual_machines_profile = self.models.VirtualMachinesProfile(
1295+
scale=self.models.ScaleProfile(
1296+
autoscale=self.models.AutoScaleProfile(
1297+
size=sizes[0],
1298+
min_count=min_count,
1299+
max_count=max_count,
1300+
)
1301+
)
1302+
)
1303+
else:
1304+
agentpool.virtual_machines_profile = self.models.VirtualMachinesProfile(
10961305
scale=self.models.ScaleProfile(
10971306
manual=[
10981307
self.models.ManualScaleProfile(
10991308
size=sizes[0],
1100-
count=count,
1309+
count=node_count,
11011310
)
11021311
]
11031312
)
11041313
)
1314+
1315+
# properties that doesn't need to be set for virtual machines agentpool
1316+
# they are for vmss only
11051317
agentpool.vm_size = None
11061318
agentpool.count = None
1319+
agentpool.enable_auto_scaling = False
1320+
agentpool.min_count = None
1321+
agentpool.max_count = None
11071322

11081323
return agentpool
11091324

@@ -1515,6 +1730,101 @@ def update_agentpool_profile_preview(self, agentpools: List[AgentPool] = None) -
15151730
# update local DNS profile
15161731
agentpool = self.update_localdns_profile(agentpool)
15171732

1733+
# update auto scaler related properties for vms pool
1734+
agentpool = self.update_auto_scaler_properties_vms(agentpool)
1735+
1736+
return agentpool
1737+
1738+
def update_auto_scaler_properties(self, agentpool: AgentPool) -> AgentPool:
1739+
"""Update auto scaler related properties for vmss Agentpool object.
1740+
1741+
This function is for vmss agentpool only.
1742+
1743+
:return: the Agentpool object
1744+
"""
1745+
self._ensure_agentpool(agentpool)
1746+
1747+
# skip it for virtual machines pool
1748+
if self.context.get_vm_set_type() == CONST_VIRTUAL_MACHINES:
1749+
return agentpool
1750+
1751+
vm_size = self.raw_param.get("node_vm_size")
1752+
if vm_size is not None:
1753+
raise InvalidArgumentValueError(
1754+
"--node-vm-size can only be used with virtual machines agentpools. "
1755+
"Updating VM size is not supported for virtual machine scale set agentpools."
1756+
)
1757+
1758+
(
1759+
update_cluster_autoscaler,
1760+
enable_cluster_autoscaler,
1761+
disable_cluster_autoscaler,
1762+
min_count,
1763+
max_count,
1764+
) = (
1765+
self.context.get_update_enable_disable_cluster_autoscaler_and_min_max_count()
1766+
)
1767+
1768+
if update_cluster_autoscaler or enable_cluster_autoscaler:
1769+
agentpool.enable_auto_scaling = True
1770+
agentpool.min_count = min_count
1771+
agentpool.max_count = max_count
1772+
1773+
if disable_cluster_autoscaler:
1774+
agentpool.enable_auto_scaling = False
1775+
agentpool.min_count = None
1776+
agentpool.max_count = None
1777+
1778+
return agentpool
1779+
1780+
def update_auto_scaler_properties_vms(self, agentpool: AgentPool) -> AgentPool:
1781+
"""Update auto scaler related properties for vmss Agentpool object.
1782+
1783+
This function is for vms agentpool only.
1784+
1785+
:return: the Agentpool object
1786+
"""
1787+
self._ensure_agentpool(agentpool)
1788+
1789+
# for virtual machines agentpool only, skip for other agentpool types
1790+
if self.context.get_vm_set_type() != CONST_VIRTUAL_MACHINES:
1791+
return agentpool
1792+
1793+
(
1794+
update_cluster_autoscaler,
1795+
enable_cluster_autoscaler,
1796+
disable_cluster_autoscaler,
1797+
min_count,
1798+
max_count,
1799+
vm_size,
1800+
) = (
1801+
self.context.get_update_enable_disable_cluster_autoscaler_and_min_max_count_vmsize_vms()
1802+
)
1803+
1804+
if update_cluster_autoscaler or enable_cluster_autoscaler:
1805+
agentpool.virtual_machines_profile = self.models.VirtualMachinesProfile(
1806+
scale=self.models.ScaleProfile(
1807+
autoscale=self.models.AutoScaleProfile(
1808+
size=vm_size,
1809+
min_count=min_count,
1810+
max_count=max_count,
1811+
)
1812+
)
1813+
)
1814+
1815+
if disable_cluster_autoscaler:
1816+
current_node_count = self.context.get_node_count_from_vms_agentpool(agentpool)
1817+
agentpool.virtual_machines_profile = self.models.VirtualMachinesProfile(
1818+
scale=self.models.ScaleProfile(
1819+
manual=[
1820+
self.models.ManualScaleProfile(
1821+
size=vm_size,
1822+
count=current_node_count,
1823+
)
1824+
]
1825+
)
1826+
)
1827+
15181828
return agentpool
15191829

15201830
def update_upgrade_settings(self, agentpool: AgentPool) -> AgentPool:

0 commit comments

Comments
 (0)