플러그인 샌드박스

이 페이지

샌드박스된 플러그인은 샌드박스 러너가 제공하는 격리된 런타임에서 실행됩니다. 마켓플레이스와 레지스트리 설치는 항상 샌드박스에서 실행되며, emdash() 통합의 sandboxed: []에 나열된 플러그인도 마찬가지입니다. plugins: []에 나열된 플러그인은 서버 프로세스에서 실행되며 러너를 사용하지 않습니다.

러너는 배포 플랫폼에 따라 다릅니다. Cloudflare Workers에서 각 플러그인은 Worker Loader 바인딩을 통해 생성된 Dynamic Worker로 실행됩니다. Node.js에서 서버는 오픈소스 Workers 런타임인 workerd를 자식 프로세스로 시작하고 각 플러그인을 그 안의 서비스로 실행합니다. emdash()sandboxRunner 옵션이 러너를 선택합니다. 이것 없이는 sandboxed: []의 플러그인이 로드되지 않으며, 구성된 marketplace는 “Marketplace requires sandboxRunner to be configured”로 빌드를 실패시킵니다.

다음 표는 각 러너가 필요로 하고 적용하는 것을 요약합니다.

Cloudflare WorkersNode.js
sandboxRunner@emdash-cms/cloudflaresandbox()"@emdash-cms/sandbox-workerd/sandbox"
요구사항Workers Paid 플랜, worker_loaders 바인딩, Worker 엔트리 포인트에서 내보낸 PluginBridgeworkerd 패키지
데이터베이스 액세스DB D1 바인딩 (구성된 어댑터와 무관)구성된 데이터베이스
적용되는 제한CPU 시간, 서브리퀘스트, 월 타임월 타임

Cloudflare Workers

Dynamic Workers는 Workers Paid 플랜에서 사용할 수 있습니다. 러너에는 아래의 바인딩과 엔트리 포인트 내보내기가 필요합니다. *-cloudflare 템플릿에는 둘 다 포함되어 있습니다.

  1. Worker Loader 바인딩을 wrangler.jsonc에 추가합니다. 러너는 LOADER라는 이름으로 읽습니다:

    {
    	"worker_loaders": [
    		{
    			"binding": "LOADER",
    		},
    	],
    }
  2. Worker 엔트리 포인트에서 PluginBridge를 내보내고, main이 해당 파일을 가리키도록 합니다. PluginBridge는 샌드박스된 플러그인이 콘텐츠, 미디어, 스토리지, 이메일에 접근하는 엔트리 포인트입니다. 러너는 엔트리 모듈의 내보내기에서 이를 찾습니다:

    import handler, { createScheduledHandler, PluginBridge } from "@emdash-cms/cloudflare/worker";
    
    export { PluginBridge };
    
    export default {
    	...handler,
    	scheduled: createScheduledHandler(),
    } satisfies ExportedHandler;
    {
    	"main": "./src/worker.ts",
    }
  3. emdash() 통합에서 러너를 선택합니다:

    import { d1, r2, sandbox } from "@emdash-cms/cloudflare";
    
    emdash({
    	database: d1({ binding: "DB" }),
    	storage: r2({ binding: "MEDIA" }),
    	sandboxRunner: sandbox(),
    });

Node.js

  1. 피어 의존성인 workerd와 함께 러너를 설치합니다:

    npm install @emdash-cms/sandbox-workerd workerd

    workerd 패키지는 선택적 의존성을 통해 현재 플랫폼(x64의 Linux, macOS, Windows; arm64의 Linux, macOS)용 바이너리를 설치합니다. 서버가 실행되는 플랫폼에서 선택적 의존성을 활성화하여 설치하세요. 멀티 스테이지 Docker 빌드에서는 런타임 스테이지와 같은 플랫폼의 스테이지에서 설치를 실행하세요.

  2. emdash() 통합에서 러너를 선택합니다:

    import { sqlite } from "emdash/db";
    
    emdash({
    	database: sqlite({ url: "file:./data/emdash.db" }),
    	sandboxRunner: "@emdash-cms/sandbox-workerd/sandbox",
    });
  3. 개발을 위해 miniflare를 개발 의존성으로 설치합니다:

    npm install -D miniflare

    NODE_ENVdevelopment(astro dev가 설정)이고 miniflare가 설치된 경우, 러너는 플러그인을 Miniflare에 전달하고, Miniflare가 자체 workerd 프로세스를 관리합니다. 아래의 크래시 정책은 적용되지 않습니다. astro previewNODE_ENVproduction으로 설정하고 node ./dist/server/entry.mjs는 설정하지 않습니다. 둘 다 workerd를 사용합니다.

workerd 프로세스 실행 방법

EmDash는 사이트 첫 번째 요청 시 초기화하는 동안 workerd를 시작하고, 샌드박스된 플러그인이 로드된 후 플러그인 서비스가 응답할 때까지 최대 10초 대기합니다. 관리자에서 플러그인을 설치하거나 업데이트하면 재시작됩니다. workerd가 stdout이나 stderr에 쓰는 모든 것은 [emdash:workerd] 접두사와 함께 서버 출력에 나타납니다.

플러그인 서비스는 127.0.0.1에서 수신하며, 서버로의 반환 채널은 Unix 도메인 소켓(Windows에서는 127.0.0.1 TCP 포트)입니다. 인바운드 포트를 열 필요가 없습니다.

