使用 Cloud Run worker 集區代管 GitHub 執行器

本教學課程會逐步說明如何使用工作站集區中的自架 GitHub 執行器,執行 GitHub 存放區中定義的工作流程。

您將部署 Cloud Run worker 集區來處理這項工作負載,並視需要部署 Cloud Run 函式,以支援 worker 集區的擴縮。

關於自行託管的 GitHub 執行器

在 GitHub Actions 工作流程中,執行器是執行工作的機器。舉例來說,執行器可以在本機複製存放區、安裝測試軟體,然後執行評估程式碼的指令。

您可以使用自行託管的執行程式,在 Cloud Run 工作站集區執行個體上執行 GitHub Actions。本教學課程說明如何依據執行中和未排程的工作數量,自動調整執行器集區的資源配置,甚至在沒有工作時將集區縮減為零。

擷取程式碼範例

如要擷取要使用的程式碼範例:

  1. 將範例存放區複製到本機電腦中:

    git clone https://github.com/GoogleCloudPlatform/cloud-run-samples
    
  2. 變更為包含 Cloud Run 範例程式碼的目錄:

    cd cloud-run-samples/github-runner
    

瞭解核心程式碼

這個範例會實作工作站集區和自動配置器,詳情請見下文。

工作站集區

工作站集區會使用以 GitHub 建立的 actions/runner 映像檔為基礎的 Dockerfile 進行設定。

除了小型輔助指令碼外,所有邏輯都包含在這個映像檔中。

FROM ghcr.io/actions/actions-runner:2.329.0

# Add scripts with right permissions.
USER root
# hadolint ignore=DL3045
COPY start.sh start.sh
RUN chmod +x start.sh

# Add start entrypoint with right permissions.
USER runner
ENTRYPOINT ["./start.sh"]

這個輔助指令碼會在容器啟動時執行,使用您建立的權杖,將容器本身註冊為設定的存放區中的暫時性執行個體。指令碼也會定義容器縮減時要採取的動作。

# Configure the current runner instance with URL, token and name.
mkdir /home/docker/actions-runner && cd /home/docker/actions-runner
echo "GitHub Repo: ${GITHUB_REPO_URL} for ${RUNNER_PREFIX}-${RUNNER_SUFFIX}"
./config.sh --unattended --url ${GITHUB_REPO_URL} --pat ${GH_TOKEN} --name ${RUNNER_NAME}

# Function to cleanup and remove runner from Github.
cleanup() {
   echo "Removing runner..."
   ./config.sh remove --unattended --pat ${GH_TOKEN}
}

# Trap signals.
trap 'cleanup; exit 130' INT
trap 'cleanup; exit 143' TERM

# Run the runner.
./run.sh & wait $!

自動配置器

自動調度器會在佇列中有新工作時擴充工作站集區,或在工作完成時縮減集區。這項功能會使用 Cloud Run API 檢查集區中的目前工作站數量,並視需要調整該值。

try:
    current_instance_count = get_current_worker_pool_instance_count()
except ValueError as e:
    return f"Could not retrieve instance count: {e}", 500

# Scale Up: If a job is queued and we have available capacity
if action == "queued" and job_status == "queued":
    print(f"Job '{job_name}' is queued.")

    if current_instance_count < MAX_RUNNERS:
        new_instance_count = current_instance_count + 1
        try:
            update_runner_instance_count(new_instance_count)
            print(f"Successfully scaled up to {new_instance_count} instances.")
        except ValueError as e:
            return f"Error scaling up instances: {e}", 500
    else:
        print(f"Max runners ({MAX_RUNNERS}) reached.")

# Scale Down: If a job is completed, check to see if there are any more pending
# or in progress jobs and scale accordingly.
elif action == "completed" and job_status == "completed":
    print(f"Job '{job_name}' completed.")

    current_queued_actions, current_running_actions = get_current_actions()
    current_actions = current_queued_actions + current_running_actions

    if current_queued_actions >= 1:
        print(
            f"GitHub says {current_queued_actions} are still pending."
            f"Won't change scaling ({current_instance_count})."
        )
    elif current_queued_actions == 0 and current_running_actions >= 1:
        print(
            f"GitHub says no queued actions, but {current_running_actions} running actions."
            f"Won't change scaling ({current_instance_count})."
        )
    elif current_actions == 0:
        print(f"GitHub says no pending actions. Scaling to zero.")
        update_runner_instance_count(0)
        print(f"Successfully scaled down to zero.")
    else:
        print(
            f"Detected an unhandled state: {current_queued_actions=}, {current_running_actions=}"
        )
