Sandboxesクイックスタート

Sandboxes クイックスタート

このクイックスタートでは、Lizard SDK で sandbox を作成し、その中で Python を実行し、ファイルを書き込み、ポートを公開して、後片付けを行います。各ステップには TypeScript と Python のコードがあります。Pro またはトライアル中の Lizard (lizard.build) アカウント、API キー、プロジェクトが必要です。まず Sandboxes とは何かを知りたい場合は、概要 を読んでください。

インストールと認証

Lizard SDK をインストールし、Settings → API keys で、または lizard keys create <name> で API キーを作成します。LIZARD_API_KEY に保存してください。すべての sandbox はプロジェクトに属します。例ではその ID を projectId(Python では project_id)として渡しています。project には slug や名前も指定できます。

npm install @lizard-build/sdk
pip install lizard-sdk
export LIZARD_API_KEY="<your-api-key>"

コマンドの実行

Sandbox.create はテンプレートから sandbox を起動します。ここでは、Python と一般的なデータ処理ライブラリを含む interpreter を使っています。timeoutMs は有効期間を設定します。SDK の既定は 5 分です。

import { Sandbox } from '@lizard-build/sdk';
 
const sandbox = await Sandbox.create('interpreter', {
  projectId: 'proj_123', timeoutMs: 300_000,
});
try {
  const result = await sandbox.process.exec("python -c 'print(2 ** 10)'");
  console.log(result.stdout, result.stderr, result.exitCode);
} finally {
  await sandbox.kill();
}
from lizard import Sandbox
 
sandbox = Sandbox.create("interpreter", project_id="proj_123", timeout_ms=300_000)
try:
    result = sandbox.process.exec_("python -c 'print(2 ** 10)'")
    print(result.stdout, result.stderr, result.exit_code)
finally:
    sandbox.kill()

0 以外の終了コードはエラーではなく結果です: exitCode(Python では exit_code)を自分で確認してください。

ファイル

テキストまたはバイトは fs.write で書き込み、テキストは fs.read で、バイトは fs.readBytes(Python では read_bytes)で読み取ります。ファイルは sandbox が存在する間だけ残ります。ただし、/workspace の Persistent Volumes に書き込んだファイルは例外です。

await sandbox.fs.write("/tmp/task.py", "print(42)\n");
const result = await sandbox.process.exec("python /tmp/task.py");
console.log(result.stdout);
sandbox.fs.write("/tmp/task.py", "print(42)\n")
result = sandbox.process.exec_("python /tmp/task.py")
print(result.stdout)

公開ポート

0.0.0.0 で待ち受けるサーバーを起動し、exposePort(port)(Python では expose_port)を呼び出します。url、hostname、accessToken が返されます。ポートは非公開です。ブラウザで url を開く(?lizard_token= が付いています)か、X-Lizard-Access-Token ヘッダーでトークンを送信してください。トークンのないリクエストには 401 が返されます。sandbox を停止するとポートは応答しなくなります。エージェントが作った 開発サーバーについては、プレビュー URL を参照してください。

await sandbox.process.exec(
  "nohup python -m http.server 3000 --bind 0.0.0.0 --directory /tmp >/tmp/http.log 2>&1 </dev/null &",
);
const { url, hostname, accessToken } = await sandbox.exposePort(3000);
console.log(url); // open in a browser
const res = await fetch(`https://${hostname}/`, {
  headers: { 'X-Lizard-Access-Token': accessToken },
});
sandbox.process.exec_(
    "nohup python -m http.server 3000 --bind 0.0.0.0 --directory /tmp >/tmp/http.log 2>&1 </dev/null &"
)
exposed = sandbox.expose_port(3000)
print(exposed.url)  # open in a browser

実行中の sandbox への再接続

sandbox ID(sandbox.sandboxId、Python では sandbox.sandbox_id)を保持しておき、別のプロセスや後のリクエストから Sandbox.connect(id) を呼び出すと、同じ sandbox のハンドルを取得できます。connect は sandbox が存在するかどうかを確認するだけです: 一時停止中の sandbox を再開せず、sandbox が停止するか期限切れになった後は NotFoundError を投げます。

const connected = await Sandbox.connect(sandbox.sandboxId);
console.log(await connected.getInfo());
connected = Sandbox.connect(sandbox.sandbox_id)
print(connected.get_info())

一時停止、再開、スナップショット

pause() は sandbox のメモリと実行中のプロセスを保存し、/workspace をそのノードに残します。一時停止が完了すると計算料金は止まりますが、Persistent Volumes のストレージ料金は引き続き発生します。resume() は同じ ID で sandbox を復元し、計算料金を再開します。どちらも処理をキューに入れてすぐに戻るため、成功を確認するには paused または running を待ってください。

await sandbox.pause();
await sandbox.waitForStatus('paused');
await sandbox.resume();
await sandbox.waitForStatus('running');
  • 一時停止すると、開いているネットワーク接続は閉じられます。再開後、sandbox 内のプログラムは再接続する必要があります。
  • プロセスを保存できない場合、sandbox は実行を続け、waitForStatus が理由付きのエラーを投げます。再開に失敗した場合、sandbox はチェックポイントを保持したまま一時停止状態に残ります。resume() を再度呼び出してください。
  • 一時停止中は有効期間の期限が止まり、再開後に続きから進みます。一時停止中の sandbox は実行数の上限に数えられません。
  • 一時停止中の sandbox は同じノードで再開します。そのノードが故障すると一時停止した状態は失われます。必要なファイルはエクスポートしてください。

スナップショットは、実行中の sandbox のメモリと /workspace のファイルを、プロジェクト専用のテンプレートとして保存します。restore するたびに、スナップショットと同じリージョンで新しい sandbox が起動します。取得前にクライアントとターミナルを閉じ、/workspace への書き込みを止めてください。接続中の Persistent Volumes は保存されません。

const saved = await sandbox.snapshot('my-app', { poolSize: 5 });
await Sandbox.waitForSnapshot(saved.id);
const copy = await Sandbox.restore(saved.id);

Firecracker の sandbox のスナップショットは、呼び出しが戻った時点で準備が完了し、ディスクから restore されるため、poolSize は適用されません。restore のたびに元の sandbox と同じサイズで実行され、そのサイズの時間単価で課金されます。pod の sandbox のスナップショットは待機中のコピーを保持します (既定で 5 つ、1〜10 で変更可能)。待機中のコピーがないときは、プールが補充されるまで restore が HTTP 503 を返すことがあり、復元した sandbox は実測使用量で課金されます。どちらの場合も、restore には size は適用されません。CLI では lizard sandbox pause <id>、lizard sandbox resume <id>、lizard sandbox snapshot <id> --name my-app、lizard sandbox restore <snapshot-id> を使います。

後片付けと次のステップ

タスクが終わったら、例の finally のように kill() を呼び出します。Python では、with Sandbox.create(...) as sandbox: を使うと、ブロックを抜けるときに sandbox が停止します。停止し忘れた sandbox は期限に達すると停止します。別の有効期間を設定しない限り、作成から 5 分後です。リソースと有効期間 を参照してください。

更新日