Hey hey hey π!
This is the second part of this series, make sure you've already read the first part :)
In the first part, we saw how to create a basic Rust API with a GET /pizza
endpoint to retrieve the list of pizza.
In this part, let's see how to containerize our app!
Let's create our container
Since AWS App Runner is a fully managed container application service, we definitely need a container!
In this section, we will see how to build that container thanks to Docker
using a Dockerfile
.
A Dockerfile
is a text file that contains all the commands needed to build our container, it's often compared to a recipe.
Rust as the base image
Let's start with
FROM rust:slim-bullseye
WORKDIR /app
COPY ./ /app
RUN cargo build --release
The first line describes the base image that we want to use, here the official rust
one.
This is really convenient since we don't have to install rust
or cargo
: both are already installed in this base image.
Then we define the current working directory, here /app
Then we copy our sources (src
+ Cargo.toml
+ Cargo.lock
) into the container.
Finally we build our API directly inside the container, in release mode, to be sure to have a production-ready binary.
Great, we now have our binary ready in our image!
But wait, we also have rust
, cargo
and a bunch of useless tools at runtime.
It was convenient to use the official rust
image to build, but we don't need it anymore for the runtime. Fortunately, Docker supports multi stage builds.
Multi-stage builds
We can specify a base image to build and another one to run! That's exactly what we want!
The idea here is to have the smallest possible image so the cold start would be extremely fast. That's why we are going to use a distroless
base image.
Those images are built by Google and contain almost nothing: no bash
, no ssh
, no utility tools which makes the image extremely small.
Let's modify our Dockerfile:
FROM rust:slim-bullseye AS BUILD
COPY ./ /app
RUN cargo build --release
FROM gcr.io/distroless/cc-debian10
COPY --from=BUILD /app/target/release/app-runner-rust /
CMD ["./app-runner-rust"]
Here we name our first image BUILD
so we can reference that later. Then from the distroless image, we copy our binary from the BUILD
image! Super convenient! I love Docker :D
Our runtime image is just the distroless base image
+ rust binary
Let's finally build it with docker build . -t maxday/app-runner
Let's check for our image size: docker images | grep maxday/app-runner
docker images | grep maxday/app-runner
maxday/app-runner latest e278afc78403 3 minutes ago 20.1MB
20.1MB!!! π€― Extremely small!
Woot woot! We now have a ready to deploy container!
Local test
Before deploying, let's make sure everything looks fine by running it locally and calling our GET /pizza
endpoint.
You can run your newly built docker image with:
docker run -d -p 8080:8080 maxday/app-runner
-
-d
meansdetached
so the container runs in the background -
-p 8080:8080
means that you're bridging the host port 8080 to the container port 8080.
You can check that your container is up and running with:
docker ps
docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
e5d8978ee6ee maxday/app-runner "./app-runner-rust" 53 seconds ago Up 53 seconds 0.0.0.0:8080->8080/tcp pedantic_kare
Now you can curl the endpoint:
curl http://localhost:8080/pizza
curl localhost:8080/pizza
[{"name":"Margherita","toppings":["tomato","fior di latte"],"price":10},{"name":"Veggie","toppings":["green peppers","onion","mushrooms"],"price":12}]
Awesome! π
Our container is now ready to be used by AWS App Runner.
See you next week for the final episode of this series to deploy it!
Like this content? Consider following me for more!
A question? Feel free to use the comment section β¬οΈ
Top comments (0)