else:
    print(
        f"Workflow job event for '{job_name}' with action '{action}' and "
        f"status '{job_status}' did not trigger a scaling action."
    )

設定身分與存取權管理

本教學課程會使用自訂服務帳戶,並授予使用已佈建資源所需的最低權限。如要設定服務帳戶,請按照下列步驟操作:

  1. gcloud 中設定專案 ID:

    gcloud config set project PROJECT_ID
    

    PROJECT_ID 替換為您的專案 ID。

  2. 建立新的身分與存取權管理服務帳戶:

    gcloud iam service-accounts create gh-runners
    

  3. 授予服務帳戶權限,使其以專案中的服務帳戶身分執行作業:

    gcloud projects add-iam-policy-binding PROJECT_ID \
      --member "serviceAccount:gh-runners@PROJECT_ID.iam.gserviceaccount.com" \
      --role=roles/iam.serviceAccountUser
    

    PROJECT_ID 替換為您的專案 ID。

擷取 GitHub 資訊

新增自行代管的執行器的 GitHub 說明文件建議透過 GitHub 網站新增執行器,然後提供用於驗證的特定權杖。

本教學課程會動態新增及移除執行器,因此需要靜態 GitHub 權杖。

如要完成本教學課程,您必須建立 GitHub 權杖,並取得與所選存放區互動的權限。

找出 GitHub 存放區

在本教學課程中,GITHUB_REPO 變數代表存放區名稱。這是 GitHub 存放區名稱中網域名稱後方的部分,適用於個人使用者存放區和機構存放區。

無論是使用者擁有的存放區還是機構擁有的存放區,您都會參照網域名稱後方的存放區名稱。

教學課程內容:

  • https://github.com/myuser/myrepoGITHUB_REPOmyuser/myrepo
  • https://github.com/mycompany/ourrepoGITHUB_REPOmycompany/ourrepo

建立存取權杖

您需要在 GitHub 上建立存取權杖,並安全地儲存在 Secret Manager 中:

  1. 確認你已登入 GitHub 帳戶。
  2. 前往 GitHub 的「Settings」>「Developer Settings」>「Personal Access Tokens」頁面。
  3. 按一下「產生新權杖」,然後選取「產生新權杖 (傳統版)」
  4. 建立具有「repo」範圍的新權杖。
  5. 按一下「產生權杖」
  6. 複製產生的權杖。

建立密鑰值

取得您剛建立的密鑰權杖,並儲存在 Secret Manager 中,然後設定存取權限。

  1. 在 Secret Manager 中建立密鑰:

    echo -n "GITHUB_TOKEN" | gcloud secrets create github_runner_token --data-file=-
    

    GITHUB_TOKEN 替換成您從 GitHub 複製的值。

  2. 授予新建立的密鑰存取權:

    gcloud secrets add-iam-policy-binding github_runner_token \
      --member "serviceAccount:gh-runners@PROJECT_ID.iam.gserviceaccount.com" \
      --role "roles/secretmanager.secretAccessor"
    

部署工作站集區

建立 Cloud Run worker 集區,處理 GitHub 動作。這個集區會使用以 GitHub 建立的 actions/runner 映像檔為基礎的映像檔。

設定 Cloud Run worker 集區

  1. 前往工作站集區的程式碼範例:

    cd worker-pool-container
    
  2. 部署工作站集區:

    gcloud beta run worker-pools deploy WORKER_POOL_NAME \
      --region WORKER_POOL_LOCATION \
      --source . \
      --scaling 1 \
      --set-env-vars GITHUB_REPO=GITHUB_REPO \
      --set-secrets GITHUB_TOKEN=github_runner_token:latest \
      --service-account gh-runners@PROJECT_ID.iam.gserviceaccount.com \
      --memory 2Gi \
      --cpu 4
    

    更改下列內容:

    如果這是您首次在這個專案中使用 Cloud Run 來源部署,系統會提示您建立預設 Artifact Registry 存放區。

使用工作站集區

現在工作站集區中只有一個執行個體,可隨時接受 GitHub Actions 的工作。

如要確認您已完成自架主機代管執行器的設定,請在存放區中叫用 GitHub 動作。

如要讓動作使用自架主機代管的 Runner,您必須變更 GitHub 動作的工作。在工作中,將 runs-on 值變更為 self-hosted

如果存放區還沒有任何動作,請參閱 GitHub Actions 快速入門

設定動作以使用自架主機代管的執行器後,請執行動作。

確認 GitHub 介面中的動作已順利完成。

部署 GitHub Runner Autoscaler

您在原始集區中部署了一個工作站,一次只能處理一個動作。視 CI 使用情況而定,您可能需要調度集區資源,以處理大量待完成的工作。

