Métricas y listado¶
El historial de métricas de un sandbox (CPU, memoria, disco), los hechos del
guest (CPU y memoria que ve) y un listado reanudable con orden y filtros. Nada de esto necesita IAM nuevo: el historial viaja por
el mismo gRPC que el resto y el listado es list-microvms.
Instantánea e historial¶
get_metrics() es una instantánea procfs (dos lecturas de /proc/stat a
100 ms). rayd además muestrea cada 5 s mientras el sandbox corre
y guarda un anillo de 8 h (5 760 muestras); get_metrics_history(start=,
end=, max_points=) lo devuelve en orden ascendente.
from datetime import datetime, timedelta, timezone
from rayito import Sandbox
with Sandbox.create() as sbx:
now = sbx.get_metrics()
print(now.cpu_used_pct, now.mem_used_bytes, now.mem_cache_bytes, now.disk_used_bytes)
since = datetime.now(timezone.utc) - timedelta(minutes=10)
for sample in sbx.get_metrics_history(start=since, max_points=60):
print(sample.timestamp, sample.cpu_used_pct, sample.mem_used_bytes)
# Desde otro proceso: la forma de clase necesita el access token
series = Sandbox.get_metrics_history(sbx.sandbox_id, access_token=sbx.access_token)
import asyncio
from rayito import AsyncSandbox
async def main() -> None:
async with await AsyncSandbox.create() as sbx:
print((await sbx.get_metrics()).cpu_used_pct)
series = await sbx.get_metrics_history(max_points=30)
print(len(series))
again = await AsyncSandbox.get_metrics_history(
sbx.sandbox_id, access_token=sbx.access_token
)
print(len(again))
asyncio.run(main())
import { Sandbox } from "rayito";
await using sbx = await Sandbox.create();
const now = await sbx.getMetrics();
console.log(now.cpuUsedPct, now.memUsedBytes, now.memCacheBytes);
const history = await sbx.getMetricsHistory({
start: new Date(Date.now() - 600_000),
maxPoints: 60,
});
for (const sample of history) {
console.log(sample.timestamp.toISOString(), sample.cpuUsedPct);
}
const series = await Sandbox.getMetricsHistory(sbx.sandboxId, {
accessToken: sbx.accessToken,
});
console.log(series.length);
SandboxMetrics:cpu_used_pct,cpu_count,mem_used_bytes,mem_total_bytes,mem_cache_bytes,disk_used_bytes,disk_total_bytesytimestamp. En el historial,cpu_used_pctes la media desde la muestra anterior (≈ 5 s), no la ventana de 100 ms de la instantánea.startyendson inclusivos (undatetimenaive es hora local);max_pointsreduce la serie a ese número de tramos (la última muestra de cada tramo con la CPU promediada). Una respuesta completa pesa ≈ 350 KB.- Hueco mientras está suspendido: no se muestrea, así que la serie salta de la última muestra antes de la pausa a la primera tras el resume. El muestreo no toca la red y no cuenta como actividad para la política de idle.
- La forma de clase necesita el access token (
access_token=oRAYITO_ACCESS_TOKEN) y nunca despierta un sandbox suspendido. - En una imagen anterior a 0.3.0,
get_metrics_history()(TSgetMetricsHistory()), en instancia y en la forma de clase, lanzaUnimplementedError(no esSandboxException) pidiendo que publiques una imagen nueva, como las transferencias y el plazo del servidor; elUnimplementedErrorgenérico queda en__cause__(TScause), y elUNIMPLEMENTEDde gRPC un nivel más abajo, en__cause__.__cause__(TScause.cause).
Hechos del guest¶
get_health() trae cpu_count y memory_total_bytes, y get_info()
rellena agent_version, cpu_count y memory_mb: son la vista del
guest, que puede no coincidir con el tamaño de la imagen (el guest informó
8016 MiB y 4 CPU con una imagen de 2048 MiB). En TypeScript: agentVersion, cpuCount, memoryMb y
memoryTotalBytes.
Listado reanudable¶
Sandbox.list() recorre list-microvms (páginas de 50) de forma perezosa.
Sandbox.paginate() da un paginador reanudable: next_items(),
has_next y next_token, un cursor opaco (lleva el nextToken de AWS
y el filtro) que puedes guardar y pasar a otro proceso.
from datetime import datetime, timedelta, timezone
from rayito import Sandbox
since = datetime.now(timezone.utc) - timedelta(hours=1)
pages = Sandbox.paginate(limit=20, order="desc", started_after=since, states=["RUNNING"])
first = pages.next_items()
for item in first:
print(item.sandbox_id, item.state, item.started_at, item.template_version)
if pages.has_next:
cursor = pages.next_token # guárdalo donde quieras
more = Sandbox.paginate(limit=20, order="desc", started_after=since,
states=["RUNNING"], next_token=cursor).next_items()
for item in Sandbox.list(template="rayito-base", order="asc"):
print(item.sandbox_id)
import { Sandbox } from "rayito";
const pages = Sandbox.paginate({ limit: 20, order: "desc", states: ["RUNNING"] });
const first = await pages.nextItems();
console.log(first.map((item) => item.sandboxId));
if (pages.hasNext) {
const cursor = pages.nextToken;
const more = await Sandbox.paginate({
limit: 20,
order: "desc",
states: ["RUNNING"],
nextToken: cursor,
}).nextItems();
console.log(more.length);
}
for await (const item of Sandbox.list({ template: "rayito-base" })) {
console.log(item.sandboxId, item.state);
}
| Filtro (Python / TS) | Dónde se aplica | Nota |
|---|---|---|
template, template_version / templateVersion |
servidor (list-microvms) |
nombre o ARN de la imagen |
states |
cliente | por defecto todo menos TERMINATING/TERMINATED |
started_after / startedAfter |
cliente | incluido |
order="asc" o "desc" |
cliente, por startedAt |
AWS no ordena: el primer item llega tras recorrer todas las páginas (O(páginas)); un token reanudado salta por identidad los items ya entregados |
metadata |
cliente, O(n) | una sonda Health por sandbox RUNNING (≈ 0,5-1 s cada una, cuenta como tráfico para su idle); nunca sondea un suspendido. Con index= (opcional, abajo): sin sondas y sobre cualquier estado no terminal |
index (opcional) |
tu tabla DynamoDB | DynamoDbIndex(...): un BatchGetItem por página; ver Índice de metadatos |
limit (sólo paginate) |
cliente | items por next_items(); todos si falta |
Un next_token sólo vale con los mismos filtros con los que se emitió; uno
manipulado o de otro filtro es InvalidArgumentException.
Listado por metadatos con índice (opcional)¶
Sin índice, list(metadata=...) sólo ve sandboxes RUNNING: los metadatos
viven en el agente y sondear uno suspendido lo despertaría. El índice de
metadatos (opcional, apagado por defecto, con coste de DynamoDB) guarda una
copia de los metadata de cada sandbox en una tabla de tu cuenta, así
puedes filtrar también los SUSPENDED sin sondear ni despertar ninguno.
from rayito import DynamoDbIndex, Sandbox
idx = DynamoDbIndex("rayito-sandboxes")
paused = list(Sandbox.list(metadata={"user": "42"}, states=["SUSPENDED"], index=idx))
Guía completa, reglas de la unión, coste e IAM: Índice de metadatos.
Desde la CLI¶
rayito sandbox list --template rayito-base
rayito sandbox list --metadata user=42 --state suspended --index-table rayito-sandboxes
rayito sandbox metrics microvm-<id> --token-file ~/.rayito/<id>.token
rayito --json sandbox metrics microvm-<id> --follow --interval 5 --token-file ~/.rayito/<id>.token
metrics imprime la instantánea de get_metrics() (CLI); conectar
despierta un sandbox suspendido.
Trazas OpenTelemetry del SDK (opcional)¶
Spans del lado del SDK (rayito.sandbox.create, rayito.commands.run,
rayito.code.run, rayito.files.*…) sobre las llamadas que ya haces,
exportados a donde configures tu TracerProvider. Apagado por defecto: sin
tracer_provider= / tracerProvider no se importa OpenTelemetry. No es
telemetría del sandbox (para eso está el historial de
métricas).
Guía completa, nombres de span, atributos y lo que nunca se registra: OpenTelemetry.
Enviar desde el sandbox las métricas del historial (CPU, memoria y
disco) a tu colector OTLP es otra función opcional, también apagada por
defecto: create(telemetry=TelemetryExport(...)), y
sbx.get_telemetry_status() (TypeScript: getTelemetryStatus()) cuenta lo
exportado y lo descartado. Ver
Exportación OTLP.
En el shim de E2B¶
sbx.get_metrics(start, end) devuelve el historial como
list[SandboxMetrics] en el formato de E2B (con mem_cache);
Sandbox.get_metrics(sandbox_id, access_token=...) es la forma de clase, y
Sandbox.list(query=SandboxQuery(state=..., started_after=..., template=...,
metadata=...), limit=, next_token=, order=) devuelve un SandboxPaginator
(Compatibilidad con E2B). Con la extensión index= (o
E2B(index=...)), query.metadata también filtra state=[PAUSED]
(Compatibilidad con E2B).