Skip to content

Commit

Permalink
first commit
Browse files Browse the repository at this point in the history
  • Loading branch information
enghwa committed Jul 8, 2019
1 parent 740498a commit f4127e9
Show file tree
Hide file tree
Showing 26 changed files with 1,743 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
node_modules
synth
cdk.context.json
6 changes: 6 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"editor.tabSize": 2,
"editor.insertSpaces": true,
"explorer.autoReveal": false,
"editor.minimap.enabled": false
}
263 changes: 263 additions & 0 deletions app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,263 @@
const cdk = require('@aws-cdk/core');
const ecs = require('@aws-cdk/aws-ecs');
const ec2 = require('@aws-cdk/aws-ec2');
const rds = require('@aws-cdk/aws-rds');
const ecs_patterns = require('@aws-cdk/aws-ecs-patterns');
const cloudwatch = require('@aws-cdk/aws-cloudwatch');
const iam = require('@aws-cdk/aws-iam');
const ssm = require('@aws-cdk/aws-ssm');
const kms = require('@aws-cdk/aws-kms');

class BaseInfraResources extends cdk.Stack {
constructor(parent, id, props) {
super(parent, id, props);

// Network to run everything in
this.vpc = new ec2.Vpc(this, 'vpc-springgroot', {
maxAZs: 3,
natGateways: 1
});
// Cluster all the containers will run in
this.cluster = new ecs.Cluster(this, 'springgroot-cluster', { vpc: this.vpc });
}
}

// RDS
class MySQLDatabase extends cdk.Stack {
constructor(parent, id, props) {
super(parent, id, props);

const dbsecuritygroup = new ec2.SecurityGroup(this, 'dbsg', {
vpc: props.vpc,
description: "database security group"
})

dbsecuritygroup.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tcp(3306), "Allow inbound to db")

const subnetGroup = new rds.CfnDBSubnetGroup(this, 'Subnet', {
subnetIds: props.vpc.privateSubnets.map(privateSubnet => privateSubnet.subnetId),
dbSubnetGroupDescription: 'Database subnet group',
});

let ssmdbpassword = ssm.StringParameter.fromSecureStringParameterAttributes(this, 'ssmDBpassword', {
parameterName: '/mysqlpassword',
version: 1
})

this.rds = new rds.CfnDBInstance(this, "mysql-single-instance", {
allocatedStorage: '80',
dbInstanceClass: 'db.m5.large',
engine: 'mysql',
dbName: 'notes_app',
engineVersion: '5.7.25',
masterUsername: 'dbaadmin',
masterUserPassword: ssmdbpassword.stringValue,
dbSubnetGroupName: subnetGroup.ref,
vpcSecurityGroups: [dbsecuritygroup.securityGroupId]
})
}
}

