GitHub Actions的按需自托管AWS EC2运行器
⚠️ 如果您喜欢这个项目,请考虑在俄罗斯占领者发动的战争中支持乌克兰。任何帮助都将不胜感激!
(图片由Nina Dzyvulska提供)
在需要时立即启动您的EC2自托管运行器。 在其上运行作业。 最后,完成后停止它。 这一切都可以作为GitHub Actions工作流程的一部分自动完成。
请参阅下方图中所示工作流程的YAML代码。
目录
使用场景
访问VPC中的私有资源
该操作可以在您需要的VPC的任何子网中启动EC2运行器 - 无论是公共还是私有子网。 通过这种方式,您可以轻松地从GitHub Actions工作流程访问VPC中的任何私有资源。
例如,您可以访问私有子网中的数据库以运行数据库迁移。
自定义硬件配置
GitHub为其Linux虚拟机提供了一种固定的硬件配置:2核CPU、7 GB RAM、14 GB SSD磁盘空间。
您的某些CI工作负载可能需要比GitHub托管的运行器提供的更强大的硬件。 在此操作中,您可以为运行器配置AWS提供的任何EC2实例类型。
例如,您可以为某些计算密集型工作负载运行c5.4xlarge EC2运行器。 或者为处理大型数据集的工作负载运行r5.xlarge EC2运行器。
节省成本
如果您的CI工作负载不需要GitHub托管运行器的性能,并且执行时间超过几分钟, 您可以考虑在AWS的更便宜、性能较低的实例上运行它。
根据GitHub的文档,您无需为自托管运行器处理的作业付费:
自托管运行器可以免费用于GitHub Actions,但您需要负责维护运行器机器的成本。
因此,GitHub只会对自托管运行器的启动和停止时间收费。 EC2自托管运行器将处理其他所有事务,因此您只需向AWS支付费用,这可能比GitHub托管运行器的价格更便宜。
使用方法
如何开始
按照以下步骤准备您的工作流程,以在EC2自托管运行器上运行:
1. 准备具有AWS访问密钥的IAM用户
-
为新的或现有的IAM用户创建新的AWS访问密钥,并具有以下最低权限:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "ec2:RunInstances", "ec2:TerminateInstances", "ec2:DescribeInstances", "ec2:DescribeInstanceStatus" ], "Resource": "*" } ] }
如果您计划使用
iam-role-name
参数将IAM角色附加到EC2运行器,则需要允许额外的权限:{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "ec2:ReplaceIamInstanceProfileAssociation", "ec2:AssociateIamInstanceProfile" ], "Resource": "*" }, { "Effect": "Allow", "Action": "iam:PassRole", "Resource": "*" } ] }
如果使用
aws-resource-tags
参数,还需要允许创建标签的权限:{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "ec2:CreateTags" ], "Resource": "*", "Condition": { "StringEquals": { "ec2:CreateAction": "RunInstances" } } } ] }
上述示例策略仅作为指南提供。通过指定您使用的资源,可以并且很可能应该进一步限制这些权限。
-
将密钥添加到GitHub的密钥中。
-
使用aws-actions/configure-aws-credentials操作将密钥设置为环境变量。
2. 准备GitHub个人访问令牌
- 创建一个具有
repo
范围的新GitHub个人访问令牌。 该操作将使用此令牌在仓库级别管理GitHub账户中的自托管运行器。 - 将令牌添加到GitHub的密钥中。
3. 准备EC2镜像
- 基于您需要的任何Linux发行版创建一个新的EC2实例。
- 使用SSH连接到实例,安装
docker
和git
,然后启用docker
服务。 对于 Amazon Linux 2,命令如下所示:
sudo yum update -y && \
sudo yum install docker -y && \
sudo yum install git -y && \
sudo yum install libicu -y && \
sudo systemctl enable docker
对于其他 Linux 发行版,命令可能略有不同。
- 安装工作流程所需的其他工具。
- 从该实例创建新的 EC2 镜像(AMI)。
- 如果在创建镜像后不再需要该实例,请将其删除。
或者,您可以使用原始 EC2 AMI,并通过工作流 YAML 文件中的 pre-runner-script
设置依赖项。请参阅下面 pre-runner-script
文档中的示例。
4. 准备带有子网和安全组的 VPC
- 创建新的 VPC 和其中的新子网。 或使用现有的 VPC 和子网。
- 在 VPC 中为运行器创建新的安全组。 只允许 443 端口的出站流量,用于从 GitHub 拉取作业。 不需要入站流量。
5. 配置 GitHub 工作流
- 创建新的 GitHub Actions 工作流或编辑现有工作流。
- 使用下面的文档和示例来配置您的工作流。
- 请不要忘记在工作流执行结束时设置一个移除 EC2 实例的作业。 否则,EC2 实例不会被移除,即使在工作流执行完成后也会继续运行。
现在您已准备就绪!
输入参数
名称 | 是否必需 | 描述 |
---|---|---|
mode | 始终必需。 | 在此处指定要使用的模式: - start - 启动新的运行器;- stop - 停止先前创建的运行器。 |
github-token | 始终必需。 | 具有 repo 作用域的 GitHub 个人访问令牌。 |
ec2-image-id | 使用 start 模式时必需。 | EC2 镜像 ID(AMI)。 新运行器将从此镜像启动。 该操作与 Amazon Linux 2 镜像兼容。 |
ec2-instance-type | 使用 start 模式时必需。 | EC2 实例类型。 |
subnet-id | 使用 start 模式时必需。 | VPC 子网 ID。 子网应属于与指定安全组相同的 VPC。 |
security-group-id | 使用 start 模式时必需。 | EC2 安全组 ID。 安全组应属于与指定子网相同的 VPC。 只应允许 443 端口的出站流量。不需要入站流量。 |
label | 使用 stop 模式时必需。 | 分配给运行器的唯一标签名称。 标签由 start 模式下操作的输出提供。当不再需要运行器时,使用标签从 GitHub 中移除运行器。 |
ec2-instance-id | 使用 stop 模式时必需。 | 创建的运行器的 EC2 实例 ID。 ID 由 start 模式下操作的输出提供。当不再需要运行器时,使用 ID 终止 EC2 实例。 |
iam-role-name | 可选。仅在 start 模式下使用。 | 要附加到创建的 EC2 运行器的 IAM 角色名称。 这允许运行器具有在 AWS 账户内运行其他操作的权限,而无需管理额外的 GitHub 密钥和 AWS 用户。 设置此项需要启动实例的角色具有额外的 AWS 权限(见上文)。 |
aws-resource-tags | 可选。仅在 start 模式下使用。 | 指定要添加到 EC2 实例和任何附加存储的标签。 此字段是标签对象的 JSON 字符串数组,每个对象包含 Key 和 Value 字段(见下面的示例)。设置此项需要启动实例的角色具有额外的 AWS 权限(见上文)。 |
runner-home-dir | 可选。仅在 start 模式下使用。 | 指定预安装 actions-runner 软件和脚本所在的目录。 |
pre-runner-script | 可选。仅在 start 模式下使用。 | 指定在运行器启动前要运行的 bash 命令。这对于使用 apt-get、yum、dnf 等安装依赖项很有用。例如:- name: 启动 EC2 运行器 |
环境变量
除了上述输入参数外,该操作还需要以下环境变量来访问您的 AWS 账户:
AWS_DEFAULT_REGION
AWS_REGION
AWS_ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY
我们建议在创建自托管运行器的步骤之前使用 aws-actions/configure-aws-credentials 操作。这个操作可以完美地设置所需的环境变量。
输出
名称 | 描述 |
---|---|
label | 分配给运行器的唯一标签名称。 该标签在两种情况下使用: - 作为后续作业的 runs-on 属性的输入;- 当不再需要运行器时,从 GitHub 中移除它。 |
ec2-instance-id | 创建的运行器的 EC2 实例 ID。 当不再需要运行器时,使用该 ID 终止 EC2 实例。 |
示例
上图所示的工作流程在 do-the-job.yml
中声明如下:
name: do-the-job
on: pull_request
jobs:
start-runner:
name: 启动自托管 EC2 运行器
runs-on: ubuntu-latest
outputs:
label: ${{ steps.start-ec2-runner.outputs.label }}
ec2-instance-id: ${{ steps.start-ec2-runner.outputs.ec2-instance-id }}
steps:
- name: 配置 AWS 凭证
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ secrets.AWS_REGION }}
- name: 启动 EC2 运行器
id: start-ec2-runner
uses: machulav/ec2-github-runner@v2
with:
mode: start
github-token: ${{ secrets.GH_PERSONAL_ACCESS_TOKEN }}
ec2-image-id: ami-123
ec2-instance-type: t3.nano
subnet-id: subnet-123
security-group-id: sg-123
iam-role-name: my-role-name # 可选,需要额外权限
aws-resource-tags: > # 可选,需要额外权限
[
{"Key": "Name", "Value": "ec2-github-runner"},
{"Key": "GitHubRepository", "Value": "${{ github.repository }}"}
]
do-the-job:
name: 在运行器上执行任务
needs: start-runner # 需要等待运行器就绪才能开始主要任务
runs-on: ${{ needs.start-runner.outputs.label }} # 在新创建的运行器上运行任务
steps:
- name: Hello World
run: echo 'Hello World!'
stop-runner:
name: 停止自托管 EC2 运行器
needs:
- start-runner # 需要获取 start-runner 作业的输出
- do-the-job # 需要等待主要任务完成
runs-on: ubuntu-latest
if: ${{ always() }} # 即使前面的作业出错也需要停止运行器
steps:
- name: 配置 AWS 凭证
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ secrets.AWS_REGION }}
- name: 停止 EC2 运行器
uses: machulav/ec2-github-runner@v2
with:
mode: stop
github-token: ${{ secrets.GH_PERSONAL_ACCESS_TOKEN }}
label: ${{ needs.start-runner.outputs.label }}
ec2-instance-id: ${{ needs.start-runner.outputs.ec2-instance-id }}
真实用户示例
在这个讨论中,你可以找到该操作用户的反馈和示例。
如果你在工作流程中使用了这个操作,也欢迎在那里添加你的使用经历 🙌
公共仓库中自托管运行器的安全性
我们建议不要在公共仓库中使用自托管运行器。
公共仓库的分支可能通过创建一个执行工作流程中代码的拉取请求,在你的自托管运行器机器上运行危险代码。
关于这个安全注意事项的更多详细信息,请查看 GitHub 文档。
许可证摘要
本代码根据 MIT 许可证 提供。