Skip to content

Commit dd4eed1

Browse files
authored
feat: Storage options for EC2 and ECS (#624)
Set volume type, IOPS and throughput with `storageOptions` for EC2 and ECS providers. ``` new Ec2RunnerProvider(stack, 'EC2 Linux', { labels: ['ec2', 'linux', 'x64'], storageSize: cdk.Size.gibibytes(500), storageOptions: { volumeType: EbsDeviceVolumeType.IO1, iops: 30000, }, }); ``` Resolves #621
1 parent 4f9814c commit dd4eed1

File tree

7 files changed

+313
-113
lines changed

7 files changed

+313
-113
lines changed

API.md

Lines changed: 98 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/providers/common.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
CustomResource,
1010
Duration,
1111
} from 'aws-cdk-lib';
12+
import { EbsDeviceVolumeType } from 'aws-cdk-lib/aws-ec2/lib/volume';
1213
import { Construct, IConstruct } from 'constructs';
1314
import { AmiRootDeviceFunction } from './ami-root-device-function';
1415
import { singletonLambda, singletonLogGroup, SingletonLogType } from '../utils';
@@ -477,6 +478,41 @@ export interface IRunnerProvider extends ec2.IConnectable, iam.IGrantable, ICons
477478
status(statusFunctionRole: iam.IGrantable): IRunnerProviderStatus;
478479
}
479480

481+
/**
482+
* Storage options for the runner instance.
483+
*/
484+
export interface StorageOptions {
485+
/**
486+
* The EBS volume type
487+
* @see https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/EBSVolumeTypes.html
488+
*
489+
* @default `EbsDeviceVolumeType.GP2`
490+
*/
491+
readonly volumeType?: EbsDeviceVolumeType;
492+
493+
/**
494+
* The number of I/O operations per second (IOPS) to provision for the volume.
495+
*
496+
* Must only be set for `volumeType`: `EbsDeviceVolumeType.IO1`
497+
*
498+
* The maximum ratio of IOPS to volume size (in GiB) is 50:1, so for 5,000 provisioned IOPS,
499+
* you need at least 100 GiB storage on the volume.
500+
*
501+
* @see https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/EBSVolumeTypes.html
502+
*
503+
* @default - none, required for `EbsDeviceVolumeType.IO1`
504+
*/
505+
readonly iops?: number;
506+
507+
/**
508+
* The throughput that the volume supports, in MiB/s
509+
* Takes a minimum of 125 and maximum of 1000.
510+
* @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-volume.html#cfn-ec2-volume-throughput
511+
* @default - 125 MiB/s. Only valid on gp3 volumes.
512+
*/
513+
readonly throughput?: number;
514+
}
515+
480516
/**
481517
* Base class for all providers with common methods used by all providers.
482518
*

src/providers/ec2.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {
2323
RunnerProviderProps,
2424
RunnerRuntimeParameters,
2525
RunnerVersion,
26+
StorageOptions,
2627
} from './common';
2728
import { IRunnerImageBuilder, RunnerImageBuilder, RunnerImageBuilderProps, RunnerImageBuilderType, RunnerImageComponent } from '../image-builders';
2829
import { MINIMAL_EC2_SSM_SESSION_MANAGER_POLICY_STATEMENT } from '../utils';
@@ -217,6 +218,11 @@ export interface Ec2RunnerProviderProps extends RunnerProviderProps {
217218
*/
218219
readonly storageSize?: cdk.Size;
219220

221+
/**
222+
* Options for runner instance storage volume.
223+
*/
224+
readonly storageOptions?: StorageOptions;
225+
220226
/**
221227
* Security Group to assign to launched runner instances.
222228
*
@@ -340,6 +346,7 @@ export class Ec2RunnerProvider extends BaseProvider implements IRunnerProvider {
340346
private readonly role: iam.Role;
341347
private readonly instanceType: ec2.InstanceType;
342348
private readonly storageSize: cdk.Size;
349+
private readonly storageOptions?: StorageOptions;
343350
private readonly spot: boolean;
344351
private readonly spotMaxPrice: string | undefined;
345352
private readonly vpc: ec2.IVpc;
@@ -355,6 +362,7 @@ export class Ec2RunnerProvider extends BaseProvider implements IRunnerProvider {
355362
this.subnets = props?.subnet ? [props.subnet] : this.vpc.selectSubnets(props?.subnetSelection).subnets;
356363
this.instanceType = props?.instanceType ?? ec2.InstanceType.of(ec2.InstanceClass.M6I, ec2.InstanceSize.LARGE);
357364
this.storageSize = props?.storageSize ?? cdk.Size.gibibytes(30); // 30 is the minimum for Windows
365+
this.storageOptions = props?.storageOptions;
358366
this.spot = props?.spot ?? false;
359367
this.spotMaxPrice = props?.spotMaxPrice;
360368

@@ -471,6 +479,9 @@ export class Ec2RunnerProvider extends BaseProvider implements IRunnerProvider {
471479
Ebs: {
472480
DeleteOnTermination: true,
473481
VolumeSize: this.storageSize.toGibibytes(),
482+
VolumeType: this.storageOptions?.volumeType,
483+
Iops: this.storageOptions?.iops,
484+
Throughput: this.storageOptions?.throughput,
474485
},
475486
}],
476487
InstanceMarketOptions: this.spot ? {

src/providers/ecs.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import {
2525
RunnerProviderProps,
2626
RunnerRuntimeParameters,
2727
RunnerVersion,
28+
StorageOptions,
2829
} from './common';
2930
import { ecsRunCommand } from './fargate';
3031
import { IRunnerImageBuilder, RunnerImageBuilder, RunnerImageBuilderProps, RunnerImageComponent } from '../image-builders';
@@ -151,6 +152,11 @@ export interface EcsRunnerProviderProps extends RunnerProviderProps {
151152
*/
152153
readonly storageSize?: cdk.Size;
153154

