1. 決策背景:為什麼我們需要自建 Runner? (Context & Motivation)
在導入 ARC 之前,我們的 CI/CD 環境面臨以下 5 大現況與挑戰,這促使我們尋求容器化的解決方案:
- 成本與額度限制: GitHub Official Hosted Runners 雖然方便,但有免費分鐘數限制與併發數 (Concurrency) 限制。隨著專案擴大,容易遇到排隊或額度耗盡的問題。
- 缺乏專用基礎設施: Organization 目前沒有配置專門用於 CI/CD 的實體機或虛擬機 (VM),導致建置資源不穩定。
- 既有資源最大化: Org 內部已經維運一套穩定的 RKE2 Kubernetes Cluster。利用現有的 K8s 算力來跑 Runner,既不用額外採購機器,也能統一維運管理。
- 安全性考量 (Supply Chain Security): 我們希望對執行環境有 100% 的掌控權。直接使用 Docker Hub 上第三方個人維護的 Runner Image 存在資安風險;透過 ARC,我們可以使用官方 Image 或自行建置安全合規的基底映像檔。
- 維運彈性與擴展性 (Scalability): GitHub 官方建議的「在 Windows/Linux/macOS 直接安裝 Binary」方式屬於「寵物模式 (Pet)」,維護困難、無法自動水平擴展,且遷移時需要重新設定環境。我們需要的是「免洗筷模式 (Cattle)」,能隨用隨丟、自動擴展。
2. 什麼是 ARC?它的優勢
導入 Actions Runner Controller (ARC) 後,我們解決了上述痛點:
- 自動擴展 (Auto-scaling):有任務時自動在 K8s 建立 Pod,沒任務時自動歸零,完美利用 RKE2 資源。
- 環境隔離 (Ephemeral):每次跑完任務就銷毀容器,保證環境乾淨,不會有殘留檔案污染下一次建置。
- GitOps 管理:所有設定(權限、映像檔、資源限制)皆透過 Helm Chart 與 YAML 管理 (IaC),告別手動 SSH 進機器設定的時代。
架構簡介
ARC 的運作分為兩個核心部分:
- Controller:負責監聽 GitHub 的 Webhook,當發現有 Workflow 處於 Queued 狀態時,指揮 K8s 建立 Runner。
- Runner Scale Set:實際執行任務的 Pod。我們採用 Docker-in-Docker (DinD) 模式,讓我們可以在 Runner 裡面執行
docker build指令。
3. 關鍵前置設定:建立 GitHub App
為了讓 K8s 能夠合法的向 GitHub 申請 Runner,我們不使用個人的 Access Token (PAT),而是建立一個 GitHub App。 這有兩個好處:
- 不受個人離職影響:App 屬於組織,不會因為某個員工離職導致 CI/CD 全掛。
- API Rate Limit 更高:GitHub App 的 API 呼叫額度遠高於個人帳號。
Step 1: 建立 App
- 進入 Organization Settings (組織設定)。

- 左側選單滑到最下方:Developer settings > GitHub Apps。

- 左側選單滑到最下方:Developer settings > GitHub Apps。

- 點擊右上角 New GitHub App。

