Skip to content

Commit

Permalink
feat: Support spot pricing for Fargate runners (#44)
Browse files Browse the repository at this point in the history
  • Loading branch information
kichik committed Jun 16, 2022
1 parent 96c99c2 commit 99d270f
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 9 deletions.
11 changes: 10 additions & 1 deletion .github/workflows/self-hosted.yml
Expand Up @@ -33,11 +33,20 @@ jobs:
arch: ARM64
label: fargate-arm64
docker: false
- os: linux
arch: X64
label: fargate-x64-spot
docker: false
- os: linux
arch: ARM64
label: fargate-arm64-spot
docker: false

runs-on: [self-hosted, "${{ matrix.os }}", "${{ matrix.arch }}", "${{ matrix.label }}"]
steps:
- run: export
- run: |
- name: Check arch
run: |
if [ "${{ matrix.arch }}" != "${RUNNER_ARCH}" ]; then
echo "Expected RUNNER_ARCH to be ${{ matrix.arch }} but it's $RUNNER_ARCH"
exit 1
Expand Down
30 changes: 30 additions & 0 deletions API.md
Expand Up @@ -322,6 +322,7 @@ Any object.
| <code><a href="#@cloudsnorkel/cdk-github-runners.FargateRunner.property.container">container</a></code> | <code>aws-cdk-lib.aws_ecs.ContainerDefinition</code> | Container definition hosting the runner. |
| <code><a href="#@cloudsnorkel/cdk-github-runners.FargateRunner.property.grantPrincipal">grantPrincipal</a></code> | <code>aws-cdk-lib.aws_iam.IPrincipal</code> | Grant principal used to add permissions to the runner role. |
| <code><a href="#@cloudsnorkel/cdk-github-runners.FargateRunner.property.label">label</a></code> | <code>string</code> | Label associated with this provider. |
| <code><a href="#@cloudsnorkel/cdk-github-runners.FargateRunner.property.spot">spot</a></code> | <code>boolean</code> | Use spot pricing for Fargate tasks. |
| <code><a href="#@cloudsnorkel/cdk-github-runners.FargateRunner.property.task">task</a></code> | <code>aws-cdk-lib.aws_ecs.FargateTaskDefinition</code> | Fargate task hosting the runner. |
| <code><a href="#@cloudsnorkel/cdk-github-runners.FargateRunner.property.securityGroup">securityGroup</a></code> | <code>aws-cdk-lib.aws_ec2.ISecurityGroup</code> | Security group attached to the task. |
| <code><a href="#@cloudsnorkel/cdk-github-runners.FargateRunner.property.vpc">vpc</a></code> | <code>aws-cdk-lib.aws_ec2.IVpc</code> | VPC used for hosting the task. |
Expand Down Expand Up @@ -412,6 +413,18 @@ Label associated with this provider.

---

##### `spot`<sup>Required</sup> <a name="spot" id="@cloudsnorkel/cdk-github-runners.FargateRunner.property.spot"></a>

```typescript
public readonly spot: boolean;
```

- *Type:* boolean

Use spot pricing for Fargate tasks.

---

##### `task`<sup>Required</sup> <a name="task" id="@cloudsnorkel/cdk-github-runners.FargateRunner.property.task"></a>

```typescript
Expand Down Expand Up @@ -1150,6 +1163,7 @@ const fargateRunnerProps: FargateRunnerProps = { ... }
| <code><a href="#@cloudsnorkel/cdk-github-runners.FargateRunnerProps.property.label">label</a></code> | <code>string</code> | GitHub Actions label used for this provider. |
| <code><a href="#@cloudsnorkel/cdk-github-runners.FargateRunnerProps.property.memoryLimitMiB">memoryLimitMiB</a></code> | <code>number</code> | The amount (in MiB) of memory used by the task. |
| <code><a href="#@cloudsnorkel/cdk-github-runners.FargateRunnerProps.property.securityGroup">securityGroup</a></code> | <code>aws-cdk-lib.aws_ec2.ISecurityGroup</code> | Security Group to assign to the task. |
| <code><a href="#@cloudsnorkel/cdk-github-runners.FargateRunnerProps.property.spot">spot</a></code> | <code>boolean</code> | Use Fargate spot capacity provider to save money. |
| <code><a href="#@cloudsnorkel/cdk-github-runners.FargateRunnerProps.property.vpc">vpc</a></code> | <code>aws-cdk-lib.aws_ec2.IVpc</code> | VPC to launch the runners in. |

---
Expand Down Expand Up @@ -1308,6 +1322,22 @@ Security Group to assign to the task.

---

##### `spot`<sup>Optional</sup> <a name="spot" id="@cloudsnorkel/cdk-github-runners.FargateRunnerProps.property.spot"></a>

```typescript
public readonly spot: boolean;
```

- *Type:* boolean
- *Default:* false

Use Fargate spot capacity provider to save money.

* Runners may fail to start due to missing capacity.
* Runners might be stopped prematurely with spot pricing.

---

##### `vpc`<sup>Optional</sup> <a name="vpc" id="@cloudsnorkel/cdk-github-runners.FargateRunnerProps.property.vpc"></a>

```typescript
Expand Down
16 changes: 10 additions & 6 deletions README.md
Expand Up @@ -31,11 +31,15 @@ The best way to browse API documentation is on [Constructs Hub][13]. It is avail

A runner provider creates compute resources on-demand and uses [actions/runner][5] to start a runner.

| Provider | Time limit | vCPUs | RAM | Storage | sudo | Docker |
|-----------|--------------------------|--------------------------|-----------------------------------|------------------------------|------|--------|
| CodeBuild | 8 hours (default 1 hour) | 2 (default), 4, 8, or 72 | 3gb (default), 7gb, 15gb or 145gb | 50gb to 824gb (default 64gb) |||
| Fargate | Unlimited | 0.25 to 4 (default 1) | 512mb to 30gb (default 2gb) | 20gb to 200gb (default 25gb) |||
| Lambda | 15 minutes | 1 to 6 (default 2) | 128mb to 10gb (default 2gb) | Up to 10gb (default 10gb) |||
| | CodeBuild | Fargate | Lambda |
|----------------|--------------------------|---------------|---------------|
| **Time limit** | 8 hours | Unlimited | 15 minutes |
| **vCPUs** | 2, 4, 8, or 72 | 0.25 to 4 | 1 to 6 |
| **RAM** | 3gb, 7gb, 15gb, or 145gb | 512mb to 30gb | 128mb to 10gb |
| **Storage** | 50gb to 824gb | 20gb to 200gb | Up to 10gb |
| **sudo** ||||
| **Docker** ||||
| **Spot** ||||

The best provider to use mostly depends on your current infrastructure. When in doubt, CodeBuild is always a good choice. Execution history and logs are easy to view, and it has no restrictive limits unless you need to run for more than 8 hours.

Expand Down Expand Up @@ -81,7 +85,7 @@ You can also create your own provider by implementing `IRunnerProvider`.

## Customizing

The default providers configured by [`GitHubRunners`](https://constructs.dev/packages/@cloudsnorkel/cdk-github-runners/v/0.0.11/api/GitHubRunners?lang=typescript) are useful for testing but probably not too much for actual production work. They run in the default VPC or no VPC and have no added IAM permissions. You would usually want to configure the providers yourself.
The default providers configured by `GitHubRunners` are useful for testing but probably not too much for actual production work. They run in the default VPC or no VPC and have no added IAM permissions. You would usually want to configure the providers yourself.

For example:

Expand Down
42 changes: 40 additions & 2 deletions src/providers/fargate.ts
Expand Up @@ -98,6 +98,38 @@ export interface FargateRunnerProps extends RunnerProviderProps {
* @default 20
*/
readonly ephemeralStorageGiB?: number;

/**
* Use Fargate spot capacity provider to save money.
*
* * Runners may fail to start due to missing capacity.
* * Runners might be stopped prematurely with spot pricing.
*
* @default false
*/
readonly spot?: boolean;
}

class EcsFargateSpotLaunchTarget implements stepfunctions_tasks.IEcsLaunchTarget {
/**
* Called when the Fargate launch type configured on RunTask
*/
public bind(_task: stepfunctions_tasks.EcsRunTask,
launchTargetOptions: stepfunctions_tasks.LaunchTargetBindOptions): stepfunctions_tasks.EcsLaunchTargetConfig {
if (!launchTargetOptions.taskDefinition.isFargateCompatible) {
throw new Error('Supplied TaskDefinition is not compatible with Fargate');
}

return {
parameters: {
CapacityProviderStrategy: [
{
CapacityProvider: 'FARGATE_SPOT',
},
],
},
};
}
}

/**
Expand Down Expand Up @@ -153,6 +185,11 @@ export class FargateRunner extends Construct implements IRunnerProvider {
*/
readonly connections: ec2.Connections;

/**
* Use spot pricing for Fargate tasks.
*/
readonly spot: boolean;

constructor(scope: Construct, id: string, props: FargateRunnerProps) {
super(scope, id);

Expand All @@ -169,6 +206,7 @@ export class FargateRunner extends Construct implements IRunnerProvider {
enableFargateCapacityProviders: true,
},
);
this.spot = props.spot ?? false;

this.task = new ecs.FargateTaskDefinition(
this,
Expand Down Expand Up @@ -218,7 +256,7 @@ export class FargateRunner extends Construct implements IRunnerProvider {
integrationPattern: IntegrationPattern.RUN_JOB, // sync
taskDefinition: this.task,
cluster: this.cluster,
launchTarget: new stepfunctions_tasks.EcsFargateLaunchTarget(),
launchTarget: this.spot ? new EcsFargateSpotLaunchTarget() : new stepfunctions_tasks.EcsFargateLaunchTarget(),
assignPublicIp: this.assignPublicIp,
securityGroups: this.securityGroup ? [this.securityGroup] : undefined,
containerOverrides: [
Expand Down Expand Up @@ -255,4 +293,4 @@ export class FargateRunner extends Construct implements IRunnerProvider {
},
);
}
}
}

0 comments on commit 99d270f

Please sign in to comment.