155+
/**
156+
* Options for runner instance storage volume.
157+
*/
158+
readonly storageOptions?: StorageOptions;
159+
154160
/**
155161
* Support building and running Docker images by enabling Docker-in-Docker (dind) and the required CodeBuild privileged mode. Disabling this can
156162
* speed up provisioning of CodeBuild runners. If you don't intend on running or building Docker images, disable this for faster start-up times.
@@ -340,6 +346,10 @@ export class EcsRunnerProvider extends BaseProvider implements IRunnerProvider {
340346
},
341347
);
342348

349+
if (props?.storageOptions && !props?.storageSize) {
350+
throw new Error('storageSize is required when storageOptions are specified');
351+
}
352+
343353
const imageBuilder = props?.imageBuilder ?? EcsRunnerProvider.imageBuilder(this, 'Image Builder');
344354
const image = this.image = imageBuilder.bindDockerImage();
345355

@@ -360,8 +370,11 @@ export class EcsRunnerProvider extends BaseProvider implements IRunnerProvider {
360370
deviceName: amiRootDevice(this, this.defaultClusterInstanceAmi().getImage(this).imageId).ref,
361371
volume: {
362372
ebsDevice: {
363-
volumeSize: props.storageSize.toGibibytes(),
364373
deleteOnTermination: true,
374+
volumeSize: props.storageSize.toGibibytes(),
375+
volumeType: props.storageOptions?.volumeType,
376+
iops: props.storageOptions?.iops,
377+
throughput: props.storageOptions?.throughput,
365378
},
366379
},
367380
},

test/default.integ.snapshot/github-runners-test.assets.json

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,19 @@
5353
}
5454
}
5555
},
56+
"88cc171bf3103a3b98ba0006fc7e5c6fdfd338c03591b344bb4cf60f1a9da18c": {
57+
"source": {
58+
"path": "asset.88cc171bf3103a3b98ba0006fc7e5c6fdfd338c03591b344bb4cf60f1a9da18c.lambda",
59+
"packaging": "zip"
60+
},
61+
"destinations": {
62+
"current_account-current_region": {
63+
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
64+
"objectKey": "88cc171bf3103a3b98ba0006fc7e5c6fdfd338c03591b344bb4cf60f1a9da18c.zip",
65+
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
66+
}
67+
}
68+
},
5669
"2fc3b84da69dcc5adb6dc4721b50c1166474fa7e5fd5f242e833d12ac28e09d9": {
5770
"source": {
5871
"path": "asset.2fc3b84da69dcc5adb6dc4721b50c1166474fa7e5fd5f242e833d12ac28e09d9.sh",
@@ -105,19 +118,6 @@
105118
}
106119
}
107120
},
108-
"88cc171bf3103a3b98ba0006fc7e5c6fdfd338c03591b344bb4cf60f1a9da18c": {
109-
"source": {
110-
"path": "asset.88cc171bf3103a3b98ba0006fc7e5c6fdfd338c03591b344bb4cf60f1a9da18c.lambda",
111-
"packaging": "zip"
112-
},
113-
"destinations": {
114-
"current_account-current_region": {
115-
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
116-
"objectKey": "88cc171bf3103a3b98ba0006fc7e5c6fdfd338c03591b344bb4cf60f1a9da18c.zip",
117-
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
118-
}
119-
}
120-
},
121121
"83c9fbef7e367049876156e1b2fd8fb89d8044003b26b693e2e7aca16fd4fb7f": {
122122
"source": {
123123
"path": "asset.83c9fbef7e367049876156e1b2fd8fb89d8044003b26b693e2e7aca16fd4fb7f.lambda",
@@ -209,15 +209,15 @@
209209
}
210210
}
211211
},
212-
"bdf474a164f615217e0702c26ada96931a06c49f175c267bfd2b35a006b799a8": {
212+
"2958bcb16280e16db04d04050757197c299db756977117494e2cbcfcc78500c5": {
213213
"source": {
214214
"path": "github-runners-test.template.json",
215215
"packaging": "file"
216216
},
217217
"destinations": {
218218
"current_account-current_region": {
219219
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
220-
"objectKey": "bdf474a164f615217e0702c26ada96931a06c49f175c267bfd2b35a006b799a8.json",
220+
"objectKey": "2958bcb16280e16db04d04050757197c299db756977117494e2cbcfcc78500c5.json",
221221
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
222222
}
223223
}

0 commit comments

Comments
 (0)