Skip to content

Commit

Permalink
added a java example for creating an ApplicationLoadBalancer with an …
Browse files Browse the repository at this point in the history
…AutoScalingGroup (#1105)

* added a java example for creating an ApplicationLoadBalancer with an AutoScalingGroup

* Update pom.xml

Change to supported release version

---------

Co-authored-by: Michael Kaiser <[email protected]>
Co-authored-by: Michael Kaiser <[email protected]>
  • Loading branch information
3 people authored Dec 27, 2024
1 parent 1e14ba1 commit 8a39ebd
Show file tree
Hide file tree
Showing 7 changed files with 435 additions and 0 deletions.
13 changes: 13 additions & 0 deletions java/application-load-balancer/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
.classpath.txt
target
.classpath
.project
.idea
.settings
.vscode
*.iml

# CDK asset staging directory
.cdk.staging
cdk.out

54 changes: 54 additions & 0 deletions java/application-load-balancer/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Application Load Balancer


<!--BEGIN STABILITY BANNER-->

---

![Stability: Stable](https://img.shields.io/badge/stability-Stable-success.svg?style=for-the-badge)

> **This is a stable example. It should successfully build out of the box**
>
> This example is built on Construct Libraries marked "Stable" and does not have any infrastructure prerequisites to build.
---
<!--END STABILITY BANNER-->

This example creates an AutoScalingGroup (containing 2 AWS Graviton2 Micro-T4G EC2 machines running the Amazon Linux 2023 AMI), and an ApplicationLoadBalancer inside a shared VPC.
It hooks up an open listener from the Load Balancer to the Scaling Group to indicate how many targets to balance between.

For more info on using Auto Scaling with Load Balancing see the AWS guide [here](https://docs.aws.amazon.com/autoscaling/ec2/userguide/autoscaling-load-balancer.html).

## Build

To build this example, you need to be in this example's root directory.
Then, run the following:

```bash
npm install -g aws-cdk
npm install
cdk synth
```

This will install the necessary CDK, then this example's dependencies, and then build the CloudFormation template.
The resulting CloudFormation template will be in the `cdk.out` directory.

## Deploy

Run `cdk deploy`.
This will deploy / redeploy the stack to AWS.
After a successful deployment, the URL of the ALB created will be available in the output of the terminal console.
At this point, the ALB URL can be used for testing.
Copy the ALB URL and paste it in the address bar of a browser.
The ALB will route the received requests between the 2 EC2 instances created initially by the ASG.
You can observe that the requests made reach different EC2 instances.
This can be observed based on the content of the web page displayed in the browser (the hello message on the web page contains the host which is different for each EC2 instance).

## Useful commands

* `mvn package` compile and run tests
* `cdk ls` list all stacks in the app
* `cdk synth` emits the synthesized CloudFormation template
* `cdk deploy` deploy this stack to your default AWS account/region
* `cdk diff` compare deployed stack with current state
* `cdk docs` open CDK documentation
76 changes: 76 additions & 0 deletions java/application-load-balancer/cdk.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
{
"app": "mvn -e -q compile exec:java",
"watch": {
"include": [
"**"
],
"exclude": [
"README.md",
"cdk*.json",
"target",
"pom.xml",
"src/test"
]
},
"context": {
"@aws-cdk/aws-lambda:recognizeLayerVersion": true,
"@aws-cdk/core:checkSecretUsage": true,
"@aws-cdk/core:target-partitions": [
"aws",
"aws-cn"
],
"@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true,
"@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true,
"@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true,
"@aws-cdk/aws-iam:minimizePolicies": true,
"@aws-cdk/core:validateSnapshotRemovalPolicy": true,
"@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true,
"@aws-cdk/aws-s3:createDefaultLoggingPolicy": true,
"@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true,
"@aws-cdk/aws-apigateway:disableCloudWatchRole": true,
"@aws-cdk/core:enablePartitionLiterals": true,
"@aws-cdk/aws-events:eventsTargetQueueSameAccount": true,
"@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true,
"@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true,
"@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true,
"@aws-cdk/aws-route53-patters:useCertificate": true,
"@aws-cdk/customresources:installLatestAwsSdkDefault": false,
"@aws-cdk/aws-rds:databaseProxyUniqueResourceName": true,
"@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": true,
"@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": true,
"@aws-cdk/aws-ec2:launchTemplateDefaultUserData": true,
"@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": true,
"@aws-cdk/aws-redshift:columnId": true,
"@aws-cdk/aws-stepfunctions-tasks:enableEmrServicePolicyV2": true,
"@aws-cdk/aws-ec2:restrictDefaultSecurityGroup": true,
"@aws-cdk/aws-apigateway:requestValidatorUniqueId": true,
"@aws-cdk/aws-kms:aliasNameRef": true,
"@aws-cdk/aws-autoscaling:generateLaunchTemplateInsteadOfLaunchConfig": true,
"@aws-cdk/core:includePrefixInUniqueNameGeneration": true,
"@aws-cdk/aws-efs:denyAnonymousAccess": true,
"@aws-cdk/aws-opensearchservice:enableOpensearchMultiAzWithStandby": true,
"@aws-cdk/aws-lambda-nodejs:useLatestRuntimeVersion": true,
"@aws-cdk/aws-efs:mountTargetOrderInsensitiveLogicalId": true,
"@aws-cdk/aws-rds:auroraClusterChangeScopeOfInstanceParameterGroupWithEachParameters": true,
"@aws-cdk/aws-appsync:useArnForSourceApiAssociationIdentifier": true,
"@aws-cdk/aws-rds:preventRenderingDeprecatedCredentials": true,
"@aws-cdk/aws-codepipeline-actions:useNewDefaultBranchForCodeCommitSource": true,
"@aws-cdk/aws-cloudwatch-actions:changeLambdaPermissionLogicalIdForLambdaAction": true,
"@aws-cdk/aws-codepipeline:crossAccountKeysDefaultValueToFalse": true,
"@aws-cdk/aws-codepipeline:defaultPipelineTypeToV2": true,
"@aws-cdk/aws-kms:reduceCrossAccountRegionPolicyScope": true,
"@aws-cdk/aws-eks:nodegroupNameAttribute": true,
"@aws-cdk/aws-ec2:ebsDefaultGp3Volume": true,
"@aws-cdk/aws-ecs:removeDefaultDeploymentAlarm": true,
"@aws-cdk/custom-resources:logApiResponseDataPropertyTrueDefault": false,
"@aws-cdk/aws-s3:keepNotificationInImportedBucket": false,
"@aws-cdk/aws-ecs:reduceEc2FargateCloudWatchPermissions": true,
"@aws-cdk/aws-dynamodb:resourcePolicyPerReplica": true,
"@aws-cdk/aws-ec2:ec2SumTimeoutEnabled": true,
"@aws-cdk/aws-appsync:appSyncGraphQLAPIScopeLambdaPermission": true,
"@aws-cdk/aws-rds:setCorrectValueForDatabaseInstanceReadReplicaInstanceResourceId": true,
"@aws-cdk/core:cfnIncludeRejectComplexResourceUpdateCreatePolicyIntrinsics": true,
"@aws-cdk/aws-lambda-nodejs:sdkV3ExcludeSmithyPackages": true,
"@aws-cdk/aws-stepfunctions-tasks:fixRunEcsTaskPolicy": true
}
}
58 changes: 58 additions & 0 deletions java/application-load-balancer/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>

<groupId>com.myorg</groupId>
<artifactId>application-load-balancer</artifactId>
<version>0.1</version>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<cdk.version>2.171.1</cdk.version>
<constructs.version>[10.0.0,11.0.0)</constructs.version>
<junit.version>5.7.1</junit.version>
<maven.compiler.target>22</maven.compiler.target>
<maven.compiler.source>22</maven.compiler.source>
</properties>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<release>17</release>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<mainClass>com.myorg.ApplicationLoadBalancerApp</mainClass>
</configuration>
</plugin>
</plugins>
</build>

<dependencies>
<dependency>
<groupId>software.amazon.awscdk</groupId>
<artifactId>aws-cdk-lib</artifactId>
<version>${cdk.version}</version>
</dependency>
<dependency>
<groupId>software.constructs</groupId>
<artifactId>constructs</artifactId>
<version>${constructs.version}</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.myorg;

import software.amazon.awscdk.App;
import software.amazon.awscdk.StackProps;

public class ApplicationLoadBalancerApp {
public static void main(final String[] args) {
var app = new App();
var stackProps = StackProps.builder().build();
new ApplicationLoadBalancerStack(app, "ApplicationLoadBalancerStack", stackProps);
app.synth();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package com.myorg;

import software.amazon.awscdk.CfnOutput;
import software.amazon.awscdk.Stack;
import software.amazon.awscdk.StackProps;
import software.amazon.awscdk.services.autoscaling.AutoScalingGroup;
import software.amazon.awscdk.services.autoscaling.RequestCountScalingProps;
import software.amazon.awscdk.services.ec2.*;
import software.amazon.awscdk.services.elasticloadbalancingv2.AddApplicationTargetsProps;
import software.amazon.awscdk.services.elasticloadbalancingv2.ApplicationListenerProps;
import software.amazon.awscdk.services.elasticloadbalancingv2.ApplicationLoadBalancer;
import software.constructs.Construct;

import java.util.List;

public class ApplicationLoadBalancerStack extends Stack {

// This user data string is required to deploy a nginx web server to each EC2 instance created.
// A simple web page that displays the host name of the EC2 instance is also deployed to nginx.
// This is useful to check if the ALB created sends requests to different EC2 instances by observing the hostname displayed.
private static final String USER_DATA_CONTENT_NGINX = """
#!/bin/bash
dnf -y upgrade
dnf -y install nginx
systemctl start nginx
systemctl enable nginx
rm -rfv /usr/share/nginx/html/ && mkdir /usr/share/nginx/html/
echo "<h1>Hello world from $(hostname -f)</h1>" > /usr/share/nginx/html/index.html
""";

public ApplicationLoadBalancerStack(final Construct scope, final String id, final StackProps props) {
super(scope, id, props);
var vpc = Vpc.Builder.create(this, "VPC")
.restrictDefaultSecurityGroup(false)
.build();
var amazonLinuxImage = AmazonLinuxImage.Builder.create()
.generation(AmazonLinuxGeneration.AMAZON_LINUX_2023)
.cpuType(AmazonLinuxCpuType.ARM_64)
.userData(UserData.custom(USER_DATA_CONTENT_NGINX))
.build();
var autoScalingGroup = AutoScalingGroup.Builder.create(this, "ASG")
.vpc(vpc)
.instanceType(InstanceType.of(InstanceClass.BURSTABLE4_GRAVITON, InstanceSize.MICRO))
.machineImage(amazonLinuxImage)
// Change the desired capacity (default value of 1) so that 2 EC2 instances are created initially instead of one.
// This way the ALB will route requests between both instances right away making it easier to test load balancing functionality.
.desiredCapacity(2)
.maxCapacity(3)
.minCapacity(1)
.build();
var applicationLoadBalancer = ApplicationLoadBalancer.Builder.create(this, "LB")
.vpc(vpc)
.loadBalancerName("LB")
.internetFacing(Boolean.TRUE)
.build();
var applicationListenerProps = ApplicationListenerProps.builder()
.port(80)
.loadBalancer(applicationLoadBalancer)
.build();
var applicationListener = applicationLoadBalancer.addListener("Listener", applicationListenerProps);
var addApplicationTargetsProps = AddApplicationTargetsProps.builder()
.port(80)
.targets(List.of(autoScalingGroup))
.build();
applicationListener.addTargets("Target", addApplicationTargetsProps);
applicationListener.getConnections().allowDefaultPortFromAnyIpv4("Open to the world");
var requestCountScalingProps = RequestCountScalingProps.builder()
.targetRequestsPerMinute(60)
.build();
// This is an example on how to create a dynamic target tracking scaling policy.
// This is based on the ALB request count per target, but it is more difficult to test.
// The dynamic scaling policy should show up though in the AWS Management Console for the ASG created.
autoScalingGroup.scaleOnRequestCount("AModestLoad", requestCountScalingProps);
CfnOutput.Builder.create(this, "ApplicationLoadBalancerURL")
.value(applicationListener.getLoadBalancer().getLoadBalancerDnsName())
.description("The DNS of the application load balancer.")
.build();
}
}
Loading

0 comments on commit 8a39ebd

Please sign in to comment.