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

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

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

關於自行託管的 GitHub 執行器

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

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

目標

在這個教學課程中,您將執行下列操作:

  • 將 Cloud Run worker 集區部署至 Cloud Run。
  • 部署 Cloud Run 函式,支援 worker 集區的擴縮作業。
  • 建立 Secret Manager 密鑰,安全地儲存符記和密鑰。
  • 部署自行託管的 GitHub 執行器,支援 GitHub 存放區。

費用

在本文件中,您會使用下列 Google Cloud的計費元件:

您可以使用 Pricing Calculator,根據預測用量估算費用。

初次使用 Google Cloud 的使用者可能符合免費試用期資格。

事前準備

  1. Sign in to your Google Cloud account. If you're new to Google Cloud, create an account to evaluate how our products perform in real-world scenarios. New customers also get $300 in free credits to run, test, and deploy workloads.
  2. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Roles required to select or create a project

    • Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
    • Create a project: To create a project, you need the Project Creator role (roles/resourcemanager.projectCreator), which contains the resourcemanager.projects.create permission. Learn how to grant roles.

    Go to project selector

  3. Verify that billing is enabled for your Google Cloud project.

  4. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Roles required to select or create a project

    • Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
    • Create a project: To create a project, you need the Project Creator role (roles/resourcemanager.projectCreator), which contains the resourcemanager.projects.create permission. Learn how to grant roles.

    Go to project selector

  5. Verify that billing is enabled for your Google Cloud project.

  6. Enable the Cloud Run, Secret Manager, Artifact Registry, and Cloud Build APIs.

    Roles required to enable APIs

    To enable APIs, you need the Service Usage Admin IAM role (roles/serviceusage.serviceUsageAdmin), which contains the serviceusage.services.enable permission. Learn how to grant roles.

    Enable the APIs

  7. 必要的角色

    如要取得完成本教學課程所需的權限,請要求管理員在專案中授予您下列 IAM 角色:

    如要進一步瞭解如何授予角色,請參閱「管理專案、資料夾和組織的存取權」。

    您或許也能透過自訂角色或其他預先定義的角色,取得必要權限。

    您必須具備編輯 GitHub 存放區設定的權限,才能設定自架主機代管的執行器。存放區可以是使用者擁有的存放區,也可以是機構擁有的存放區。

擷取程式碼範例

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

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

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

    cd cloud-run-samples/github-runner
    

瞭解核心程式碼

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

工作站集區

worker 集區會根據 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
    

使用自動調度資源的執行器

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

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

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

清除所用資源

為避免系統向您的 Google Cloud 帳戶收取額外費用,請刪除您在本教學課程中部署的所有資源。

刪除專案

如果您是為了這個教學課程建立新專案,請刪除該專案。如果您使用現有專案,且需要保留專案,但不想保留您在本教學課程中新增的變更,請刪除您為本教學課程建立的資源

如要避免付費,最簡單的方法就是刪除您為了本教學課程所建立的專案。

如要刪除專案:

  1. In the Google Cloud console, go to the Manage resources page.

    Go to Manage resources

  2. In the project list, select the project that you want to delete, and then click Delete.
  3. In the dialog, type the project ID, and then click Shut down to delete the project.

刪除教學課程資源

  1. 刪除您在本教學課程中部署的 Cloud Run 服務。 Cloud Run 服務收到要求後才會開始計費。

    如要刪除 Cloud Run 服務,請執行下列指令:

    gcloud run services delete SERVICE-NAME

    SERVICE-NAME 改為您的服務名稱。

    您也可以從Google Cloud 控制台刪除 Cloud Run 服務。

  2. 移除您在教學課程設定期間新增的gcloud預設區域設定:

     gcloud config unset run/region
    
  3. 移除專案設定:

     gcloud config unset project
    
  4. 刪除在本教學課程中建立的其他 Google Cloud 資源:

後續步驟