Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Storage options for EC2 and ECS #624

Merged
merged 2 commits into from
Sep 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 98 additions & 0 deletions API.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

36 changes: 36 additions & 0 deletions src/providers/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
CustomResource,
Duration,
} from 'aws-cdk-lib';
import { EbsDeviceVolumeType } from 'aws-cdk-lib/aws-ec2/lib/volume';
import { Construct, IConstruct } from 'constructs';
import { AmiRootDeviceFunction } from './ami-root-device-function';
import { singletonLambda, singletonLogGroup, SingletonLogType } from '../utils';
Expand Down Expand Up @@ -477,6 +478,41 @@ export interface IRunnerProvider extends ec2.IConnectable, iam.IGrantable, ICons
status(statusFunctionRole: iam.IGrantable): IRunnerProviderStatus;
}

/**
* Storage options for the runner instance.
*/
export interface StorageOptions {
/**
* The EBS volume type
* @see https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/EBSVolumeTypes.html
*
* @default `EbsDeviceVolumeType.GP2`
*/
readonly volumeType?: EbsDeviceVolumeType;

/**
* The number of I/O operations per second (IOPS) to provision for the volume.
*
* Must only be set for `volumeType`: `EbsDeviceVolumeType.IO1`
*
* The maximum ratio of IOPS to volume size (in GiB) is 50:1, so for 5,000 provisioned IOPS,
* you need at least 100 GiB storage on the volume.
*
* @see https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/EBSVolumeTypes.html
*
* @default - none, required for `EbsDeviceVolumeType.IO1`
*/
readonly iops?: number;

/**
* The throughput that the volume supports, in MiB/s
* Takes a minimum of 125 and maximum of 1000.
* @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-volume.html#cfn-ec2-volume-throughput
* @default - 125 MiB/s. Only valid on gp3 volumes.
*/
readonly throughput?: number;
}

/**
* Base class for all providers with common methods used by all providers.
*
Expand Down
11 changes: 11 additions & 0 deletions src/providers/ec2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
RunnerProviderProps,
RunnerRuntimeParameters,
RunnerVersion,
StorageOptions,
} from './common';
import { IRunnerImageBuilder, RunnerImageBuilder, RunnerImageBuilderProps, RunnerImageBuilderType, RunnerImageComponent } from '../image-builders';
import { MINIMAL_EC2_SSM_SESSION_MANAGER_POLICY_STATEMENT } from '../utils';
Expand Down Expand Up @@ -217,6 +218,11 @@ export interface Ec2RunnerProviderProps extends RunnerProviderProps {
*/
readonly storageSize?: cdk.Size;

/**
* Options for runner instance storage volume.
*/
readonly storageOptions?: StorageOptions;

/**
* Security Group to assign to launched runner instances.
*
Expand Down Expand Up @@ -340,6 +346,7 @@ export class Ec2RunnerProvider extends BaseProvider implements IRunnerProvider {
private readonly role: iam.Role;
private readonly instanceType: ec2.InstanceType;
private readonly storageSize: cdk.Size;
private readonly storageOptions?: StorageOptions;
private readonly spot: boolean;
private readonly spotMaxPrice: string | undefined;
private readonly vpc: ec2.IVpc;
Expand All @@ -355,6 +362,7 @@ export class Ec2RunnerProvider extends BaseProvider implements IRunnerProvider {
this.subnets = props?.subnet ? [props.subnet] : this.vpc.selectSubnets(props?.subnetSelection).subnets;
this.instanceType = props?.instanceType ?? ec2.InstanceType.of(ec2.InstanceClass.M6I, ec2.InstanceSize.LARGE);
this.storageSize = props?.storageSize ?? cdk.Size.gibibytes(30); // 30 is the minimum for Windows
this.storageOptions = props?.storageOptions;
this.spot = props?.spot ?? false;
this.spotMaxPrice = props?.spotMaxPrice;

Expand Down Expand Up @@ -471,6 +479,9 @@ export class Ec2RunnerProvider extends BaseProvider implements IRunnerProvider {
Ebs: {
DeleteOnTermination: true,
VolumeSize: this.storageSize.toGibibytes(),
VolumeType: this.storageOptions?.volumeType,
Iops: this.storageOptions?.iops,
Throughput: this.storageOptions?.throughput,
},
}],
InstanceMarketOptions: this.spot ? {
Expand Down
15 changes: 14 additions & 1 deletion src/providers/ecs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
RunnerProviderProps,
RunnerRuntimeParameters,
RunnerVersion,
StorageOptions,
} from './common';
import { ecsRunCommand } from './fargate';
import { IRunnerImageBuilder, RunnerImageBuilder, RunnerImageBuilderProps, RunnerImageComponent } from '../image-builders';
Expand Down Expand Up @@ -151,6 +152,11 @@ export interface EcsRunnerProviderProps extends RunnerProviderProps {
*/
readonly storageSize?: cdk.Size;

