..

Building smaller docker image

Category: en

So I am currently working on one Rust server project. After I built the service, as usual, I wrote Dockerfile so that later I can deploy the application in Kubernetes (Vultr is my choice now because of the pricing).

There are 3 candidates for my final image base. These are

Initially, I wanted to use Rust official images for building. However, there are a few issues with it

  1. need to statically link my built to the musl libc because scratch and alpine are based on musl libc not glibc.
  2. OpenSSL issue with cross-compilation, you can read more about it here.

glibc issue can be solved by simply installing the library and adding the target for cross-compilation as below. But the tough part comes with SSL.

RUN apt-get install 

RUN apt-get install -y musl-tools

RUN rustup target add x86_64-unknown-linux-musl

RUN cargo build --target x86_64-unknown-linux-musl --release

For both issues, someone already built a base image (rust-musl-builder) just for cross-compiling rust application. Therefore, I am using it to build my application.

# ==========
# Build step
# ==========
FROM ekidd/rust-musl-builder AS builder
WORKDIR /subtext

# copy files
COPY ./src ./src
COPY ./Cargo.toml .
COPY ./Rocket.toml .

# https://github.com/emk/rust-musl-builder#making-static-releases-with-travis-ci-and-github
ADD --chown=rust:rust . ./

# build release
RUN cargo build --release

# ===========
# Final image
# ===========
FROM scratch
WORKDIR /subtext

# copy build
COPY --from=builder /subtext/target/x86_64-unknown-linux-musl/release/subtext_api ./
COPY --from=builder /subtext/Rocket.toml ./

CMD ["/subtext/subtext_api"]


Built images

In Final image step, I used scratch, alpine, and distroless.

  • Scratch has the lowest file size because it is barebone.
  • Alpine is also another lightweight image, and it is also kind of a defacto standard.
  • Distroless is a security-oriented image maintained by Google, and based on Debian packages therefore using this I will not need to do static linking musl.

rust_docker_images

So scratch is the best candidate to achieve the lowest image size, however, it comes with some costs. Especially with SSL. Scratch images cannot perform SSL cert certification because it does not come with SSL certs. You can read more about it here.

You can simply fix in scratch by installing it yourself or use alpine instead. With only ~6mb more, alpine comes with a ton of more utility tools as well.

Then for distroless, even though it is security oriented, there are a few issues with it. You can read the entire article from Red Hat here, but the key point is the size matters. A larger size also increases the attack surface.

Trivy for scanning vulnerability

This is shown by scanning all those 3 images using Trivy, ironically distroless comes with quite a few CVEs from libc. Trivy is an awesome tool to scan vulnerabilities for file systems and images.

trivy_1

trivy_2


Lower binary size (for Rust)

Until now, we are focusing on optimizing the image size. We can also optimize the Rust built as well to even lower the size. That however comes at the cost of performance. But if you are interested you can look up the following 2 links below.