def build_auto_scaling_group()

in source/idea/idea-administrator/src/ideaadministrator/app/cdk/stacks/cluster_manager_stack.py [0:0]


    def build_auto_scaling_group(self):

        key_pair_name = self.context.config().get_string('cluster.network.ssh_key_pair', required=True)
        is_public = self.context.config().get_bool('cluster-manager.ec2.autoscaling.public', False) and len(self.cluster.public_subnets) > 0
        base_os = self.context.config().get_string('cluster-manager.ec2.autoscaling.base_os', required=True)
        instance_ami = self.context.config().get_string('cluster-manager.ec2.autoscaling.instance_ami', required=True)
        instance_type = self.context.config().get_string('cluster-manager.ec2.autoscaling.instance_type', required=True)
        volume_size = self.context.config().get_int('cluster-manager.ec2.autoscaling.volume_size', default=200)
        enable_detailed_monitoring = self.context.config().get_bool('cluster-manager.ec2.autoscaling.enable_detailed_monitoring', default=False)
        min_capacity = self.context.config().get_int('cluster-manager.ec2.autoscaling.min_capacity', default=1)
        max_capacity = self.context.config().get_int('cluster-manager.ec2.autoscaling.max_capacity', default=3)
        cooldown_minutes = self.context.config().get_int('cluster-manager.ec2.autoscaling.cooldown_minutes', default=5)
        default_instance_warmup = self.context.config().get_int('cluster-manager.ec2.autoscaling.default_instance_warmup', default=15)
        new_instances_protected_from_scale_in = self.context.config().get_bool('cluster-manager.ec2.autoscaling.new_instances_protected_from_scale_in', default=True)
        elb_healthcheck_grace_time_minutes = self.context.config().get_int('cluster-manager.ec2.autoscaling.elb_healthcheck.grace_time_minutes', default=15)
        scaling_policy_target_utilization_percent = self.context.config().get_int('cluster-manager.ec2.autoscaling.cpu_utilization_scaling_policy.target_utilization_percent', default=80)
        scaling_policy_estimated_instance_warmup_minutes = self.context.config().get_int('cluster-manager.ec2.autoscaling.cpu_utilization_scaling_policy.estimated_instance_warmup_minutes', default=15)
        rolling_update_max_batch_size = self.context.config().get_int('cluster-manager.ec2.autoscaling.rolling_update_policy.max_batch_size', default=1)
        rolling_update_min_instances_in_service = self.context.config().get_int('cluster-manager.ec2.autoscaling.rolling_update_policy.min_instances_in_service', default=1)
        rolling_update_pause_time_minutes = self.context.config().get_int('cluster-manager.ec2.autoscaling.rolling_update_policy.pause_time_minutes', default=15)
        metadata_http_tokens = self.context.config().get_string('cluster-manager.ec2.autoscaling.metadata_http_tokens', required=True)
        https_proxy = self.context.config().get_string('cluster.network.https_proxy', required=False, default='')
        no_proxy = self.context.config().get_string('cluster.network.no_proxy', required=False, default='')
        proxy_config = {}
        if Utils.is_not_empty(https_proxy):
            proxy_config = {
                    'http_proxy': https_proxy,
                    'https_proxy': https_proxy,
                    'no_proxy': no_proxy
                    }
        kms_key_id = self.context.config().get_string('cluster.ebs.kms_key_id', required=False, default=None)
        if kms_key_id is not None:
             kms_key_arn = self.get_kms_key_arn(kms_key_id)
             ebs_kms_key = kms.Key.from_key_arn(scope=self.stack, id=f'ebs-kms-key', key_arn=kms_key_arn)
        else:
             ebs_kms_key = kms.Alias.from_alias_name(scope=self.stack, id=f'ebs-kms-key-default', alias_name='alias/aws/ebs')

        if is_public:
            vpc_subnets = ec2.SubnetSelection(
                subnets=self.cluster.public_subnets
            )
        else:
            vpc_subnets = ec2.SubnetSelection(
                subnets=self.cluster.private_subnets
            )

        block_device_name = Utils.get_ec2_block_device_name(base_os)

        user_data = BootstrapUserDataBuilder(
            aws_region=self.aws_region,
            bootstrap_package_uri=self.bootstrap_package_uri,
            install_commands=[
                '/bin/bash cluster-manager/setup.sh'
            ],
            proxy_config=proxy_config,
            base_os=base_os,
            bootstrap_source_dir_path=ideaadministrator.props.bootstrap_source_dir
        ).build()

        launch_template = ec2.LaunchTemplate(
            self.stack, f'{self.module_id}-lt',
            instance_type=ec2.InstanceType(instance_type),
            machine_image=ec2.MachineImage.generic_linux({
                self.aws_region: instance_ami
            }),
            security_group=self.cluster_manager_security_group,
            user_data=ec2.UserData.custom(cdk.Fn.sub(user_data)),
            key_name=key_pair_name,
            block_devices=[ec2.BlockDevice(
                device_name=block_device_name,
                volume=ec2.BlockDeviceVolume(ebs_device=ec2.EbsDeviceProps(
                    encrypted=True,
                    kms_key=ebs_kms_key,
                    volume_size=volume_size,
                    volume_type=ec2.EbsDeviceVolumeType.GP3
                ))
            )],
            role=self.cluster_manager_role,
            require_imdsv2=True if metadata_http_tokens == "required" else False,
            associate_public_ip_address=is_public
        )

        self.auto_scaling_group = asg.AutoScalingGroup(
            self.stack, 'cluster-manager-asg',
            vpc=self.cluster.vpc,
            vpc_subnets=vpc_subnets,
            auto_scaling_group_name=f'{self.cluster_name}-{self.module_id}-asg',
            launch_template=launch_template,
            instance_monitoring=asg.Monitoring.DETAILED if enable_detailed_monitoring else asg.Monitoring.BASIC,
            group_metrics=[asg.GroupMetrics.all()],
            min_capacity=min_capacity,
            max_capacity=max_capacity,
            new_instances_protected_from_scale_in=new_instances_protected_from_scale_in,
            default_instance_warmup=cdk.Duration.minutes(default_instance_warmup),
            cooldown=cdk.Duration.minutes(cooldown_minutes),
            health_check=asg.HealthCheck.elb(
                grace=cdk.Duration.minutes(elb_healthcheck_grace_time_minutes)
            ),
            update_policy=asg.UpdatePolicy.rolling_update(
                max_batch_size=rolling_update_max_batch_size,
                min_instances_in_service=rolling_update_min_instances_in_service,
                pause_time=cdk.Duration.minutes(rolling_update_pause_time_minutes)
            ),
            termination_policies=[
                asg.TerminationPolicy.DEFAULT
            ]
        )

        self.auto_scaling_group.scale_on_cpu_utilization(
            'cpu-utilization-scaling-policy',
            target_utilization_percent=scaling_policy_target_utilization_percent,
            estimated_instance_warmup=cdk.Duration.minutes(scaling_policy_estimated_instance_warmup_minutes)
        )

        cdk.Tags.of(self.auto_scaling_group).add(constants.IDEA_TAG_NODE_TYPE, constants.NODE_TYPE_APP)
        cdk.Tags.of(self.auto_scaling_group).add(constants.IDEA_TAG_NAME, f'{self.cluster_name}-{self.module_id}')
        self.auto_scaling_group.node.add_dependency(self.notifications_sqs_queue)

        if not enable_detailed_monitoring:
            self.add_nag_suppression(
                construct=self.auto_scaling_group,
                suppressions=[IdeaNagSuppression(rule_id='AwsSolutions-EC28', reason='detailed monitoring is a configurable option to save costs')],
                apply_to_children=True
            )

        self.add_nag_suppression(
            construct=self.auto_scaling_group,
            suppressions=[
                IdeaNagSuppression(rule_id='AwsSolutions-AS3', reason='ASG notifications scaling notifications can be managed via AWS Console')
            ]
        )