Skip to content

Commit

Permalink
Packer config for building the RunsOn AMI. (#21832)
Browse files Browse the repository at this point in the history
  • Loading branch information
benjyw authored Jan 15, 2025
1 parent e061067 commit ef428fb
Show file tree
Hide file tree
Showing 2 changed files with 144 additions and 20 deletions.
121 changes: 121 additions & 0 deletions build-support/packer/runson/runson.pkr.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
# Packer config to create the AMI we use with https://runs-on.com/
# for custom Linux-ARM64 CI runners.

# RunsOn deprecate features periodically, and eventually refuse to work with old AMIs.
# So we need to recreate the AMI every few weeks. Currently we do this manually,
# but should consider automating it.

# To create the AMI using this config:
#
# Preliminaries:
#
# 1. Install Packer: https://developer.hashicorp.com/packer/tutorials/docker-get-started/get-started-install-cli
#
# 2. Ensure that you have CLI credentials for an IAM user in the pantsbuild AWS account,
# and that this user has the relevant permissions (e.g., by attaching
# the PackerAmazonPlugin policy). We'll assume that this user's creds are in
# a profile called `pants` in ~/.aws/credentials.
#
# Running Packer:
#
# 1. Run `packer init build-support/packer/runson/runson.pkr.hcl` to initialize state.
#
# 2. Per our policy, your user must use MFA. So you must acquire temporary credentials to pass to Packer:
#
# `aws --profile=pants sts get-session-token --serial-number arn:aws:iam::AWSACCOUNTID:mfa/USERNAME --token-code XXXXXX`
# Where AWSACCOUNTID is pantsbuild's AWS account ID, USERNAME is your username, and XXXXXX is the code
# generated by your MFA token.
#
# This will emit temporary credentials as follows:
#
# {
# "Credentials": {
# "AccessKeyId": "ACCESSKEYID",
# "SecretAccessKey": "SECRETACCESSKEY",
# "SessionToken": "SESSIONTOKEN",
# }
# }
#
# 3. Run Packer build with the temporary creds:
# ```
# AWS_ACCESS_KEY_ID=ACCESSKEYID AWS_SECRET_ACCESS_KEY=SECRETACCESSKEY AWS_SESSION_TOKEN=SESSIONTOKEN \
# packer build build-support/packer/runson/runson.pkr.hcl
# ```
#
# This will take ~7 minutes but will eventually give you the ID of the AMI:
#
# ```
# ==> Builds finished. The artifacts of successful builds are:
# --> build-custom-runson-ami.amazon-ebs.runson: AMIs were created:
# us-east-1: AMI-ID
# ```
#
# Updating RunsOn Config:
#
# 1. Create a PR that sets the `ami` field in .github/runs-on.yml to this new AMI-ID.
# Once this PR is merged into the main branch, RunsOn will start using the new AMI
# when it creates on-the-fly CI runners.
#
# Note that since the Pants repo is public, RunsOn will only use config in the default
# branch of the repo, in our case `main`. If the reason you're updating the AMI is that
# CI is failing on an expired previous AMI, you will need to merge into main without green CI.
# This will require you to temporarily disable the branch protection rule on main.
#
# 2. Once the new config is in main, and any in-flight CI runs have finished, we can deregister
# the old AMI via the AWS web console, to avoid confusion.
#
# NOTE: If you edit this file, run `packer fmt build-support/packer/runson/runson.pkr.hcl` to format it
# and `packer validate build-support/packer/runson/runson.pkr.hcl` to validate it.
#
# See https://runs-on.com/guides/building-custom-ami-with-packer/ for RunsOn's documentation relating
# to generating compatible AMIs with Packer.

packer {
required_plugins {
amazon = {
version = ">= 1.2.8"
source = "github.com/hashicorp/amazon"
}
}
}

source "amazon-ebs" "runson" {
ami_name = "ubuntu22-full-arm64-python3.7-3.13-{{timestamp}}"
instance_type = "t4g.nano"
region = "us-east-1"
source_ami_filter {
filters = {
name = "runs-on-v2.2-ubuntu22-full-arm64-*"
root-device-type = "ebs"
virtualization-type = "hvm"
}
most_recent = true
owners = ["135269210855"] # RunsOn's AWS account ID
}
# Packer creates a temporary keypair on the temporary instance it uses
# to create the AMI, so we don't need to configure that.
ssh_username = "ubuntu"
user_data = "#!/bin/bash\nsystemctl start ssh"
}

build {
name = "build-custom-runson-ami"
sources = [
"source.amazon-ebs.runson"
]
provisioner "shell" {
inline = [
"sudo apt-get install -y software-properties-common",
"sudo add-apt-repository -y ppa:deadsnakes/ppa",
"sudo apt-get update",
"sudo apt-get install -y \\",
"python3.7 python3.7-dev python3.7-venv \\",
"python3.8 python3.8-dev python3.8-venv \\",
"python3.9 python3.9-dev python3.9-venv \\",
"python3.10 python3.10-dev python3.10-venv \\",
"python3.11 python3.11-dev python3.11-venv \\",
"python3.12 python3.12-dev python3.12-venv \\",
"python3.13 python3.13-dev python3.13-venv",
]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,27 +10,31 @@ GitHub Actions does not have affordable hosted aarch64 runners.
For a while we enjoyed hardware donated by the Works on ARM program, but that has now been terminated,
so we instead rely on self-hosted EC2 instances.

We use [Runs On](https://runs-on.com/) to simplify the management of these instances. Runs On monitors
We use [RunsOn](https://runs-on.com/) to simplify the management of these instances. RunsOn monitors
requests for runners, launches EC2 instances on the fly to satify those requests, and terminates them
when the workflows complete.

## The Custom AMI

We configure Runs On to launch these instances with a custom AMI that pre-installs the Python interpreters
needed to bootstrap and test Pants on aarch64. This custom AMI is built on top of the standard Runs On
[Ubuntu 22 ARM64](runs-on-v2.2-ubuntu22-full-arm64-*) AMI.
We configure RunsOn to launch these instances with a custom AMI that pre-installs the Python interpreters
needed to bootstrap and test Pants on aarch64. This custom AMI is built on top of the standard RunsOn
Ubuntu 22 ARM64 AMI.

It may occasionally be necessary to update this AMI, for example to pick up updates to the underlying standard
AMI. To do so manually:
AMI.

To do so with Packer, see instructions in [build-support/packer/runson/runson.pkr.hcl]()

To do so manually:

### Create a temporary EC2 instance on the standard AMI

In the AWS web console, initiate creation of a temporary instance that we use just to build the custom AMI.

- Any instance type will do, t4g.nano for example.
- Any ARM64 instance type will do, t4g.nano for example.
- To select the base AMI for the instance, click on "Browse more AMIs", then the "Community AMIs" tab,
then search for `runs-on-v2.2-ubuntu22-full-arm64` and pick the latest standard AMI, as described in
the Runs On [images repo](https://github.com/runs-on/runner-images-for-aws).
the RunsOn [images repo](https://github.com/runs-on/runner-images-for-aws).
- Select `pantsbuild.org.bastion` as the instance's SSH keypair.
- Allow the wizard to create a security group.
- The default storage settings are fine.
Expand All @@ -55,10 +59,10 @@ You will need the `pantsbuild.org.bastion` private key, which is in our 1Passwor
Once you are in a bash prompt on the instance, run the following:

```bash
sudo apt install -y software-properties-common
sudo apt-get install -y software-properties-common
sudo add-apt-repository -y ppa:deadsnakes/ppa
sudo apt update
sudo apt install -y \
sudo apt-get update
sudo apt-get install -y \
python3.7 python3.7-dev python3.7-venv \
python3.8 python3.8-dev python3.8-venv \
python3.9 python3.9-dev python3.9-venv \
Expand All @@ -73,26 +77,25 @@ to install the necessary Pythons.
### Create an AMI from the instance

From the instance's actions menu in the web console select "Images" and then "Create image".
The image name doesn't strictly matter, but `ubuntu22-full-arm64-python3.7-3.13`
is a good name for consistency (multiple AMIs may have the same name).
The image name doesn't strictly matter, but `ubuntu22-full-arm64-python3.7-3.13-{{timestamp}}`
is a good name for consistency with the Packer-generated AMIs, where `{{timestamp}}` is a
recent POSIX time, e.g., as returned by `date +%s`.

### Terminate the temporary instance

Once the AMI status shows as "Available", terminate the temporary instance and delete its security group
(whose name will match launch-wizard-*).

We hope to [automate the above via Packer](https://runs-on.com/guides/building-custom-ami-with-packer/) at some point.
(whose name will match `launch-wizard-*`).

### Update the Runs On config
### Update the RunsOn config

Edit `.github/runs-on.yml` and update the `ami` field. Note that the logical image name in this file
happens to be the same as the AMI name, but doesn't strictly have to be. This is the name that we
happens to be a prefix of the AMI name, but doesn't strictly have to be. This is the name that we
target in the `runs-on` stanza of a CI job in `generate_github_workflows.py`.

Note that the new AMI will only be used in PR CI jobs after the config change is merged into the
target branch, so the CI job for the update itself will still use the old AMI.
Note that the new AMI will only be used in PR CI jobs after the config change is merged into main,
so the CI job for the update itself will still use the old AMI.

### Deregister the old AMI

After the config update has been merged into `main` and any release branches it needs to be in,
we can deregister the old AMI in the AWS web console, to avoid confusion.
we can deregister the old AMI via the AWS web console, to avoid confusion.

0 comments on commit ef428fb

Please sign in to comment.