Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions builder/node.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ ARG AKERNEL_ENABLE_RUNC=false
ARG AKERNEL_ENABLE_FIRECRACKER=true
ARG SANDBOXD_BUILD_IMAGE=golang:1.25.5-bookworm
ARG DISTILL_FS_BUILD_IMAGE=rust:1.85.0-bookworm
ARG OPEN_YR_VERSION=0.10.1rc4
ARG OPEN_YR_VERSION=0.10.2rc2
ARG OPEN_YR_CORE_WHEEL_URL=
ARG OPEN_YR_CORE_WHEEL_SHA256=
ARG OPEN_YR_RELEASE_BASE_URL=https://openyuanrong.obs.cn-southwest-2.myhuaweicloud.com/release
ARG OPEN_YR_CORE_AMD64_SHA256=65c1f27e7e700a253a2e907dea0273e85f1c76610e48c93544caa6bcc07ac3af
ARG OPEN_YR_CORE_ARM64_SHA256=29d25c3388c2913346035ee8df9b8159e702de7a8e6f783e3e218ab773896333
ARG OPEN_YR_CORE_AMD64_SHA256=8cdefba9a415a7a35b6f39bf847e7fb933ad6ca55b27b7dec2f377ece47198c4
ARG OPEN_YR_CORE_ARM64_SHA256=2e9d2d18922b87721fcc3e92fa959cdd2ce026a51b18512c7c71ab24ac6a6eaa
ARG GVISOR_DOWNLOAD_IMAGE=ubuntu:24.04
ARG GVISOR_RELEASE
ARG GVISOR_AMD64_URL
Expand Down Expand Up @@ -420,5 +420,6 @@ LABEL org.opencontainers.image.version="${AKERNEL_VERSION}" \
org.akernel.firecracker.release="${FIRECRACKER_RELEASE}" \
org.akernel.firecracker.enabled="${AKERNEL_ENABLE_FIRECRACKER}"

ENV YR_LOG_PATH=${YR_INSTALLATION_DIR}/logs
ENV YR_LOG_PATH=${YR_INSTALLATION_DIR}/logs \
YR_IMAGE_PROCESS_CONFIG=/run/akernel/yr-image-process.json
STOPSIGNAL SIGRTMIN+3
4 changes: 2 additions & 2 deletions builder/runtime.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@ ARG PYTHON_311_VERSION=3.11.13
ARG PYTHON_312_VERSION=3.12.11
ARG PYTHON_313_VERSION=3.13.5
ARG PYTHON_314_VERSION=3.14.6
ARG OPEN_YR_VERSION=0.10.1rc4
ARG OPEN_YR_VERSION=0.10.2rc2
ARG OPEN_YR_LEGACY_SDK_VERSION=0.9.9

FROM ${AKERNEL_RUNTIME_BASE_IMAGE} AS rrt-download

ARG OPEN_YR_VERSION
ARG RRT_RUNTIME_URL=https://openyuanrong.obs.cn-southwest-2.myhuaweicloud.com/release/${OPEN_YR_VERSION}/linux/amd64/rrt-runtime-amd64
ARG RRT_RUNTIME_SHA256=7c2064531e91fba8b9bbe96ac9e706cd496a198551657ee5461a078a2f7ea9ea
ARG RRT_RUNTIME_SHA256=d67efcaaf07a5ca57079706ad1d5006d57e796baedf727db9392a877b34e365d

