Skip to content

Commit 1af7aaf

Browse files
committed
[AKS]Autoscaling support for VMs agentpool
1 parent e3623c8 commit 1af7aaf

File tree

7 files changed

+1252
-6
lines changed

7 files changed

+1252
-6
lines changed

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: 312 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,192 @@ 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 "_AKSAgentPoolContext__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 "_AKSAgentPoolContext__validate_counts_in_autoscaler" by default. Besides
902+
if both enable_cluster_autoscaler and update_cluster_autoscaler are specified, a MutuallyExclusiveArgumentError
903+
will be raised. If enable_cluster_autoscaler or update_cluster_autoscaler is specified and there are multiple
904+
agent pool profiles, an ArgumentUsageError will be raised. If enable_cluster_autoscaler is specified and
905+
autoscaler is already enabled in `ap`, it will output warning messages and exit with code 0. If
906+
update_cluster_autoscaler is specified and autoscaler is not enabled in `ap`, it will raise an
907+
InvalidArgumentValueError. If disable_cluster_autoscaler is specified and autoscaler is not enabled in `ap`,
908+
it will output warning messages and exit with code 0.
909+
910+
:return: a tuple containing four elements: update_cluster_autoscaler of bool type, enable_cluster_autoscaler
911+
of bool type, disable_cluster_autoscaler of bool type, min_count of int type or None and max_count of int type
912+
or None
913+
"""
914+
update_cluster_autoscaler = self.raw_param.get("update_cluster_autoscaler", False)
915+
916+
# enable_cluster_autoscaler
917+
# read the original value passed by the command
918+
enable_cluster_autoscaler = self.raw_param.get("enable_cluster_autoscaler", False)
919+
920+
# disable_cluster_autoscaler
921+
# read the original value passed by the command
922+
disable_cluster_autoscaler = self.raw_param.get("disable_cluster_autoscaler", False)
923+
924+
# min_count
925+
# read the original value passed by the command
926+
min_count = self.raw_param.get("min_count")
927+
928+
# max_count
929+
# read the original value passed by the command
930+
max_count = self.raw_param.get("max_count")
931+
932+
# vm_size
933+
# read the original value passed by the command
934+
vm_size = self.raw_param.get("node_vm_size")
935+
936+
# validation
937+
if self.agentpool_decorator_mode == AgentPoolDecoratorMode.MANAGED_CLUSTER:
938+
# For multi-agent pool, use the az aks nodepool command
939+
if (enable_cluster_autoscaler or update_cluster_autoscaler) and len(self._agentpools) > 1:
940+
raise ArgumentUsageError(
941+
'There are more than one node pool in the cluster. Please use "az aks nodepool" command '
942+
"to update per node pool auto scaler settings"
943+
)
944+
945+
if enable_cluster_autoscaler + update_cluster_autoscaler + disable_cluster_autoscaler > 1:
946+
raise MutuallyExclusiveArgumentError(
947+
"Can only specify one of --enable-cluster-autoscaler, --update-cluster-autoscaler and "
948+
"--disable-cluster-autoscaler"
949+
)
950+
951+
if not update_cluster_autoscaler and vm_size is not None:
952+
raise MutuallyExclusiveArgumentError(
953+
"--node-vm-size is only applicable when updating cluster autoscaler settings "
954+
"with --updata-cluster-autoscaler"
955+
)
956+
957+
self._AKSAgentPoolContext__validate_counts_in_autoscaler(
958+
None,
959+
enable_cluster_autoscaler or update_cluster_autoscaler,
960+
min_count,
961+
max_count,
962+
mode=self.get_mode(),
963+
decorator_mode=DecoratorMode.UPDATE,
964+
)
965+
966+
autoscale_profile = (
967+
self.agentpool.virtual_machines_profile
968+
and self.agentpool.virtual_machines_profile.scale
969+
and self.agentpool.virtual_machines_profile.scale.autoscale
970+
)
971+
972+
manual_scale_profile = (
973+
self.agentpool.virtual_machines_profile
974+
and self.agentpool.virtual_machines_profile.scale
975+
and self.agentpool.virtual_machines_profile.scale.manual
976+
)
977+
978+
# if enabling cluster autoscaler
979+
if enable_cluster_autoscaler:
980+
if autoscale_profile:
981+
logger.warning(
982+
"Cluster autoscaler is already enabled for this node pool.\n"
983+
'Please run "az aks --update-cluster-autoscaler" '
984+
"if you want to update min-count or max-count."
985+
)
986+
raise DecoratorEarlyExitException()
987+
if manual_scale_profile:
988+
if len(manual_scale_profile) != 1:
989+
raise InvalidArgumentValueError(
990+
"Autoscaler cannot be enabled on node pool with multiple manual scale profiles.\n"
991+
"Please ensure that only one manual scale profile exists before enabling autoscaler."
992+
)
993+
994+
# if updating cluster autoscaler
995+
if update_cluster_autoscaler and not autoscale_profile:
996+
raise InvalidArgumentValueError(
997+
"Cluster autoscaler is not enabled for this virtual machines node pool.\n"
998+
'Run "az aks nodepool update --enable-cluster-autoscaler" '
999+
"to enable cluster with min-count and max-count."
1000+
)
1001+
1002+
# if disabling cluster autoscaler
1003+
if disable_cluster_autoscaler and not autoscale_profile:
1004+
logger.warning(
1005+
"Cluster autoscaler is already disabled for this node pool."
1006+
)
1007+
raise DecoratorEarlyExitException()
1008+
1009+
# if vm_size is not specified, use the size from the existing agentpool profile
1010+
if vm_size is None:
1011+
if autoscale_profile:
1012+
vm_size = autoscale_profile.size
1013+
1014+
if manual_scale_profile:
1015+
vm_size = manual_scale_profile[0].size
1016+
1017+
return (
1018+
update_cluster_autoscaler,
1019+
enable_cluster_autoscaler,
1020+
disable_cluster_autoscaler,
1021+
min_count,
1022+
max_count,
1023+
vm_size,
1024+
)
8381025

8391026
class AKSPreviewAgentPoolAddDecorator(AKSAgentPoolAddDecorator):
8401027
def __init__(
@@ -1080,20 +1267,45 @@ def set_up_virtual_machines_profile(self, agentpool: AgentPool) -> AgentPool:
10801267

10811268
sizes = self.context.get_vm_sizes()
10821269
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(
1270+
raise InvalidArgumentValueError(f"We only accept single sku size for scale profile. {sizes} is invalid.")
1271+
1272+
(
1273+
node_count,
1274+
enable_auto_scaling,
1275+
min_count,
1276+
max_count,
1277+
) = (
1278+
self.context.get_node_count_and_enable_cluster_autoscaler_min_max_count_vms()
1279+
)
1280+
1281+
if enable_auto_scaling:
1282+
agentpool.virtual_machines_profile = self.models.VirtualMachinesProfile(
1283+
scale=self.models.ScaleProfile(
1284+
autoscale=self.models.AutoScaleProfile(
1285+
size=sizes[0],
1286+
min_count=min_count,
1287+
max_count=max_count,
1288+
)
1289+
)
1290+
)
1291+
else:
1292+
agentpool.virtual_machines_profile = self.models.VirtualMachinesProfile(
10861293
scale=self.models.ScaleProfile(
10871294
manual=[
10881295
self.models.ManualScaleProfile(
10891296
size=sizes[0],
1090-
count=count,
1297+
count=node_count,
10911298
)
10921299
]
10931300
)
10941301
)
1302+
1303+
# properties that doesn't need to be set for virtual machines agentpool
10951304
agentpool.vm_size = None
10961305
agentpool.count = None
1306+
agentpool.enable_auto_scaling = False
1307+
agentpool.min_count = None
1308+
agentpool.max_count = None
10971309

10981310
return agentpool
10991311

@@ -1505,6 +1717,101 @@ def update_agentpool_profile_preview(self, agentpools: List[AgentPool] = None) -
15051717
# update local DNS profile
15061718
agentpool = self.update_localdns_profile(agentpool)
15071719

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

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

0 commit comments

Comments
 (0)