Skip to content

feat: Add multi-runner capability #2472

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

Merged
merged 53 commits into from
Oct 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
d4b0e68
feat: Remove support check_run (#2521)
npalm Oct 14, 2022
d17d8d4
feat: Remove old scale down mechanism (< 0.19.0) (#2519)
npalm Oct 14, 2022
bae3cf0
feat: added changes for multi runner.
npalm Oct 14, 2022
e5bb0e8
fix: region.
navdeepg2021 Sep 26, 2022
1d798d7
fix: more fixes.
navdeepg2021 Sep 26, 2022
680dc8a
tuple to list.
navdeepg2021 Sep 26, 2022
ebbbae7
fixes.
navdeepg2021 Sep 26, 2022
d41e002
fixes.
navdeepg2021 Sep 26, 2022
f0f9710
fixes.
navdeepg2021 Sep 27, 2022
14aa4b5
fixes.
navdeepg2021 Sep 27, 2022
13b5957
fixes.
navdeepg2021 Sep 27, 2022
2d34ee8
fixes.
navdeepg2021 Sep 28, 2022
757d761
fix: formatting.
npalm Oct 14, 2022
7ef7d99
fix: formatting.
navdeepg2021 Sep 28, 2022
d9387a0
fix: formatting.
navdeepg2021 Sep 28, 2022
adb4cf8
fix: moved some blocks outside runner config.
navdeepg2021 Sep 28, 2022
a42848e
fix: few more updates
navdeepg2021 Oct 5, 2022
938fb1b
fix: liniting.
navdeepg2021 Oct 5, 2022
e909b0b
fix: updated example output
navdeepg2021 Oct 5, 2022
6c4c9b1
changed runner group name.
navdeepg2021 Oct 5, 2022
baee588
fix: updated the tests.
navdeepg2021 Oct 5, 2022
907ecbc
fix: addressed review comments.
npalm Oct 14, 2022
d3f3265
fix: linting issues.
navdeepg2021 Oct 6, 2022
ce72966
fix: formatting.
navdeepg2021 Oct 6, 2022
8c04804
fix: updated tf version.
navdeepg2021 Oct 6, 2022
22ca2cf
fix: Remove removed prerelease option
npalm Oct 11, 2022
8ca804f
Add ubuntu runner to example
npalm Oct 11, 2022
2c1afc5
refactor: use each instead of count
npalm Oct 14, 2022
809aad0
fix: few small issues.
navdeepg2021 Oct 12, 2022
97ade12
refactor: syncer to count for multi runner
npalm Oct 12, 2022
4afe114
fix: comments.
navdeepg2021 Oct 12, 2022
67f2fa5
fix: added Readme.
navdeepg2021 Oct 12, 2022
a135aef
fix: errors.
navdeepg2021 Oct 13, 2022
2d44451
move variable to runner config
npalm Oct 13, 2022
7cc96aa
fix: updated the readme.
navdeepg2021 Oct 13, 2022
330b1e3
Add todos
npalm Oct 13, 2022
19363d1
feat: added windows runner configuration, completed todos and added t…
npalm Oct 14, 2022
b1553bd
chore: Update docs
npalm Oct 17, 2022
42301e4
fix: reverted tf versions.
navdeepg2021 Oct 17, 2022
5bdfdb7
fix: addressed comments.
navdeepg2021 Oct 17, 2022
1c4e687
fix: missed.
navdeepg2021 Oct 17, 2022
04d09a7
fix: formatting.
navdeepg2021 Oct 17, 2022
9733e2c
Update terraform versions in CI
npalm Oct 18, 2022
0c9e275
Update terraform versions in CI
npalm Oct 18, 2022
020bb94
Update docs
npalm Oct 18, 2022
c3e97f1
fix: coverage.
navdeepg2021 Oct 18, 2022
3970ff6
Update docs
npalm Oct 18, 2022
39f0406
improve test coverage webhook
npalm Oct 18, 2022
b07d8ad
Apply suggestions from code review
npalm Oct 18, 2022
c91d204
fix: formatting.
navdeepg2021 Oct 18, 2022
812ba97
Merge branch 'next' into features/multi-runner
Oct 18, 2022
4d2abb4
fix: fixed merge issues.
navdeepg2021 Oct 18, 2022
7d0c40e
fix: syntax.
navdeepg2021 Oct 18, 2022
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
35 changes: 31 additions & 4 deletions .github/workflows/terraform.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
name: Verify module
strategy:
matrix:
terraform: [1.1.3, "latest"]
terraform: [1.3.2, "latest"]
runs-on: ubuntu-latest
container:
image: hashicorp/terraform:${{ matrix.terraform }}
Expand All @@ -29,7 +29,7 @@ jobs:
touch modules/runner-binaries-syncer/lambdas/runner-binaries-syncer/runner-binaries-syncer.zip
- name: terraform init
run: terraform init -get -backend=false -input=false
- if: contains(matrix.terraform, '1.1.')
- if: contains(matrix.terraform, '1.3.')
name: check terraform formatting
run: terraform fmt -recursive -check=true -write=false
- if: contains(matrix.terraform, 'latest') # check formatting for the latest release but avoid failing the build
Expand All @@ -44,7 +44,7 @@ jobs:
strategy:
fail-fast: false
matrix:
terraform: [1.0.11, 1.1.3, "latest"]
terraform: [1.0.11, 1.1.9, 1.2.9, "latest"]
example:
["default", "ubuntu", "prebuilt", "arm64", "ephemeral", "windows"]
defaults:
Expand All @@ -57,7 +57,7 @@ jobs:
- uses: actions/checkout@v3
- name: terraform init
run: terraform init -get -backend=false -input=false
- if: contains(matrix.terraform, '1.1.')
- if: contains(matrix.terraform, '1.3.')
name: check terraform formatting
run: terraform fmt -recursive -check=true -write=false
- if: contains(matrix.terraform, 'latest') # check formatting for the latest release but avoid failing the build
Expand All @@ -66,3 +66,30 @@ jobs:
continue-on-error: true
- name: validate terraform011
run: terraform validate


verify_multi_runner_example:
name: Verify Multi-Runner examples
strategy:
fail-fast: false
matrix:
terraform: [1.3.2, "latest"]
defaults:
run:
working-directory: examples/multi-runner
runs-on: ubuntu-latest
container:
image: hashicorp/terraform:${{ matrix.terraform }}
steps:
- uses: actions/checkout@v3
- name: terraform init
run: terraform init -get -backend=false -input=false
- if: contains(matrix.terraform, '1.3.')
name: check terraform formatting
run: terraform fmt -recursive -check=true -write=false
- if: contains(matrix.terraform, 'latest') # check formatting for the latest release but avoid failing the build
name: check terraform formatting
run: terraform fmt -recursive -check=true -write=false
continue-on-error: true
- name: validate terraform
run: terraform validate
27 changes: 11 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ This [Terraform](https://www.terraform.io/) module creates the required infrastr
- [Motivation](#motivation)
- [Overview](#overview)
- [Major configuration options.](#major-configuration-options)
- [ARM64 support via Graviton/Graviton2 instance-types](#arm64-support-via-gravitongraviton2-instance-types)
- [Usages](#usages)
- [Setup GitHub App (part 1)](#setup-github-app-part-1)
- [Setup terraform module](#setup-terraform-module)
Expand All @@ -25,7 +24,6 @@ This [Terraform](https://www.terraform.io/) module creates the required infrastr
- [Experimental - Optional queue to publish GitHub workflow job events](#experimental---optional-queue-to-publish-github-workflow-job-events)
- [Examples](#examples)
- [Sub modules](#sub-modules)
- [ARM64 configuration for submodules](#arm64-configuration-for-submodules)
- [Debugging](#debugging)
- [Security Consideration](#security-consideration)
- [Requirements](#requirements)
Expand Down Expand Up @@ -81,16 +79,13 @@ Besides these permissions, the lambdas also need permission to CloudWatch (for l
To be able to support a number of use-cases the module has quite a lot of configuration options. We try to choose reasonable defaults. The several examples also show for the main cases how to configure the runners.

- Org vs Repo level. You can configure the module to connect the runners in GitHub on an org level and share the runners in your org. Or set the runners on repo level and the module will install the runner to the repo. There can be multiple repos but runners are not shared between repos.
- Checkrun vs Workflow job event. You can configure the webhook in GitHub to send checkrun or workflow job events to the webhook. Workflow job events are introduced by GitHub in September 2021 and are designed to support scalable runners. We advise when possible using the workflow job event, you can set `runner_enable_workflow_job_labels_check = true` to let the webhook only accept jobs based on the labels configured. The webhook will check the custom labels provided via the variable `runner_extra_labels` and the GitHub managed labels, "self-hosted", OS and architecture. The OS and architecture are derived from the settings. By default the check is disabled.
- Multi-Runner module. This modules allows to create multiple runner configurations with a single webhook and single GitHub App to simply deployment of different types of runners. Refer to the [ReadMe](.modules/../modules/multi-runner/README.md) for more information to understand the functionality.
- Workflow job event. You can configure the webhook in GitHub to send workflow job events to the webhook. Workflow job events are introduced by GitHub in September 2021 and are designed to support scalable runners. We advise when possible using the workflow job event.
- Linux vs Windows. you can configure the OS types linux and win. Linux will be used by default.
- Re-use vs Ephemeral. By default runners are re-used for till detected idle. Once idle they will be removed from the pool. To improve security we are introducing ephemeral runners. Those runners are only used for one job. Ephemeral runners are only working in combination with the workflow job event. We also suggest using a pre-build AMI to improve the start time of jobs.
- GitHub Cloud vs GitHub Enterprise Server (GHES). The runner support GitHub Cloud as well GitHub Enterprise Server. For GHES we rely on our community to test and support. We have no possibility to test ourselves on GHES.
- Spot vs on-demand. The runners use either the EC2 spot or on-demand life cycle. Runners will be created via the AWS [CreateFleet API](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_CreateFleet.html). The module (scale up lambda) will request via the CreateFleet API to create instances in one of the subnets and of the specified instance types.


#### ARM64 support via Graviton/Graviton2 instance-types

When using the default example or top-level module, specifying `instance_types` that match a Graviton/Graviton 2 (ARM64) architecture (e.g. a1, t4g or any 6th-gen `g` or `gd` type), you must also specify `runner_architecture = "arm64"` and the sub-modules will be automatically configured to provision with ARM64 AMIs and leverage GitHub's ARM64 action runner. See below for more details.
- ARM64 support via Graviton/Graviton2 instance-types. When using the default example or top-level module, specifying `instance_types` that match a Graviton/Graviton 2 (ARM64) architecture (e.g. a1, t4g or any 6th-gen `g` or `gd` type), you must also specify `runner_architecture = "arm64"` and the sub-modules will be automatically configured to provision with ARM64 AMIs and leverage GitHub's ARM64 action runner. See below for more details.

## Usages

Expand Down Expand Up @@ -334,11 +329,13 @@ Examples are located in the [examples](./examples) directory. The following exam

- _[Default](examples/default/README.md)_: The default example of the module
- _[ARM64](examples/arm64/README.md)_: Example usage with ARM64 architecture
- _[Ubuntu](examples/ubuntu/README.md)_: Example usage of creating a runner using Ubuntu AMIs.
- _[Windows](examples/windows/README.md)_: Example usage of creating a runner using Windows as the OS.
- _[Ephemeral](examples/ephemeral/README.md)_: Example usages of ephemeral runners based on the default example.
- _[Prebuilt Images](examples/prebuilt/README.md)_: Example usages of deploying runners with a custom prebuilt image.
- _[Multi Runner](examples/multi-runner/README.md)_ : Example usage of creating a multi runner which creates multiple runners/ configurations with a single deployment
- _[Permissions boundary](examples/permissions-boundary/README.md)_: Example usages of permissions boundaries.
- _[Prebuilt Images](examples/prebuilt/README.md)_: Example usages of deploying runners with a custom prebuilt image.
- _[Ubuntu](examples/ubuntu/README.md)_: Example usage of creating a runner using Ubuntu AMIs.
- _[Windows](examples/windows/README.md)_: Example usage of creating a runner using Windows as the OS.


## Sub modules

Expand All @@ -349,15 +346,14 @@ The following submodules are the core of the module and are mandatory:
- _[runner-binaries-syncer](./modules/runner-binaries-syncer/README.md)_ - Syncs the action runner distribution.
- _[runners](./modules/runners/README.md)_ - Scales the action runners up and down
- _[webhook](./modules/webhook/README.md)_ - Handles GitHub webhooks
- _[multi-runner](./modules/multi-runner/README.md) - Creates multiple runner configurations in a single deployment

The following sub modules are optional and are provided as example or utility:

- _[download-lambda](./modules/download-lambda/README.md)_ - Utility module to download lambda artifacts from GitHub Release
- _[setup-iam-permissions](./modules/setup-iam-permissions/README.md)_ - Example module to setup permission boundaries

### ARM64 configuration for submodules

When using the top level module configure `runner_architecture = "arm64"` and ensure the list of `instance_types` matches. When not using the top-level, ensure these properties are set on the submodules.
ARM64 configuration for submodules. When using the top level module configure `runner_architecture = "arm64"` and ensure the list of `instance_types` matches. When not using the top-level, ensure these properties are set on the submodules.

## Debugging

Expand Down Expand Up @@ -484,8 +480,7 @@ We welcome any improvement to the standard module to make the default as secure
| <a name="input_runner_boot_time_in_minutes"></a> [runner\_boot\_time\_in\_minutes](#input\_runner\_boot\_time\_in\_minutes) | The minimum time for an EC2 runner to boot and register as a runner. | `number` | `5` | no |
| <a name="input_runner_ec2_tags"></a> [runner\_ec2\_tags](#input\_runner\_ec2\_tags) | Map of tags that will be added to the launch template instance tag specifications. | `map(string)` | `{}` | no |
| <a name="input_runner_egress_rules"></a> [runner\_egress\_rules](#input\_runner\_egress\_rules) | List of egress rules for the GitHub runner instances. | <pre>list(object({<br> cidr_blocks = list(string)<br> ipv6_cidr_blocks = list(string)<br> prefix_list_ids = list(string)<br> from_port = number<br> protocol = string<br> security_groups = list(string)<br> self = bool<br> to_port = number<br> description = string<br> }))</pre> | <pre>[<br> {<br> "cidr_blocks": [<br> "0.0.0.0/0"<br> ],<br> "description": null,<br> "from_port": 0,<br> "ipv6_cidr_blocks": [<br> "::/0"<br> ],<br> "prefix_list_ids": null,<br> "protocol": "-1",<br> "security_groups": null,<br> "self": null,<br> "to_port": 0<br> }<br>]</pre> | no |
| <a name="input_runner_enable_workflow_job_labels_check"></a> [runner\_enable\_workflow\_job\_labels\_check](#input\_runner\_enable\_workflow\_job\_labels\_check) | If set to true all labels in the workflow job even are matched against the custom labels and GitHub labels (os, architecture and `self-hosted`). When the labels are not matching the event is dropped at the webhook. | `bool` | `false` | no |
| <a name="input_runner_enable_workflow_job_labels_check_all"></a> [runner\_enable\_workflow\_job\_labels\_check\_all](#input\_runner\_enable\_workflow\_job\_labels\_check\_all) | If set to true all labels in the workflow job must match the GitHub labels (os, architecture and `self-hosted`). When false if __any__ label matches it will trigger the webhook. `runner_enable_workflow_job_labels_check` must be true for this to take effect. | `bool` | `true` | no |
| <a name="input_runner_enable_workflow_job_labels_check_all"></a> [runner\_enable\_workflow\_job\_labels\_check\_all](#input\_runner\_enable\_workflow\_job\_labels\_check\_all) | If set to true all labels in the workflow job must match the GitHub labels (os, architecture and `self-hosted`). When false if __any__ label matches it will trigger the webhook. | `bool` | `true` | no |
| <a name="input_runner_extra_labels"></a> [runner\_extra\_labels](#input\_runner\_extra\_labels) | Extra (custom) labels for the runners (GitHub). Separate each label by a comma. Labels checks on the webhook can be enforced by setting `enable_workflow_job_labels_check`. GitHub read-only labels should not be provided. | `string` | `""` | no |
| <a name="input_runner_group_name"></a> [runner\_group\_name](#input\_runner\_group\_name) | Name of the runner group. | `string` | `"Default"` | no |
| <a name="input_runner_iam_role_managed_policy_arns"></a> [runner\_iam\_role\_managed\_policy\_arns](#input\_runner\_iam\_role\_managed\_policy\_arns) | Attach AWS or customer-managed IAM policies (by ARN) to the runner IAM role | `list(string)` | `[]` | no |
Expand Down
3 changes: 0 additions & 3 deletions examples/ephemeral/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,6 @@ module "runners" {
enable_organization_runners = true
runner_extra_labels = "default,example"

# enable workflow labels check
# runner_enable_workflow_job_labels_check = true

# enable access to the runners via SSM
enable_ssm_on_runners = true

Expand Down
60 changes: 60 additions & 0 deletions examples/multi-runner/.terraform.lock.hcl

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

48 changes: 48 additions & 0 deletions examples/multi-runner/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Action runners deployment of Multiple-Runner-Configurations-Together example

This module shows how to create GitHub action runners with multiple runner configuration together in one deployment. This example has the configurations for the following runner types with the relevant labels supported by them as matchers:

- Linux ARM64 `["self-hosted", "linux", "arm64", "amazon"]`
- Linux Ubuntu `["self-hosted", "linux", "x64", "ubuntu"]`
- Linux X64 `["self-hosted", "linux", "x64", "amazon"]`
- Windows X64 `["self-hosted", "windows", "x64", "servercore-2022"]`

The module will decide the runner for the workflow job based on the match in the labels defined in the workflow job and runner configuration. Also the runner configuration allows the match to be exact or non-exact match. We recommend to use only exact matches.

For exact match, all the labels defined in the workflow should be present in the runner configuration matchers and for non-exact match, some of the labels in the workflow, when present in runner configuration, shall be enough for the runner configuration to be used for the job. First the exact matchers are applied, next the non exact ones.

## Webhook

For the list of provided runner configurations, there will be a single webhook and only a single Github App to receive the notifications for all types of workflow triggers.

## Lambda distribution

Per combination of OS and architecture a lambda distribution syncer will be created. For this example there will be three instances (windows X64, linux X64, linux ARM).

## Usages

Steps for the full setup, such as creating a GitHub app can be found in the root module's [README](../../README.md). First download the Lambda releases from GitHub. Alternatively you can build the lambdas locally with Node or Docker, there is a simple build script in `<root>/.ci/build.sh`. In the `main.tf` you can simply remove the location of the lambda zip files, the default location will work in this case.

> Ensure you have set the version in `lambdas-download/main.tf` for running the example. The version needs to be set to a GitHub release version, see https://github.com/philips-labs/terraform-aws-github-runner/releases

```bash
cd lambdas-download
terraform init
terraform apply
cd ..
```

Before running Terraform, ensure the GitHub app is configured. See the [configuration details](../../README.md#usages) for more details.

```bash
terraform init
terraform apply
```

You can receive the webhook details by running:

```bash
terraform output -raw webhook_secret
```

Be-aware some shells will print some end of line character `%`.
25 changes: 25 additions & 0 deletions examples/multi-runner/lambdas-download/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
locals {
version = "<REPLACE_BY_GITHUB_RELEASE_VERSION>"
}

module "lambdas" {
source = "../../../modules/download-lambda"
lambdas = [
{
name = "webhook"
tag = local.version
},
{
name = "runners"
tag = local.version
},
{
name = "runner-binaries-syncer"
tag = local.version
}
]
}

output "files" {
value = module.lambdas.files
}
Loading