For enhanced flexibility, users can bring existing containerized apps to Cerebrium. These can range from standard Python apps to compiled Rust binaries, provided a functional Dockerfile is supplied to build the app.
There are multiple benefits to building using Dockerfiles on Cerebrium, including the ability to bring existing containerized apps to the platform and maintaining consistent deployment environments that are easily managed locally.
Building Dockerized Python Apps
This example demonstrates a simple FastAPI server that has been containerized:
from fastapi import FastAPI
app = FastAPI()
@app.post("/hello")
def hello():
return {"message": "Hello Cerebrium!"}
@app.get("/health")
def health():
return "OK"
@app.get("/ready")
def ready():
return "OK"
The application is built using the following Dockerfile:
# Base image
FROM python:3.12-bookworm
RUN apt-get update && apt-get install dumb-init
RUN update-ca-certificates
# Source code
COPY . .
# Dependencies
RUN pip install -r requirements.txt
# Configuration
EXPOSE 8192
CMD ["python", "-m", "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8192"]
When creating a Dockerfile for Cerebrium, there are three key requirements:
- You must expose a port using the
EXPOSE command - this port will be referenced later in your cerebrium.toml configuration
- Either a
CMD or ENTRYPOINT directive must be defined in your Dockerfile OR the entrypoint key in your cerebrium.toml under [runtime.docker]. This specifies what runs when the container starts. The TOML configuration will take precedence
- Set the working directory using
WORKDIR to ensure your application runs from the correct location (defaults to root directory if not specified)
Update cerebrium.toml to include a docker runtime section with the dockerfile_path parameter:
[deployment]
name = "my-docker-app"
[runtime.docker]
dockerfile_path = "./Dockerfile"
port = 8192
healthcheck_endpoint = "/health"
readycheck_endpoint = "/ready"
The configuration requires the following parameters:
dockerfile_path: The relative path to the Dockerfile used to build the app.
port: The port the server listens on.
healthcheck_endpoint: The endpoint used to confirm instance health. If unspecified, defaults to a TCP ping on the configured port. If the health check registers a non-200 response, it will be considered unhealthy, and be restarted should it not recover timely.
readycheck_endpoint: The endpoint used to confirm if the instance is ready to receive. If unspecified, defaults to a TCP ping on the configured port. If the ready check registers a non-200 response, it will not be a viable target for request routing.
entrypoint (optional): The command to start the application. Will be required if CMD or ENTRYPOINT is not defined in the given dockerfile.
Entrypoint Precedence
The entrypoint parameter in cerebrium.toml always takes precedence
over the CMD or ENTRYPOINT instruction in your Dockerfile. If you specify
an entrypoint in your TOML configuration, it will be used regardless of what
CMD or ENTRYPOINT is defined in your Dockerfile.
If your Dockerfile does not contain a CMD or ENTRYPOINT instruction, you must specify the entrypoint parameter in your cerebrium.toml:
[deployment]
name = "my-docker-app"
[runtime.docker]
dockerfile_path = "./Dockerfile"
entrypoint = ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8192"]
port = 8192
healthcheck_endpoint = "/health"
readycheck_endpoint = "/ready"
If you want to override your Dockerfile’s CMD at deploy time without modifying the Dockerfile, simply add the entrypoint parameter to your TOML configuration:
[deployment]
name = "my-docker-app"
[runtime.docker]
dockerfile_path = "./Dockerfile"
# This will override any CMD in your Dockerfile
entrypoint = ["python", "server.py", "--port", "8192"]
port = 8192
When specifying a dockerfile_path, all dependencies and necessary commands
should be installed and executed within the Dockerfile. Dependencies listed
under dependencies.*, as well as shell_commands and pre_build_commands,
will be ignored.
Building Generic Dockerized Apps
Cerebrium supports apps in languages other than Python, provided a Dockerfile is supplied. The following example demonstrates a Rust-based API server using the Axum framework:
use axum::{
routing::{get, post},
Json, Router,
};
use serde_json::json;
async fn hello() -> Json<serde_json::Value> {
Json(json!({ "message": "Hello Cerebrium!" }))
}
async fn health() -> &'static str {
"OK"
}
async fn ready() -> &'static str {
"OK"
}
#[tokio::main]
async fn main() {
let app = Router::new()
.route("/hello", post(hello))
.route("/health", get(health))
.route("/ready", get(health));
tracing::info!("Listening on port 8192");
let listener = tokio::net::TcpListener::bind("0.0.0.0:8192").await.unwrap();
axum::serve(listener, app).await.unwrap();
}
In this case, a multi-stage Dockerfile is used to separate the build step, creating a smaller and more secure image for the runtime:
# Build stage
FROM rust:bookworm as build
RUN apt-get update && apt-get install dumb-init
RUN update-ca-certificates
# Project setup
RUN USER=root cargo new --bin rs_server
WORKDIR /rs_server
# Dependencies
COPY Cargo.lock ./Cargo.lock
COPY Cargo.toml ./Cargo.toml
# Cache dependencies
RUN cargo build --release
RUN rm src/*.rs
# Source code
COPY src/* src/
# Build
RUN rm ./target/release/deps/rs_server*
RUN cargo build --release
# Runtime stage
FROM gcr.io/distroless/base-debian12
WORKDIR /
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=build /lib/x86_64-linux-gnu/libgcc_s.so.1 /lib/x86_64-linux-gnu/libgcc_s.so.1
COPY --from=build /rs_server/target/release/rs_server /rs_server
COPY --from=build /usr/bin/dumb-init /usr/bin/dumb-init
EXPOSE 8192
CMD ["dumb-init", "--", "/rs_server"]
Similarly to the FastAPI webserver, the application should be configured in the cerebrium.toml file:
[deployment]
name = "rust-server"
[runtime.docker]
dockerfile_path = "./Dockerfile"
port = 8192
healthcheck_endpoint = "/health"
readycheck_endpoint = "/ready"