class FargateService extends cdk.Stack {
constructor(parent, id, props) {
super(parent, id, props);

this.springgroot = new ecs_patterns.LoadBalancedFargateService(this, 'springgrootsvc', {
cluster: props.cluster,
image: ecs.ContainerImage.fromAsset('./springgroot-jpa'),
containerPort: 8080,
desiredCount: 2,
cpu: '512',
memoryLimitMiB: '1024',
environment: {
// AWS_XRAY_CONTEXT_MISSING: 'LOG_ERROR'
'springdatasourceurl': `jdbc:mysql://` + props.springgrootDB.attrEndpointAddress + `:3306/notes_app?autoReconnect=true&useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true`,
'springdatasourceusername': 'dbaadmin'
},
createLogs: true
})

//customize healthcheck
this.springgroot.targetGroup.configureHealthCheck({
"port": 'traffic-port',
"path": '/',
"intervalSecs": 5,
"timeoutSeconds": 4,
"healthyThresholdCount": 2,
"unhealthyThresholdCount": 2,
"healthyHttpCodes": "200,301,302"
})

//https://docs.aws.amazon.com/AmazonECS/latest/developerguide/specifying-sensitive-data.html
let ssmMasterKey = kms.Key.fromKeyArn(this, 'ssmmasterkey', 'arn:aws:kms:' + this.region + ':' + this.account + ':alias/aws/ssm')
ssmMasterKey.grantDecrypt(this.springgroot.service.taskDefinition.executionRole)

let ssmdbpassword = ssm.StringParameter.fromSecureStringParameterAttributes(this, 'ssmDBpassword', {
parameterName: '/mysqlpassword',
version: 1
})
// ssmdbpassword.grantRead(this.springgroot.service.taskDefinition.executionRole) -> can't use this as it gives "ssm:GetParameter"
this.springgroot.service.taskDefinition.executionRole.addToPolicy(
new iam.PolicyStatement({
actions: ["ssm:GetParameters"],
resources: [ssmdbpassword.parameterArn]
})
)

// Work Around Missing AWS CDK Features - https://docs.aws.amazon.com/cdk/latest/guide/cfn_layer.html
// https://github.com/awslabs/aws-cdk/pull/2994
let fgs = this.springgroot.service.taskDefinition.node.findChild('Resource')
fgs.addPropertyOverride('ContainerDefinitions.0.Secrets', [{
Name: "mysqlpassword",
ValueFrom: ssmdbpassword.parameterArn
}])

// ## Autoscaling Tasks - Target Tracking
let springgrootServiceAutoScaleTask = this.springgroot.service.autoScaleTaskCount({
maxCapacity: 20,
minCapacity: 2
})

// custom metric target tracking
// while true; do aws cloudwatch put-metric-data --metric-name CDKTestingCustomMetric --namespace "CDK/Testing" --value $(( ( RANDOM % 10 ) + 180 )); sleep 60; done
springgrootServiceAutoScaleTask.scaleToTrackCustomMetric('CustomMetricScaling', {
metric: new cloudwatch.Metric({
namespace: "CDK/Testing",
metricName: 'CDKTestingCustomMetric',
statistic: 'avg',
periodSec: 60
}),
targetValue: 150,
scaleInCooldownSec: 60, //default 300
scaleOutCooldownSec: 60,
policyName: "KeepIt150"
})

// ==== Cloudwatch Dashboard
this.dashboard = new cloudwatch.Dashboard(this, "springgroot2-dashboard");
this.dashboard.addWidgets(
new cloudwatch.TextWidget({
markdown: '# Springboot on Fargate Demo Dashboard',
width: 24
})
)

this.dashboard.addWidgets(new cloudwatch.GraphWidget({
title: "springgroot Task Count",
width: 8,
leftYAxis: {
min: 0
},
left: [new cloudwatch.Metric({
namespace: "AWS/ECS",
metricName: 'CPUUtilization',
label: "Running",
dimensions: {
ServiceName: this.springgroot.service.serviceName,
ClusterName: props.cluster.clusterName
},
statistic: 'n',
period: cdk.Duration.minutes(1)
})]
}),
new cloudwatch.GraphWidget({
title: "ReqCountPerTarget",
left: [new cloudwatch.Metric({
namespace: "AWS/ApplicationELB",
metricName: "RequestCountPerTarget",
dimensions: {
TargetGroup: this.springgroot.targetGroup.targetGroupFullName,
LoadBalancer: this.springgroot.loadBalancer.loadBalancerFullName
},
color: '#98df8a',
statistic: 'sum',
period: cdk.Duration.minutes(1)
})
],
stacked: true
}),
new cloudwatch.GraphWidget({
title: "Custom Metric",
left: [new cloudwatch.Metric({
namespace: "CDK/Testing",
metricName: "CDKTestingCustomMetric",
color: '#d62728',
statistic: 'avg',
period: cdk.Duration.minutes(1),
// HorizontalAnnotation: {
// value: 150,
// label: "breach 150"
// }
})
]
})
)

this.dashboard.addWidgets(new cloudwatch.GraphWidget({
title: "springgroot Task CPU",
width: 8,
leftYAxis: {
min: 0
},
left: [
new cloudwatch.Metric({
namespace: "AWS/ECS",
metricName: 'CPUUtilization',
label: "CPUUtilization",
dimensions: {
ServiceName: this.springgroot.service.serviceName,
ClusterName: props.cluster.clusterName
},
statistic: 'avg',
period: cdk.Duration.minutes(1)
})],
right: [new cloudwatch.Metric({
namespace: "AWS/ECS",
metricName: 'MemoryUtilization',
label: "MemoryUtilization",
dimensions: {
ServiceName: this.springgroot.service.serviceName,
ClusterName: props.cluster.clusterName
},
statistic: 'avg',
period: cdk.Duration.minutes(1)
})]
}),
new cloudwatch.GraphWidget({
title: "TargetResponseTime (P95)",
left: [new cloudwatch.Metric({
namespace: "AWS/ApplicationELB",
metricName: "TargetResponseTime",
dimensions: {
TargetGroup: this.springgroot.targetGroup.targetGroupFullName,
LoadBalancer: this.springgroot.loadBalancer.loadBalancerFullName
},
color: '#2ca02c',
statistic: 'p95',
period: cdk.Duration.minutes(1)
})
],
stacked: true
}),
)
// -- end CW Dashboard
}
}

class App extends cdk.App {
constructor(argv) {
super(argv);
this.baseResources = new BaseInfraResources(this, 'springgroot-base-infra');

this.springgrootDB = new MySQLDatabase(this, "springgroot-db", {
vpc: this.baseResources.vpc,
ecscluster: this.baseResources.cluster
});

this.springbootApp = new FargateService(this, 'springgroot-fargate-svc', {
cluster: this.baseResources.cluster,
springgrootDB: this.springgrootDB.rds
})
}
}
new App().synth();
3 changes: 3 additions & 0 deletions cdk.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"app": "node app1.js"
}
23 changes: 23 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"description": "cdk quick start",
"devDependencies": {
"aws-cdk": "0.36.1",
"@aws-cdk/core": "0.36.1",
"@aws-cdk/aws-ecs": "0.36.1"
},
"scripts": {
"diff": "cdk diff",
"deploy": "cdk deploy --require-approval never",
"synth": "cdk synth --output synth",
"destroy": "cdk destroy"
},
"dependencies": {
"@aws-cdk/aws-elasticloadbalancingv2": "^0.36.1",
"@aws-cdk/aws-ecs-patterns": "^0.36.1",
"@aws-cdk/aws-iam": "^0.36.1",
"@aws-cdk/aws-logs": "^0.36.1",
"@aws-cdk/aws-rds": "^0.36.1",
"@aws-cdk/aws-ssm": "^0.36.1",
"@aws-cdk/aws-kms": "^0.36.1"
}
}
29 changes: 29 additions & 0 deletions springgroot-jpa/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
HELP.md
/target/
!.mvn/wrapper/maven-wrapper.jar

### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache

### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr

### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
/build/

### VS Code ###
.vscode/
Loading

0 comments on commit f4127e9

Please sign in to comment.