Completed DynamoDB + DAX Benchmarker with a nice TUI to boot
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
import { Tags } from "aws-cdk-lib";
|
||||
import { Construct } from "constructs";
|
||||
import { EnvironmentProps } from "./types";
|
||||
import { Instance, InstanceClass, InstanceSize, InstanceType, MachineImage, Peer, Port, SecurityGroup, SubnetType, Vpc } from "aws-cdk-lib/aws-ec2";
|
||||
import { IRole, Role, ServicePrincipal } from "aws-cdk-lib/aws-iam";
|
||||
|
||||
export class DaxBastionHost extends Construct {
|
||||
public readonly instanceRole: IRole;
|
||||
public readonly instance: Instance;
|
||||
|
||||
constructor(scope: Construct, id: string, environmentProps: EnvironmentProps, daxSecurityGroup: SecurityGroup) {
|
||||
super(scope, id);
|
||||
|
||||
Tags.of(this).add('Application', 'dynamodb-dax-benchmarker');
|
||||
|
||||
const { removalPolicy, user, vpcId, localIp, sshKeyName } = environmentProps;
|
||||
const localIpCidr = `${localIp}/32`;
|
||||
|
||||
const vpc = Vpc.fromLookup(this, 'Vpc', { vpcId });
|
||||
|
||||
const bastionHostSecurityGroup = new SecurityGroup(this, `${user}-dax-sg`, {
|
||||
vpc,
|
||||
description: `Allow SSH, Elasticsearch, and DAX access for ${user}`,
|
||||
securityGroupName: `${user}-dax-bastion-host-sg`
|
||||
});
|
||||
bastionHostSecurityGroup.applyRemovalPolicy(removalPolicy);
|
||||
bastionHostSecurityGroup.addIngressRule(Peer.ipv4(localIpCidr), Port.tcp(22), "Allow SSH access to this instance from the users public IP");
|
||||
bastionHostSecurityGroup.addIngressRule(Peer.ipv4(localIpCidr), Port.tcp(9200), "Allow the host to communicate with the users locally running Elasticsearch cluster");
|
||||
bastionHostSecurityGroup.addIngressRule(daxSecurityGroup, Port.allTraffic());
|
||||
daxSecurityGroup.addIngressRule(bastionHostSecurityGroup, Port.allTraffic());
|
||||
|
||||
this.instanceRole = new Role(this, `${user}-bastion-role`, {
|
||||
roleName: `${user}-bastion-role`,
|
||||
assumedBy: new ServicePrincipal('ec2.amazonaws.com'),
|
||||
});
|
||||
this.instanceRole.applyRemovalPolicy(removalPolicy);
|
||||
|
||||
this.instance = new Instance(this, `${user}-dax-bastion-host`, {
|
||||
vpc,
|
||||
instanceType: InstanceType.of(InstanceClass.T2, InstanceSize.SMALL),
|
||||
machineImage: MachineImage.latestAmazonLinux2023(),
|
||||
instanceName: `${user}-dax-bastion-host`,
|
||||
keyName: sshKeyName,
|
||||
vpcSubnets: vpc.selectSubnets({ subnetType: SubnetType.PUBLIC }),
|
||||
securityGroup: bastionHostSecurityGroup,
|
||||
role: this.instanceRole
|
||||
});
|
||||
this.instance.applyRemovalPolicy(removalPolicy);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
import { Construct } from "constructs";
|
||||
import { EnvironmentProps } from "./types";
|
||||
import { CfnOutput, Stack, Tags } from "aws-cdk-lib";
|
||||
import { CfnCluster, CfnSubnetGroup } from "aws-cdk-lib/aws-dax";
|
||||
import { Effect, PolicyDocument, PolicyStatement, Role, ServicePrincipal } from "aws-cdk-lib/aws-iam";
|
||||
import { SecurityGroup, SubnetType, Vpc } from "aws-cdk-lib/aws-ec2";
|
||||
import { DynamoDbBenchmarkTable } from "./dynamodb";
|
||||
import { DaxBastionHost } from "./bastion-host";
|
||||
|
||||
export class DaxBenchmarkingStack extends Stack {
|
||||
constructor(scope: Construct, id: string, environmentProps: EnvironmentProps) {
|
||||
super(scope, id, environmentProps);
|
||||
|
||||
Tags.of(this).add('Application', 'dynamodb-dax-benchmarker');
|
||||
|
||||
const { user, removalPolicy, vpcId } = environmentProps;
|
||||
const { table } = new DynamoDbBenchmarkTable(this, `${user}-dynamodb-benchmark-table`, environmentProps);
|
||||
|
||||
const vpc = Vpc.fromLookup(this, 'Vpc', { vpcId });
|
||||
|
||||
const daxSecurityGroup = new SecurityGroup(this, `${user}-dax-sg`, {
|
||||
vpc,
|
||||
securityGroupName: `${user}-dax-sg`
|
||||
});
|
||||
daxSecurityGroup.applyRemovalPolicy(removalPolicy);
|
||||
|
||||
const { instanceRole, instance } = new DaxBastionHost(this, `${user}-dax-bastion-host`, environmentProps, daxSecurityGroup);
|
||||
|
||||
const daxClusterName = `${user}-high-velocity`;
|
||||
const daxFullAccessPolicy = new PolicyStatement({
|
||||
effect: Effect.ALLOW,
|
||||
actions: [
|
||||
"dynamodb:BatchGetItem",
|
||||
"dynamodb:GetItem",
|
||||
"dynamodb:Query",
|
||||
"dynamodb:Scan",
|
||||
"dynamodb:BatchWriteItem",
|
||||
"dynamodb:DeleteItem",
|
||||
"dynamodb:PutItem",
|
||||
"dynamodb:UpdateItem",
|
||||
"dynamodb:DescribeLimits",
|
||||
"dynamodb:DescribeTimeToLive",
|
||||
"dynamodb:DescribeTable",
|
||||
"dynamodb:ListTables"
|
||||
],
|
||||
resources: [table.tableArn]
|
||||
});
|
||||
|
||||
const daxServiceRole = new Role(this, `${daxClusterName}-role`, {
|
||||
assumedBy: new ServicePrincipal("dax.amazonaws.com"),
|
||||
inlinePolicies: {
|
||||
DAXFullAccess: new PolicyDocument({
|
||||
statements: [daxFullAccessPolicy]
|
||||
})
|
||||
}
|
||||
});
|
||||
daxServiceRole.applyRemovalPolicy(removalPolicy);
|
||||
|
||||
instanceRole.addToPrincipalPolicy(daxFullAccessPolicy);
|
||||
|
||||
const subnetGroup = new CfnSubnetGroup(this, `${user}-dax-subnet-group`, {
|
||||
subnetIds: vpc.selectSubnets({
|
||||
subnetType: SubnetType.PRIVATE_ISOLATED
|
||||
}).subnetIds,
|
||||
subnetGroupName: `${user}-dax-subnet-group`,
|
||||
});
|
||||
subnetGroup.applyRemovalPolicy(removalPolicy);
|
||||
|
||||
const daxCluster = new CfnCluster(this, daxClusterName, {
|
||||
iamRoleArn: daxServiceRole.roleArn,
|
||||
nodeType: 'dax.r5.large',
|
||||
replicationFactor: 3,
|
||||
securityGroupIds: [daxSecurityGroup.securityGroupId],
|
||||
subnetGroupName: subnetGroup.subnetGroupName,
|
||||
availabilityZones: vpc.availabilityZones,
|
||||
clusterEndpointEncryptionType: 'TLS',
|
||||
clusterName: daxClusterName,
|
||||
sseSpecification: {
|
||||
sseEnabled: true,
|
||||
}
|
||||
});
|
||||
daxCluster.applyRemovalPolicy(removalPolicy);
|
||||
daxCluster.addDependency(subnetGroup);
|
||||
|
||||
new CfnOutput(this, 'DaxEndpoint', { value: daxCluster.attrClusterDiscoveryEndpointUrl });
|
||||
new CfnOutput(this, 'InstanceId', { value: instance.instanceId });
|
||||
new CfnOutput(this, 'InstancePublicIp', { value: instance.instancePublicIp });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import {Tags} from "aws-cdk-lib";
|
||||
import {Construct} from "constructs";
|
||||
import {EnvironmentProps} from "./types";
|
||||
import {AttributeType, BillingMode, Table} from "aws-cdk-lib/aws-dynamodb";
|
||||
|
||||
export class DynamoDbBenchmarkTable extends Construct {
|
||||
public readonly table: Table;
|
||||
|
||||
constructor(scope: Construct, id: string, environmentProps: EnvironmentProps) {
|
||||
super(scope, id);
|
||||
|
||||
Tags.of(this).add('Application', 'dynamodb-dax-benchmarker');
|
||||
|
||||
const { baseTableName, removalPolicy, user } = environmentProps;
|
||||
const tableName = `${user}-${baseTableName}`;
|
||||
|
||||
this.table = new Table(this, tableName, {
|
||||
partitionKey: {
|
||||
name: 'id',
|
||||
type: AttributeType.STRING
|
||||
},
|
||||
tableName,
|
||||
removalPolicy,
|
||||
billingMode: BillingMode.PAY_PER_REQUEST
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import {RemovalPolicy, StackProps} from "aws-cdk-lib";
|
||||
|
||||
export interface EnvironmentProps extends StackProps {
|
||||
readonly baseTableName: string
|
||||
readonly removalPolicy: RemovalPolicy
|
||||
readonly user: string
|
||||
readonly vpcId: string
|
||||
readonly localIp: string
|
||||
readonly sshKeyName: string
|
||||
}
|
||||
Reference in New Issue
Block a user