/**
* Options for runner instance storage volume.
*/
readonly storageOptions?: StorageOptions;

/**
* Support building and running Docker images by enabling Docker-in-Docker (dind) and the required CodeBuild privileged mode. Disabling this can
* speed up provisioning of CodeBuild runners. If you don't intend on running or building Docker images, disable this for faster start-up times.
Expand Down Expand Up @@ -340,6 +346,10 @@ export class EcsRunnerProvider extends BaseProvider implements IRunnerProvider {
},
);

if (props?.storageOptions && !props?.storageSize) {
throw new Error('storageSize is required when storageOptions are specified');
}

const imageBuilder = props?.imageBuilder ?? EcsRunnerProvider.imageBuilder(this, 'Image Builder');
const image = this.image = imageBuilder.bindDockerImage();

Expand All @@ -360,8 +370,11 @@ export class EcsRunnerProvider extends BaseProvider implements IRunnerProvider {
deviceName: amiRootDevice(this, this.defaultClusterInstanceAmi().getImage(this).imageId).ref,
volume: {
ebsDevice: {
volumeSize: props.storageSize.toGibibytes(),
deleteOnTermination: true,
volumeSize: props.storageSize.toGibibytes(),
volumeType: props.storageOptions?.volumeType,
iops: props.storageOptions?.iops,
throughput: props.storageOptions?.throughput,
},
},
},
Expand Down
30 changes: 15 additions & 15 deletions test/default.integ.snapshot/github-runners-test.assets.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,19 @@
}
}
},
"88cc171bf3103a3b98ba0006fc7e5c6fdfd338c03591b344bb4cf60f1a9da18c": {
"source": {
"path": "asset.88cc171bf3103a3b98ba0006fc7e5c6fdfd338c03591b344bb4cf60f1a9da18c.lambda",
"packaging": "zip"
},
"destinations": {
"current_account-current_region": {
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
"objectKey": "88cc171bf3103a3b98ba0006fc7e5c6fdfd338c03591b344bb4cf60f1a9da18c.zip",
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
}
}
},
"2fc3b84da69dcc5adb6dc4721b50c1166474fa7e5fd5f242e833d12ac28e09d9": {
"source": {
"path": "asset.2fc3b84da69dcc5adb6dc4721b50c1166474fa7e5fd5f242e833d12ac28e09d9.sh",
Expand Down Expand Up @@ -105,19 +118,6 @@
}
}
},
"88cc171bf3103a3b98ba0006fc7e5c6fdfd338c03591b344bb4cf60f1a9da18c": {
"source": {
"path": "asset.88cc171bf3103a3b98ba0006fc7e5c6fdfd338c03591b344bb4cf60f1a9da18c.lambda",
"packaging": "zip"
},
"destinations": {
"current_account-current_region": {
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
"objectKey": "88cc171bf3103a3b98ba0006fc7e5c6fdfd338c03591b344bb4cf60f1a9da18c.zip",
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
}
}
},
"83c9fbef7e367049876156e1b2fd8fb89d8044003b26b693e2e7aca16fd4fb7f": {
"source": {
"path": "asset.83c9fbef7e367049876156e1b2fd8fb89d8044003b26b693e2e7aca16fd4fb7f.lambda",
Expand Down Expand Up @@ -209,15 +209,15 @@
}
}
},
"bdf474a164f615217e0702c26ada96931a06c49f175c267bfd2b35a006b799a8": {
"2958bcb16280e16db04d04050757197c299db756977117494e2cbcfcc78500c5": {
"source": {
"path": "github-runners-test.template.json",
"packaging": "file"
},
"destinations": {
"current_account-current_region": {
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
"objectKey": "bdf474a164f615217e0702c26ada96931a06c49f175c267bfd2b35a006b799a8.json",
"objectKey": "2958bcb16280e16db04d04050757197c299db756977117494e2cbcfcc78500c5.json",
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
}
}
Expand Down
Loading
Loading