SandboxesБыстрый старт

Быстрый старт Sandboxes

В этом руководстве вы создадите sandbox с помощью Lizard SDK, запустите в нём Python, запишете файл, откроете порт и освободите ресурсы. У каждого шага есть код на TypeScript и Python. Вам нужны аккаунт Lizard (lizard.build) на Pro или в пробном периоде, API-ключ и проект. Чтобы сначала узнать, что такое Sandboxes, прочитайте обзор.

Установка и вход

Установите Lizard SDK и создайте API-ключ в Settings → API keys или командой lizard keys create <name>. Сохраните его в 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 из шаблона, здесь это interpreter с Python и основными библиотеками для работы с данными. timeoutMs задаёт срок жизни; в SDK по умолчанию это пять минут.

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()

Ненулевой код завершения возвращается в результате, а не вызывает исключение: проверяйте exitCode (в Python: exit_code) сами.

Файлы

fs.write записывает текст или байты, fs.read читает текст, а fs.readBytes (в Python: read_bytes) читает байты. Файлы доступны, только пока работает sandbox, если не записать их на том Persistent Volumes в /workspace.

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 порт больше не обслуживается. Про dev-сервер, который собрал агент, см. 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

Сохраните ID sandbox (sandbox.sandboxId, в Python: sandbox.sandbox_id) и вызовите Sandbox.connect(id) из другого процесса или в более позднем запросе, чтобы получить объект для того же sandbox. connect только проверяет, что sandbox существует: он не возобновляет приостановленный sandbox и выбрасывает NotFoundError, если sandbox уже остановлен через kill или его срок истёк.

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() восстанавливает sandbox с тем же ID и возобновляет оплату вычислений. Оба вызова ставят задачу в очередь и сразу возвращают ответ; дождитесь статуса paused или running, чтобы подтвердить успех.

await sandbox.pause();
await sandbox.waitForStatus('paused');
await sandbox.resume();
await sandbox.waitForStatus('running');
  • Пауза закрывает открытые сетевые соединения. После возобновления программы в sandbox должны подключиться заново.
  • Если процессы не удаётся сохранить, sandbox продолжает работать, а waitForStatus выбрасывает ошибку с причиной. Если возобновление не удалось, sandbox остаётся на паузе с сохранённым состоянием; вызовите resume() ещё раз.
  • Пока sandbox на паузе, срок жизни не расходуется; после возобновления отсчёт продолжается. Приостановленный sandbox не учитывается в лимите запущенных.
  • Приостановленный sandbox возобновляется на том же узле. Если узел выйдет из строя, сохранённое состояние пропадёт; выгрузите нужные файлы заранее.

Снимок (snapshot) сохраняет память и файлы /workspace работающего sandbox как приватный шаблон проекта. Каждый 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);

Снимок sandbox на Firecracker готов сразу после возврата вызова и восстанавливается с диска, поэтому poolSize к нему не применяется. Каждый restore запускается с размером источника и оплачивается по почасовой цене этого размера. Снимок sandbox, работающего как pod, держит готовые копии, по умолчанию пять (от одной до десяти); если готовых копий нет, restore может вернуть HTTP 503, пока пул не пополнится, а восстановленный sandbox оплачивается по измеренному расходу. В обоих случаях size к restore не применяется. В CLI используйте lizard sandbox pause <id>, lizard sandbox resume <id>, lizard sandbox snapshot <id> --name my-app и lizard sandbox restore <snapshot-id>.

Очистка и дальнейшие шаги

Вызывайте kill(), когда задача завершена, как это делают примеры в блоке finally. В Python with Sandbox.create(...) as sandbox: останавливает sandbox при выходе из блока. Если вы не остановите sandbox, он завершит работу через пять минут после создания либо по истечении заданного вами срока; см. ресурсы и сроки жизни.

Обновлено