Skip to content

Commit

Permalink
Add ec2 isntance options for data and ml nodes
Browse files Browse the repository at this point in the history
Signed-off-by: Rishabh Singh <sngri@amazon.com>
  • Loading branch information
rishabh6788 committed May 18, 2023
1 parent b450ca4 commit e3a24f0
Show file tree
Hide file tree
Showing 5 changed files with 224 additions and 10 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ In order to deploy both the stacks the user needs to provide a set of required a
| clientNodeCount (Optional) | integer | Number of dedicated client nodes, default is 0 |
| ingestNodeCount (Optional) | integer | Number of dedicated ingest nodes, default is 0 |
| mlNodeCount (Optional) | integer | Number of dedicated machine learning nodes, default is 0 |
| dataInstanceType (Optional) | integer | Ec2 instance type for data node, defaults to r5.large. See options in `lib/opensearch-config/node-config.ts` for available options. |
| mlInstanceType (Optional) | integer | Ec2 instance type for ml node, defaults to r5.large. See options in `lib/opensearch-config/node-config.ts` for available options. |
| jvmSysProps (Optional) | string | A comma-separated list of key=value pairs that will be added to `jvm.options` as JVM system properties. |
| additionalConfig (Optional) | string | Additional opensearch.yml config parameters passed as JSON. e.g., `--context additionalConfig='{"plugins.security.nodes_dn": ["CN=*.example.com, OU=SSL, O=Test, L=Test, C=DE", "CN=node.other.com, OU=SSL, O=Test, L=Test, C=DE"], "plugins.security.nodes_dn_dynamic_config_enabled": false}'` |
| suffix (Optional) | string | An optional string identifier to be concatenated with infra stack name. |
Expand Down
19 changes: 12 additions & 7 deletions lib/infra/infra-stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ export interface infraProps extends StackProps{
readonly mlNodeStorage: number,
readonly jvmSysPropsString?: string,
readonly additionalConfig?: string,
readonly dataEc2InstanceType: InstanceType,
readonly mlEc2InstanceType: InstanceType,
readonly use50PercentHeap: boolean,
readonly isInternal: boolean,
}
Expand Down Expand Up @@ -84,7 +86,10 @@ export class InfraStack extends Stack {
assumedBy: new ServicePrincipal('ec2.amazonaws.com'),
});

const ec2InstanceType = (props.cpuType === AmazonLinuxCpuType.X86_64)
const singleNodeInstanceType = (props.cpuType === AmazonLinuxCpuType.X86_64)
? InstanceType.of(InstanceClass.R5, InstanceSize.XLARGE) : InstanceType.of(InstanceClass.R6G, InstanceSize.XLARGE);

const defaultInstanceType = (props.cpuType === AmazonLinuxCpuType.X86_64)
? InstanceType.of(InstanceClass.C5, InstanceSize.XLARGE) : InstanceType.of(InstanceClass.C6G, InstanceSize.XLARGE);