- 填寫基本資料:
- GitHub App name: 取個易辨識的名字,例如
arc-runner-my-org(全網唯一,若重複請加後綴) - 這邊範例取
tw-aquaman-arc-demo-app
- 這邊範例取
- Homepage URL: 填寫公司官網或組織 GitHub 網址即可 (例如
https://github.com/my-org) - 這邊範例取
https://github.com/tw-aquaman
- 這邊範例取
- Webhook: 取消勾選 (Active)。
- 說明:新版 ARC (Scale Set) 採主動連線 (Long Polling),無需對外開放 K8s Public IP,內網環境更安全。
- GitHub App name: 取個易辨識的名字,例如


Step 2:設定權限 (Permissions)
這是 ARC 能否正常運作的核心。請精確設定以下權限:
- Repository permissions (儲存庫權限)
- Actions:
Read-only(讓 Controller 能讀取 Workflow 檔案) - Metadata:
Read-only(預設必選)
- Actions:

- Organization permissions (組織權限)
- Self-hosted runners:
Read and write(若未開啟此權限,註冊時將回傳 403 Error)
- Self-hosted runners:

(設定完後,點擊最下方的 Create GitHub App)
Step 3:取得憑證 (Credentials)
建立成功後,您會進入 App 的設定頁面,請收集以下三樣東西:
- App ID: 頁面上方會顯示 (例如
2681460)。

- Private Key(私鑰):
- 滑到頁面最下方 Private keys 區塊。
- 點擊 Generate a private key。
- 電腦會自動下載一個
.pem檔案 - 注意:這是唯一的鑰匙,請妥善保存,不要 commit 到 git repo 中!


Step 4:安裝 App 到組織並取得 Installation ID
App 建立好只是「存在」而已,必須「安裝」到組織才算生效。
- 點擊左側 Install App > 選擇組織 > Install。

- 選擇 All repositories 或特定 Repo,點擊 Install。

- 獲取 Installation ID:
- 安裝完成後,網頁會跳轉到該 App 在組織內的設定頁。
- 請看瀏覽器的網址列,以範例來說是
https://github.com/organizations/tw-aquaman/settings/installations/104849848 - 最後面那串數字
104849848就是 Installation ID。 - (請務必記錄下來,這跟 App ID 是不一樣的!)


4. K8s 部署實作 (Helm Chart)
4.1 設定環境變數
為了避免指令出錯,建議先將憑證資訊設為變數
export APP_ID="2681460" # Step 3 取得
export INSTALLATION_ID="104849848" # Step 4 取得
export PRIVATE_KEY_PATH="github-arc-private-key.pem"
4.2 安裝 Controller
Controller 負責全域管理,建議安裝在獨立 Namespace。
這邊範例的 namespace 為 github-arc-systems。
helm install arc \
oci://ghcr.io/actions/actions-runner-controller-charts/gha-runner-scale-set-controller \
--namespace github-arc-systems \
--create-namespace
4.3 配置 Runner Set (支援 Docker Build)
為了讓 Runner 能打包 Docker Image,我們需要開啟 Privileged 模式並掛載 DinD Sidecar。
所以先建立一個 values-dind.yaml 檔案。
values-dind.yaml 配置檔:
githubConfigUrl: "https://github.com/YOUR_ORG_NAME" # 替換為你的 Org 網址
githubConfigSecret: "github-arc-secret" # 必須對應 K8s Secret 名稱
template:
spec:
containers:
- name: runner
image: ghcr.io/actions/actions-runner:latest
command: ["/home/runner/run.sh"]
env:
- name: DOCKER_HOST
value: tcp://localhost:2375
volumeMounts:
- name: work
mountPath: /home/runner/_work
- name: dind
image: docker:dind
args: ["dockerd", "--host=tcp://0.0.0.0:2375"]
securityContext:
privileged: true
volumeMounts:
- name: work
mountPath: /home/runner/_work
- name: dind-storage
mountPath: /var/lib/docker
volumes:
- name: work
emptyDir: {}
- name: dind-storage
emptyDir: {}
4.4 建立 Secret 與部署 Runner
將 GitHub App 憑證寫入 K8s Secret,並啟動 Runner Set。
# 1. 建立 github-arc-runners namespace
kubectl create ns github-arc-runners
# 2. 建立 Secret (注意 Namespace 要跟 Runner 一樣)
kubectl create secret generic github-arc-secret \
--namespace github-arc-runners \
--from-literal=github_app_id=${APP_ID} \
--from-literal=github_app_installation_id=${INSTALLATION_ID} \
--from-file=github_app_private_key=${PRIVATE_KEY_PATH}
# 3. 安裝 Runner Set
helm install org-runner-set \
oci://ghcr.io/actions/actions-runner-controller-charts/gha-runner-scale-set \
--namespace github-arc-runners \
--values values-dind.yaml
5. 實際應用:撰寫第一個 K8s Runner Workflow
環境建置完成後,我們就可以在 GitHub Actions 中使用這個強大的 K8s Runner。與使用 GitHub 官方 Runner 唯一的區別在於 runs-on 的標籤設定。
5.1 關鍵語法:runs-on
在 Workflow YAML 中,runs-on 的值必須對應我們在 Helm 安裝時設定的 Runner Scale Set Name。
- GitHub Hosted Runner:
runs-on: ubuntu-latest - Our K8s Runner:
runs-on: org-runner-set - (註:
org-runner-set是我們在 Helm 安裝指令中取的名字)
- (註:
5.2 範例:測試 Docker Build 功能
由於我們配置了 DinD (Docker-in-Docker) 模式,這裡提供一個測試 Docker 建置的 Workflow 範例。請在專案中建立 .github/workflows/test-arc.yml 和一個簡單的 dockerfile
test-arc.yml 配置檔:
name: Test ARC Runner
on:
workflow_dispatch:
push:
jobs:
simple-test:
runs-on: org-runner-set ## 這裏要指定我們在 Hele 設定的名字(4.4 的第 3 步)
steps:
- name: Run Echo
run: echo "Hello! Successfully running on Rancher ARC Runner."
test-build:
runs-on: org-runner-set ## 這裏要指定我們在 Hele 設定的名字(4.4 的第 3 步)
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Wait for Docker to be ready
run: |
echo "Waiting for Docker daemon to be ready..."
timeout=60
while ! docker info > /dev/null 2>&1; do
if [ $timeout -le 0 ]; then
echo "❌ Docker daemon failed to start within 60 seconds."
exit 1
fi
echo "Sleeping... ($timeout seconds left)"
sleep 2
timeout=$((timeout - 2))
done
echo "✅ Docker is ready!"
- name: Build Docker Image
run: |
echo "🚀 Starting build..."
docker build -t test-image:v1 .
- name: Run Container Verification
run: |
echo "🧪 Testing container execution..."
docker run --rm test-image:v1
dockerfile 內容:
FROM alpine:latest
RUN echo "🚧 Image is building on K8s Runner..." > build_log.txt
CMD ["echo", "🎉 Success! Docker-in-Docker is working perfectly!"]
把程式碼推上到 GitHub 後,到 Repo 的 Actions 點擊我們剛剛的 action 後可以看到我剛寫的 Jobs 都有被執行

在我們的 test-build Job 也有正常執行

6. 常見錯誤排查 (Troubleshooting)
在建置過程中,我們解決了以下幾個核心問題:
- Runner Pod 沒出現
- 原因:Controller 找不到 Secret
- 解決方案:檢查 Secret 是否建立在
github-arc-runnersNamespace,且名稱與 YAML 中的githubConfigSecret一致。
- Log 顯示 403 Forbidden
- 原因:GitHub App 權限不足
- 解決方案:檢查 GitHub App 設定:
Organization permissions->Self-hosted runners是否已設為 Read and write。
- Log 顯示 "Resource not accessible"
- 原因:誤用 Installation ID
- 解決方案:檢查 Secret 內的 ID 是否誤填為「個人帳號安裝的 ID」,需更新為「組織安裝的 ID」。
- Docker Build 失敗
- 原因:無法連線 Docker Daemon
- 解決方案:確認
values.yaml已設定DOCKER_HOST且 Sidecar 已開啟privileged: true。
7. 結論
透過 ARC,我們成功利用現有的 RKE2 K8s 資源,建立了一套低成本、高擴展、且符合資安要求的 CI/CD 基礎設施。這不僅解決了當前的額度問題,也為未來的 DevOps 流程打下標準化的基礎。





















