From 0edefab171d3520e01fdfa878b646ce364f022eb Mon Sep 17 00:00:00 2001 From: Elijah Roussos Date: Tue, 7 Jan 2025 00:04:30 -0500 Subject: [PATCH] feat: rust dockerfile example Prettified Code! fix axum rework move custom runtimes to Containers --- .../container-images/custom-dockerfiles.mdx | 136 ++++++++++++++++++ .../custom-web-servers.mdx | 6 +- mint.json | 42 ++++-- 3 files changed, 170 insertions(+), 14 deletions(-) create mode 100644 cerebrium/container-images/custom-dockerfiles.mdx rename cerebrium/{endpoints => container-images}/custom-web-servers.mdx (91%) diff --git a/cerebrium/container-images/custom-dockerfiles.mdx b/cerebrium/container-images/custom-dockerfiles.mdx new file mode 100644 index 0000000..b01e472 --- /dev/null +++ b/cerebrium/container-images/custom-dockerfiles.mdx @@ -0,0 +1,136 @@ +--- +title: "Containerized Runtimes with Custom Dockerfiles" +description: "Run generic containerized applications on Cerebrium with your own Dockerfile" +--- + +For even greater flexibility, you are able to bring your own, already functional containerized applications to Cerebrium! You can build anything from standard Python to +compiled Rust, as long as you supply a `Dockerfile` to build your Cerebrium app with. + +## Building Dockerized Python applications + +Suppose we have a simple FastAPI server we have already dockerized. + +```python +from fastapi import FastAPI + +app = FastAPI() + +@app.post("/hello") +def hello(): + return {"message": "Hello Cerebrium!"} + +@app.get("/health") +def health(): + return "Ok" +``` + +Since this application is dockerized, we have a simple, previously defined `Dockerfile`: + +```dockerfile +FROM python:3.12-bookworm +COPY . . +RUN pip install uvicorn fastapi +EXPOSE 5000 +CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "5000"] +``` + +This is just a regular Dockerfile. +However, it does include a `CMD` clause so that the container can be run. +We can leverage this Dockerfile in Cerebrium by using it directly to build this application. + +Configure this application in `cerebrium.toml` by adding a custom runtime section with a `dockerfile_path` parameter: + +```toml +[cerebrium.runtime.custom] +port = 5000 +healthcheck_endpoint = "/health" +dockerfile_path = "./Dockerfile" +``` + +The configuration requires three key parameters: + +- `port`: The port your server listens on +- `healthcheck_endpoint`: The endpoint that confirms server health. If empty, will default to a TCP ping of your specified port +- `dockerfile_path`: The relative path of the project Dockerfile used to build the application + +If your Dockerfile does not contain a `CMD` clause, you should also specify `entrypoint` in your `cerebrium.toml`: + +```toml +[cerebrium.runtime.custom] +entrypoint = ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "5000"] +... +``` + + + If a dockerfile path is specified, you must take care to install all the + dependencies your app needs in the Dockerfile, as well as run any commands. + All dependencies specified under `cerebrium.dependencies.*` will be ignored, + and `cerebrium.deployment.shell_commands` & + `cerebrium.deployment.pre_build_commands` will not be run. + + +## Building Generic Dockerized applications + +So long as you supply a Dockerfile, you are not limited to just Python! +Here's an equivalent Rust based API server in the Axum Framework to demonstrate how this works: + +```rust +use axum::{ + routing::{get, post}, + Json, Router, +}; +use serde_json::json; +use std::net::SocketAddr; +use tracing::Level; + +async fn hello() -> Json { + Json(json!({ "message": "Hello Cerebrium!" })) +} + +async fn health() -> &'static str { + "Ok" +} + +#[tokio::main] +async fn main() { + let app = Router::new() + .route("/hello", post(hello)) + .route("/health", get(health)); + let addr = SocketAddr::from(([127, 0, 0, 1], 5000)); + tracing::info!("Listening on {}", addr); + axum::Server::bind(&addr.parse().unwrap()) + .serve(app.into_make_service()) + .await + .unwrap(); +} +``` + +In this case, we use a multi-stage Dockerfile: + +```dockerfile +# Build Stage +FROM rust:bookworm as builder +WORKDIR /usr/src/app +COPY Cargo.toml Cargo.lock ./ +RUN cargo fetch + +# Copy the rest of the application source code and Build +COPY . . +RUN cargo build --release + + +# Runtime Stage +FROM scratch +COPY --from=builder /usr/src/app/target/release/axum_server /app +EXPOSE 5000 +CMD ["/app"] +``` + +Configure this application in `cerebrium.toml` similarly to the FastAPI webserver and we are done! + +```toml +[cerebrium.runtime.custom] +port = 5000 +healthcheck_endpoint = "/health" +dockerfile = "./Dockerfile" +``` diff --git a/cerebrium/endpoints/custom-web-servers.mdx b/cerebrium/container-images/custom-web-servers.mdx similarity index 91% rename from cerebrium/endpoints/custom-web-servers.mdx rename to cerebrium/container-images/custom-web-servers.mdx index 4acc3f2..beb178d 100644 --- a/cerebrium/endpoints/custom-web-servers.mdx +++ b/cerebrium/container-images/custom-web-servers.mdx @@ -1,6 +1,6 @@ --- -title: "Custom Web Servers" -description: "Run ASGI/WSGI python apps on Cerebrium" +title: "Custom Python Web Servers" +description: "Run ASGI/WSGI Python apps on Cerebrium" --- While Cerebrium's default runtime works well for most app needs, teams sometimes need more control over their web server implementation. Using ASGI or WSGI servers through Cerebrium's custom runtime feature enables capabilities like custom authentication, dynamic batching, frontend dashboards, public endpoints, and websockets. @@ -42,7 +42,7 @@ The configuration requires three key parameters: - `entrypoint`: The command that starts your server - `port`: The port your server listens on -- `healthcheck_endpoint`: The endpoint that confirms server health +- `healthcheck_endpoint`: The endpoint that confirms server health. If empty, will default to a TCP ping of your specified port For ASGI applications like FastAPI, include the appropriate server package diff --git a/mint.json b/mint.json index fe3daa7..101061f 100644 --- a/mint.json +++ b/mint.json @@ -4,7 +4,9 @@ "light": "/logo/light.svg", "dark": "/logo/dark.svg" }, - "versions": ["v4"], + "versions": [ + "v4" + ], "favicon": "/favicon.png", "colors": { "primary": "#EB3A6F", @@ -81,7 +83,11 @@ }, { "group": "Container Images", - "pages": ["cerebrium/container-images/defining-container-images"] + "pages": [ + "cerebrium/container-images/defining-container-images", + "cerebrium/endpoints/custom-web-servers", + "cerebrium/endpoints/custom-dockerfiles" + ] }, { "group": "GPUs and Other Resources", @@ -100,7 +106,9 @@ }, { "group": "Deployments", - "pages": ["cerebrium/deployments/ci-cd"] + "pages": [ + "cerebrium/deployments/ci-cd" + ] }, { "group": "Endpoints", @@ -110,17 +118,20 @@ "cerebrium/endpoints/streaming", "cerebrium/endpoints/websockets", "cerebrium/endpoints/webhook", - "cerebrium/endpoints/async", - "cerebrium/endpoints/custom-web-servers" + "cerebrium/endpoints/async" ] }, { "group": "Storage", - "pages": ["cerebrium/storage/managing-files"] + "pages": [ + "cerebrium/storage/managing-files" + ] }, { "group": "Integrations", - "pages": ["cerebrium/integrations/vercel"] + "pages": [ + "cerebrium/integrations/vercel" + ] }, { "group": "Other concepts", @@ -139,7 +150,9 @@ "v4/examples/featured", { "group": "Advanced Concepts", - "pages": ["v4/examples/mistral-vllm"] + "pages": [ + "v4/examples/mistral-vllm" + ] }, { "group": "Large Language Models", @@ -150,7 +163,9 @@ }, { "group": "Integrations", - "pages": ["v4/examples/langchain-langsmith"] + "pages": [ + "v4/examples/langchain-langsmith" + ] }, { "group": "Voice", @@ -162,11 +177,16 @@ }, { "group": "Image & Video", - "pages": ["v4/examples/comfyUI", "v4/examples/sdxl"] + "pages": [ + "v4/examples/comfyUI", + "v4/examples/sdxl" + ] }, { "group": "Python Apps", - "pages": ["v4/examples/asgi-gradio-interface"] + "pages": [ + "v4/examples/asgi-gradio-interface" + ] } ] },