Skip to content

Commit a784a81

Browse files
committed
[AKS] Autoscaling support for VMs pool
1 parent e3623c8 commit a784a81

7 files changed

Lines changed: 3565 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
@@ -2229,6 +2229,9 @@
22292229
- name: --localdns-config
22302230
type: string
22312231
short-summary: Set the localDNS Profile for a nodepool with a JSON config file.
2232+
- name: --node-vm-size -s
2233+
type: string
2234+
short-summary: VM size for Kubernetes nodes. Only configurable when updating the autoscale settings of a VirtualMachines node pool.
22322235
examples:
22332236
- name: Reconcile the nodepool back to its current state.
22342237
text: az aks nodepool update -g MyResourceGroup -n nodepool1 --cluster-name MyManagedCluster
@@ -2240,6 +2243,8 @@
22402243
text: az aks nodepool update --update-cluster-autoscaler --min-count 1 --max-count 10 -g MyResourceGroup -n nodepool1 --cluster-name MyManagedCluster
22412244
- name: Change a node pool to system mode
22422245
text: az aks nodepool update --mode System -g MyResourceGroup -n nodepool1 --cluster-name MyManagedCluster
2246+
- name: Update cluster autoscaler vm size, min-count and max-count for virtual machines node pool
2247+
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
22432248
"""
22442249

22452250
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
@@ -1874,6 +1874,11 @@ def load_arguments(self, _):
18741874
'localdns_config',
18751875
help='Path to a JSON file to configure the local DNS profile for an existing nodepool.',
18761876
)
1877+
c.argument(
1878+
"node_vm_size",
1879+
options_list=["--node-vm-size", "-s"],
1880+
completer=get_vm_size_completion_list,
1881+
)
18771882

18781883
with self.argument_context("aks nodepool upgrade") as c:
18791884
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
@@ -835,6 +836,194 @@ def get_localdns_profile(self):
835836
return profile
836837
return None
837838

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

8391028
class AKSPreviewAgentPoolAddDecorator(AKSAgentPoolAddDecorator):
8401029
def __init__(
@@ -1080,20 +1269,46 @@ def set_up_virtual_machines_profile(self, agentpool: AgentPool) -> AgentPool:
10801269

10811270
sizes = self.context.get_vm_sizes()
10821271
if len(sizes) != 1:
1083-
raise InvalidArgumentValueError(f"We only accept single sku size for manual profile. {sizes} is invalid.")
1084-
count, _, _, _ = self.context.get_node_count_and_enable_cluster_autoscaler_min_max_count()
1085-
agentpool.virtual_machines_profile = self.models.VirtualMachinesProfile(
1272+
raise InvalidArgumentValueError(f"We only accept single sku size for scale profile. {sizes} is invalid.")
1273+
1274+
(
1275+
node_count,
1276+
enable_auto_scaling,
1277+
min_count,
1278+
max_count,
1279+
) = (
1280+
self.context.get_node_count_and_enable_cluster_autoscaler_min_max_count_vms()
1281+
)
1282+
1283+
if enable_auto_scaling:
1284+
agentpool.virtual_machines_profile = self.models.VirtualMachinesProfile(
1285+
scale=self.models.ScaleProfile(
1286+
autoscale=self.models.AutoScaleProfile(
1287+
size=sizes[0],
1288+
min_count=min_count,
1289+
max_count=max_count,
1290+
)
1291+
)
1292+
)
1293+
else:
1294+
agentpool.virtual_machines_profile = self.models.VirtualMachinesProfile(
10861295
scale=self.models.ScaleProfile(
10871296
manual=[
10881297
self.models.ManualScaleProfile(
10891298
size=sizes[0],
1090-
count=count,
1299+
count=node_count,
10911300
)
10921301
]
10931302
)
10941303
)
1304+
1305+
# properties that doesn't need to be set for virtual machines agentpool
1306+
# they are for vmss only
10951307
agentpool.vm_size = None
10961308
agentpool.count = None
1309+
agentpool.enable_auto_scaling = False
1310+
agentpool.min_count = None
1311+
agentpool.max_count = None
10971312

10981313
return agentpool
10991314

@@ -1505,6 +1720,101 @@ def update_agentpool_profile_preview(self, agentpools: List[AgentPool] = None) -
15051720
# update local DNS profile
15061721
agentpool = self.update_localdns_profile(agentpool)
15071722

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

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

0 commit comments

Comments
 (0)