RUN apt-get update && \
apt-get install -y --no-install-recommends ca-certificates curl && \
Expand Down
2 changes: 2 additions & 0 deletions builder/scripts/master_entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ BASE_DIR=$(
export DEPLOY_PATH="/home/yuanrong/master/"
mkdir -p "$DEPLOY_PATH"
export YR_LOG_PATH="$DEPLOY_PATH/log"
export YR_IMAGE_PROCESS_CONFIG="${YR_IMAGE_PROCESS_CONFIG:-/run/akernel/yr-image-process.json}"

# If ConfigMap-mounted config exists, symlink it to override the baked-in default
[ -f /etc/otel-collector/otel_config.yaml ] && ln -sf /etc/otel-collector/otel_config.yaml /home/yuanrong/otel_config.yaml
Expand Down Expand Up @@ -96,6 +97,7 @@ exec "${YR_BIN}" start --master --block true \
--trace_config "$(cat /home/yuanrong/trace/trace_config.json)" \
--ds_rpc_thread_num 128 \
--function_proxy_merge_process_enable true \
--force_low_reliability_instance true \
--enable_traefik_provider=${ENABLE_TRAEFIK_PROVIDER} \
--traefik_http_entry_point=${TRAEFIK_HTTP_ENTRYPOINT:-websecure} \
--traefik_enable_tls=${TRAEFIK_ENABLE_TLS:-false} \
Expand Down
1 change: 1 addition & 0 deletions builder/scripts/yr_node_bootstrap.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
# SPDX-License-Identifier: Apache-2.0
ulimit -n 32768
export YR_RUNTIME_BACKEND=sandboxd
export YR_IMAGE_PROCESS_CONFIG="${YR_IMAGE_PROCESS_CONFIG:-/run/akernel/yr-image-process.json}"

resolve_node_ip() {
local default_device
Expand Down
2 changes: 1 addition & 1 deletion builder/systemd_services/yuanrong.service
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Description=yuanrong.service
[Service]
#Type=simple
PIDFile=/run/yuanrong.pid
PassEnvironment=ETCD_PORT ETCD_PEER_PORT ETCD_ADDRESS HOSTNAME AKS_LOCAL_MODE AKERNEL_NODE_IP INSTANCE_IP LITEBUS_DATA_KEY YR_LOG_PATH YR_INSTALLATION_DIR YR_RRT_CONTROL_SOCKET_PATH ENABLE_METRICS ENABLE_TRACE TRAEFIK_MODE TRAEFIK_ENABLE_TLS TRAEFIK_HTTP_ENTRYPOINT
PassEnvironment=ETCD_PORT ETCD_PEER_PORT ETCD_ADDRESS HOSTNAME AKS_LOCAL_MODE AKERNEL_NODE_IP INSTANCE_IP LITEBUS_DATA_KEY YR_LOG_PATH YR_INSTALLATION_DIR YR_RRT_CONTROL_SOCKET_PATH YR_IMAGE_PROCESS_CONFIG ENABLE_METRICS ENABLE_TRACE TRAEFIK_MODE TRAEFIK_ENABLE_TLS TRAEFIK_HTTP_ENTRYPOINT
Environment="CONTAINER_EP=unix:///run/sandboxd/sandboxd.sock"
Environment="RUNTIME_HOME_DIR=/home/yuanrong/runtime"
Environment="YR_NOSET_CUDA_VISIBLE_DEVICES=1"
Expand Down
2 changes: 2 additions & 0 deletions deploy/akernel/charts/core/templates/node/daemonset.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ spec:
value: "1"
- name: YR_RRT_CONTROL_SOCKET_PATH
value: "/run/akernel"
- name: YR_IMAGE_PROCESS_CONFIG
value: {{ .Values.yuanrong.imageProcessConfig | default "/run/akernel/yr-image-process.json" | quote }}
- name: ETCD_ADDRESS
value: {{ get $nodeEtcd "host" | default (printf "akernel-etcd.%s.svc.cluster.local" .Release.Namespace) | quote }}
- name: ETCD_PORT
Expand Down
3 changes: 3 additions & 0 deletions deploy/akernel/charts/core/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ image:
tag: "latest"
pullPolicy: IfNotPresent

yuanrong:
imageProcessConfig: "/run/akernel/yr-image-process.json"

auth:
# Existing Secret that contains the JWT signing seed. For
# `helm template | kubectl apply`, prefer pre-creating this Secret with
Expand Down
2 changes: 2 additions & 0 deletions deploy/standalone/start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ TOKEN_FILE="${DATA_DIR}/token"
SANDBOXD_CONFIG_FILE="${DATA_DIR}/sandboxd/config.toml"
AKERNEL_NAT_BACKEND="${AKERNEL_NAT_BACKEND:-iptables}"
AKERNEL_ENABLE_RUNC="${AKERNEL_ENABLE_RUNC:-false}"
YR_IMAGE_PROCESS_CONFIG="${YR_IMAGE_PROCESS_CONFIG:-/run/akernel/yr-image-process.json}"
LITEBUS_DATA_KEY=""

# Container runtime command (docker or pouch)
Expand Down Expand Up @@ -362,6 +363,7 @@ start_node_container() {
--restart always \
-e AKS_LOCAL_MODE="true" \
-e YR_RRT_CONTROL_SOCKET_PATH="/run/akernel" \
-e YR_IMAGE_PROCESS_CONFIG="${YR_IMAGE_PROCESS_CONFIG}" \
-e TRAEFIK_MODE="http" \
-e TRAEFIK_HTTP_ENTRYPOINT="web" \
-e TRAEFIK_ENABLE_TLS="false" \
Expand Down
9 changes: 9 additions & 0 deletions sdk/python/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,15 @@ with Sandbox(image="ubuntu:24.04") as sandbox:
print(sandbox.commands.run("cat /etc/os-release").stdout)
```

To start the effective OCI image `ENTRYPOINT` and `CMD` as the managed sandbox
workload, enable entrypoint inheritance and wait for its exit status:

```python
with Sandbox(image="example/worker:latest", inherit_entrypoint=True) as sandbox:
exit_code = sandbox.wait_entrypoint()
print(exit_code, sandbox.entrypoint_exit_info)
```

Or use an object in S3-compatible storage as the rootfs:

```python
Expand Down
7 changes: 7 additions & 0 deletions sdk/python/akernel_sdk/_backends/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ class SandboxSpec:
reverse_tunnel: HttpReverseTunnel | None
detached: bool
failover: bool
inherit_entrypoint: bool
node_id: str | None
xpu: str | None
storage_mb: int | None
Expand Down Expand Up @@ -151,6 +152,12 @@ def is_running(self) -> bool: ...
def get_info(self) -> SandboxInfo: ...

def reload(self) -> bool: ...

def wait_entrypoint(self) -> int: ...

@property
def entrypoint_exit_info(self) -> Mapping[str, object] | None: ...

def update_network_policy(self, policy: NetworkPolicy | None) -> None: ...

def terminate(self) -> None: ...
Expand Down
41 changes: 41 additions & 0 deletions sdk/python/akernel_sdk/_backends/openyuanrong_sandbox.py
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,36 @@ def reload(self) -> bool:
except Exception:
return False

def wait_entrypoint(self) -> int:
if not self._spec.inherit_entrypoint:
raise RuntimeError("inherit_entrypoint was not enabled for this sandbox")
wait = getattr(self._sandbox, "wait_entrypoint", None)
if not callable(wait):
raise UnsupportedBackendFeatureError(
"The installed openyuanrong-sandbox backend does not support "
"waiting for an inherited image entrypoint. Upgrade it to "
"0.10.2rc1 or newer."
)
try:
return int(wait())
except Exception as error:
raise _convert_error(
"wait for inherited image entrypoint", error
) from error

@property
def entrypoint_exit_info(self) -> Mapping[str, object] | None:
if not self._spec.inherit_entrypoint:
return None
value = getattr(self._sandbox, "entrypoint_exit_info", None)
if value is None:
return None
if not isinstance(value, Mapping):
raise BackendOperationError(
"inherited image entrypoint returned invalid exit information"
)
return dict(value)

def update_network_policy(self, policy: NetworkPolicy | None) -> None:
if self._terminated or self._closed:
raise BackendOperationError(
Expand Down Expand Up @@ -414,12 +444,21 @@ def _validate(self, spec: SandboxSpec) -> None:
def create(self, spec: SandboxSpec) -> BackendSession:
self._validate(spec)
supports_failover = _supports_keyword(yr_sandbox.Sandbox, "failover")
supports_inherit_entrypoint = _supports_keyword(
yr_sandbox.Sandbox, "inherit_entrypoint"
)
if spec.failover and not supports_failover:
raise UnsupportedBackendFeatureError(
"The installed openyuanrong-sandbox backend does not support "
"automatic sandbox failover. Upgrade it to a version with "
"failover support."
)
if spec.inherit_entrypoint and not supports_inherit_entrypoint:
raise UnsupportedBackendFeatureError(
"The installed openyuanrong-sandbox backend does not support "
"inheriting image ENTRYPOINT and CMD. Upgrade it to 0.10.2rc1 "
"or newer."
)
rootfs = None
if spec.rootfs is not None:
rootfs = yr_sandbox.S3Config(
Expand Down Expand Up @@ -492,6 +531,8 @@ def create(self, spec: SandboxSpec) -> BackendSession:
)
if supports_failover:
create_args["failover"] = spec.failover
if supports_inherit_entrypoint:
create_args["inherit_entrypoint"] = spec.inherit_entrypoint
try:
sandbox = yr_sandbox.Sandbox(**create_args)
except Exception as error:
Expand Down
15 changes: 15 additions & 0 deletions sdk/python/akernel_sdk/_backends/openyuanrong_sdk.py
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,16 @@ def reload(self) -> bool:
"Use the default 'openyuanrong-sandbox' backend."
)

def wait_entrypoint(self) -> int:
raise UnsupportedBackendFeatureError(
"Backend 'openyuanrong-sdk' does not support inheriting image "
"ENTRYPOINT and CMD. Use the default 'openyuanrong-sandbox' backend."
)

@property
def entrypoint_exit_info(self) -> Mapping[str, object] | None:
return None

def update_network_policy(self, policy: NetworkPolicy | None) -> None:
raise UnsupportedBackendFeatureError(
"Backend 'openyuanrong-sdk' does not support dynamic network policy "
Expand Down Expand Up @@ -289,6 +299,11 @@ def __init__(self, _config: BackendConfig) -> None:
_impl.ensure_initialized()

def create(self, spec: SandboxSpec) -> BackendSession:
if spec.inherit_entrypoint:
raise UnsupportedBackendFeatureError(
"Backend 'openyuanrong-sdk' does not support inheriting image "
"ENTRYPOINT and CMD. Use the default 'openyuanrong-sandbox' backend."
)
if spec.failover:
raise UnsupportedBackendFeatureError(
"Backend 'openyuanrong-sdk' does not support automatic sandbox "
Expand Down
26 changes: 26 additions & 0 deletions sdk/python/akernel_sdk/sandbox.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ def __init__(
node_id: str | None = None,
*,
failover: bool = False,
inherit_entrypoint: bool = False,
xpu: str | None = None,
storage_mb: int | None = None,
network_policy: NetworkPolicy | None = None,
Expand Down Expand Up @@ -223,6 +224,8 @@ def __init__(
failover: Restore the same logical sandbox on its original node
from the latest local anonymous checkpoint after failure.
Use :meth:`reload` to request the same rollback explicitly.
inherit_entrypoint: Start the OCI image's effective ENTRYPOINT and
CMD as the sandbox workload. Valid only with ``image``.
Comment thread
tianyuzhou95 marked this conversation as resolved.
xpu: Experimental whole-device accelerator request in
``type:model:count`` format. Currently only exact-model NVIDIA
GPU requests are supported. The backend validates runtime
Expand Down Expand Up @@ -301,6 +304,10 @@ def __init__(
raise TypeError("detached must be a boolean")
if not isinstance(failover, bool):
raise TypeError("failover must be a boolean")
if not isinstance(inherit_entrypoint, bool):
raise TypeError("inherit_entrypoint must be a boolean")
if inherit_entrypoint and image is None:
raise ValueError("inherit_entrypoint requires an image")
if node_id is not None:
if not isinstance(node_id, str):
raise TypeError("node_id must be a string")
Expand Down Expand Up @@ -345,6 +352,7 @@ def __init__(
self._memory = memory
self._xpu = normalized_xpu
self._storage_mb = storage_mb
self._inherit_entrypoint = inherit_entrypoint
self._id = ""

spec = SandboxSpec(
Expand All @@ -365,6 +373,7 @@ def __init__(
reverse_tunnel=reverse_tunnel,
detached=detached,
failover=failover,
inherit_entrypoint=inherit_entrypoint,
node_id=node_id,
xpu=normalized_xpu,
storage_mb=storage_mb,
Expand Down Expand Up @@ -467,6 +476,23 @@ def reload(self) -> bool:
return False
return self._session.reload()

def wait_entrypoint(self) -> int:
"""Wait for the inherited OCI image process and return its exit code."""
Comment thread
tianyuzhou95 marked this conversation as resolved.
Outdated

if not self._inherit_entrypoint:
raise RuntimeError("inherit_entrypoint was not enabled for this sandbox")
if self._closed or self._session is None:
raise RuntimeError("sandbox is closed")
return self._session.wait_entrypoint()

@property
def entrypoint_exit_info(self) -> Mapping[str, object] | None:
"""Structured exit details cached after :meth:`wait_entrypoint`."""

if not self._inherit_entrypoint or self._session is None:
return None
return self._session.entrypoint_exit_info

def update_network_policy(self, policy: NetworkPolicy | None) -> None:
"""Atomically replace the complete network policy of this sandbox.

Expand Down
2 changes: 1 addition & 1 deletion sdk/python/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ classifiers = [
]
dependencies = [
"idna>=3,<4",
"openyuanrong-sandbox==0.10.1rc4",
"openyuanrong-sandbox==0.10.2rc2",
"websockets>=10.0",
"dockerfile-parse>=2.0.1",
]
Expand Down
Loading