자식 프로세스는 서버 환경에서 PATH, HOME, TMPDIR, TMP, TEMP, LANG, LC_ALL만 받으므로 서버 환경의 시크릿은 샌드박스에 들어가지 않습니다. 더 많은 변수를 전달하려면 EMDASH_WORKERD_PASSTHROUGH_ENV를 쉼표로 구분된 변수 이름 목록으로 설정하세요.

workerd가 예기치 않게 종료되면 러너는 [emdash:workerd] workerd exited with <reason>를 기록하고 다음 호출 시 재시작하며, 지연은 1초에서 시작하여 30초까지 두 배로 증가합니다. 60초 이내에 workerd가 5번 이상 크래시하면 러너는 재시작을 중단하고 [emdash:workerd] workerd crashed 5 times in 60 seconds, giving up을 기록합니다. 그 이후로 서버가 재시작될 때까지 모든 샌드박스된 플러그인 훅과 라우트는 Plugin sandbox unavailable for <plugin>: workerd is not running으로 실패합니다. 서버에 SIGTERM을 보내면 workerd도 함께 종료됩니다.

리소스 제한

각 러너는 플러그인 호출당 동일한 제한 세트를 적용합니다. 제한은 고정되어 있으며, emdash() 통합에는 이를 위한 옵션이 없습니다.

제한Cloudflare WorkersNode.js
CPU 시간50 msWorker Loader가 적용; 플러그인이 제한에 도달하면 throw적용되지 않음
서브리퀘스트10Worker Loader가 적용; 플러그인이 제한에 도달하면 throw적용되지 않음
메모리128 MB플러그인별로 적용되지 않음; 플랫폼의 아이솔레이트 메모리 상한이 적용적용되지 않음
월 타임30 s러너가 적용러너가 적용

훅이나 라우트가 월 타임 제한을 초과하면 호출은 Plugin <id> exceeded wall-time limit of 30000ms during hook:<name> (또는 route:<name>)으로 실패합니다. 훅의 경우, EmDash는 EmDash: Sandboxed plugin <id> 접두사로 실패를 기록하고 해당 플러그인의 결과 없이 요청을 계속합니다. 제한을 초과한 플러그인 라우트는 호출자에게 실패합니다.

러너를 사용할 수 없는 경우

구성된 러너도 사용할 수 없을 수 있습니다: Cloudflare Workers에서 worker_loaders 바인딩이나 PluginBridge 내보내기가 누락된 경우, Node.js에서 workerd가 설치되지 않았거나 바이너리가 실행되지 않는 경우입니다. EmDash는 런타임 시작 시 다음 경고를 기록합니다:

EmDash: Plugin sandbox is configured but not available on this platform. Sandboxed plugins will not be loaded. If using @emdash-cms/sandbox-workerd/sandbox, ensure workerd is installed.

sandboxed: []의 플러그인은 로드되지 않고, 설치된 마켓플레이스 및 레지스트리 플러그인은 실행되지 않으며, 관리자에서의 새 설치는 오류 코드 SANDBOX_NOT_AVAILABLE로 실패합니다. 사이트의 나머지 부분은 영향을 받지 않습니다.

샌드박스된 플러그인을 인프로세스로 실행

emdash()에서 sandbox: false를 설정하면 sandboxed: []의 플러그인과 설치된 마켓플레이스 플러그인을 격리나 제한 없이 서버 프로세스에서 실행합니다. 이것은 플러그인의 버그와 샌드박스의 버그를 구분하는 디버깅 옵션입니다. 다음 구성은 Node.js 사이트에서 샌드박스를 끕니다:

emdash({
	sandboxRunner: "@emdash-cms/sandbox-workerd/sandbox",
	sandbox: false,
});

Cloudflare Workers에서 런타임은 sandbox: false is not supported in Cloudflare Workers로 시작을 거부합니다.

문제 해결

각 항목은 서버가 기록하는 메시지 또는 관리자가 반환하는 오류 코드로 시작합니다.

”Plugin sandbox is configured but not available on this platform”

Cloudflare Workers에서 두 가지 요구사항을 확인하세요: wrangler.jsoncLOADER라는 이름의 worker_loaders 바인딩이 있고, mainPluginBridge를 내보내는 파일을 가리키는지 확인합니다. 바인딩 배포에는 Workers Paid 플랜이 필요합니다.

Node.js에서 러너가 사용하는 바이너리를 실행합니다:

npx workerd --version

명령이 실패하면 workerdnode_modules에 없거나 설치된 바이너리가 이 플랫폼에서 실행되지 않습니다. 대상 플랫폼에서 선택적 의존성을 활성화하여 다시 설치하세요.

”workerd failed to start within 10 seconds”

자식 프로세스는 시작되었지만 플러그인 서비스가 10초 이내에 응답하지 않았습니다. 이 메시지 전의 [emdash:workerd] 접두사 줄에는 구성 및 시작 오류를 포함한 workerd 자체의 출력이 포함됩니다. 러너는 다음 호출 시 재시도합니다.

”workerd crashed 5 times in 60 seconds, giving up”

러너가 workerd 재시작을 중단했습니다. 이 메시지 전의 [emdash:workerd] workerd exited with <reason> 줄은 각 크래시의 종료 코드나 시그널을 나타냅니다. 원인을 수정한 후 서버를 재시작하세요.

플러그인 설치 시 SANDBOX_NOT_AVAILABLE

러너가 누락되거나 사용할 수 없어 관리자의 설치 요청이 거부되었습니다. 플랫폼용 러너를 구성하거나 위의 시작 경고 원인을 수정하고 재배포하세요.