Skip to content

Commit 831dda3

Browse files
Add launch-ec2-runner-with-fallback action
This action is used to launch an EC2 instance (either as a spot instance or a dedicated instance) in a desired availability zone. If no availability, then backup availability zones will be tried until one is successful. Signed-off-by: Courtney Pacheco <6019922+courtneypacheco@users.noreply.github.com>
1 parent 68bc43a commit 831dda3

24 files changed

+2608
-17
lines changed

.github/workflows/lint.yml

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -52,17 +52,15 @@ jobs:
5252
- name: "ruff"
5353
commands: |
5454
tox -e ruff
55-
## TODO: Uncomment `pylint` once the first in-house, Python-based action is merged
56-
# - name: "pylint"
57-
# commands: |
58-
# echo "::add-matcher::.github/workflows/matchers/pylint.json"
59-
# tox -e fastlint
60-
# tox -e lint
61-
## TODO: Uncomment `mypy` once the first in-house, Python-based action is merged
62-
# - name: "mypy"
63-
# commands: |
64-
# echo "::add-matcher::.github/workflows/matchers/mypy.json"
65-
# tox -e mypy
55+
- name: "pylint"
56+
commands: |
57+
echo "::add-matcher::.github/workflows/matchers/pylint.json"
58+
tox -e fastlint
59+
tox -e lint
60+
- name: "mypy"
61+
commands: |
62+
echo "::add-matcher::.github/workflows/matchers/mypy.json"
63+
tox -e mypy
6664
- name: "yamllint"
6765
commands: |
6866
tox -e yamllint

