Rust + Docker: Easy packaging of your applications

Rust + Docker: Easy packaging of your applications

·

2 min read

Let's first create a simple web app with a health check endpoint by running the commands below.

cargo new rust_docker_app --bin
cd rust_docker_app
cargo add actix-web
touch Dockerfile

So what we've actually done here:

  • created a new binary crate, called rust_docker_app

  • added web framework dependency (I prefer Actix, but you can choose any)

  • created empty DockerFile

Now let's create a web server with one simple health check endpoint. To do this, you need to update your main.rs file with:

use actix_web::{App, HttpServer, HttpResponse, get};

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        App::new()
            .service(ping)
    })
    .bind(("0.0.0.0", 8080))?
    .run()
    .await
}

#[get("/ping")]
async fn ping() -> HttpResponse {
    HttpResponse::Ok().body("pong")
}

So we created an app with 0.0.0.0 address and 8080 port bindings.

0.0.0.0 is the special “all interfaces” address. You must set a docker container’s main process (Actix web app in our case) to bind to it, or it will be unreachable from outside the container.

Our app has only one endpoint, which responds with a "pong" message all the time.

Now let's update our Dockerfile with:

FROM rust:1.66.0 as build
COPY . /rust_docker_app
WORKDIR /rust_docker_app

RUN --mount=type=cache,target=/usr/local/cargo/registry --mount=type=cache,target=/root/target \
    cargo build --release

FROM debian:buster-slim as runtime
COPY --from=build /rust_docker_app/target/release/rust_docker_app .
CMD ["./rust_docker_app"]

We don't need any project files except compiled binary in our image, that's why we use multi-stage build.

You can read more about multi-stage builds here: https://docs.docker.com/build/building/multi-stage/

So what does this Dockerfile do step by step:

  • copy project files to the first (build) stage

  • compile the code

  • copy the binary executable file from the "build" stage to the "runtime" stage, leaving behind everything else we don’t want in the final image

  • run the compiled binary

This part is needed --mount=type=cache,target=/usr/local/cargo/registry for caching Rust dependencies and it defines which path should be cached as a container image layer.

Now we are ready to build our image. Simply just run the below command in the project root directory:

docker build -t rust_docker_app .

And finally, let's run our web server:

docker run -p 8080:8080 rust_docker_app

Now you can access the health check endpoint by http://127.0.0.1:8080/ping. Just try to get it via browser or CURL or any other HTTP client.

I hope you enjoy it. As always you can find working example in my GitHub repository :)