Skip to content

Commit

Permalink
Provide support to configure new instance types like r5.4xl/8xl, r6g.…
Browse files Browse the repository at this point in the history
…4xl/8xl and i3 instances (#47)

Signed-off-by: Sorabh Hamirwasia <hsorabh@amazon.com>
Co-authored-by: Sorabh Hamirwasia <hsorabh@amazon.com>
  • Loading branch information
sohami and Sorabh Hamirwasia committed Aug 1, 2023
1 parent 6a504e1 commit 440fe0e
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 12 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ In order to deploy both the stacks the user needs to provide a set of required a
| use50PercentHeap (Optional) | boolean | Boolean flag to use 50% of physical memory as heap. Default is 1GB. e.g., `--context use50PercentHeap=true` |
| isInternal (Optional) | boolean | Boolean flag to make network load balancer internal. Default is internet-facing e.g., `--context isInternal=true` |
| enableRemoteStore (Optional) | boolean | Boolean flag to enable Remote Store feature e.g., `--context enableRemoteStore=true`. See [Enable Remote Store Feature](#enable-remote-store-feature) for more details. |
| storageVolumeType (Optional) | string | EBS volume type for all the nodes (data, ml, cluster manager), defaults to gp2. See `lib/opensearch-config/node-config.ts` for available options. E.g., `-c storageVolumeType=gp3`. For SSD based instance (i.e. i3 family), it is used for root volume configuration. |



Expand Down
17 changes: 9 additions & 8 deletions lib/infra/infra-stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import {
SubnetType,
} from 'aws-cdk-lib/aws-ec2';
import { ManagedPolicy, Role, ServicePrincipal } from 'aws-cdk-lib/aws-iam';
import { AutoScalingGroup, BlockDeviceVolume, Signals } from 'aws-cdk-lib/aws-autoscaling';
import { AutoScalingGroup, BlockDeviceVolume, EbsDeviceVolumeType, Signals } from 'aws-cdk-lib/aws-autoscaling';
import { LogGroup, RetentionDays } from 'aws-cdk-lib/aws-logs';
import {
CfnOutput, RemovalPolicy, Stack, StackProps, Tags,
Expand Down Expand Up @@ -60,7 +60,8 @@ export interface infraProps extends StackProps{
readonly mlEc2InstanceType: InstanceType,
readonly use50PercentHeap: boolean,
readonly isInternal: boolean,
readonly enableRemoteStore: boolean
readonly enableRemoteStore: boolean,
readonly storageVolumeType: EbsDeviceVolumeType
}

export class InfraStack extends Stack {
Expand Down Expand Up @@ -144,7 +145,7 @@ export class InfraStack extends Stack {
securityGroup: props.securityGroup,
blockDevices: [{
deviceName: '/dev/xvda',
volume: BlockDeviceVolume.ebs(props.dataNodeStorage, { deleteOnTermination: true }),
volume: BlockDeviceVolume.ebs(props.dataNodeStorage, { deleteOnTermination: true, volumeType: props.storageVolumeType }),
}],
init: CloudFormationInit.fromElements(...InfraStack.getCfnInitElement(this, clusterLogGroup, props)),
initOptions: {
Expand Down Expand Up @@ -195,7 +196,7 @@ export class InfraStack extends Stack {
securityGroup: props.securityGroup,
blockDevices: [{
deviceName: '/dev/xvda',
volume: BlockDeviceVolume.ebs(50, { deleteOnTermination: true }),
volume: BlockDeviceVolume.ebs(50, { deleteOnTermination: true, volumeType: props.storageVolumeType }),
}],
init: CloudFormationInit.fromElements(...InfraStack.getCfnInitElement(this, clusterLogGroup, props, 'manager')),
initOptions: {
Expand Down Expand Up @@ -228,7 +229,7 @@ export class InfraStack extends Stack {
blockDevices: [{
deviceName: '/dev/xvda',
// eslint-disable-next-line max-len
volume: (seedConfig === 'seed-manager') ? BlockDeviceVolume.ebs(50, { deleteOnTermination: true }) : BlockDeviceVolume.ebs(props.dataNodeStorage, { deleteOnTermination: true }),
volume: (seedConfig === 'seed-manager') ? BlockDeviceVolume.ebs(50, { deleteOnTermination: true, volumeType: props.storageVolumeType }) : BlockDeviceVolume.ebs(props.dataNodeStorage, { deleteOnTermination: true, volumeType: props.storageVolumeType }),
}],
init: CloudFormationInit.fromElements(...InfraStack.getCfnInitElement(this, clusterLogGroup, props, seedConfig)),
initOptions: {
Expand All @@ -255,7 +256,7 @@ export class InfraStack extends Stack {
securityGroup: props.securityGroup,
blockDevices: [{
deviceName: '/dev/xvda',
volume: BlockDeviceVolume.ebs(props.dataNodeStorage, { deleteOnTermination: true }),
volume: BlockDeviceVolume.ebs(props.dataNodeStorage, { deleteOnTermination: true, volumeType: props.storageVolumeType }),
}],
init: CloudFormationInit.fromElements(...InfraStack.getCfnInitElement(this, clusterLogGroup, props, 'data')),
initOptions: {
Expand Down Expand Up @@ -285,7 +286,7 @@ export class InfraStack extends Stack {
securityGroup: props.securityGroup,
blockDevices: [{
deviceName: '/dev/xvda',
volume: BlockDeviceVolume.ebs(50, { deleteOnTermination: true }),
volume: BlockDeviceVolume.ebs(50, { deleteOnTermination: true, volumeType: props.storageVolumeType }),
}],
init: CloudFormationInit.fromElements(...InfraStack.getCfnInitElement(this, clusterLogGroup, props, 'client')),
initOptions: {
Expand Down Expand Up @@ -316,7 +317,7 @@ export class InfraStack extends Stack {
securityGroup: props.securityGroup,
blockDevices: [{
deviceName: '/dev/xvda',
volume: BlockDeviceVolume.ebs(props.mlNodeStorage, { deleteOnTermination: true }),
volume: BlockDeviceVolume.ebs(props.mlNodeStorage, { deleteOnTermination: true, volumeType: props.storageVolumeType }),
}],
init: CloudFormationInit.fromElements(...InfraStack.getCfnInitElement(this, clusterLogGroup, props, 'ml')),
initOptions: {
Expand Down
41 changes: 41 additions & 0 deletions lib/opensearch-config/node-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ this file be licensed under the Apache-2.0 license or a
compatible open source license. */

import { InstanceClass, InstanceSize, InstanceType } from 'aws-cdk-lib/aws-ec2';
import { EbsDeviceVolumeType } from 'aws-cdk-lib/aws-autoscaling';

export const nodeConfig = new Map<string, object>();

Expand Down Expand Up @@ -45,8 +46,15 @@ export enum x64Ec2InstanceType {
R5_LARGE = 'r5.large',
R5_XLARGE = 'r5.xlarge',
R5_2XLARGE = 'r5.2xlarge',
R5_4XLARGE = 'r5.4xlarge',
R5_8XLARGE = 'r5.8xlarge',
G5_LARGE = 'g5.large',
G5_XLARGE = 'g5.xlarge',
I3_LARGE = 'i3.large',
I3_XLARGE = 'i3.xlarge',
I3_2XLARGE = 'i3.2xlarge',
I3_4XLARGE = 'i3.4xlarge',
I3_8XLARGE = 'i3.8xlarge',
INF1_XLARGE = 'inf1.xlarge',
INF1_2XLARGE = 'inf1.2xlarge'
}
Expand All @@ -59,6 +67,8 @@ export enum arm64Ec2InstanceType {
R6G_LARGE = 'r6g.large',
R6G_XLARGE = 'r6g.xlarge',
R6G_2XLARGE = 'r6g.2xlarge',
R6G_4XLARGE = 'r6g.4xlarge',
R6G_8XLARGE = 'r6g.8xlarge',
G5G_LARGE = 'g5g.large',
G5G_XLARGE = 'g5g.xlarge'
}
Expand All @@ -79,10 +89,24 @@ export const getX64InstanceTypes = (instanceType: string) => {
return InstanceType.of(InstanceClass.R5, InstanceSize.XLARGE);
case x64Ec2InstanceType.R5_2XLARGE:
return InstanceType.of(InstanceClass.R5, InstanceSize.XLARGE2);
case x64Ec2InstanceType.R5_4XLARGE:
return InstanceType.of(InstanceClass.R5, InstanceSize.XLARGE4);
case x64Ec2InstanceType.R5_8XLARGE:
return InstanceType.of(InstanceClass.R5, InstanceSize.XLARGE8);
case x64Ec2InstanceType.G5_LARGE:
return InstanceType.of(InstanceClass.G5, InstanceSize.LARGE);
case x64Ec2InstanceType.G5_XLARGE:
return InstanceType.of(InstanceClass.G5, InstanceSize.XLARGE);
case x64Ec2InstanceType.I3_LARGE:
return InstanceType.of(InstanceClass.I3, InstanceSize.LARGE);
case x64Ec2InstanceType.I3_XLARGE:
return InstanceType.of(InstanceClass.I3, InstanceSize.XLARGE);
case x64Ec2InstanceType.I3_2XLARGE:
return InstanceType.of(InstanceClass.I3, InstanceSize.XLARGE2);
case x64Ec2InstanceType.I3_4XLARGE:
return InstanceType.of(InstanceClass.I3, InstanceSize.XLARGE4);
case x64Ec2InstanceType.I3_8XLARGE:
return InstanceType.of(InstanceClass.I3, InstanceSize.XLARGE8);
case x64Ec2InstanceType.INF1_XLARGE:
return InstanceType.of(InstanceClass.INF1, InstanceSize.XLARGE);
case x64Ec2InstanceType.INF1_2XLARGE:
Expand All @@ -108,6 +132,10 @@ export const getArm64InstanceTypes = (instanceType: string) => {
return InstanceType.of(InstanceClass.R6G, InstanceSize.XLARGE);
case arm64Ec2InstanceType.R6G_2XLARGE:
return InstanceType.of(InstanceClass.R6G, InstanceSize.XLARGE2);
case arm64Ec2InstanceType.R6G_4XLARGE:
return InstanceType.of(InstanceClass.R6G, InstanceSize.XLARGE4);
case arm64Ec2InstanceType.R6G_8XLARGE:
return InstanceType.of(InstanceClass.R6G, InstanceSize.XLARGE8);
case arm64Ec2InstanceType.G5G_LARGE:
return InstanceType.of(InstanceClass.G5G, InstanceSize.LARGE);
case arm64Ec2InstanceType.G5G_XLARGE:
Expand All @@ -116,3 +144,16 @@ export const getArm64InstanceTypes = (instanceType: string) => {
throw new Error(`Invalid instance type provided, please provide any one the following: ${Object.values(arm64Ec2InstanceType)}`);
}
};

export const getVolumeType = (volumeType: string) => {
switch (volumeType) {
case EbsDeviceVolumeType.STANDARD.valueOf():
return EbsDeviceVolumeType.STANDARD;
case EbsDeviceVolumeType.GP2.valueOf():
return EbsDeviceVolumeType.GP2;
case EbsDeviceVolumeType.GP3.valueOf():
return EbsDeviceVolumeType.GP3;
default:
throw new Error('Invalid volume type provided, please provide any one of the following: standard, gp2, gp3');
}
};
17 changes: 16 additions & 1 deletion lib/os-cluster-entrypoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,15 @@ import {
AmazonLinuxCpuType, InstanceType, IVpc, SecurityGroup, Vpc,
} from 'aws-cdk-lib/aws-ec2';
import { dump } from 'js-yaml';
import { EbsDeviceVolumeType } from 'aws-cdk-lib/aws-autoscaling';
import { NetworkStack } from './networking/vpc-stack';
import { InfraStack } from './infra/infra-stack';
import {
x64Ec2InstanceType, arm64Ec2InstanceType, getX64InstanceTypes, getArm64InstanceTypes,
arm64Ec2InstanceType,
getArm64InstanceTypes,
getVolumeType,
getX64InstanceTypes,
x64Ec2InstanceType,
} from './opensearch-config/node-config';

enum cpuArchEnum{
Expand Down Expand Up @@ -55,6 +60,7 @@ export class OsClusterEntrypoint {
let ymlConfig: string = 'undefined';
let dataEc2InstanceType: InstanceType;
let mlEc2InstanceType: InstanceType;
let volumeType: EbsDeviceVolumeType;

const x64InstanceTypes: string[] = Object.keys(x64Ec2InstanceType);
const arm64InstanceTypes: string[] = Object.keys(arm64Ec2InstanceType);
Expand Down Expand Up @@ -155,6 +161,14 @@ export class OsClusterEntrypoint {
dataNodeStorage = parseInt(dataSize, 10);
}

const inputVolumeType = `${scope.node.tryGetContext('storageVolumeType')}`;
if (inputVolumeType.toString() === 'undefined') {
// use gp2 volume by default
volumeType = getVolumeType('gp2');
} else {
volumeType = getVolumeType(inputVolumeType);
}

const mlSize = `${scope.node.tryGetContext('mlNodeStorage')}`;
if (mlSize === 'undefined') {
mlNodeStorage = 100;
Expand Down Expand Up @@ -234,6 +248,7 @@ export class OsClusterEntrypoint {
use50PercentHeap,
isInternal,
enableRemoteStore,
storageVolumeType: volumeType,
...props,
});

Expand Down
42 changes: 39 additions & 3 deletions test/os-cluster.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ test('Test Resources with security enabled single-node cluster', () => {
restrictServerAccessTo: 'pl-12345',
dataNodeStorage: 200,
isInternal: true,
storageVolumeType: 'gp3',
},
});

Expand Down Expand Up @@ -188,6 +189,7 @@ test('Test Resources with security enabled single-node cluster', () => {
{
Ebs: {
VolumeSize: 200,
VolumeType: 'gp3',
},
},
],
Expand Down Expand Up @@ -227,7 +229,7 @@ test('Throw error on wrong cpu arch to instance mapping', () => {
} catch (error) {
expect(error).toBeInstanceOf(Error);
// eslint-disable-next-line max-len
expect(error.message).toEqual('Invalid instance type provided, please provide any one the following: m6g.xlarge,m6g.2xlarge,c6g.large,c6g.xlarge,r6g.large,r6g.xlarge,r6g.2xlarge,g5g.large,g5g.xlarge');
expect(error.message).toEqual('Invalid instance type provided, please provide any one the following: m6g.xlarge,m6g.2xlarge,c6g.large,c6g.xlarge,r6g.large,r6g.xlarge,r6g.2xlarge,r6g.4xlarge,r6g.8xlarge,g5g.large,g5g.xlarge');
}
});

Expand All @@ -245,7 +247,7 @@ test('Throw error on ec2 instance outside of enum list', () => {
restrictServerAccessTo: 'pl-12345',
dataNodeStorage: 200,
isInternal: true,
dataInstanceType: 'r5.4xlarge',
dataInstanceType: 'r5.16xlarge',
},
});
// WHEN
Expand All @@ -259,7 +261,7 @@ test('Throw error on ec2 instance outside of enum list', () => {
} catch (error) {
expect(error).toBeInstanceOf(Error);
// eslint-disable-next-line max-len
expect(error.message).toEqual('Invalid instance type provided, please provide any one the following: m5.xlarge,m5.2xlarge,c5.large,c5.xlarge,r5.large,r5.xlarge,r5.2xlarge,g5.large,g5.xlarge,inf1.xlarge,inf1.2xlarge');
expect(error.message).toEqual('Invalid instance type provided, please provide any one the following: m5.xlarge,m5.2xlarge,c5.large,c5.xlarge,r5.large,r5.xlarge,r5.2xlarge,r5.4xlarge,r5.8xlarge,g5.large,g5.xlarge,i3.large,i3.xlarge,i3.2xlarge,i3.4xlarge,i3.8xlarge,inf1.xlarge,inf1.2xlarge');
}
});

Expand Down Expand Up @@ -300,6 +302,7 @@ test('Test multi-node cluster with only data-nodes', () => {
{
Ebs: {
VolumeSize: 200,
VolumeType: 'gp2',
},
},
],
Expand Down Expand Up @@ -394,3 +397,36 @@ test('Test multi-node cluster with remote-store enabled', () => {
},
});
});

test('Throw error on unsupported ebs volume type', () => {
const app = new App({
context: {
securityDisabled: false,
minDistribution: false,
distributionUrl: 'www.example.com',
cpuArch: 'x64',
singleNodeCluster: false,
dashboardsUrl: 'www.example.com',
distVersion: '1.0.0',
serverAccessType: 'prefixList',
restrictServerAccessTo: 'pl-12345',
dataNodeStorage: 200,
isInternal: true,
dataInstanceType: 'r5.4xlarge',
storageVolumeType: 'io1',
},
});
// WHEN
try {
const testStack = new OsClusterEntrypoint(app, {
env: { account: 'test-account', region: 'us-east-1' },
});

// eslint-disable-next-line no-undef
fail('Expected an error to be thrown');
} catch (error) {
expect(error).toBeInstanceOf(Error);
// eslint-disable-next-line max-len
expect(error.message).toEqual('Invalid volume type provided, please provide any one of the following: standard, gp2, gp3');
}
});

0 comments on commit 440fe0e

Please sign in to comment.