OCIApp is a framework for building and running dependency-sandboxed Python applications using OCI artifacts.
OCIApp has 3 phases:
- define an
Applicationwith some custom task / handler / etc. - build it as an
.ociappartifact withociapp-build - distribute the
.ociappto some external party (or yourself) - execute it with
Runtime.execute.
from ociapp import Application
from pydantic import BaseModel
class EchoRequest(BaseModel):
value: str
class EchoResponse(BaseModel):
value: str
class EchoApplication(Application[EchoRequest, EchoResponse]):
async def execute(self, request: EchoRequest) -> EchoResponse:
return EchoResponse(value=request.value)
app = EchoApplication()Declare the build entrypoint in pyproject.toml:
[tool.ociapp-build]
mode = "managed"
entrypoint = "echo_app.main:app"
system-packages = ["vim"]
# [advanced] or if you want to use a custom Containerfile (Dockerfile)
[tool.ociapp-build]
mode = "custom"
containerfile = "Containerfile.custom"Note: With a "custom" build, you're responsible for ensuring the container has proper user permissions, includes your application, and runs
ociapp serve --appon boot.
Then package it into a distributable .ociapp archive:
ociapp-build . --output-dir distfrom pathlib import Path
from ociapp_runtime import Runtime, DockerAdapter
async with (
Runtime(
engine=DockerAdapter(), # engine implementation (default DockerAdapter)
startup_timeout=10, # max time to wait for a container to start before failing (default 10s)
request_timeout=30, # max time for a execution request to complete (default 30s)
shutdown_timeout=10, # max time to wait for a container to gracefully stop before killing it (default 10s)
idle_timeout=900, # duration to keep an idle container running before stopping it (default 900s)
reaper_interval=1, # how frequently to check for and reap idle containers (default 1s)
) as runtime
):
response = await runtime.execute(
Path("dist/echo-app-0.1.0.ociapp"), {"value": "hello"}
)
print(response)
# ==> {"value": "hello"}See example/echo-app and example/runtime_demo.py for more detail.
OCIApp encompases 3 packages. The uv workspace uses ociapp-runtime as the
root package, while ociapp and ociapp-build remain under packages/.
ociapp: An SDK for defining arbitrary Python applicationsociapp-build: A standalone CLI that builds.ociappOCI archives via Docker Buildxociapp-runtime: A library for implementing OCIApp Runtimes, which handle spinning up and managing a pool of application containers to fulfill execution requests.
ociapp exposes an Application class, which expects an implemention of an single execute method. It also includes the ociapp serve command, which ociapp-build uses as OCI artifact's ENTRYPOINT; it spins up a UDS server that listens for, and responds to, execution requests by actually invoking .execute.
ociapp-runtime primarily implements the Runtime, which:
- exposes a simple
.executeinterface for running theexecuteof a provided OCI artifact with a given request payload. - a warm pool of application containers, which it manages by spinning up new ones as needed and tearing down idle ones.
- a UDS client for every active application container.
sequenceDiagram
autonumber
actor Host
participant RT as Runtime
participant C as Container
participant S as App Server
participant App as Application
Host->>RT: execute request
opt no warm container
RT->>C: start container
C->>S: boot app server
RT->>C: wait for UDS
RT->>C: open session
end
RT->>C: send request over UDS
C->>S: receive request
S->>App: validate + execute
App-->>S: return response
S-->>C: send response over UDS
C-->>RT: receive response
RT-->>Host: return result
opt idle timeout or shutdown
RT->>C: stop container
end
- Decouple
Runtimeownership fromexecuteso that the runtime can exist on a separate host- a
LocalExecutorfor when the runtime exists on the same machine - a
RemoteExecutorfor when the runtime is decoupled
- a
- Refactor
RuntimeandEngineto:- support local on-machine docker via
LocalEngine - a
K8Engineto support a node-local resource that spins up K8 containers.
- support local on-machine docker via