You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在容器中使用临时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

  1. 进入GitHub → Settings → Developer settings → GitHub Apps → New GitHub App
  2. 配置权限:给Administration(仓库级)或Organization administration(组织级)设置Read and write权限
  3. 生成并下载私钥(保存为github-app.pem)
  4. 记录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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 02:21:01