From a701f597d1126e38cae8a783184a67397dd5b3c1 Mon Sep 17 00:00:00 2001 From: jichangjun Date: Thu, 27 Nov 2025 17:55:07 +0800 Subject: [PATCH] =?UTF-8?q?feat(github-self-hosted-runner):=20=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=20GitHub=20Actions=20Self-hosted=20Runner=20Terraform?= =?UTF-8?q?=20=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 Terraform 配置文件用于自动化部署 GitHub Actions runner - 包含虚拟机自动配置脚本 runner_setup.sh - 支持自定义 runner 标签、Docker 安装和额外软件包 - 提供详细的架构说明和使用文档 - 添加安全最佳实践和故障排查指南 --- github-self-hosted-runner/ARCHITECTURE.md | 376 +++++++++++++ github-self-hosted-runner/README.md | 466 ++++++++++++++++ github-self-hosted-runner/data.tf | 48 ++ .../how-to-setup-self-github-action-runner.md | 520 ++++++++++++++++++ github-self-hosted-runner/info.md | 0 github-self-hosted-runner/main.tf | 35 ++ github-self-hosted-runner/outputs.tf | 54 ++ github-self-hosted-runner/runner_setup.sh | 198 +++++++ .../terraform.tfvars.example | 48 ++ github-self-hosted-runner/variables.tf | 120 ++++ github-self-hosted-runner/versions.tf | 18 + 11 files changed, 1883 insertions(+) create mode 100644 github-self-hosted-runner/ARCHITECTURE.md create mode 100644 github-self-hosted-runner/README.md create mode 100644 github-self-hosted-runner/data.tf create mode 100644 github-self-hosted-runner/how-to-setup-self-github-action-runner.md create mode 100644 github-self-hosted-runner/info.md create mode 100644 github-self-hosted-runner/main.tf create mode 100644 github-self-hosted-runner/outputs.tf create mode 100644 github-self-hosted-runner/runner_setup.sh create mode 100644 github-self-hosted-runner/terraform.tfvars.example create mode 100644 github-self-hosted-runner/variables.tf create mode 100644 github-self-hosted-runner/versions.tf diff --git a/github-self-hosted-runner/ARCHITECTURE.md b/github-self-hosted-runner/ARCHITECTURE.md new file mode 100644 index 0000000..56320cb --- /dev/null +++ b/github-self-hosted-runner/ARCHITECTURE.md @@ -0,0 +1,376 @@ +# 架构说明 + +## 整体流程图 + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ Terraform 部署流程 │ +└─────────────────────────────────────────────────────────────────┘ + +1. 用户配置 + ├── terraform.tfvars (你的配置) + │ ├── github_repo_url + │ ├── github_token + │ └── instance_type + │ +2. Terraform 初始化 + ├── terraform init + │ └── 下载 qiniu、random provider + │ +3. 资源规划 + ├── terraform plan + │ ├── 查询 Ubuntu 24.04 镜像 (data.tf) + │ ├── 生成随机后缀 (data.tf) + │ └── 计算本地变量 (data.tf) + │ +4. 资源创建 + ├── terraform apply + │ ├── 创建随机密码 (main.tf) + │ ├── 创建虚拟机 (main.tf) + │ │ ├── 规格: instance_type + │ │ ├── 镜像: Ubuntu 24.04 + │ │ └── 初始化脚本: runner_setup.sh + │ │ + │ └── 虚拟机启动后自动执行: + │ └── runner_setup.sh + │ ├── [1/7] 更新系统 & 安装依赖 + │ ├── [2/7] 创建 runner 用户 + │ ├── [3/7] 安装 Docker (可选) + │ ├── [4/7] 下载 GitHub Runner + │ ├── [5/7] 获取 Registration Token + │ ├── [6/7] 配置 Runner + │ └── [7/7] 安装为系统服务并启动 + │ +5. 输出结果 + └── terraform output + ├── runner_name + ├── runner_private_ip + ├── runner_labels + └── instance_password (sensitive) +``` + +## 文件关系图 + +``` +┌────────────────────────────────────────────────────────────────┐ +│ 文件依赖关系 │ +└────────────────────────────────────────────────────────────────┘ + +terraform.tfvars.example ←── 复制修改 ──→ terraform.tfvars (你的配置) + │ + ↓ + variables.tf (定义变量) + │ + ┌───────────────────────────────┼───────────────────┐ + │ │ │ + ↓ ↓ ↓ + versions.tf data.tf main.tf + (Provider 版本要求) (数据查询 & 本地变量) (创建虚拟机) + │ + ↓ + runner_setup.sh + (虚拟机初始化脚本) + │ + ┌───────────────────────────────────────────────────┘ + │ + ↓ + outputs.tf + (输出部署结果信息) +``` + +## 核心组件说明 + +### 1. versions.tf - Provider 配置 + +```hcl +provider "qiniu" ──→ 连接七牛云 API +provider "random" ──→ 生成随机字符串/密码 +``` + +**作用**: 定义 Terraform 使用哪些云服务商的 API + +### 2. variables.tf - 输入参数 + +```hcl +variable "github_repo_url" ──→ 你要部署 Runner 的仓库 +variable "github_token" ──→ 用于注册 Runner 的 Token +variable "instance_type" ──→ 虚拟机规格 (2核4GB, 4核8GB...) +variable "enable_docker" ──→ 是否安装 Docker +variable "runner_labels" ──→ 自定义标签 +``` + +**作用**: 用户可配置的参数,通过 `terraform.tfvars` 传入 + +### 3. data.tf - 数据查询 & 计算 + +```hcl +┌─────────────────────────────────────────────┐ +│ 数据查询 │ +├─────────────────────────────────────────────┤ +│ data "qiniu_compute_images" │ +│ ├── 查询所有官方镜像 │ +│ └── 筛选 Ubuntu 24.04 LTS │ +├─────────────────────────────────────────────┤ +│ 资源创建 │ +├─────────────────────────────────────────────┤ +│ resource "random_string" │ +│ └── 生成 6 位随机后缀 (abc123) │ +├─────────────────────────────────────────────┤ +│ 本地变量计算 │ +├─────────────────────────────────────────────┤ +│ locals { │ +│ runner_suffix = "abc123" │ +│ ubuntu_image_id = "img-xxxxx" │ +│ runner_name = "runner-abc123" │ +│ runner_labels = ["self-hosted", ...] │ +│ github_owner = "your-org" │ +│ github_repo = "your-repo" │ +│ } │ +└─────────────────────────────────────────────┘ +``` + +**作用**: 查询镜像、生成后缀、计算变量 + +### 4. main.tf - 核心资源 + +```hcl +┌────────────────────────────────────────────┐ +│ 1. 生成虚拟机密码 │ +├────────────────────────────────────────────┤ +│ random_password "runner_instance_password" │ +│ └── 16位随机密码 (包含大小写数字特殊字符) │ +├────────────────────────────────────────────┤ +│ 2. 创建虚拟机实例 │ +├────────────────────────────────────────────┤ +│ qiniu_compute_instance "github_runner" │ +│ ├── name: github-runner-abc123 │ +│ ├── instance_type: ecs.t1.c2m4 │ +│ ├── image_id: Ubuntu 24.04 │ +│ ├── system_disk_size: 50GB │ +│ ├── password: <随机密码> │ +│ └── user_data: │ +└────────────────────────────────────────────┘ +``` + +**作用**: 创建虚拟机并注入初始化脚本 + +### 5. runner_setup.sh - 初始化脚本 + +```bash +┌──────────────────────────────────────────────────┐ +│ 虚拟机启动后自动执行 │ +├──────────────────────────────────────────────────┤ +│ [1/7] apt update & install 基础软件 │ +│ ├── curl, jq, git │ +│ └── 用户指定的额外软件包 │ +├──────────────────────────────────────────────────┤ +│ [2/7] 创建 runner 用户 │ +│ ├── useradd -m -s /bin/bash runner │ +│ ├── 添加到 sudo 组 │ +│ └── 配置无密码 sudo (仅 docker/systemctl) │ +├──────────────────────────────────────────────────┤ +│ [3/7] 安装 Docker (如果 enable_docker=true) │ +│ ├── 添加 Docker 官方源 │ +│ ├── 安装 Docker Engine │ +│ └── 添加 runner 到 docker 组 │ +├──────────────────────────────────────────────────┤ +│ [4/7] 下载 GitHub Actions Runner │ +│ ├── 查询最新版本号 │ +│ ├── 下载 tar.gz │ +│ └── 解压到 /home/runner/actions-runner │ +├──────────────────────────────────────────────────┤ +│ [5/7] 获取 Registration Token │ +│ ├── 解析 github_repo_url │ +│ └── 调用 GitHub API 获取 token │ +├──────────────────────────────────────────────────┤ +│ [6/7] 配置 Runner │ +│ └── ./config.sh --url ... --token ... │ +├──────────────────────────────────────────────────┤ +│ [7/7] 安装为系统服务 │ +│ ├── ./svc.sh install runner │ +│ └── ./svc.sh start │ +└──────────────────────────────────────────────────┘ +``` + +**作用**: 自动化配置整个 Runner 环境 + +### 6. outputs.tf - 输出结果 + +```hcl +┌────────────────────────────────────────────┐ +│ 部署完成后显示的信息 │ +├────────────────────────────────────────────┤ +│ ✓ runner_instance_id → i-xxxxxxxx │ +│ ✓ runner_name → runner-abc123 │ +│ ✓ runner_private_ip → 10.0.0.123 │ +│ ✓ runner_labels → [self-hosted...] │ +│ ✓ ssh_connection_command → ssh root@... │ +│ ✓ runner_status_check → ssh ... status │ +│ ✓ instance_password → *** (sensitive) │ +└────────────────────────────────────────────┘ +``` + +**作用**: 显示部署结果和操作指南 + +## Terraform 工作原理 + +### State 管理 + +``` +terraform.tfstate (本地状态文件) +├── 记录所有已创建的资源 +├── 包含资源的真实 ID 和属性 +└── 用于对比配置变化 + +工作流程: +1. terraform plan + ├── 读取配置文件 (*.tf) + ├── 读取状态文件 (terraform.tfstate) + └── 对比差异 → 显示变更计划 + +2. terraform apply + ├── 执行变更计划 + ├── 调用云服务商 API + └── 更新状态文件 +``` + +### 依赖关系自动解析 + +``` +Terraform 自动分析资源之间的依赖: + +random_string + │ + ├──→ locals.runner_suffix + │ │ + │ └──→ main.tf 中的虚拟机名称 + │ +random_password + │ + └──→ main.tf 中的虚拟机密码 + +data.qiniu_compute_images + │ + ├──→ locals.ubuntu_image_id + │ │ + │ └──→ main.tf 中的镜像 ID + +执行顺序: +1. 先创建 random_string、random_password +2. 查询 data source +3. 计算 locals +4. 最后创建虚拟机 (依赖前面所有结果) +``` + +## 与 MySQL 模板的对比 + +| 特性 | MySQL 模板 | GitHub Runner 模板 | +|------|-----------|-------------------| +| **核心资源** | MySQL 数据库实例 | GitHub Actions Runner | +| **初始化脚本** | `mysql_standalone.sh` | `runner_setup.sh` | +| **主要配置** | 用户名、密码、数据库名 | GitHub URL、Token、标签 | +| **输出信息** | MySQL 连接地址 | Runner 名称、IP、SSH 命令 | +| **可选功能** | 无 | Docker、额外软件包 | +| **外部 API** | 无 | GitHub API (获取 token) | +| **服务管理** | systemd (mysql) | systemd (actions.runner) | + +## 技术亮点 + +### 1. 模板化脚本 (templatefile) + +```hcl +user_data = base64encode(templatefile("${path.module}/runner_setup.sh", { + github_token = var.github_token, + runner_name = local.runner_name, + ... +})) +``` + +**好处**: +- 将配置参数安全地注入到 shell 脚本 +- 使用单引号避免特殊字符问题 +- 脚本模板化,易于维护 + +### 2. 变量验证 (validation) + +```hcl +validation { + condition = can(regex("^https://github\\.com/...", var.github_repo_url)) + error_message = "必须是有效的 GitHub 仓库 URL" +} +``` + +**好处**: +- 在 plan 阶段就发现配置错误 +- 提供清晰的错误提示 +- 避免部署后才发现问题 + +### 3. 敏感信息保护 (sensitive) + +```hcl +variable "github_token" { + sensitive = true +} + +output "instance_password" { + sensitive = true +} +``` + +**好处**: +- 防止密码在日志中明文显示 +- 需要显式 `terraform output ` 查看 + +### 4. 自动化 Token 获取 + +```bash +# 通过 API 自动获取 Registration Token +REG_TOKEN=$(curl -s -X POST \ + -H "Authorization: token $GITHUB_TOKEN" \ + https://api.github.com/repos/$OWNER/$REPO/actions/runners/registration-token \ + | jq -r '.token') +``` + +**好处**: +- 解决 Token 1 小时过期问题 +- 用户只需提供长期 PAT +- 自动化程度高 + +## 使用场景决策树 + +``` +需要 CI/CD Runner? + │ + ├─ 是 → 工作负载类型? + │ ├─ 轻量级测试 → ecs.t1.c2m4 (2核4GB) + │ ├─ 前端构建 → ecs.t1.c4m8 (4核8GB) + │ ├─ 后端编译 → ecs.t1.c8m16 (8核16GB) + │ └─ Docker构建 → ecs.t1.c8m16 + enable_docker=true + │ + └─ 否 → 考虑其他模板 (如 mysql) +``` + +## 扩展思路 + +### 未来可以添加的功能 + +1. **自动扩缩容** + - 根据 GitHub workflow queue 长度自动创建/销毁 Runner + - 需要配合 Lambda/Serverless 函数 + +2. **监控告警** + - 集成七牛云监控 + - Runner 离线/磁盘满自动告警 + +3. **多 Runner 支持** + - 使用 `count` 或 `for_each` 创建多个实例 + - 自动分配不同标签 + +4. **安全加固** + - 配置防火墙规则 + - 定期自动更新系统 + - 日志收集 + +5. **备份恢复** + - 定期备份虚拟机快照 + - 一键恢复功能 diff --git a/github-self-hosted-runner/README.md b/github-self-hosted-runner/README.md new file mode 100644 index 0000000..c137031 --- /dev/null +++ b/github-self-hosted-runner/README.md @@ -0,0 +1,466 @@ +# GitHub Self-hosted Runner Terraform 模板 + +快速在七牛云上部署 GitHub Actions Self-hosted Runner 的 Terraform 模板。 + +## 目录结构 + +``` +github-self-hosted-runner/ +├── README.md # 本文档 +├── versions.tf # Terraform 和 Provider 版本定义 +├── variables.tf # 输入变量定义 +├── data.tf # 数据源和本地变量 +├── main.tf # 虚拟机资源定义 +├── runner_setup.sh # Runner 自动配置脚本 +├── outputs.tf # 输出值定义 +└── how-to-setup-self-github-action-runner.md # Runner 设置详细文档 +``` + +## 前置要求 + +1. **Terraform** 版本 > 0.12.0 +2. **七牛云账号** 并配置好认证信息 +3. **GitHub Personal Access Token** (需要以下权限之一) + - 仓库级:`repo` 权限 + - 组织级:`admin:org` 权限 + +### 获取 GitHub Token + +访问 [GitHub Settings → Developer settings → Personal access tokens](https://github.com/settings/tokens) + +创建 token 并勾选权限: +- `repo` (完整仓库访问权限) +- 或 `admin:org` (组织管理权限) + +## 快速开始 + +### 1. 创建配置文件 + +创建 `terraform.tfvars` 文件(注意:此文件包含敏感信息,不要提交到 Git): + +```hcl +# GitHub 配置 +github_repo_url = "https://github.com/your-org/your-repo" +github_token = "ghp_xxxxxxxxxxxxxxxxxxxx" # 你的 GitHub Token + +# 虚拟机配置(可选,使用默认值) +instance_type = "ecs.t1.c2m4" # 2核4GB +instance_system_disk_size = 50 # 50GB 磁盘 + +# Runner 配置(可选) +runner_name = "my-custom-runner" # 留空则自动生成 +runner_labels = ["gpu", "production"] # 自定义标签 + +# 其他配置 +enable_docker = true # 是否安装 Docker +additional_packages = ["nodejs", "python3-pip"] # 额外软件包 +``` + +### 2. 初始化 Terraform + +```bash +cd github-self-hosted-runner +terraform init +``` + +**Terraform 知识点:** +- `terraform init` 会下载所需的 provider 插件(qiniu、random) +- 只需要在第一次使用或修改 provider 配置后执行 + +### 3. 预览部署计划 + +```bash +terraform plan +``` + +**Terraform 知识点:** +- `terraform plan` 显示将要创建/修改/删除的资源 +- 这是个安全的只读操作,不会真正创建资源 +- 可以检查配置是否正确 + +### 4. 执行部署 + +```bash +terraform apply +``` + +**Terraform 知识点:** +- `terraform apply` 会真正创建资源 +- 执行前会再次显示计划,需要输入 `yes` 确认 +- 部署过程中会创建: + 1. 随机字符串(资源后缀) + 2. 随机密码(虚拟机 root 密码) + 3. 查询 Ubuntu 24.04 镜像 + 4. 创建虚拟机实例 + 5. 执行初始化脚本(安装 Runner) + +### 5. 查看输出信息 + +部署完成后会显示: + +``` +Outputs: + +runner_instance_id = "i-xxxxxxxx" +runner_name = "runner-abc123" +runner_private_ip = "10.0.0.123" +runner_labels = ["self-hosted", "linux", "x64", "gpu", "production"] +ssh_connection_command = "ssh root@10.0.0.123" +runner_status_check = "ssh root@10.0.0.123 'cd /home/runner/actions-runner && sudo ./svc.sh status'" +``` + +要查看敏感信息(root 密码): + +```bash +terraform output instance_password +``` + +## 配置参数说明 + +### 必填参数 + +| 参数 | 类型 | 说明 | 示例 | +|------|------|------|------| +| `github_repo_url` | string | GitHub 仓库 URL | `https://github.com/owner/repo` | +| `github_token` | string | GitHub Personal Access Token | `ghp_xxxxxxxxxxxx` | + +### 可选参数 + +| 参数 | 类型 | 默认值 | 说明 | +|------|------|--------|------| +| `instance_type` | string | `ecs.t1.c2m4` | 虚拟机规格(见下表) | +| `instance_system_disk_size` | number | `50` | 系统盘大小(GiB) | +| `runner_name` | string | 自动生成 | Runner 名称 | +| `runner_labels` | list(string) | `[]` | 自定义标签 | +| `runner_username` | string | `runner` | Runner 进程的用户名 | +| `enable_docker` | bool | `true` | 是否安装 Docker | +| `additional_packages` | list(string) | `[]` | 额外安装的软件包 | + +### 虚拟机规格选择 + +根据工作负载选择合适的规格: + +| 工作类型 | 推荐规格 | CPU/内存 | 适用场景 | +|---------|---------|----------|---------| +| 轻量级测试 | `ecs.t1.c2m4` | 2核/4GB | 单元测试、linting | +| 前端构建 | `ecs.t1.c4m8` | 4核/8GB | npm/yarn 构建 | +| 后端编译 | `ecs.t1.c8m16` | 8核/16GB | Java/Go 编译 | +| Docker 构建 | `ecs.t1.c8m16` | 8核/16GB | 镜像构建 | +| 大型项目 | `ecs.t1.c16m32` | 16核/32GB | 复杂构建流程 | + +详细规格参考 `variables.tf` 中的 validation 规则。 + +## 使用场景示例 + +### 场景 1: 基础 CI/CD Runner + +```hcl +github_repo_url = "https://github.com/myorg/myapp" +github_token = "ghp_xxxxxxxxxxxx" +instance_type = "ecs.t1.c2m4" +enable_docker = true +``` + +在 workflow 中使用: + +```yaml +# .github/workflows/ci.yml +jobs: + build: + runs-on: [self-hosted, linux, x64] + steps: + - uses: actions/checkout@v4 + - run: npm test +``` + +### 场景 2: Docker 构建专用 Runner + +```hcl +github_repo_url = "https://github.com/myorg/myapp" +github_token = "ghp_xxxxxxxxxxxx" +instance_type = "ecs.t1.c8m16" +runner_labels = ["docker", "high-memory"] +enable_docker = true +``` + +在 workflow 中使用: + +```yaml +jobs: + docker-build: + runs-on: [self-hosted, docker, high-memory] + steps: + - uses: actions/checkout@v4 + - run: docker build -t myapp . +``` + +### 场景 3: 多语言开发环境 + +```hcl +github_repo_url = "https://github.com/myorg/myapp" +github_token = "ghp_xxxxxxxxxxxx" +instance_type = "ecs.t1.c4m8" +runner_labels = ["nodejs", "python", "go"] +additional_packages = [ + "nodejs", + "npm", + "python3-pip", + "golang-go" +] +``` + +## 部署后管理 + +### 检查 Runner 状态 + +```bash +# SSH 登录到虚拟机 +ssh root@ + +# 查看服务状态 +cd /home/runner/actions-runner +sudo ./svc.sh status + +# 查看实时日志 +journalctl -u actions.runner.* -f +``` + +### 在 GitHub 上验证 + +访问你的仓库:`Settings` → `Actions` → `Runners` + +应该能看到状态为 "Idle" 的 Runner。 + +### 更新 Runner + +```bash +# SSH 登录 +ssh root@ + +# 停止服务 +cd /home/runner/actions-runner +sudo ./svc.sh stop + +# 切换到 runner 用户 +su - runner + +# 下载新版本 +cd actions-runner +# ... 按照官方文档更新 + +# 重启服务 +sudo ./svc.sh start +``` + +### 移除 Runner + +```bash +# 方法 1: 使用 Terraform 销毁 +terraform destroy + +# 方法 2: 手动移除(如果需要保留虚拟机) +ssh root@ +cd /home/runner/actions-runner +sudo ./svc.sh stop +sudo ./svc.sh uninstall +./config.sh remove --token +``` + +## Terraform 基础知识速查 + +### 常用命令 + +```bash +# 初始化项目(下载 provider) +terraform init + +# 格式化代码 +terraform fmt + +# 验证配置 +terraform validate + +# 查看当前状态 +terraform show + +# 列出所有资源 +terraform state list + +# 查看某个资源详情 +terraform state show qiniu_compute_instance.github_runner + +# 查看所有输出值 +terraform output + +# 查看特定输出值 +terraform output runner_private_ip + +# 销毁所有资源 +terraform destroy +``` + +### 工作流程 + +``` +编写配置 → init → plan → apply → 资源创建完成 + ↓ + 修改配置 + ↓ + plan → apply → 资源更新 + ↓ + destroy → 资源销毁 +``` + +### 核心概念 + +1. **Resource(资源)**: 要创建的云资源(虚拟机、数据库等) +2. **Variable(变量)**: 输入参数,可自定义 +3. **Output(输出)**: 部署完成后显示的信息 +4. **Data Source(数据源)**: 查询已存在的资源 +5. **Local(本地变量)**: 内部计算使用的变量 +6. **Provider(提供商)**: 云服务商的 API 插件 + +### State 管理 + +Terraform 会在本地生成 `terraform.tfstate` 文件记录资源状态: + +- 不要手动编辑此文件 +- 团队协作时建议使用远程 backend(S3、Consul 等) +- 可以用 `.gitignore` 忽略此文件 + +## 故障排查 + +### Runner 未显示在 GitHub + +1. 检查 GitHub Token 权限 + ```bash + # 测试 token 是否有效 + curl -H "Authorization: token ghp_xxxx" https://api.github.com/user + ``` + +2. 查看虚拟机日志 + ```bash + ssh root@ + journalctl -u actions.runner.* --no-pager -n 100 + ``` + +3. 检查初始化脚本执行情况 + ```bash + ssh root@ + cat /var/log/cloud-init-output.log + ``` + +### 磁盘空间不足 + +```bash +# 清理 Docker 缓存 +docker system prune -af --volumes + +# 清理旧的 workflow 临时文件 +cd /home/runner/actions-runner/_work/_temp +rm -rf * +``` + +### 连接超时 + +检查网络配置: +```bash +# 测试网络连接 +ping github.com +curl -I https://api.github.com +``` + +### 重新配置 Runner + +如果需要重新配置(比如更换仓库): + +```bash +ssh root@ +cd /home/runner/actions-runner + +# 停止服务 +sudo ./svc.sh stop + +# 移除旧配置 +./config.sh remove --token + +# 重新配置(获取新的 registration token) +./config.sh --url --token --labels ... + +# 重新安装服务 +sudo ./svc.sh install runner +sudo ./svc.sh start +``` + +## 安全建议 + +1. **不要将 `terraform.tfvars` 提交到 Git** + ```bash + echo "terraform.tfvars" >> .gitignore + echo "*.tfstate*" >> .gitignore + ``` + +2. **使用有限权限的 Token** + - 仓库级 Runner 只需要 `repo` 权限 + - 避免使用 `admin:org` 除非必要 + +3. **定期更新系统和 Runner** + ```bash + # 登录虚拟机 + apt update && apt upgrade -y + ``` + +4. **限制 Runner 用户的 sudo 权限** + - 模板已默认限制为 `docker` 和 `systemctl` + - 如需修改,编辑 `runner_setup.sh` 中的 sudo 配置 + +5. **使用防火墙** + ```bash + ufw enable + ufw allow ssh + ufw allow out 443/tcp # HTTPS to GitHub + ``` + +## 高级用法 + +### 一虚拟机多 Runner + +修改模板创建多个 runner 目录: + +```hcl +# 需要自定义模板,不在本示例范围内 +# 建议:生产环境推荐一虚拟机一 Runner,配合自动扩缩容 +``` + +### 组织级 Runner + +修改 `runner_setup.sh` 中的 API 端点: + +```bash +# 将 +https://api.github.com/repos/$OWNER/$REPO/actions/runners/registration-token + +# 改为 +https://api.github.com/orgs/$ORG/actions/runners/registration-token +``` + +### 自定义镜像 + +如果需要使用自定义镜像而不是 Ubuntu 24.04: + +修改 `data.tf`: +```hcl +locals { + ubuntu_image_id = "your-custom-image-id" +} +``` + +## 参考资料 + +- [GitHub Actions Self-hosted Runners 官方文档](https://docs.github.com/en/actions/hosting-your-own-runners) +- [Terraform 官方文档](https://www.terraform.io/docs) +- 项目内文档: `how-to-setup-self-github-action-runner.md` + +## License + +MIT diff --git a/github-self-hosted-runner/data.tf b/github-self-hosted-runner/data.tf new file mode 100644 index 0000000..2b7a491 --- /dev/null +++ b/github-self-hosted-runner/data.tf @@ -0,0 +1,48 @@ +# ========================================== +# 生成资源后缀,避免命名冲突 +# ========================================== + +resource "random_string" "resource_suffix" { + length = 6 + upper = false + lower = true + numeric = true + special = false +} + +# ========================================== +# 查询可用的 Ubuntu 镜像 +# ========================================== + +data "qiniu_compute_images" "available_official_images" { + type = "Official" + state = "Available" +} + +# ========================================== +# 本地变量定义 +# ========================================== + +locals { + # 资源后缀 + runner_suffix = random_string.resource_suffix.result + + # 选用 Ubuntu 24.04 LTS 镜像 + ubuntu_image_id = one([ + for item in data.qiniu_compute_images.available_official_images.items : item + if item.os_distribution == "Ubuntu" && item.os_version == "24.04 LTS" + ]).id + + # Runner 名称(用户指定或自动生成) + runner_name = var.runner_name != "" ? var.runner_name : "runner-${local.runner_suffix}" + + # 合并默认标签和用户自定义标签 + runner_labels = concat( + ["self-hosted", "linux", "x64"], + var.runner_labels + ) + + # 从 GitHub URL 提取 owner 和 repo + github_owner = split("/", trimprefix(var.github_repo_url, "https://github.com/"))[0] + github_repo = split("/", trimprefix(var.github_repo_url, "https://github.com/"))[1] +} diff --git a/github-self-hosted-runner/how-to-setup-self-github-action-runner.md b/github-self-hosted-runner/how-to-setup-self-github-action-runner.md new file mode 100644 index 0000000..98864a3 --- /dev/null +++ b/github-self-hosted-runner/how-to-setup-self-github-action-runner.md @@ -0,0 +1,520 @@ +# how to set up self-hosted github action runner + +## 关键认知 + +### 1. 一个虚机跑一个 runner 还是多个 runner? + +**默认行为:** +- 一个 runner 进程默认只能同时运行一个 job +- job 完成后,runner 会继续等待下一个 job + +**决策依据:** + +| 场景 | 建议配置 | 原因 | +|------|---------|------| +| 高并发 CI/CD | 一虚机一 runner | 资源隔离,避免干扰;易于扩缩容;失败影响范围小 | +| 资源充足的大型虚机 | 一虚机多 runner | 提高资源利用率;需注意端口/磁盘冲突 | +| 特定工具链 | 一虚机一 runner | 环境干净,依赖隔离 | +| 成本敏感 | 一虚机多 runner | 节省虚机成本;需做好资源限制 | + +**最佳实践:** +- 生产环境推荐 **一虚机一 runner**,配合自动扩缩容 +- 如需一虚机多 runner,每个 runner 需在不同目录下配置,例如: + ```bash + /home/runner1/actions-runner + /home/runner2/actions-runner + ``` + +### 2. 虚机配置选择 + +**最小配置:** +- CPU: 2 核 +- 内存: 4 GB +- 磁盘: 20 GB + +**推荐配置(根据工作负载):** + +| 工作类型 | CPU | 内存 | 磁盘 | 说明 | +|---------|-----|------|------|------| +| 轻量级测试 | 2-4 核 | 4-8 GB | 30 GB | 简单的单元测试、linting | +| 前端构建 | 4-8 核 | 8-16 GB | 50 GB | npm/yarn 构建,node_modules 占用大 | +| 后端编译 | 8-16 核 | 16-32 GB | 100 GB | Java/C++ 编译,缓存依赖多 | +| Docker 构建 | 4-8 核 | 16-32 GB | 100 GB | 镜像层缓存占用空间大 | +| 机器学习训练 | 16+ 核 | 32+ GB | 200+ GB | 需 GPU 支持 | + +**注意事项:** +- 预留 20-30% 的资源余量 +- 磁盘建议使用 SSD,提升 I/O 性能 +- 考虑网络带宽(拉取代码、依赖、上传产物) + +### 3. Runner 用户权限设置 + +**为什么不能用 root?** +- **安全风险**: job 中的恶意代码会以 root 权限执行 +- **最小权限原则**: runner 不需要 root 权限 +- **官方限制**: GitHub Actions runner 会检测并拒绝以 root 运行 + +**创建专用账户:** +```bash +# 创建 runner 用户 +sudo useradd -m -s /bin/bash runner + +# 添加 sudo 权限(仅在必要时) +sudo usermod -aG sudo runner +``` + +**visudo 配置(允许无密码 sudo):** +```bash +sudo visudo + +# 添加以下行(谨慎使用,仅在 CI/CD 必需时) +runner ALL=(ALL) NOPASSWD: ALL + +# 更安全的方式:只允许特定命令 +runner ALL=(ALL) NOPASSWD: /usr/bin/docker, /usr/bin/systemctl +``` + +**runner 账户 vs root 的区别:** + +| 特性 | runner 账户 | root | +|------|-----------|------| +| UID | 1000+ | 0 | +| 权限范围 | 受限,仅自己的文件 | 所有系统文件 | +| sudo 能力 | 需配置 | 原生拥有 | +| 安全性 | 高(隔离) | 低(全权限) | +| 适用场景 | CI/CD runner | 系统管理 | + +### 4. 让 runner 只针对私有仓库运行 + +**方法 1: 组织级别设置** +1. 进入 GitHub 组织设置: `Settings` → `Actions` → `Runners` +2. 添加 runner 时选择 **Organization** 级别 +3. 在 `Runner groups` 中创建分组,设置仓库访问权限: + - 点击 `New runner group` + - 勾选 `Private repositories` 或选择特定私有仓库 + - 将 runner 添加到该分组 + +**方法 2: 仓库级别设置** +- 直接在私有仓库的 `Settings` → `Actions` → `Runners` 中添加 runner +- 该 runner 只会为这个仓库服务 + +**方法 3: Workflow 文件中限制** +```yaml +# .github/workflows/ci.yml +name: CI +on: [push] + +jobs: + build: + runs-on: self-hosted + # 只在私有仓库运行 + if: github.event.repository.private == true + steps: + - uses: actions/checkout@v4 + - run: echo "Running on private repo" +``` + +### 5. Runner 标签(Labels)使用 + +**添加标签:** + +配置 runner 时可以添加自定义标签: +```bash +# 配置时添加标签 +./config.sh --url https://github.com/your-org/your-repo \ + --token YOUR_TOKEN \ + --labels gpu,cuda-12,ubuntu-22.04 + +# 默认标签: self-hosted, linux/windows/macOS, x64/ARM64 +``` + +**在 Workflow 中使用:** + +```yaml +# .github/workflows/ml-training.yml +name: ML Training + +on: [push] + +jobs: + # 使用带 GPU 的 runner + train-model: + runs-on: [self-hosted, gpu, cuda-12] + steps: + - uses: actions/checkout@v4 + - name: Train model + run: python train.py --gpu + + # 使用普通 runner + unit-test: + runs-on: [self-hosted, linux, x64] + steps: + - uses: actions/checkout@v4 + - name: Run tests + run: npm test + + # 使用多个标签精确匹配 + build-docker: + runs-on: [self-hosted, linux, docker, high-memory] + steps: + - uses: actions/checkout@v4 + - name: Build image + run: docker build -t myapp . +``` + +**实际例子 - 不同环境构建:** + +```yaml +# .github/workflows/multi-platform.yml +name: Multi-Platform Build + +on: [push] + +jobs: + build-linux: + runs-on: [self-hosted, linux, amd64] + steps: + - run: make build-linux + + build-windows: + runs-on: [self-hosted, windows, x64] + steps: + - run: .\build.ps1 + + build-arm: + runs-on: [self-hosted, linux, arm64] + steps: + - run: make build-arm +``` + +**标签管理最佳实践:** +- 使用语义化标签: `os-version-arch-feature` +- 示例: `ubuntu-22.04-x64-docker`, `windows-2022-gpu` +- 避免过于细粒度,保持灵活性 + +### 6. Runner Token 机制 + +**Token 特性:** + +| 特性 | 说明 | +|------|------| +| 有效期 | **1 小时**(注册 token) | +| 作用域 | 仓库级/组织级/企业级 | +| 是否变化 | 每次生成都不同 | +| 数量限制 | 无限制,但同时有效的 token 有限 | + +**获取 Token:** + +1. **仓库级 Token:** + ``` + https://github.com/{owner}/{repo}/settings/actions/runners/new + ``` + +2. **组织级 Token:** + ``` + https://github.com/organizations/{org}/settings/actions/runners/new + ``` + +3. **通过 API 获取(推荐自动化):** + ```bash + # 使用 GitHub CLI + gh api \ + --method POST \ + -H "Accept: application/vnd.github+json" \ + /repos/OWNER/REPO/actions/runners/registration-token + + # 返回示例 + { + "token": "LLBF3JGZDX3P5PMEXLND6TS6FCWO6", + "expires_at": "2024-01-01T01:22:59.000Z" + } + ``` + +**自动化配置(解决 token 过期问题):** + +```bash +#!/bin/bash +# auto-register-runner.sh + +ORG="your-org" +REPO="your-repo" +GITHUB_TOKEN="ghp_xxxx" # Personal Access Token (需要 admin:org 权限) + +# 获取注册 token +REG_TOKEN=$(curl -s -X POST \ + -H "Authorization: token ${GITHUB_TOKEN}" \ + -H "Accept: application/vnd.github+json" \ + https://api.github.com/repos/${ORG}/${REPO}/actions/runners/registration-token \ + | jq -r '.token') + +# 配置 runner +cd /home/runner/actions-runner +./config.sh \ + --url https://github.com/${ORG}/${REPO} \ + --token ${REG_TOKEN} \ + --labels self-hosted,linux,x64 \ + --unattended +``` + +**重要提示:** +- Token 1 小时后失效,但已配置的 runner 不受影响 +- 重新配置 runner 需要新 token +- 使用 PAT(Personal Access Token)可自动化获取 registration token + +### 7. svc.sh 文件详解 + +`svc.sh` 是 runner 作为系统服务运行的管理脚本。 + +**位置:** +``` +/home/runner/actions-runner/svc.sh +``` + +**主要功能:** + +```bash +# 安装为系统服务 +sudo ./svc.sh install [username] + +# 启动服务 +sudo ./svc.sh start + +# 停止服务 +sudo ./svc.sh stop + +# 查看状态 +sudo ./svc.sh status + +# 卸载服务 +sudo ./svc.sh uninstall +``` + +**内部实现:** + +创建 systemd 服务文件(Linux): +```ini +# /etc/systemd/system/actions.runner.{org}-{repo}.{hostname}.service +[Unit] +Description=GitHub Actions Runner ({org}.{repo}.{hostname}) +After=network.target + +[Service] +ExecStart=/home/runner/actions-runner/runsvc.sh +User=runner +WorkingDirectory=/home/runner/actions-runner +KillMode=process +KillSignal=SIGTERM +TimeoutStopSec=5min + +[Install] +WantedBy=multi-user.target +``` + +**vs run.sh 的区别:** +- `svc.sh`: 作为系统服务运行(后台,开机自启) +- `run.sh`: 前台交互式运行(调试用) + +### 8. run.sh 文件详解 + +`run.sh` 是 runner 的启动脚本(前台模式)。 + +**位置:** +``` +/home/runner/actions-runner/run.sh +``` + +**用途:** + +1. **交互式调试:** + ```bash + cd /home/runner/actions-runner + ./run.sh + + # 输出示例: + # √ Connected to GitHub + # 2024-01-01 10:00:00Z: Listening for Jobs + ``` + +2. **查看实时日志:** + - 所有输出直接打印到终端 + - 适合排查连接/配置问题 + +3. **临时测试:** + - 验证 runner 配置是否正确 + - 测试 workflow 执行 + +**内部流程:** +```bash +# 简化版流程 +#!/bin/bash + +# 1. 加载配置 +source .env +source .credentials + +# 2. 启动 Runner.Listener 进程 +./bin/Runner.Listener run + +# 3. 监听 GitHub 的 job 队列 +# 4. 执行 job +# 5. 清理临时文件 +# 6. 继续监听 +``` + +**何时使用:** + +| 场景 | 使用脚本 | 原因 | +|------|---------|------| +| 生产环境 | `svc.sh` | 后台运行,自动重启 | +| 调试配置 | `run.sh` | 实时查看日志 | +| 测试 workflow | `run.sh` | 快速验证 | +| 开机自启 | `svc.sh` | systemd 管理 | + +**停止运行:** +- `run.sh`: `Ctrl+C` +- `svc.sh`: `sudo ./svc.sh stop` + +## 具体构建步骤 + +### Step 1: 准备虚拟机 + +```bash +# 更新系统 +sudo apt update && sudo apt upgrade -y + +# 安装必要的依赖 +sudo apt install -y curl jq git + +# 创建 runner 用户 +sudo useradd -m -s /bin/bash runner +sudo usermod -aG sudo runner +``` + +### Step 2: 下载 Runner + +```bash +# 切换到 runner 用户 +sudo su - runner + +# 创建目录 +mkdir actions-runner && cd actions-runner + +# 下载最新版本 (检查: https://github.com/actions/runner/releases) +curl -o actions-runner-linux-x64-2.311.0.tar.gz \ + -L https://github.com/actions/runner/releases/download/v2.311.0/actions-runner-linux-x64-2.311.0.tar.gz + +# 解压 +tar xzf ./actions-runner-linux-x64-2.311.0.tar.gz +``` + +### Step 3: 配置 Runner + +```bash +# 获取 token (从 GitHub UI 或 API) +# 仓库级: https://github.com/{owner}/{repo}/settings/actions/runners/new +# 组织级: https://github.com/organizations/{org}/settings/actions/runners/new + +# 配置 runner +./config.sh \ + --url https://github.com/{owner}/{repo} \ + --token YOUR_TOKEN \ + --name my-runner \ + --labels self-hosted,linux,x64,gpu \ + --work _work \ + --unattended + +# --unattended: 非交互模式 +# --replace: 如果 runner 已存在则替换 +``` + +### Step 4: 安装为服务 + +```bash +# 安装服务 +sudo ./svc.sh install runner + +# 启动服务 +sudo ./svc.sh start + +# 查看状态 +sudo ./svc.sh status + +# 查看日志 +journalctl -u actions.runner.* -f +``` + +### Step 5: 验证 + +```bash +# 检查 runner 是否在线 +# GitHub UI: Settings → Actions → Runners +# 应该看到 runner 状态为 "Idle" + +# 测试 workflow +# 创建 .github/workflows/test.yml: +``` + +```yaml +name: Test Self-Hosted Runner +on: [push] + +jobs: + test: + runs-on: [self-hosted, linux] + steps: + - uses: actions/checkout@v4 + - name: Print system info + run: | + echo "Hostname: $(hostname)" + echo "OS: $(uname -a)" + echo "CPU: $(nproc)" + echo "Memory: $(free -h)" +``` + +### Step 6: 安全加固(可选但推荐) + +```bash +# 限制 sudo 权限 +sudo visudo +# 添加: runner ALL=(ALL) NOPASSWD: /usr/bin/docker + +# 启用防火墙 +sudo ufw enable +sudo ufw allow ssh +sudo ufw allow out 443/tcp # HTTPS to GitHub + +# 定期更新 +sudo crontab -e +# 添加: 0 2 * * * apt update && apt upgrade -y +``` + +### 故障排查 + +**Runner 无法连接:** +```bash +# 检查网络 +ping github.com + +# 检查日志 +journalctl -u actions.runner.* --no-pager -n 100 + +# 重新配置 +./config.sh remove --token YOUR_TOKEN +./config.sh --url ... --token NEW_TOKEN +``` + +**磁盘空间不足:** +```bash +# 清理旧的 workflow 文件 +cd _work/_temp +rm -rf * + +# 清理 Docker (如果使用) +docker system prune -af --volumes +``` + +**权限问题:** +```bash +# 确保 runner 用户拥有目录权限 +sudo chown -R runner:runner /home/runner/actions-runner +``` diff --git a/github-self-hosted-runner/info.md b/github-self-hosted-runner/info.md new file mode 100644 index 0000000..e69de29 diff --git a/github-self-hosted-runner/main.tf b/github-self-hosted-runner/main.tf new file mode 100644 index 0000000..e517800 --- /dev/null +++ b/github-self-hosted-runner/main.tf @@ -0,0 +1,35 @@ +# ========================================== +# GitHub Actions Self-hosted Runner +# ========================================== + +# 生成虚拟机的随机密码 +resource "random_password" "runner_instance_password" { + length = 16 + special = true + lower = true + upper = true + numeric = true +} + +# 创建 Runner 虚拟机实例 +resource "qiniu_compute_instance" "github_runner" { + instance_type = var.instance_type + name = format("github-runner-%s", local.runner_suffix) + description = format("GitHub Actions Runner for %s/%s", local.github_owner, local.github_repo) + image_id = local.ubuntu_image_id + system_disk_size = var.instance_system_disk_size + + # 使用 templatefile 渲染初始化脚本,传递变量 + user_data = base64encode(templatefile("${path.module}/runner_setup.sh", { + github_token = var.github_token, + github_repo_url = var.github_repo_url, + runner_name = local.runner_name, + runner_labels = join(",", local.runner_labels), + runner_username = var.runner_username, + enable_docker = var.enable_docker, + additional_packages = join(" ", var.additional_packages), + })) + + # 虚拟机的 root 密码 + password = random_password.runner_instance_password.result +} diff --git a/github-self-hosted-runner/outputs.tf b/github-self-hosted-runner/outputs.tf new file mode 100644 index 0000000..31518e4 --- /dev/null +++ b/github-self-hosted-runner/outputs.tf @@ -0,0 +1,54 @@ +# ========================================== +# 输出部署结果信息 +# ========================================== + +output "runner_instance_id" { + value = qiniu_compute_instance.github_runner.id + description = "GitHub Runner 虚拟机实例 ID" +} + +output "runner_instance_name" { + value = qiniu_compute_instance.github_runner.name + description = "GitHub Runner 虚拟机实例名称" +} + +output "runner_private_ip" { + value = qiniu_compute_instance.github_runner.private_ip_addresses[0].ipv4 + description = "Runner 虚拟机的内网 IP 地址" +} + +output "runner_name" { + value = local.runner_name + description = "GitHub Runner 的注册名称" +} + +output "runner_labels" { + value = local.runner_labels + description = "GitHub Runner 的标签列表" +} + +output "runner_repository" { + value = var.github_repo_url + description = "Runner 所属的 GitHub 仓库" +} + +output "instance_password" { + value = random_password.runner_instance_password.result + description = "虚拟机 root 密码(敏感信息)" + sensitive = true +} + +output "ssh_connection_command" { + value = "ssh root@${qiniu_compute_instance.github_runner.private_ip_addresses[0].ipv4}" + description = "SSH 连接命令(需要密码)" +} + +output "runner_status_check" { + value = "ssh root@${qiniu_compute_instance.github_runner.private_ip_addresses[0].ipv4} 'cd /home/${var.runner_username}/actions-runner && sudo ./svc.sh status'" + description = "检查 Runner 状态的命令" +} + +output "runner_logs_command" { + value = "ssh root@${qiniu_compute_instance.github_runner.private_ip_addresses[0].ipv4} 'journalctl -u actions.runner.* -f'" + description = "查看 Runner 日志的命令" +} diff --git a/github-self-hosted-runner/runner_setup.sh b/github-self-hosted-runner/runner_setup.sh new file mode 100644 index 0000000..8e8a2dd --- /dev/null +++ b/github-self-hosted-runner/runner_setup.sh @@ -0,0 +1,198 @@ +#!/bin/bash +set -e + +# ========================================== +# GitHub Actions Self-hosted Runner Setup +# 参考文档: how-to-setup-self-github-action-runner.md +# ========================================== + +# Terraform 模板变量(单引号避免特殊字符问题) +GITHUB_TOKEN='${github_token}' +GITHUB_REPO_URL='${github_repo_url}' +RUNNER_NAME='${runner_name}' +RUNNER_LABELS='${runner_labels}' +RUNNER_USERNAME='${runner_username}' +ENABLE_DOCKER='${enable_docker}' +ADDITIONAL_PACKAGES='${additional_packages}' + +echo "==========================================" +echo "Starting GitHub Runner Setup" +echo "==========================================" + +# ========================================== +# Step 1: 系统更新和基础依赖安装 +# ========================================== + +echo "[1/7] Updating system and installing dependencies..." +apt-get update +DEBIAN_FRONTEND=noninteractive apt-get upgrade -y +DEBIAN_FRONTEND=noninteractive apt-get install -y \ + curl \ + jq \ + git \ + build-essential \ + libssl-dev \ + ca-certificates \ + gnupg \ + lsb-release + +# 安装用户指定的额外软件包 +if [[ -n "$ADDITIONAL_PACKAGES" ]]; then + echo "Installing additional packages: $ADDITIONAL_PACKAGES" + DEBIAN_FRONTEND=noninteractive apt-get install -y $ADDITIONAL_PACKAGES +fi + +# ========================================== +# Step 2: 创建 Runner 专用账户 +# 参考文档第3节:为什么不能用 root +# ========================================== + +echo "[2/7] Creating runner user account..." + +# 检查用户是否已存在 +if id "$RUNNER_USERNAME" &>/dev/null; then + echo "User $RUNNER_USERNAME already exists" +else + # 创建用户并添加到 sudo 组 + useradd -m -s /bin/bash "$RUNNER_USERNAME" + usermod -aG sudo "$RUNNER_USERNAME" + + # 配置无密码 sudo(仅针对 Docker 和 systemctl) + echo "$RUNNER_USERNAME ALL=(ALL) NOPASSWD: /usr/bin/docker, /usr/bin/systemctl" >> /etc/sudoers.d/runner-nopasswd + chmod 0440 /etc/sudoers.d/runner-nopasswd +fi + +# ========================================== +# Step 3: 安装 Docker (可选) +# ========================================== + +if [[ "$ENABLE_DOCKER" == "true" ]]; then + echo "[3/7] Installing Docker..." + + # 添加 Docker 官方 GPG key + mkdir -p /etc/apt/keyrings + curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg + + # 添加 Docker repository + echo \ + "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \ + $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null + + # 安装 Docker + apt-get update + DEBIAN_FRONTEND=noninteractive apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin + + # 添加 runner 用户到 docker 组 + usermod -aG docker "$RUNNER_USERNAME" + + # 启动 Docker + systemctl enable docker + systemctl start docker +else + echo "[3/7] Skipping Docker installation" +fi + +# ========================================== +# Step 4: 下载 GitHub Actions Runner +# 参考文档 Step 2 +# ========================================== + +echo "[4/7] Downloading GitHub Actions Runner..." + +# 切换到 runner 用户的 home 目录 +RUNNER_HOME="/home/$RUNNER_USERNAME" +RUNNER_DIR="$RUNNER_HOME/actions-runner" + +# 创建 runner 目录 +mkdir -p "$RUNNER_DIR" +cd "$RUNNER_DIR" + +# 获取最新版本的 runner +RUNNER_VERSION=$(curl -s https://api.github.com/repos/actions/runner/releases/latest | jq -r '.tag_name' | sed 's/v//') + +# 下载 runner +curl -o actions-runner-linux-x64-$RUNNER_VERSION.tar.gz \ + -L "https://github.com/actions/runner/releases/download/v$RUNNER_VERSION/actions-runner-linux-x64-$RUNNER_VERSION.tar.gz" + +# 解压 +tar xzf ./actions-runner-linux-x64-$RUNNER_VERSION.tar.gz +rm -f ./actions-runner-linux-x64-$RUNNER_VERSION.tar.gz + +# 修改所有者为 runner 用户 +chown -R "$RUNNER_USERNAME:$RUNNER_USERNAME" "$RUNNER_DIR" + +# ========================================== +# Step 5: 获取 Registration Token +# 参考文档第6节:Token 机制 +# ========================================== + +echo "[5/7] Obtaining registration token from GitHub..." + +# 从 repo URL 中提取 owner 和 repo +REPO_PATH=$(echo "$GITHUB_REPO_URL" | sed 's|https://github.com/||') +OWNER=$(echo "$REPO_PATH" | cut -d'/' -f1) +REPO=$(echo "$REPO_PATH" | cut -d'/' -f2) + +# 通过 API 获取 registration token +REG_TOKEN=$(curl -s -X POST \ + -H "Authorization: token $GITHUB_TOKEN" \ + -H "Accept: application/vnd.github+json" \ + "https://api.github.com/repos/$OWNER/$REPO/actions/runners/registration-token" \ + | jq -r '.token') + +if [[ -z "$REG_TOKEN" || "$REG_TOKEN" == "null" ]]; then + echo "Error: Failed to obtain registration token. Check if:" + echo "1. GitHub token has correct permissions (admin:org or repo)" + echo "2. Repository URL is correct" + exit 1 +fi + +echo "Registration token obtained successfully" + +# ========================================== +# Step 6: 配置 Runner +# 参考文档 Step 3 +# ========================================== + +echo "[6/7] Configuring GitHub Actions Runner..." + +# 切换到 runner 用户执行配置 +su - "$RUNNER_USERNAME" -c "cd $RUNNER_DIR && ./config.sh \ + --url $GITHUB_REPO_URL \ + --token $REG_TOKEN \ + --name $RUNNER_NAME \ + --labels $RUNNER_LABELS \ + --work _work \ + --unattended \ + --replace" + +# ========================================== +# Step 7: 安装为系统服务并启动 +# 参考文档第7节:svc.sh 详解 +# ========================================== + +echo "[7/7] Installing runner as a service..." + +# 安装服务(需要 root 权限) +cd "$RUNNER_DIR" +./svc.sh install "$RUNNER_USERNAME" + +# 启动服务 +./svc.sh start + +# 查看状态 +./svc.sh status + +echo "==========================================" +echo "GitHub Runner Setup Completed!" +echo "==========================================" +echo "Runner Name: $RUNNER_NAME" +echo "Runner Labels: $RUNNER_LABELS" +echo "Repository: $GITHUB_REPO_URL" +echo "" +echo "Check runner status:" +echo " sudo ./svc.sh status" +echo "" +echo "View logs:" +echo " journalctl -u actions.runner.* -f" +echo "==========================================" diff --git a/github-self-hosted-runner/terraform.tfvars.example b/github-self-hosted-runner/terraform.tfvars.example new file mode 100644 index 0000000..487def7 --- /dev/null +++ b/github-self-hosted-runner/terraform.tfvars.example @@ -0,0 +1,48 @@ +# ========================================== +# GitHub Self-hosted Runner 配置示例 +# +# 使用方法: +# 1. 复制此文件: cp terraform.tfvars.example terraform.tfvars +# 2. 修改下面的值 +# 3. 运行: terraform apply +# ========================================== + +# ========== 必填参数 ========== + +# GitHub 仓库 URL +github_repo_url = "https://github.com/your-org/your-repo" + +# GitHub Personal Access Token (需要 repo 或 admin:org 权限) +# 获取地址: https://github.com/settings/tokens +github_token = "ghp_xxxxxxxxxxxxxxxxxxxx" + + +# ========== 可选参数 ========== + +# 虚拟机规格选择(根据工作负载选择) +# - ecs.t1.c2m4 (2核4GB) - 轻量级测试 +# - ecs.t1.c4m8 (4核8GB) - 前端构建 +# - ecs.t1.c8m16 (8核16GB) - 后端编译/Docker构建 +instance_type = "ecs.t1.c2m4" + +# 系统盘大小(GiB),建议至少 50GB +instance_system_disk_size = 50 + +# Runner 名称(留空则自动生成) +runner_name = "" + +# 自定义标签(会自动添加 self-hosted, linux, x64) +runner_labels = [] +# 示例: +# runner_labels = ["gpu", "production", "docker"] + +# Runner 进程的用户名 +runner_username = "runner" + +# 是否安装 Docker +enable_docker = true + +# 额外安装的软件包 +additional_packages = [] +# 示例: +# additional_packages = ["nodejs", "npm", "python3-pip", "golang-go"] diff --git a/github-self-hosted-runner/variables.tf b/github-self-hosted-runner/variables.tf new file mode 100644 index 0000000..668ae09 --- /dev/null +++ b/github-self-hosted-runner/variables.tf @@ -0,0 +1,120 @@ +# ========================================== +# 虚拟机配置 +# ========================================== + +variable "instance_type" { + type = string + description = "GitHub Runner instance type (规格选择参考文档中的推荐配置)" + default = "ecs.t1.c2m4" # 2核4GB,适合轻量级测试 + + validation { + condition = contains([ + "ecs.t1.c1m2", # 1核2GB + "ecs.t1.c2m4", # 2核4GB + "ecs.t1.c4m8", # 4核8GB + "ecs.t1.c8m16", # 8核16GB + "ecs.t1.c12m24", # 12核24GB + "ecs.t1.c16m32", # 16核32GB + "ecs.t1.c24m48", # 24核48GB + "ecs.t1.c32m64", # 32核64GB + "ecs.g1.c16m120", # 16核120GB (高内存) + "ecs.g1.c32m240", # 32核240GB (高内存) + "ecs.c1.c1m2", + "ecs.c1.c2m4", + "ecs.c1.c4m8", + "ecs.c1.c8m16", + "ecs.c1.c16m32", + "ecs.c1.c24m48", + "ecs.c1.c12m24", + "ecs.c1.c32m64", + ], var.instance_type) + error_message = "instance_type must be one of the allowed types" + } +} + +variable "instance_system_disk_size" { + type = number + description = "System disk size in GiB (建议至少 50GB 用于存储构建缓存)" + default = 50 + + validation { + condition = var.instance_system_disk_size >= 20 + error_message = "instance_system_disk_size must be at least 20 GiB" + } +} + +# ========================================== +# GitHub 配置 +# ========================================== + +variable "github_repo_url" { + type = string + description = "GitHub repository URL (e.g., https://github.com/owner/repo)" + + validation { + condition = can(regex("^https://github\\.com/[a-zA-Z0-9_-]+/[a-zA-Z0-9_-]+$", var.github_repo_url)) + error_message = "github_repo_url must be a valid GitHub repo URL: https://github.com/owner/repo" + } +} + +variable "github_token" { + type = string + description = "GitHub Personal Access Token (需要 admin:org 或 repo 权限)" + sensitive = true + + validation { + condition = length(var.github_token) > 20 + error_message = "github_token appears to be invalid (too short)" + } +} + +variable "runner_labels" { + type = list(string) + description = "Custom labels for the runner (会自动添加 self-hosted, linux, x64)" + default = [] + + validation { + condition = alltrue([ + for label in var.runner_labels : can(regex("^[a-zA-Z0-9_-]+$", label)) + ]) + error_message = "runner_labels must contain only alphanumeric, underscore, and hyphen characters" + } +} + +variable "runner_name" { + type = string + description = "Custom runner name (如果为空则自动生成)" + default = "" + + validation { + condition = var.runner_name == "" || length(var.runner_name) <= 64 + error_message = "runner_name must be 64 characters or less" + } +} + +# ========================================== +# 可选:Runner 账户配置 +# ========================================== + +variable "runner_username" { + type = string + description = "Username for the runner process (默认使用 'runner')" + default = "runner" + + validation { + condition = can(regex("^[a-z_][a-z0-9_-]*$", var.runner_username)) + error_message = "runner_username must be a valid Linux username" + } +} + +variable "enable_docker" { + type = bool + description = "是否安装 Docker (用于 Docker 构建任务)" + default = true +} + +variable "additional_packages" { + type = list(string) + description = "Additional apt packages to install (例如: ['nodejs', 'python3-pip'])" + default = [] +} diff --git a/github-self-hosted-runner/versions.tf b/github-self-hosted-runner/versions.tf new file mode 100644 index 0000000..5ad6e7b --- /dev/null +++ b/github-self-hosted-runner/versions.tf @@ -0,0 +1,18 @@ +terraform { + required_version = "> 0.12.0" + + required_providers { + qiniu = { + source = "hashicorp/qiniu" + version = "~> 1.0.0" + } + random = { + source = "hashicorp/random" + version = "~> 3.0" + } + } +} + +provider "qiniu" {} + +provider "random" {}