Runner
runner 是宿主連線的長期執行行程。它驗證每個宿主,從已安裝的套件中找出宿主指名的外掛套件,再為它啟動外掛行程。宿主從不傳送程式碼。
架設 runner
Section titled “架設 runner”- 安裝:在 Linux 或 macOS 上、Node.js 22.22 以上,把
@graftland/cli、@graftland/plugin-remote、建置好的外掛套件及其相依安裝到某個安裝目錄的node_modules。透過套件的exports公開每份契約,例如my-plugin/contract,並在宿主上安裝同一個套件。 - 撰寫
runner.yml,其中只有一個remote/runner設定項目。 - 啟動 runner:
graft start runner.yml。 - 指向 runner:讓宿主的
remote設定項目指向它,再執行graft start host.yml。
- id: runner name: remote/runner config: root: ./installation state: ./state listen: - url: wss://0.0.0.0:7443 cert: ./certs/server.crt key: ./certs/server.key auth: jwt: issuer: https://issuer.example audience: [graft] algorithms: [ES256] keys: jwksUrl: https://issuer.example/.well-known/jwks.json- id: greeter name: remote config: contract: my-plugin/contract connect: url: wss://runner.example:7443 ca: ./certs/ca.crt auth: jwt: token: { file: ./runner-token.txt } config: greeting: Hello| 欄位 | 必填 | 預設值 | 意義 |
|---|---|---|---|
root |
是 | — | 安裝目錄。契約套件從它的 node_modules 解析。 |
state |
是 | — | 狀態目錄,不存在時會建立。規則見下表。 |
lease |
否 | 15 seconds |
實例的預設租約。 |
grace |
否 | 5 seconds |
從中斷外掛行程到強制結束它之間的時間。 |
listen |
是 | — | listener,每個位址一個。見 listen 的形式。 |
| 規則 | 說明 |
|---|---|
| 路徑 | 相對於 runner.yml,包括 root、state 與憑證檔案。 |
| 安裝目錄 | runner 執行期間保持不變。部署時先安裝新套件,再在宿主上重新啟動該設定項目。 |
| 狀態目錄 | 放在本機檔案系統上,同一時間只給一個 runner 使用,重新啟動後保留。第二個 runner 會以 StateDirectoryLocked 失敗。 |
| 全新狀態 | 若某些身分仍在別處使用,不要改用空的狀態目錄:generation 會從頭開始。 |
| 啟動命令 | graft start runner.yml,日誌寫到標準錯誤。只有單一 stdio listener 時才加 --stdio。 |
| 啟動 | 所有 listener 就緒才算啟動完成。失敗時會關閉已開啟的部分,以 原始錯誤 用非零狀態退出。 |
| listener 之後失敗 | runner 記錄 Remote listener failed,其他 listener 與外掛繼續執行。 |
| 服務管理器 | 用會停止所有後代行程的服務管理器執行 runner,例如設定 KillMode=control-group 的 systemd。 |
listen 的形式
Section titled “listen 的形式”| 形式 | 欄位 |
|---|---|
url: ws://host:port |
不能有 cert 或 key。 |
url: wss://host:port |
cert 與 key:伺服器憑證與金鑰。 |
unix: path |
要建立的 socket 檔案。 |
stdio: true |
identity,預設 anonymous。不能有 auth。見 SSH 與 stdio。 |
transport: { name, config } |
自訂 listener。 |
- 每個元素取一種傳輸形式,最多一種
auth,見傳輸與驗證。 - WebSocket listener 只服務一個路徑
/graft,子協定為graft-remote.v1。url的路徑必須為空或/graft。沒有其他 HTTP 端點,也沒有健康檢查。 - 所有 listener 共用同一組實例,加起來最多只能用一個 JWT issuer。
實例是 runner 上某個外掛位置的名稱。它與驗證得到的宿主身分一起決定一條連線屬於哪個外掛行程。同一身分、同一實例的新連線會替換舊行程,這次啟動會得到下一個 generation。狀態目錄按身分與實例保存 generation。
| 情況 | 怎麼做 |
|---|---|
| 預設名稱 | loader 設定項目的 id。保持穩定;loader 會把產生的 id 存回設定檔。 |
| 兩個宿主,身分與實例都相同 | 它們會互相替換。讓每個宿主有自己的身分,或設定不同的 connect.instance。 |
| 從同一設定項目 fork 出兩個遠端外掛 | 子外掛繼承設定項目 id。為每個設定 connect.instance。 |
| 內嵌群組 | 群組 id 只在兄弟之間唯一。請檢查最終名稱。 |
不經 loader 使用 Remote.plugin |
沒有設定項目 id。設定 connect.instance,否則載入以 MissingInstance 失敗。 |
connect.expect 在外掛啟動前確認 runner 安裝的版本:
connect: url: wss://runner.example:7443 expect: version: 1.4.0| 欄位 | 比對對象 |
|---|---|
version |
runner 上契約套件 package.json 中的 version。 |
digest |
套件檔案的 SHA-256 十六進位摘要,排除所有 node_modules,因此不涵蓋相依。只有設定時才計算。 |
不符時以 PackageError 失敗,訊息會同時顯示兩個值。
SSH 與 stdio
Section titled “SSH 與 stdio”stdio
runner 透過標準輸入輸出溝通,所以宿主可以用 SSH 這類命令連到它,由 SSH 負責驗證與加密。
- id: runner name: remote/runner config: root: /srv/graft/installation state: /srv/graft/state/billing listen: - stdio: true identity: billing在 runner 帳號的 authorized_keys 中把宿主的金鑰限制為這個命令,再用 command
連線:
restrict,command="/usr/local/bin/graft start /srv/graft/stdio.yml --stdio" ssh-ed25519 AAAA... billing-hostconnect: command: [ssh, -T, billing@runner.example]| 規則 | 說明 |
|---|---|
沒有 auth |
command 與 stdio 都不接受 auth,none 也不行。identity 只是替宿主命名。 |
| 唯一 listener | stdio listener 必須是其 runner 唯一的 listener。 |
| 各自的狀態目錄 | 每個 SSH 身分用自己的狀態目錄。同一時間對同一目錄的第二個工作階段會被拒絕。 |
| 工作階段結束 | 輸入結束時,graft start 停止外掛、確認它已退出、釋放狀態目錄並以 0 退出;失敗時以非零狀態退出並回報原因。 |
| 乾淨的輸出 | 標準輸出承載協定位元組。使用 -T,不要印出 banner。 |
| 退出證明 | SSH 退出不能證明外掛已停止,只有 runner 的確認可以。 |