Dropwizard bundle to signal the AWS AutoScalingGroup via the AWS CloudFormation SignalResource API when running on an AWS EC2 instance.
If you're deploying Dropwizard webservices on AWS using CloudFormation Templates and AutoScaling Groups with Rolling Updates then this is the bundle you've been looking for!
In short it makes your time to perform a rolling deploy as short as possible as it only depends on how fast your app can start. It's very handy when you app needs to do some work at startup sometimes (database migrations, provisioning, etc), but not others.
The key here is to set the Resources.<YourASG_Name>.UpdatePolicy.WaitOnResourceSignals to true. You can read up on what that means exactly here but the TL;DR is have the rolling update wait until signaled that everything worked to continue. This bundle provides that signaling!
{
"AWSTemplateFormatVersion" : "2010-09-09",
"Description" : "AWS CloudFormation for a Dropwizard.io Webservice using instances in a AutoScalingGroup.",
"Parameters" : {...},
"Resources" : {
"DropwizardWebserviceASG" : {
"Type" : "AWS::AutoScaling::AutoScalingGroup",
"UpdatePolicy" : {
"AutoScalingRollingUpdate": {
"MaxBatchSize": "2",
"MinInstancesInService": 2,
"PauseTime": "PT10M",
"SuspendProcesses": [
"HealthCheck",
"ReplaceUnhealthy",
"AZRebalance",
"AlarmNotification",
"ScheduledActions"
],
"WaitOnResourceSignals": "true"
}
},
"Properties" : {
"AvailabilityZones" : [
"us-west-2a",
"us-west-2b"
],
"LaunchConfigurationName" : { "Ref" : "LaunchConfig" },
"MinSize" : 2,
"MaxSize" : 8
}
},
{...other resources}
}
}
The credentials used by your CloudFormation client need to have the following permissions:
- cloudformation:DescribeStackResource
- Used to confirm that a stack update is in progress, before attempting to signal the resource state.
- cloudformation:SignalResource
Add this to your Dropwizard Webservice pom.xml.
<dependency>
<groupId>net.eldeen.dropwizard</groupId>
<artifactId>cf-signal-resource-bundle</artifactId>
<version>2.1</version>
<dependency>
In your class that extends io.dropwizard.Application add the bundle. Use config to provide the name of the AutoScalingGroup,
the Stack Name, and the AWS Region.
public class Main extends Application <AppConfig> {
@Override
public void initialize(Bootstrap<StipendConfig> bootstrap) {
bootstrap.addBundle(new CfSignalResourceBundle());
}
// [...]
}
Your application's Dropwizard Config should look like this:
public class AppConfig extends Configuration {
@Valid
@NotNull
private CfSignalResourceConfig cfSignalResource;
@JsonProperty
public CfSignalResourceConfig getCfSignalResource() {
return cfSignalResourceConfig;
}
// [...]
}
And add the actual config values to your configuration yml. The config values awsRegion and ec2InstanceId are optional. If missing they will be fetched from
com.amazonaws.util.EC2MetadataUtils#getEC2InstanceRegion() and com.amazonaws.util.EC2MetadataUtils#getInstanceId() respectively.
Typical config:
cfSignalResource:
asgResourceName: yourASG_ResourceName
stackName: yourASG_StackName
Full config:
cfSignalResource:
asgResourceName: yourASG_ResourceName
stackName: yourASG_StackName
ec2InstanceId: yourASG_spun_up_ec2InstanceId
awsRegion: us-west-2
If you have the case where you deploy the same Dropwizard Artifact in environments that are not AWS, you'll want to skip trying to signal an ASG that isn't there. Setup your config for that environment like so:
cfSignalResource:
skip: true
All the other values are ignored when skip: true is present so they can be omitted. The CfSignalResourceBundle will
not attempt to do any AWS related calls when this value is true.
If you have these values as environment variables you may want to have Dropwizard use those instead.
public class Main extends Application<AppConfig> {
// [...]
@Override
public void initialize(Bootstrap<AppConfig> bootstrap) {
// Enable variable substitution with environment variables
bootstrap.setConfigurationSourceProvider(
new SubstitutingSourceProvider(bootstrap.getConfigurationSourceProvider(),
new EnvironmentVariableSubstitutor()
)
);
}
// [...]
}
The config yaml can use the environment variable values, or provide defaults if they aren't set, following the rules of the configured EnvironmentVariableSubstitutor from above.
cfSignalResource:
asgResourceName: ${ASG_RESOURCE_NAME}
stackName: ${ASG_STACK_NAME:-yourASG_StackName}