Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/vbem/multi-runners
Multi self-hosted runners on single host!
https://github.com/vbem/multi-runners
actions-runner proxy
Last synced: 7 days ago
JSON representation
Multi self-hosted runners on single host!
- Host: GitHub
- URL: https://github.com/vbem/multi-runners
- Owner: vbem
- License: apache-2.0
- Created: 2023-09-27T05:12:46.000Z (over 1 year ago)
- Default Branch: main
- Last Pushed: 2024-10-27T09:35:21.000Z (2 months ago)
- Last Synced: 2024-12-22T09:10:35.326Z (14 days ago)
- Topics: actions-runner, proxy
- Language: Shell
- Homepage:
- Size: 102 KB
- Stars: 160
- Watchers: 5
- Forks: 13
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
- Codeowners: .github/CODEOWNERS
Awesome Lists containing this project
- awesome-runners - vbem/multi-runners - runners.svg)](https://github.com/vbem/multi-runners/blob/master/LICENSE.md) [![GitHub contributors](https://img.shields.io/github/contributors/vbem/multi-runners.svg)](https://github.com/vbem/multi-runners/graphs/contributors/) [![GitHub Stars](https://img.shields.io/github/stars/vbem/multi-runners.svg)](https://github.com/vbem/multi-runners/stargazers/) [![GitHub issues](https://img.shields.io/github/issues/vbem/multi-runners.svg)](https://GitHub.com/vbem/multi-runners/issues/) [![GitHub issues-closed](https://img.shields.io/github/issues-closed/vbem/multi-runners.svg)](https://GitHub.com/vbem/multi-runners/issues?q=is%3Aissue+is%3Aclosed) | bare metal/VM | ✅ | Org, Repo, Labels, RunnerGroups | CLI control | ❌ | x86, AMD64, ARM, ARM64 | CLI control | yes | no | yes | no | possible | yes (CLI) | no | (The matrix (might be better readable on [GitHub pages](https://jonico.github.io/awesome-runners/)) / A word about self-hosted action runner images / virtual environments and how to test locally)
README
# multi-runners
[![awesome-runners](https://img.shields.io/badge/listed%20on-awesome--runners-blue.svg)](https://github.com/jonico/awesome-runners)
[![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/vbem/multi-runners?label=Release&logo=github)](https://github.com/vbem/multi-runners/releases)
[![Linter](https://github.com/vbem/multi-runners/actions/workflows/linter.yml/badge.svg)](https://github.com/vbem/multi-runners/actions/workflows/linter.yml)
![GitHub closed issues](https://img.shields.io/github/issues-closed/vbem/multi-runners?logo=github)🌈🌈🌈 **Multi self-hosted GitHub action runners on single host!** 🌈🌈🌈
## Introduction
This application is designed for controlling multi [self-hosted GitHub Action runners](https://docs.github.com/en/actions/hosting-your-own-runners/managing-self-hosted-runners/about-self-hosted-runners) on single host, when [Actions Runner Controller (ARC)](https://docs.github.com/en/actions/hosting-your-own-runners/managing-self-hosted-runners-with-actions-runner-controller/quickstart-for-actions-runner-controller) is not feasible in your engineering environment. This application has following advantages:
- Single Linux host required.
- Single Bash script.
- Lightweight wrapper of [GitHub official self-hosted runner](https://github.com/actions/runner).
- Both *github.com* and *GitHub Enterprise* are support.
- Either *organization* or *repository* or *GitHub Cloud Enterprise* level runners are supported.## Usage
```plain
mr.bash - https://github.com/vbem/multi-runnersEnvironment variables:
MR_GITHUB_BASEURL=https://github.com
MR_GITHUB_API_BASEURL=https://api.github.com
MR_RELEASE_URL=
MR_USER_BASE=
MR_GITHUB_PAT=***Sub-commands:
add Add one self-hosted runner on this host
e.g. ./mr.bash add --org ORG --repo REPO --labels cloud:ali,region:cn-shanghai
e.g. ./mr.bash add --org ORG --count 3
del Delete one self-hosted runner on this host
e.g. ./mr.bash del --user runner-1
e.g. ./mr.bash del --org ORG --count 3
list List all runners on this host
e.g. ./mr.bash list
download Download GitHub Actions Runner release tar to /tmp/
Detect latest on github.com/actions/runner/releases if MR_RELEASE_URL empty
e.g. ./mr.bash download
pat2token Get runner registration token from GitHub PAT (MR_GITHUB_PAT)
e.g. ./mr.bash pat2token --org SOME_OWNER --repo SOME_REPOOptions:
--enterprise GitHub Cloud Enterprise name, optional
--org GitHub organization name
--repo GitHub repository name, registration on organization-level if empty
--user Linux local username of runner
--labels Extra labels for the runner
--group Runner group for the runner
--token Runner registration token, takes precedence over MR_GITHUB_PAT
--dotenv The lines to set in runner's '.env' files
--count The number to add or del, optional, defaults to 1 for add and all for del
-h --help Show this help.
```### Download this application
This application requires to be run under a Linux user with **non-password sudo permission** (e.g., `%runners ALL=(ALL) NOPASSWD:ALL`). It's also fine to run this application by `root`:
```bash
git clone https://github.com/vbem/multi-runners.git
cd multi-runners
./mr.bash --help
```### Setup PAT
This application requires a [GitHub personal access token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens) with smallest permissions and shortest expiration time. Only `add`/`del`/`pat2token` sub-commands need this PAT. You can remove it on *GitHub* after multi-runners' setup.
PAT types | Repository level runners | Organization level runners
--- | --- | ---
*Fine-grained PAT* (recommended) | Referring to [repository API](https://docs.github.com/en/rest/actions/self-hosted-runners?apiVersion=2022-11-28#create-a-registration-token-for-a-repository), the `administration:write` permission is required. | Referring to [organization policy](https://docs.github.com/en/organizations/managing-programmatic-access-to-your-organization/setting-a-personal-access-token-policy-for-your-organization) & [organization API](https://docs.github.com/en/rest/actions/self-hosted-runners?apiVersion=2022-11-28#create-a-registration-token-for-an-organization), the `organization_self_hosted_runners:write` permission is required.
*Classic PAT* | Referring to [repository API](https://docs.github.com/en/rest/actions/self-hosted-runners?apiVersion=2022-11-28#create-a-registration-token-for-a-repository), need the `repo` scope | Refer to [organization API](https://docs.github.com/en/rest/actions/self-hosted-runners?apiVersion=2022-11-28#create-a-registration-token-for-an-organization), need the `admin:org` scope; if the repository is private, `repo` scope is also required.During runtime, you can set your *PAT* in environment variable `MR_GITHUB_PAT`. **To simplify subsequent execution, you can define any environment variable in `.env` file**. For example,
```bash
# .env file under the directory of this application
MR_GITHUB_PAT='github_pat_***********'
ALL_PROXY=socks5h://localhost
```You can run following command to check whether or not your PAT can generate [GitHub Actions runners' registration-token](https://docs.github.com/en/actions/hosting-your-own-runners/managing-self-hosted-runners/autoscaling-with-self-hosted-runners#authentication-requirements):
```bash
./mr.bash pat2token --org --repo
```### Download the latest version of GitHub Actions package
If environment variable `MR_RELEASE_URL` is empty, this application will download the [latest version of GitHub Actions runners tar package](https://github.com/actions/runner/releases) to local directory `/tmp/` during runtime.
```bash
./mr.bash download
```If your Linux host is internet bandwidth limited, you can also manually upload it from laptop to `/tmp/`, and set the `MR_RELEASE_URL` env in `.env` file, e.g. `/tmp/actions-runner-linux-x64-2.345.6.tar.gz`.
### GitHub Enterprise Server editions
*GitHub Enterprise Server* editions usually have different server and API URL prefixes comparing with *github.com*, you can set them in environment variables `MR_GITHUB_BASEURL` and `MR_GITHUB_API_BASEURL`.
### GitHub Enterprise Cloud level registration
For *GitHub Enterprise Cloud* level registration, you can specify the `--enterprise` option to set the *GitHub Enterprise Cloud* name.
### Setup multi-runners on single host
To setup multi-runners, you can simplify run following command multi times:
```bash
# 1 runner for repository `/`
./mr.bash add --org --repo# 2 runners for repository `/`
./mr.bash add --org --repo --count 2# 3 runners for organization ``
./mr.bash add --org --count 3
```This application will create one Linux local user for one runner via `useradd` command. The *Base Directory* of these users is read from `HOME` setting in your `/etc/default/useradd` file by default (typically `/home`). You can also set it in environment variable `MR_USER_BASE` to override system-wide default.
### List all runners on current host
This application also integrated status check of runners.
```bash
./mr.bash list
```Which outputs,
```bash
runner-0 537M running https://github.com//
runner-1 537M running https://github.com//
runner-2 537M running https://github.com//
runner-3 537M running https://github.com/
runner-4 537M running https://github.com/
runner-5 537M running https://github.com/
```### Delete an existing runner
```bash
# delete an existing runner by its local Linux username.
./mr.bash del --user# delete all runners for specific repository
./mr.bash del --org --repo# delete multi runners by `--count` options.
./mr.bash del --org --count 2
```### Specify runner in workflow file
In [`jobs..runs-on`](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idruns-on), target runners can be based on the labels as follows via [GitHub context](https://docs.github.com/en/actions/learn-github-actions/contexts#github-context):
```yaml
# For organization level self-hosted runners
runs-on: [self-hosted, '${{ github.repository_owner }}']# For repository level self-hosted runners
runs-on: [self-hosted, '${{ github.repository }}']
```### Set environment variables into runners process
As described in GitHub official document, there's an approach to [inject environment variables into runners process](https://docs.github.com/en/actions/hosting-your-own-runners/managing-self-hosted-runners/using-a-proxy-server-with-self-hosted-runners#using-a-env-file-to-set-the-proxy-configuration) via the `.env` file before configuring or starting the self-hosted runners. This can be achieved via the `--dotenv` option, for example:
```bash
./mr.bash add --org --repo --dotenv 'TZ=Asia/Shanghai' --dotenv 'PATH=\$PATH:/mybin'
```Then the following lines will be added to `.env` file located in self-hosted runner's directory before its configuring and starting:
```plain
TZ=Asia/Shanghai
PATH=$PATH:/mybin
```## Case Study - Deploy multi runners on single host which can not access GitHub directly
A multi-national corporation adopted GitHub as its centralized engineering efficiency platform. But in a country branch, according to some network blockade/bandwidth/QoS reasons, neither GitHub-hosted runners can access endpoints in this country stably, nor virtual machines in this country can access GitHub liberally.
In such a bad situation, we still need to setup reliable self-hosted runners in this country. What should we do? 🤣
A cost-conscious solution can be described as following architecture:
```plain
Endpoints <-------- VM-Runners ----> Firewall ----> VM-Proxy ----> GitHub
\ / | \
-------------------------- | ----> Other endpoints
Branch office network Remote Proxy
```A host *VM-Runners* is required for self-hosted runners, which is placed in this country and:
- Can access endpoints of this country branch
- Can NOT access *GitHub* directly or stablyA tiny specification host *VM-Proxy* is required as ***Remote Proxy***, which is deployed in a place that:
- Can access *GitHub* directly and stably
- Can be accessed by *VM-Runners* directly and stablyMeanwhile, **outbound traffics from *VM-Runners* MUST be routed by predefined rules**:
- [Requests to *GitHub* endpoints](https://docs.github.com/en/actions/hosting-your-own-runners/managing-self-hosted-runners/about-self-hosted-runners#communication-requirements) and non-local endpoints should be forward to the *Remote Proxy* on *VM-Proxy*
- Requests to local endpoints should be handled directlyLet's implement this solution. 🧐
On *VM-Proxy*, we can setup a *Remote Proxy* that's not easy to be blocked by the firewall, such as [*SS*](https://github.com/shadowsocks), [*TJ*](https://github.com/trojan-gfw), [*XR*](https://github.com/XTLS), etc. These particular proxies have their own deployment and configuration methods. Please read their documents for more information. It's advised to set the outbound IP of *VM-Runners* as the only whitelist of the *Remote Proxy* port on *VM-Proxy* to avoid active detection from the firewall.
Before setup runners on *VM-Runners*, we need a [***Local Proxy***](https://docs.github.com/en/actions/hosting-your-own-runners/managing-self-hosted-runners/using-a-proxy-server-with-self-hosted-runners) on *VM-Runners*.
Usually firstly we need to setup the client of selected *Remote Proxy* which exposes a SOCKS5 proxy on *VM-Runners* (this local SOCKS5 localhost port will unconditionally forward all traffics to *VM-Proxy*), and then setup a [*privoxy*](https://www.privoxy.org/) on top of previous local SOCKS5 for [domain based forwarding](https://www.privoxy.org/user-manual/config.html#SOCKS). These configurations are complex and prone to errors. Via [*Clash*](https://github.com/Dreamacro/clash), we can combine both client of *Remote Proxy* and domain based forwarding into only one *Local Proxy*. The example configuration file and startup script of *Clash* and given in this repo's [clash.example/](clash.example/) directory.
Assume the *Local Proxy* was launched as `socks5h://localhost:7890`, we can test it via following commands on *VM-Runners*:
```bash
# Without *Local Proxy*, it will print outbound public IP of *VM-Runners*
curl -s -4 icanhazip.com# With *Local Proxy*, it will print outbound public IP of *VM-Proxy* !!!
all_proxy=socks5h://localhost:7890 curl -s -4 icanhazip.com
```When *Local Proxy* is ready, we start self-hosted runners' setup on *VM-Runners*.
As *VM-Runners* Can NOT access *GitHub* directly or stably, use *Local Proxy* to clone this repository:
```bash
all_proxy=socks5h://localhost:7890 git clone https://github.com/vbem/multi-runners
cd multi-runners
```As self-hosted runners' tar package downloading and registration-token fetching also requires communication with GitHub, we also configure *Local Proxy* for this application:
```bash
cat > .env <<- __
MR_GITHUB_PAT=''
all_proxy='socks5h://localhost:7890'
__
```To download the self-hosted runners' tar package from *GitHub*:
```bash
./mr.bash download
```To validate your *PAT* has sufficient permissions for self-hosted runners registration on your GitHub organization `https://github.com/`:
```bash
./mr.bash pat2token --org
```To setup two self-hosted runners on *VM-Runners* for your GitHub organization:
```bash
./mr.bash add --org --dotenv 'all_proxy=socks5h://localhost:7890'
./mr.bash add --org --dotenv 'all_proxy=socks5h://localhost:7890'
```To check the status of self-hosted runners:
```bash
./mr.bash list
```To check the *Local Proxy* works well in your runners' process, you can add a simple workflow `.github/workflows/test-local-proxy.yml` in your repository. If `icanhazip.com` was configured as a following-to-remote domain, the workflow run will print outbound public IP of *VM-Proxy*, even though this workflow actually runs on *VM-Proxy*.
```yaml
---
name: Test Local Proxy works in my self-hosted runners
on:
workflow_dispatch:
jobs:
test:
runs-on: [self-hosted, '${{ github.repository }}']
steps:
- run: |
curl -s -4 icanhazip.com
...
```