如何在容器中使用临时Runner实现每次作业的干净构建环境
解决方案:动态配置临时GitHub自托管Runner
针对你遇到的认证令牌单次使用问题,核心思路是将Runner注册流程从镜像构建阶段移到容器启动阶段,通过两种方式动态获取有效注册令牌,实现每次启动干净的临时Runner。
方案1:使用可复用的组织/仓库注册令牌(简单易实现)
GitHub的组织或仓库级注册令牌支持重复使用(默认有效期90天,可刷新),完全适配你每次启动容器注册临时Runner的需求。
步骤1:获取注册令牌
- 仓库级:进入目标仓库 →
Settings→Actions→Runners→New self-hosted runner,复制页面中的Registration token - 组织级:进入组织 →
Settings→Actions→Runners→New self-hosted runner,复制注册令牌
步骤2:编写Dockerfile(仅构建Runner基础环境)
不要在构建阶段执行config.sh,只完成依赖安装和Runner包下载:
FROM debian:bookworm-slim # 安装Runner必需依赖 RUN apt-get update && apt-get install -y --no-install-recommends \ curl git ca-certificates \ && rm -rf /var/lib/apt/lists/* # 下载GitHub Runner(替换为你需要的版本号) RUN RUNNER_VERSION="2.317.0" && \ curl -L "https://github.com/actions/runner/releases/download/v${RUNNER_VERSION}/actions-runner-linux-x64-${RUNNER_VERSION}.tar.gz" | tar -xz -C /opt && \ chmod +x /opt/config.sh /opt/run.sh # 复制自定义启动脚本 COPY entrypoint.sh /entrypoint.sh RUN chmod +x /entrypoint.sh ENTRYPOINT ["/entrypoint.sh"]
步骤3:编写启动脚本entrypoint.sh(动态注册临时Runner)
每次容器启动时,用传入的注册令牌执行config.sh并启动Runner:
#!/bin/bash set -e # 从环境变量读取配置(启动容器时传入) RUNNER_REPO_URL="${RUNNER_REPO_URL}" RUNNER_TOKEN="${RUNNER_TOKEN}" # 生成随机Runner名称,避免重复 RUNNER_NAME="ephemeral-runner-$(uuidgen)" # 配置临时Runner cd /opt ./config.sh \ --url "${RUNNER_REPO_URL}" \ --token "${RUNNER_TOKEN}" \ --name "${RUNNER_NAME}" \ --ephemeral \ --unattended \ --replace # 启动Runner ./run.sh
步骤4:配置systemd服务(自动重启容器)
创建/etc/systemd/system/github-ephemeral-runner.service文件:
[Unit] Description=GitHub Ephemeral Self-Hosted Runner After=docker.service Requires=docker.service [Service] Restart=always RestartSec=10 # 替换为你的仓库地址和注册令牌 Environment="RUNNER_REPO_URL=https://github.com/your-org/your-repo" Environment="RUNNER_TOKEN=your-registration-token" ExecStart=/usr/bin/docker run --rm \ --name github-ephemeral-runner \ --env RUNNER_REPO_URL=${RUNNER_REPO_URL} \ --env RUNNER_TOKEN=${RUNNER_TOKEN} \ your-runner-image:latest [Install] WantedBy=multi-user.target
启用并启动服务:
systemctl daemon-reload && systemctl enable --now github-ephemeral-runner.service
方案2:使用GitHub App动态生成令牌(高安全场景)
如果需要严格的权限管控(避免长期复用令牌),可以通过GitHub App动态生成临时注册令牌,每次启动容器时自动调用API获取。
步骤1:创建并配置GitHub App
- 进入GitHub →
Settings→Developer settings→GitHub Apps→New GitHub App - 配置权限:给
Administration(仓库级)或Organization administration(组织级)设置Read and write权限 - 生成并下载私钥(保存为
github-app.pem) - 记录App的
App ID和Installation ID(安装App到组织/仓库后可在App设置中查看)
步骤2:修改启动脚本entrypoint.sh(添加令牌生成逻辑)
需要额外安装jq和openssl依赖(用于处理JSON和生成JWT):
#!/bin/bash set -e # 从环境变量读取GitHub App配置 APP_ID="${APP_ID}" INSTALLATION_ID="${INSTALLATION_ID}" PRIVATE_KEY="${PRIVATE_KEY}" RUNNER_REPO_URL="${RUNNER_REPO_URL}" RUNNER_NAME="ephemeral-runner-$(uuidgen)" # 生成JWT令牌用于GitHub API认证 generate_jwt() { local header='{"alg":"RS256","typ":"JWT"}' local payload=$(printf '{"iat":%d,"exp":%d,"iss":%d}' "$(date +%s)" "$(( $(date +%s) + 600 ))" "${APP_ID}") local header_b64=$(echo -n "${header}" | base64 | tr '+/' '-_' | tr -d '=') local payload_b64=$(echo -n "${payload}" | base64 | tr '+/' '-_' | tr -d '=') local signature=$(echo -n "${header_b64}.${payload_b64}" | openssl dgst -sha256 -sign <(echo -n "${PRIVATE_KEY}") | base64 | tr '+/' '-_' | tr -d '=') echo "${header_b64}.${payload_b64}.${signature}" } # 获取安装访问令牌 get_installation_token() { local jwt=$(generate_jwt) curl -s -X POST \ -H "Authorization: Bearer ${jwt}" \ -H "Accept: application/vnd.github.v3+json" \ "https://api.github.com/app/installations/${INSTALLATION_ID}/access_tokens" | jq -r '.token' } # 获取Runner注册令牌(仓库级示例,组织级替换为/orgs/your-org路径) get_runner_token() { local installation_token=$(get_installation_token) curl -s -X POST \ -H "Authorization: token ${installation_token}" \ -H "Accept: application/vnd.github.v3+json" \ "${RUNNER_REPO_URL%/}/actions/runners/registration-token" | jq -r '.token' } # 动态获取临时注册令牌 RUNNER_TOKEN=$(get_runner_token) # 配置并启动临时Runner cd /opt ./config.sh \ --url "${RUNNER_REPO_URL}" \ --token "${RUNNER_TOKEN}" \ --name "${RUNNER_NAME}" \ --ephemeral \ --unattended \ --replace ./run.sh
步骤3:更新Dockerfile和systemd服务
- Dockerfile中添加
jq openssl到依赖安装列表 - systemd服务中传入GitHub App的环境变量(注意私钥需要转义换行):
[Unit] Description=GitHub Ephemeral Self-Hosted Runner (GitHub App) After=docker.service Requires=docker.service [Service] Restart=always RestartSec=10 Environment="APP_ID=your-app-id" Environment="INSTALLATION_ID=your-installation-id" Environment="PRIVATE_KEY=$(cat /path/to/github-app.pem | tr '\n' ' ')" Environment="RUNNER_REPO_URL=https://github.com/your-org/your-repo" ExecStart=/usr/bin/docker run --rm \ --name github-ephemeral-runner \ --env APP_ID=${APP_ID} \ --env INSTALLATION_ID=${INSTALLATION_ID} \ --env PRIVATE_KEY="${PRIVATE_KEY}" \ --env RUNNER_REPO_URL=${RUNNER_REPO_URL} \ your-runner-image:latest [Install] WantedBy=multi-user.target
关键注意事项
--ephemeral参数会让Runner完成一次作业后自动退出,配合systemd的Restart=always实现自动重启,生成全新构建环境- 方案1的注册令牌有效期90天,到期后需重新生成并更新systemd配置
- 方案2生成的注册令牌仅单次有效,安全性更高,适合企业级场景
- 容器启动时添加
--rm参数,确保退出后自动清理资源
内容的提问来源于stack exchange,提问作者Jason C
相关产品推荐
相关产品推荐