.isort.cfg

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,9 @@ import_heading_thirdparty=Third Party
77
import_heading_firstparty=First Party
88
import_heading_localfolder=Local
99
known_firstparty=
10-
known_localfolder=tuning
10+
known_localfolder=tuning
11+
12+
# isort falsely flags some GitHub action Python files as problematic, and tries to add
13+
# comments to existing import statements for the wrong reasons. Therefore, we skip these
14+
# files
15+
skip_glob=actions/launch-ec2-runner-with-fallback/launch_ec2_runner/*.py

.yamllint.yaml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,7 @@ rules:
88
line-length:
99
max: 120
1010
allow-non-breakable-words: true
11-
allow-non-breakable-inline-mappings: false
11+
allow-non-breakable-inline-mappings: false
12+
ignore:
13+
# REASON: This file needs lines that are longer than 120 in order to load variables to the env
14+
- /actions/launch-ec2-runner-with-fallback/action.yml

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ Below is a list of the in-house GitHub actions stored in this repository:
1616
| Name | Description | Example Use Cases |
1717
| --- | --- | --- |
1818
| [free-disk-space](./actions/free-disk-space/free-disk-space.md) | Used to reclaim disk space on either a GitHub or EC2 runner. | <ul><li>If a CI job tends to fail due to "insufficient disk space"</li><li>If you want to reduce cloud costs by reclaiming disk space instead of increasing your writeable cloud storage to compensate for a bloated EC2 image</li></ul> |
19+
| [launch-ec2-runner-with-fallback](./actions/launch-ec2-runner-with-fallback/launch-ec2-runner-with-fallback.md) | Used launch an EC2 instance in AWS, either as a spot instance or a dedicated instance. If your preferred availability zone lacks availability for your instance type, "backup" availability zones will be tried. | <ul><li>Insufficient capacity in AWS (i.e., AWS lacks availablility for your desired EC2 instance type in your preferred availability zone)</li><li>Cost savings (i.e., You want to try launching your EC2 runner as a spot instance first)</li></ul> |
1920

2021
## How to Use One or More In-House GitHub Actions
2122

actions/free-disk-space/action.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
13
name: 'Free Disk Space'
24
description: 'Frees disk space on the runner'
5+
author: 'InstructLab'
36
runs:
47
using: "composite"
58
steps:

actions/launch-ec2-runner-with-fallback/action.yml

Lines changed: 856 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
# Launch EC2 Runner with Fallback
2+
3+
## Overview
4+
5+
This action is used to launch an EC2 instance (EC2 runner) in AWS -- either as a spot instance or a dedicated instance. It essentially leverages the [machulav/ec2-github-runner](https://github.com/machulav/ec2-github-runner) GitHub action under the hood to launch EC2 instance in AWS, but implements "fallback logic" if one or more attempts to launch an EC2 instance fail.
6+
7+
## When to Use it?
8+
9+
### Insufficient Capacity: AWS Lacks Availablility for your Desired EC2 Instance Type in a Given Availability Zone
10+
11+
If one of your CI workflows attempts to launch an EC2 instance in AWS but fails due to an `InsufficientInstanceCapacity` error (aka AWS doesn't have any instances available for that instance type), you can leverage this action to try other regions as a backup.
12+
13+
### Cost Savings: You want to Try Launching Your EC2 Runner as a Spot Instance First
14+
15+
Spot instances are generally cheaper and can be sufficient for certain sitautions. Just be mindful of the implications of a spot instance. The official AWS documentation emphasizes that spot instances ["can be interrupted by Amazon EC2 when EC2 needs the capacity back."](https://docs.aws.amazon.com/whitepapers/latest/cost-optimization-leveraging-ec2-spot-instances/how-spot-instances-work.html) Thus, if your instance type is "very popular" amongst other AWS users and you can't afford to have interruptions on your EC2 instances, you should actively avoid launching your EC2 instances as spot instances.
16+
17+
## Which AWS Regions and Availability Zones are Supported?
18+
19+
The pricing for EC2 instances depends on the region, with some regions charging more money for the same instance type compared to other regions. Given that information, the currently "supported" AWS regions and availability zones for launching an EC2 instance are:
20+
21+
* US East 1 (Virginia) - `us-east-1`
22+
* `us-east-1a`
23+
* `us-east-1b`
24+
* `us-east-1c`
25+
* `us-east-1d`
26+
* `us-east-1e`
27+
* `us-east-1f`
28+
* US East 2 (Ohio) - `us-east-2`
29+
* `us-east-2a`
30+
* `us-east-2b`
31+
* `us-east-2c`
32+
33+
34+
## How to Call this Action from a Job within a GitHub Workflow
35+
36+
Consider a simple job definition within a GitHub workflow file that is used to launch an EC2 instance in AWS. You would first call the GitHub `actions/checkout` action to "checkout" this action and store it locally. A list of supported inputs is provided in the next subsection, followed by an example in the following subsection.
37+
38+
### Supported Inputs
39+
40+
#### Required inputs:
41+
42+
| Name | Description | Example Value |
43+
| --- | --- | --- |
44+
| `aws-access-key-id` | AWS access key ID that will be used to launch your desired instance. | `AKIAIOSFODNN7EXAMPLE` |
45+
| `aws-resource-tags` | Resource tags to apply to the desired AWS instance, upon successful launch. | `[{"Key": "Name", "Value": "my-runner"}]`|
46+
| `aws-secret-access-key` | AWS secret access key that will be used to launch your desired instance. | `wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY` |
47+
| `ec2-instance-type` | The desired AWS instance type to use, regardless of region. (Note that some instance types are not available in certain regions.) | `g4dn.2xlarge` |
48+
| `github-token` | GitHub Personal Access Token with the `repo` scope assigned. | `ghp_xxxxxxxxx` |
49+
| `regions-config` | A JSON string that defines which regions and subnets to try, along with the AMIs and security groups to use within those regions. | See example in next sub-section |
50+
51+
#### Optional inputs:
52+
53+
| Name | Description | Example value |
54+
| --- | --- | --- |
55+
| `try-spot-instance-first` | If set to "true", then the EC2 instance will be launched as a spot instance rather than a dedicated EC2 instance. If a spot instance cannot be launched in any of the desired availability zones (e.g., due to insufficient capacity on AWS), then a dedicated instance will be tried next. (Note: This option is not always desirable for certain instance types.) | `true` |
56+
57+
#### `regions-config` Formatting
58+
59+
This input must be a valid JSON string. It is essentially a list of configuration items, where each configuration item corresponds to the configuration for launching an EC2 instance a specific AWS region. The required fields are:
60+
61+
| Name | Description | Example Value |
62+
| --- | --- | --- |
63+
| `region` | the AWS region code. (You can view [the official "Available AWS Regions" table](https://docs.aws.amazon.com/global-infrastructure/latest/regions/aws-regions.html#available-regions) to see the supported, available codes.) | `us-east-2` |
64+
| `subnets` | a map which tells the GitHub action | - |
65+
| `ec2-ami` | the AMI ID to use in that specific region. (Note that AMI IDs are unique to each region.) | `ami-0123456789` |
66+
| `security-group-id` | security group ID to use when launching the EC2 instance. (Note that security group IDs are unique to each region.) | `sg-02ce123456e7893c7` |
67+
68+
<details>
69+
70+
<summary>Example individual configuration item for launching an EC2 instance in `us-east-1`</summary>
71+
72+
```json
73+
{
74+
"region": "us-east-1",
75+
"subnets": {
76+
"us-east-1a": "${{ secrets.SUBNET_US_EAST_1A }}",
77+
"us-east-1b": "${{ secrets.SUBNET_US_EAST_1B }}",
78+
"us-east-1c": "${{ secrets.SUBNET_US_EAST_1C }}",
79+
},
80+
"ec2-ami": "${{ secrets.EC2_AMI_US_EAST_1 }}",
81+
"security-group-id": "${{ secrets.SECURITY_GROUP_ID_US_EAST_1 }}"
82+
}
83+
```
84+
</details>
85+
86+
Whether you have one or more region configurations, you will need to place them in a list. For example, if you only want to launch in `us-east-1`, your `regions-config` input would be formatted like so:
87+
88+
89+
<details>
90+
91+
<summary>Example format for `regions-config` for only 1 region</summary>
92+
93+
```json
94+
[
95+
{
96+
"region": "us-east-1",
97+
"subnets": {
98+
"us-east-1a": "${{ secrets.SUBNET_US_EAST_1A }}",
99+
"us-east-1b": "${{ secrets.SUBNET_US_EAST_1B }}",
100+
"us-east-1c": "${{ secrets.SUBNET_US_EAST_1C }}",
101+
},
102+
"ec2-ami": "${{ secrets.EC2_AMI_US_EAST_1 }}",
103+
"security-group-id": "${{ secrets.SECURITY_GROUP_ID_US_EAST_1 }}"
104+
}
105+
]
106+
```
107+
108+
</details>
109+
110+
<details>
111+
112+
<summary>Example format for `regions-config` for multiple regions</summary>
113+
114+
```json
115+
[
116+
{
117+
"region": "us-east-1",
118+
"subnets": {
119+
"us-east-1a": "${{ secrets.SUBNET_US_EAST_1A }}",
120+
"us-east-1b": "${{ secrets.SUBNET_US_EAST_1B }}",
121+
"us-east-1c": "${{ secrets.SUBNET_US_EAST_1C }}",
122+
},
123+
"ec2-ami": "${{ secrets.EC2_AMI_US_EAST_1 }}",
124+
"security-group-id": "${{ secrets.SECURITY_GROUP_ID_US_EAST_1 }}"
125+
},
126+
{
127+
"region": "us-east-2",
128+
"subnets": {
129+
"us-east-2a": "${{ secrets.SUBNET_US_EAST_2A }}",
130+
"us-east-2b": "${{ secrets.SUBNET_US_EAST_2B }}",
131+
"us-east-2c": "${{ secrets.SUBNET_US_EAST_2C }}",
132+
"us-east-2d": "${{ secrets.SUBNET_US_EAST_2D }}",
133+
"us-east-2e": "${{ secrets.SUBNET_US_EAST_2E }}",
134+
},
135+
"ec2-ami": "${{ secrets.EC2_AMI_US_EAST_2 }}",
136+
"security-group-id": "${{ secrets.SECURITY_GROUP_ID_US_EAST_2 }}"
137+
}
138+
]
139+
```
140+
141+
</details>
142+
143+
### Example Usage
144+
145+
```yaml
146+
jobs:
147+
start-ec2-runner:
148+
runs-on: ubuntu-latest
149+
steps:
150+
151+
- name: Checkout "launch-ec2-runner-with-fallback" in-house CI action
152+
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
153+
with:
154+
# The action is stored in this repository, so we need to tell GitHub to pull from: {org}/{repo}
155+
repository: instructlab/ci-actions
156+
# clone the "ci-actions" repo to a local directory called "ci-actions", instead of overwriting the current WORKDIR contents
157+
path: ci-actions
158+
# Only checkout the relevant GitHub action
159+
sparse-checkout: |
160+
actions/launch-ec2-runner-with-fallback
161+
162+
- name: Launch EC2 Runner with Fallback
163+
# Make sure to provide the relative path to `launch-ec2-runner-with-fallback`
164+
uses: ./ci-actions/actions/launch-ec2-runner-with-fallback
165+
with:
166+
# (OPTIONAL) Cost-savings inputs
167+
try-spot-instance-first: "true"
168+
# (REQUIRED) Authentication inputs
169+
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
170+
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
171+
github-token: ${{ secrets.GITHUB_TOKEN }}
172+
# (REQUIRED) Common EC2 instance configuration inputs
173+
ec2-instance-type: g4dn.2xlarge
174+
aws-resource-tags: >
175+
[
176+
{"Key": "Name", "Value": "instructlab-ci-github-xlarge-runner"},
177+
{"Key": "GitHubRepository", "Value": "${{ github.repository }}"},
178+
{"Key": "GitHubRef", "Value": "${{ github.ref }}"},
179+
{"Key": "GitHubPR", "Value": "${{ github.event.number }}"}
180+
]
181+
# (REQUIRED) AWS regions to try launching your EC2 instance in. (If desired, you can
182+
# omit one of the supported regions below. You can also omit specific
183+
# subnets within those regions.)
184+
regions-config: >
185+
[
186+
{
187+
"region": "us-east-1",
188+
"subnets": {
189+
"us-east-1a": "${{ secrets.SUBNET_US_EAST_1A }}",
190+
"us-east-1b": "${{ secrets.SUBNET_US_EAST_1B }}",
191+
"us-east-1c": "${{ secrets.SUBNET_US_EAST_1C }}",
192+
},
193+
"ec2-ami": "${{ secrets.EC2_AMI_US_EAST_1 }}",
194+
"security-group-id": "${{ secrets.SECURITY_GROUP_ID_US_EAST_1 }}"
195+
},
196+
{
197+
"region": "us-east-2",
198+
"subnets": {
199+
"us-east-2a": "${{ secrets.SUBNET_US_EAST_2A }}",
200+
"us-east-2b": "${{ secrets.SUBNET_US_EAST_2B }}",
201+
"us-east-2c": "${{ secrets.SUBNET_US_EAST_2C }}",
202+
"us-east-2d": "${{ secrets.SUBNET_US_EAST_2D }}",
203+
"us-east-2e": "${{ secrets.SUBNET_US_EAST_2E }}",
204+
},
205+
"ec2-ami": "${{ secrets.EC2_AMI_US_EAST_2 }}",
206+
"security-group-id": "${{ secrets.SECURITY_GROUP_ID_US_EAST_2 }}"
207+
}
208+
]
209+
```
210+
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pytest

actions/launch-ec2-runner-with-fallback/launch_ec2_runner/requirements.txt

Whitespace-only changes.

actions/launch-ec2-runner-with-fallback/launch_ec2_runner/src/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)