Skip to content

Commit 45c943b

Browse files
committed
feat(ec2): support throughput on LaunchTemplate EBS volumes
This support was simply not included in ec2 when it was added to autoscaling in aws#22441. I have copied that PR's implementation implementation to ec2 and similarly adapted its tests. Fixes aws#24341.
1 parent 5864782 commit 45c943b

File tree

9 files changed

+144
-4
lines changed

9 files changed

+144
-4
lines changed

packages/@aws-cdk-testing/framework-integ/test/aws-ec2/test/integ.launch-template.js.snapshot/aws-cdk-ec2-lt-metadata-1.assets.json

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/@aws-cdk-testing/framework-integ/test/aws-ec2/test/integ.launch-template.js.snapshot/aws-cdk-ec2-lt-metadata-1.template.json

+10
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,16 @@
159159
"Type": "AWS::EC2::LaunchTemplate",
160160
"Properties": {
161161
"LaunchTemplateData": {
162+
"BlockDeviceMappings": [
163+
{
164+
"DeviceName": "/dev/xvda",
165+
"Ebs": {
166+
"Throughput": 250,
167+
"VolumeSize": 15,
168+
"VolumeType": "gp3"
169+
}
170+
}
171+
],
162172
"MetadataOptions": {
163173
"HttpEndpoint": "enabled",
164174
"HttpProtocolIpv6": "enabled",

packages/@aws-cdk-testing/framework-integ/test/aws-ec2/test/integ.launch-template.js.snapshot/manifest.json

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/@aws-cdk-testing/framework-integ/test/aws-ec2/test/integ.launch-template.js.snapshot/tree.json

+10
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/@aws-cdk-testing/framework-integ/test/aws-ec2/test/integ.launch-template.ts

+7
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,13 @@ const lt = new ec2.LaunchTemplate(stack, 'LT', {
2222
httpTokens: ec2.LaunchTemplateHttpTokens.REQUIRED,
2323
instanceMetadataTags: true,
2424
securityGroup: sg1,
25+
blockDevices: [{
26+
deviceName: '/dev/xvda',
27+
volume: ec2.BlockDeviceVolume.ebs(15, {
28+
volumeType: ec2.EbsDeviceVolumeType.GP3,
29+
throughput: 250,
30+
}),
31+
}],
2532
});
2633

2734
const sg2 = new ec2.SecurityGroup(stack, 'sg2', {

packages/aws-cdk-lib/aws-ec2/README.md

+27
Original file line numberDiff line numberDiff line change
@@ -1603,6 +1603,33 @@ new ec2.Instance(this, 'Instance', {
16031603

16041604
```
16051605

1606+
To specify the throughput value for `gp3` volumes, use the `throughput` property:
1607+
1608+
```ts
1609+
declare const vpc: ec2.Vpc;
1610+
declare const instanceType: ec2.InstanceType;
1611+
declare const machineImage: ec2.IMachineImage;
1612+
1613+
const kmsKey = new Key(this, 'KmsKey')
1614+
1615+
new ec2.Instance(this, 'Instance', {
1616+
vpc,
1617+
instanceType,
1618+
machineImage,
1619+
1620+
// ...
1621+
1622+
blockDevices: [
1623+
{
1624+
deviceName: '/dev/sda1',
1625+
volume: ec2.BlockDeviceVolume.ebs(100, {
1626+
volumeType: ec2.EbsDeviceVolumeType.GP3,
1627+
throughput: 250,
1628+
}),
1629+
},
1630+
],
1631+
});
1632+
16061633
#### EBS Optimized Instances
16071634

16081635
An Amazon EBSoptimized instance uses an optimized configuration stack and provides additional, dedicated capacity for Amazon EBS I/O. This optimization provides the best performance for your EBS volumes by minimizing contention between Amazon EBS I/O and other traffic from your instance.

packages/aws-cdk-lib/aws-ec2/lib/private/ebs-util.ts

+20-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,25 @@ function synthesizeBlockDeviceMappings<RT, NDT>(construct: Construct, blockDevic
2525

2626
if (ebs) {
2727

28-
const { iops, volumeType, kmsKey, ...rest } = ebs;
28+
const { iops, throughput, volumeType, kmsKey, ...rest } = ebs;
29+
30+
if (throughput) {
31+
if (volumeType != EbsDeviceVolumeType.GP3) {
32+
throw new Error(`throughput requires volumeType: ${EbsDeviceVolumeType.GP3}, got: ${volumeType}.`);
33+
}
34+
35+
if (throughput < 125 || throughput > 1000) {
36+
throw new Error(`throughput must be between 125 and 1000, got ${throughput}.`);
37+
}
38+
39+
const maximumThroughputRatio = 0.25;
40+
if (iops) {
41+
const iopsRatio = (throughput / iops);
42+
if (iopsRatio > maximumThroughputRatio) {
43+
throw new Error(`Throughput (MiBps) to iops ratio of ${iopsRatio} is too high; maximum is ${maximumThroughputRatio} MiBps per iops`);
44+
}
45+
}
46+
}
2947

3048
if (!iops) {
3149
if (volumeType === EbsDeviceVolumeType.IO1 || volumeType === EbsDeviceVolumeType.IO2) {
@@ -43,6 +61,7 @@ function synthesizeBlockDeviceMappings<RT, NDT>(construct: Construct, blockDevic
4361
finalEbs = {
4462
...rest,
4563
iops,
64+
throughput,
4665
volumeType,
4766
kmsKeyId: kmsKey?.keyArn,
4867
};

packages/aws-cdk-lib/aws-ec2/lib/volume.ts

+11
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,17 @@ export interface EbsDeviceOptionsBase {
7070
* `@aws-cdk/aws-ec2:ebsDefaultGp3Volume` is enabled.
7171
*/
7272
readonly volumeType?: EbsDeviceVolumeType;
73+
74+
/**
75+
* The throughput to provision for a `gp3` volume.
76+
*
77+
* Valid Range: Minimum value of 125. Maximum value of 1000.
78+
*
79+
* @see https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/EBSVolumeTypes.html
80+
*
81+
* @default - 125 MiB/s.
82+
*/
83+
readonly throughput?: number;
7384
}
7485

7586
/**

packages/aws-cdk-lib/aws-ec2/test/launch-template.test.ts

+56
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,12 @@ describe('LaunchTemplate', () => {
314314
}, {
315315
deviceName: 'ephemeral',
316316
volume: BlockDeviceVolume.ephemeral(0),
317+
}, {
318+
deviceName: 'gp3-with-throughput',
319+
volume: BlockDeviceVolume.ebs(15, {
320+
volumeType: EbsDeviceVolumeType.GP3,
321+
throughput: 350,
322+
}),
317323
},
318324
];
319325

@@ -366,10 +372,60 @@ describe('LaunchTemplate', () => {
366372
DeviceName: 'ephemeral',
367373
VirtualName: 'ephemeral0',
368374
},
375+
{
376+
DeviceName: 'gp3-with-throughput',
377+
Ebs: {
378+
VolumeSize: 15,
379+
VolumeType: 'gp3',
380+
Throughput: 350,
381+
},
382+
},
369383
],
370384
},
371385
});
372386
});
387+
test.each([124, 1001])('throws if throughput is set less than 125 or more than 1000', (throughput) => {
388+
expect(() => {
389+
new LaunchTemplate(stack, 'LaunchTemplate', {
390+
blockDevices: [{
391+
deviceName: 'ebs',
392+
volume: BlockDeviceVolume.ebs(15, {
393+
volumeType: EbsDeviceVolumeType.GP3,
394+
throughput,
395+
}),
396+
}],
397+
});
398+
}).toThrow(/throughput property takes a minimum of 125 and a maximum of 1000/);
399+
});
400+
test.each([
401+
...Object.values(EbsDeviceVolumeType).filter((v) => v !== 'gp3'),
402+
])('throws if throughput is set on any volume type other than GP3', (volumeType) => {
403+
expect(() => {
404+
new LaunchTemplate(stack, 'LaunchTemplate', {
405+
blockDevices: [{
406+
deviceName: 'ebs',
407+
volume: BlockDeviceVolume.ebs(15, {
408+
volumeType: volumeType,
409+
throughput: 150,
410+
}),
411+
}],
412+
});
413+
}).toThrow(/throughput property requires volumeType: EbsDeviceVolumeType.GP3/);
414+
});
415+
test('throws if throughput / iops ratio is greater than 0.25', () => {
416+
expect(() => {
417+
new LaunchTemplate(stack, 'LaunchTemplate', {
418+
blockDevices: [{
419+
deviceName: 'ebs',
420+
volume: BlockDeviceVolume.ebs(15, {
421+
volumeType: EbsDeviceVolumeType.GP3,
422+
throughput: 751,
423+
iops: 3000,
424+
}),
425+
}],
426+
});
427+
}).toThrow('Throughput (MiBps) to iops ratio of 0.25033333333333335 is too high; maximum is 0.25 MiBps per iops');
428+
});
373429

374430
test('Given instance profile', () => {
375431
// GIVEN

0 commit comments

Comments
 (0)