def autoscale_rule_create(cmd, client, autoscale_name, resource_group_name, condition, scale, profile_name=DEFAULT_PROFILE_NAME, cooldown=5, source=None, timegrain="avg 1m"): from azure.mgmt.monitor.models import ScaleRule, ScaleAction, ScaleDirection autoscale_settings = client.get(resource_group_name, autoscale_name) profile = next(x for x in autoscale_settings.profiles if x.name == profile_name) condition.metric_resource_uri = source or autoscale_settings.target_resource_uri condition.statistic = timegrain.statistic condition.time_grain = timegrain.time_grain rule = ScaleRule( metric_trigger=condition, scale_action=ScaleAction( direction=scale.direction, type=scale.type, cooldown='PT{}M'.format(cooldown), value=scale.value) ) profile.rules.append(rule) autoscale_settings = client.create_or_update(resource_group_name, autoscale_name, autoscale_settings) profile = next(x for x in autoscale_settings.profiles if x.name == profile_name) # determine if there are unbalanced rules scale_out_rule_count = len([x for x in profile.rules if x.scale_action.direction == ScaleDirection.increase]) scale_in_rule_count = len([x for x in profile.rules if x.scale_action.direction == ScaleDirection.decrease]) if scale_out_rule_count and not scale_in_rule_count: logger.warning("Profile '%s' has rules to scale out but none to scale in. " "Recommend creating at least 1 scale in rule.", profile_name) elif scale_in_rule_count and not scale_out_rule_count: logger.warning("Profile '%s' has rules to scale in but none to scale out. " "Recommend creating at least 1 scale out rule.", profile_name) return rule
def __call__(self, parser, namespace, values, option_string=None): from azure.mgmt.monitor.models import ScaleAction, ScaleType if len(values) == 1: # workaround because CMD.exe eats > character... Allows condition to be # specified as a quoted expression values = values[0].split(' ') if len(values) != 2: from knack.util import CLIError raise CLIError('usage error: --scale {in,out,to} VALUE[%]') dir_val = values[0] amt_val = values[1] scale_type = None if dir_val == 'to': scale_type = ScaleType.exact_count.value elif str(amt_val).endswith('%'): scale_type = ScaleType.percent_change_count.value amt_val = amt_val[:-1] # strip off the percent else: scale_type = ScaleType.change_count.value scale = ScaleAction( direction=get_autoscale_scale_direction_map()[dir_val], type=scale_type, cooldown=None, # this will be filled in later value=amt_val) namespace.scale = scale
def create_rule_instance(params): rule = params.copy() rule['metric_resource_uri'] = rule.get('metric_resource_uri', self.target) rule['time_grain'] = timedelta(minutes=rule.get('time_grain', 0)) rule['time_window'] = timedelta(minutes=rule.get('time_window', 0)) rule['cooldown'] = timedelta(minutes=rule.get('cooldown', 0)) return ScaleRule(metric_trigger=MetricTrigger(**rule), scale_action=ScaleAction(**rule))
def shutdown_scaleset_rule(queue_uri: str) -> ScaleRule: return ScaleRule( # Scale in if there are 0 or more messages in the queue (aka: every time) metric_trigger=MetricTrigger( metric_name="ApproximateMessageCount", metric_resource_uri=queue_uri, # Check every 10 minutes time_grain=timedelta(minutes=5), # The average amount of messages there are in the pool queue time_aggregation=TimeAggregationType.AVERAGE, statistic=MetricStatisticType.SUM, # Over the past 10 minutes time_window=timedelta(minutes=5), operator=ComparisonOperationType.GREATER_THAN_OR_EQUAL, threshold=0, divide_per_instance=False, ), scale_action=ScaleAction( direction=ScaleDirection.DECREASE, type=ScaleType.CHANGE_COUNT, value=1, cooldown=timedelta(minutes=5), ), )
def create_auto_scale_profile( queue_uri: str, min: int, max: int, default: int, scale_out_amount: int, scale_out_cooldown: int, scale_in_amount: int, scale_in_cooldown: int, ) -> AutoscaleProfile: return AutoscaleProfile( name=str(uuid.uuid4()), capacity=ScaleCapacity(minimum=min, maximum=max, default=max), # Auto scale tuning guidance: # https://docs.microsoft.com/en-us/azure/architecture/best-practices/auto-scaling rules=[ ScaleRule( metric_trigger=MetricTrigger( metric_name="ApproximateMessageCount", metric_resource_uri=queue_uri, # Check every 15 minutes time_grain=timedelta(minutes=15), # The average amount of messages there are in the pool queue time_aggregation=TimeAggregationType.AVERAGE, statistic=MetricStatisticType.COUNT, # Over the past 15 minutes time_window=timedelta(minutes=15), # When there's more than 1 message in the pool queue operator=ComparisonOperationType.GREATER_THAN_OR_EQUAL, threshold=1, divide_per_instance=False, ), scale_action=ScaleAction( direction=ScaleDirection.INCREASE, type=ScaleType.CHANGE_COUNT, value=scale_out_amount, cooldown=timedelta(minutes=scale_out_cooldown), ), ), # Scale in ScaleRule( # Scale in if no work in the past 20 mins metric_trigger=MetricTrigger( metric_name="ApproximateMessageCount", metric_resource_uri=queue_uri, # Check every 10 minutes time_grain=timedelta(minutes=10), # The average amount of messages there are in the pool queue time_aggregation=TimeAggregationType.AVERAGE, statistic=MetricStatisticType.SUM, # Over the past 10 minutes time_window=timedelta(minutes=10), # When there's no messages in the pool queue operator=ComparisonOperationType.EQUALS, threshold=0, divide_per_instance=False, ), scale_action=ScaleAction( direction=ScaleDirection.DECREASE, type=ScaleType.CHANGE_COUNT, value=scale_in_amount, cooldown=timedelta(minutes=scale_in_cooldown), ), ), ], )
def autoscale_rule_create(cmd, client, autoscale_name, resource_group_name, condition, scale, profile_name=DEFAULT_PROFILE_NAME, cooldown=5, source=None, timegrain="avg 1m"): from azure.mgmt.monitor.models import ScaleRule, ScaleAction, ScaleDirection from azure.mgmt.core.tools import parse_resource_id, resource_id autoscale_settings = client.get(resource_group_name, autoscale_name) profile = _identify_profile(autoscale_settings.profiles, profile_name) condition.metric_resource_uri = source or autoscale_settings.target_resource_uri condition.statistic = timegrain.statistic condition.time_grain = timegrain.time_grain def preprocess_for_spring_cloud_service(): try: result = parse_resource_id(autoscale_settings.target_resource_uri) if result['namespace'].lower( ) == 'microsoft.appplatform' and result['type'].lower( ) == 'spring': if condition.metric_namespace is None: condition.metric_namespace = "Microsoft.AppPlatform/Spring" logger.warning( 'Set metricNamespace to Microsoft.AppPlatform/Spring') if source is None: condition.metric_resource_uri = resource_id( subscription=result['subscription'], resource_group=result['resource_group'], namespace=result['namespace'], type=result['type'], name=result['name']) logger.warning( 'Set metricResourceUri to Spring Cloud service') except KeyError: pass preprocess_for_spring_cloud_service() rule = ScaleRule(metric_trigger=condition, scale_action=ScaleAction( direction=scale.direction, type=scale.type, cooldown='PT{}M'.format(cooldown), value=scale.value)) profile.rules.append(rule) autoscale_settings = client.create_or_update(resource_group_name, autoscale_name, autoscale_settings) # determine if there are unbalanced rules scale_out_rule_count = len([ x for x in profile.rules if x.scale_action.direction == ScaleDirection.increase ]) scale_in_rule_count = len([ x for x in profile.rules if x.scale_action.direction == ScaleDirection.decrease ]) if scale_out_rule_count and not scale_in_rule_count: logger.warning( "Profile '%s' has rules to scale out but none to scale in. " "Recommend creating at least 1 scale in rule.", profile_name) elif scale_in_rule_count and not scale_out_rule_count: logger.warning( "Profile '%s' has rules to scale in but none to scale out. " "Recommend creating at least 1 scale out rule.", profile_name) return rule