const nlb = new NetworkLoadBalancer(this, 'clusterNlb', {
Expand Down Expand Up @@ -116,7 +121,7 @@ export class InfraStack extends Stack {
console.log('Single node value is true, creating single node configurations');
singleNodeInstance = new Instance(this, 'single-node-instance', {
vpc: props.vpc,
instanceType: ec2InstanceType,
instanceType: singleNodeInstanceType,
machineImage: MachineImage.latestAmazonLinux({
generation: AmazonLinuxGeneration.AMAZON_LINUX_2,
cpuType: props.cpuType,
Expand Down Expand Up @@ -164,7 +169,7 @@ export class InfraStack extends Stack {
if (managerAsgCapacity > 0) {
const managerNodeAsg = new AutoScalingGroup(this, 'managerNodeAsg', {
vpc: props.vpc,
instanceType: ec2InstanceType,
instanceType: defaultInstanceType,
machineImage: MachineImage.latestAmazonLinux({
generation: AmazonLinuxGeneration.AMAZON_LINUX_2,
cpuType: props.cpuType,
Expand Down Expand Up @@ -196,7 +201,7 @@ export class InfraStack extends Stack {

const seedNodeAsg = new AutoScalingGroup(this, 'seedNodeAsg', {
vpc: props.vpc,
instanceType: ec2InstanceType,
instanceType: (seedConfig === 'seed-manager') ? defaultInstanceType : props.dataEc2InstanceType,
machineImage: MachineImage.latestAmazonLinux({
generation: AmazonLinuxGeneration.AMAZON_LINUX_2,
cpuType: props.cpuType,
Expand All @@ -223,7 +228,7 @@ export class InfraStack extends Stack {

const dataNodeAsg = new AutoScalingGroup(this, 'dataNodeAsg', {
vpc: props.vpc,
instanceType: ec2InstanceType,
instanceType: props.dataEc2InstanceType,
machineImage: MachineImage.latestAmazonLinux({
generation: AmazonLinuxGeneration.AMAZON_LINUX_2,
cpuType: props.cpuType,
Expand Down Expand Up @@ -253,7 +258,7 @@ export class InfraStack extends Stack {
} else {
clientNodeAsg = new AutoScalingGroup(this, 'clientNodeAsg', {
vpc: props.vpc,
instanceType: ec2InstanceType,
instanceType: defaultInstanceType,
machineImage: MachineImage.latestAmazonLinux({
generation: AmazonLinuxGeneration.AMAZON_LINUX_2,
cpuType: props.cpuType,
Expand Down Expand Up @@ -284,7 +289,7 @@ export class InfraStack extends Stack {
if (props.mlNodeCount > 0) {
const mlNodeAsg = new AutoScalingGroup(this, 'mlNodeAsg', {
vpc: props.vpc,
instanceType: ec2InstanceType,
instanceType: props.mlEc2InstanceType,
machineImage: MachineImage.latestAmazonLinux({
generation: AmazonLinuxGeneration.AMAZON_LINUX_2,
cpuType: props.cpuType,
Expand Down
76 changes: 76 additions & 0 deletions lib/opensearch-config/node-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ The OpenSearch Contributors require contributions made to
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';

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

nodeConfig.set('manager', {
Expand Down Expand Up @@ -34,3 +36,77 @@ nodeConfig.set('ml', {
'node.name': 'ml-node',
'node.roles': ['ml'],
});

export enum x64Ec2InstanceType {
M5_XLARGE = 'm5.xlarge',
M5_2XLARGE = 'm5.2xlarge',
C5_LARGE = 'c5.large',
C5_XLARGE = 'c5.xlarge',
R5_LARGE = 'r5.large',
R5_XLARGE = 'r5.xlarge',
G5_LARGE = 'g5.large',
G5_XLARGE = 'g5.xlarge',
INF1_XLARGE = 'inf1.xlarge',
INF1_2XLARGE = 'inf1.2xlarge'
}

export enum arm64Ec2InstanceType {
M6G_XLARGE = 'm6g.xlarge',
M6G_2XLARGE = 'm6g.2xlarge',
C6G_LARGE = 'c6g.large',
C6G_XLARGE = 'c6g.xlarge',
R6G_LARGE = 'r6g.large',
R6G_XLARGE = 'r6g.xlarge',
G5G_LARGE = 'g5g.large',
G5G_XLARGE = 'g5g.xlarge'
}

export const getX64InstanceTypes = (instanceType: string) => {
switch (instanceType) {
case x64Ec2InstanceType.M5_XLARGE:
return InstanceType.of(InstanceClass.M5, InstanceSize.XLARGE);
case x64Ec2InstanceType.M5_2XLARGE:
return InstanceType.of(InstanceClass.M5, InstanceSize.XLARGE2);
case x64Ec2InstanceType.C5_LARGE:
return InstanceType.of(InstanceClass.C5, InstanceSize.LARGE);
case x64Ec2InstanceType.C5_XLARGE:
return InstanceType.of(InstanceClass.C5, InstanceSize.XLARGE);
case x64Ec2InstanceType.R5_LARGE:
return InstanceType.of(InstanceClass.R5, InstanceSize.LARGE);
case x64Ec2InstanceType.R5_XLARGE:
return InstanceType.of(InstanceClass.R5, InstanceSize.XLARGE);
case x64Ec2InstanceType.G5_LARGE:
return InstanceType.of(InstanceClass.G5, InstanceSize.LARGE);
case x64Ec2InstanceType.G5_XLARGE:
return InstanceType.of(InstanceClass.G5, InstanceSize.XLARGE);
case x64Ec2InstanceType.INF1_XLARGE:
return InstanceType.of(InstanceClass.INF1, InstanceSize.XLARGE);
case x64Ec2InstanceType.INF1_2XLARGE:
return InstanceType.of(InstanceClass.INF1, InstanceSize.XLARGE2);
default:
throw new Error(`Invalid instance type provided, please provide any one the following: ${Object.values(x64Ec2InstanceType)}`);
}
};

export const getArm64InstanceTypes = (instanceType: string) => {
switch (instanceType) {
case arm64Ec2InstanceType.M6G_XLARGE:
return InstanceType.of(InstanceClass.M6G, InstanceSize.XLARGE);
case arm64Ec2InstanceType.M6G_2XLARGE:
return InstanceType.of(InstanceClass.M6G, InstanceSize.XLARGE2);
case arm64Ec2InstanceType.C6G_LARGE:
return InstanceType.of(InstanceClass.C6G, InstanceSize.LARGE);
case arm64Ec2InstanceType.C6G_XLARGE:
return InstanceType.of(InstanceClass.C6G, InstanceSize.XLARGE);
case arm64Ec2InstanceType.R6G_LARGE:
return InstanceType.of(InstanceClass.R6G, InstanceSize.LARGE);
case arm64Ec2InstanceType.R6G_XLARGE:
return InstanceType.of(InstanceClass.R6G, InstanceSize.XLARGE);
case arm64Ec2InstanceType.G5G_LARGE:
return InstanceType.of(InstanceClass.G5G, InstanceSize.LARGE);
case arm64Ec2InstanceType.G5G_XLARGE:
return InstanceType.of(InstanceClass.G5G, InstanceSize.XLARGE);
default:
throw new Error(`Invalid instance type provided, please provide any one the following: ${Object.values(arm64Ec2InstanceType)}`);
}
};
32 changes: 31 additions & 1 deletion lib/os-cluster-entrypoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,33 @@ compatible open source license. */
import { Construct } from 'constructs';
import { Stack, StackProps } from 'aws-cdk-lib';
import {
AmazonLinuxCpuType, IVpc, SecurityGroup, Vpc,
AmazonLinuxCpuType, InstanceType, IVpc, SecurityGroup, Vpc,
} from 'aws-cdk-lib/aws-ec2';
import { dump } from 'js-yaml';
import { NetworkStack } from './networking/vpc-stack';
import { InfraStack } from './infra/infra-stack';
import {
x64Ec2InstanceType, arm64Ec2InstanceType, getX64InstanceTypes, getArm64InstanceTypes,
} from './opensearch-config/node-config';

enum cpuArchEnum{
X64='x64',
ARM64='arm64'
}

const getInstanceType = (instanceType: string, arch: string) => {
if (arch === 'x64') {
if (instanceType !== 'undefined') {
return getX64InstanceTypes(instanceType);
}
return getX64InstanceTypes('r5.large');
}
if (instanceType !== 'undefined') {
return getArm64InstanceTypes(instanceType);
}
return getArm64InstanceTypes('r6g.large');
};

export class OsClusterEntrypoint {
public stacks: Stack[] = [];

Expand All @@ -37,7 +53,11 @@ export class OsClusterEntrypoint {
let dataNodeStorage: number;
let mlNodeStorage: number;
let ymlConfig: string = 'undefined';
let dataEc2InstanceType: InstanceType;
let mlEc2InstanceType: InstanceType;

const x64InstanceTypes: string[] = Object.keys(x64Ec2InstanceType);
const arm64InstanceTypes: string[] = Object.keys(arm64Ec2InstanceType);
const vpcId: string = scope.node.tryGetContext('vpcId');
const securityGroupId = scope.node.tryGetContext('securityGroupId');
const cidrRange = scope.node.tryGetContext('cidr');
Expand Down Expand Up @@ -69,14 +89,22 @@ export class OsClusterEntrypoint {
const dashboardUrl = `${scope.node.tryGetContext('dashboardsUrl')}`;

const cpuArch = `${scope.node.tryGetContext('cpuArch')}`;

const dataInstanceType = `${scope.node.tryGetContext('dataInstanceType')}`;
const mlInstanceType = `${scope.node.tryGetContext('mlInstanceType')}`;

if (cpuArch.toString() === 'undefined') {
throw new Error('cpuArch parameter is required. The provided value should be either x64 or arm64, any other value is invalid');
// @ts-ignore
} else if (Object.values(cpuArchEnum).includes(cpuArch.toString())) {
if (cpuArch.toString() === cpuArchEnum.X64) {
instanceCpuType = AmazonLinuxCpuType.X86_64;
dataEc2InstanceType = getInstanceType(dataInstanceType, cpuArch.toString());
mlEc2InstanceType = getInstanceType(mlInstanceType, cpuArch.toString());
} else {
instanceCpuType = AmazonLinuxCpuType.ARM_64;
dataEc2InstanceType = getInstanceType(dataInstanceType, cpuArch.toString());
mlEc2InstanceType = getInstanceType(mlInstanceType, cpuArch.toString());
}
} else {
throw new Error('Please provide a valid cpu architecture. The valid value can be either x64 or arm64');
Expand Down Expand Up @@ -184,6 +212,8 @@ export class OsClusterEntrypoint {
clientNodeCount: clientCount,
cpuArch,
cpuType: instanceCpuType,
dataEc2InstanceType,
mlEc2InstanceType,
dashboardsUrl: dashboardUrl,
dataNodeCount: dataCount,
distributionUrl,
Expand Down
Loading

0 comments on commit e3a24f0

Please sign in to comment.