使用有效的 GitHub 執行器部署工作站集區後,請設定自動調整程式,根據動作佇列中的工作狀態佈建工作站執行個體。

這項實作會監聽 workflow_job 事件。建立工作流程工作時,系統會擴大工作站集區,工作完成後則會縮減。集區不會擴充至設定的執行個體數量上限,且所有執行中的工作完成後,集區會縮減至零。

您可以根據工作負載調整這個自動調度器。

建立 Webhook 密鑰值

如要建立 Webhook 的密鑰值,請按照下列步驟操作:

  1. 建立 Secret Manager 密鑰,內含任意字串值。

    echo -n "WEBHOOK_SECRET" | gcloud secrets create github_webhook_secret --data-file=-
    

    WEBHOOK_SECRET 替換為任意字串值。

  2. 將密鑰存取權授予自動調整服務帳戶:

    gcloud secrets add-iam-policy-binding github_webhook_secret \
      --member "serviceAccount:gh-runners@PROJECT_ID.iam.gserviceaccount.com" \
      --role "roles/secretmanager.secretAccessor"
    

部署函式以接收 Webhook 要求

如要部署函式來接收 Webhook 要求,請按照下列步驟操作:

  1. 前往 Webhook 的程式碼範例:

    cd ../autoscaler
    
  2. 部署 Cloud Run 函式:

    gcloud run deploy github-runner-autoscaler \
      --function github_webhook_handler \
      --region WORKER_POOL_LOCATION \
      --source . \
      --set-env-vars GITHUB_REPO=GITHUB_REPO \
      --set-env-vars WORKER_POOL_NAME=WORKER_POOL_NAME \
      --set-env-vars WORKER_POOL_LOCATION=WORKER_POOL_LOCATION \
      --set-env-vars MAX_RUNNERS=5 \
      --set-secrets GITHUB_TOKEN=github_runner_token:latest \
      --set-secrets WEBHOOK_SECRET=github_webhook_secret:latest \
      --service-account gh-runners@PROJECT_ID.iam.gserviceaccount.com \
      --allow-unauthenticated
    

    更改下列內容:

    • GITHUB_REPO 網域名稱後方的 GitHub 存放區名稱部分
    • WORKER_POOL_NAME 工作站集區的名稱
    • WORKER_POOL_LOCATION 工作站集區的區域
    • REPOSITORY_NAME GitHub 存放區名稱
  3. 請記下服務部署的網址。您會在後續步驟中使用這個值。

  4. 授予服務帳戶更新工作站集區的權限:

    gcloud alpha run worker-pools add-iam-policy-binding WORKER_POOL_NAME \
      --member "serviceAccount:gh-runners@PROJECT_ID.iam.gserviceaccount.com" \
      --role=roles/run.developer
    

    PROJECT_ID 替換為您的專案 ID。

建立 GitHub Webhook

如要建立 GitHub Webhook,請按照下列步驟操作:

  1. 確認你已登入 GitHub 帳戶。
  2. 前往 GitHub 存放區。
  3. 按一下「設定」
  4. 在「程式碼和自動化」下方,按一下「Webhook」
  5. 按一下 [Add Webhook]
  6. 輸入下列指令:

    1. 在「Payload URL」中,輸入您先前部署的 Cloud Run 函式網址。

      網址格式為:https://github-runner-autoscaler-PROJECTNUM.REGION.run.app,其中 PROJECTNUM 是專案的唯一數字 ID,REGION 則是您部署服務的區域。

    2. 在「Content type」中,選取「application/json」

    3. 在「Secret」部分,輸入您先前建立的 WEBHOOK_SECRET 值。

    4. 在「SSL verification」部分,選取「Enable SSL verification」

    5. 在「您要透過哪些事件觸發這個 Webhook?」中,選取「讓我選取個別事件」

    6. 在事件選取畫面中,選取「工作流程工作」。取消選取其他選項。

    7. 按一下 [Add Webhook]

縮減工作站集區

現在已設定 Webhook,因此您不必在集區中保留工作站。這也能確保沒有工作要執行時,不會有任何執行中的工作人員,進而降低成本。

  • 調整集區,將資源調度率降至零:

    gcloud beta run worker-pools update WORKER_POOL_NAME \
      --region WORKER_POOL_LOCATION \
      --scaling 0
    

使用自動調度執行器

如要確認自動調度資源的執行器是否正常運作,請執行先前設定的動作 runs-on: self-hosted

您可以在存放區的「Actions」分頁追蹤 GitHub Actions 的進度。

如要檢查 Webhook 函式和工作集區的執行情況,請分別查看 Cloud Run 函式和 Cloud Run 工作集區的「記錄」分頁。