Topics:

Minimal Dockerfile for Building Rust via @JesusGuzmanJr

Repo: https://github.com/JesusGuzmanJr/CodeSnippets/blob/main/Dockerfile

Jesus shared a minimal Dockerfile for building Rust in a CI pipeline. It utilizes cargo-chef which is a cargo-subcommand to speed up Rust Docker builds using Docker layer caching. It does this in following steps:

  • Compute a recipe file, or a set of information required to build dependencies. The relevant command is cargo chef prepare --recipe-path /build/recipe.json

  • Cache dependencies using the recipe (cargo chef cook --release --recipe-path recipe.json)

  • Build the application (using cargo-make and wasm-bindgen). The final command is cargo make build --profile production

  • Run the application.

FROM fedora:33 as planning_layer
WORKDIR /build
RUN curl --proto '=https' --tlsv1.3 -sSf https://sh.rustup.rs | sh -s -- -y
ENV PATH="/root/.cargo/bin:${PATH}"
RUN dnf -y install gcc-c++
RUN cargo install cargo-chef
COPY . .
RUN cargo chef prepare --recipe-path /build/recipe.json

FROM fedora:33 as dependency_cache_layer
WORKDIR /build
RUN curl --proto '=https' --tlsv1.3 -sSf https://sh.rustup.rs | sh -s -- -y
ENV PATH="/root/.cargo/bin:${PATH}"
RUN dnf -y install gcc-c++ pkg-config openssl-static
COPY --from=planning_layer /build/recipe.json recipe.json
RUN cargo install cargo-chef
RUN cargo chef cook --release --recipe-path recipe.json

FROM fedora:33 as build_layer
WORKDIR /build
RUN curl --proto '=https' --tlsv1.3 -sSf https://sh.rustup.rs | sh -s -- -y
ENV PATH="/root/.cargo/bin:${PATH}"
RUN dnf -y install gcc-c++ pkg-config musl-gcc git perl-core
RUN git clone git://git.openssl.org/openssl.git
RUN cd openssl && git checkout OpenSSL_1_1_1-stable
RUN cd openssl && ./config -fPIC no-weak-ssl-ciphers no-async --prefix=/usr/local/ssl --openssldir=/usr/local/ssl
RUN cd openssl && make && make install
ENV OPENSSL_STATIC true
ENV OPENSSL_DIR /usr/local/ssl
RUN cargo install trunk
RUN cargo install cargo-make
RUN cargo install wasm-bindgen-cli --version 0.2.70
RUN rustup target add wasm32-unknown-unknown
RUN rustup target add x86_64-unknown-linux-musl
RUN dnf -y install nodejs npm
RUN npm install -g sass
COPY . .
COPY --from=dependency_cache_layer /build/target target
COPY --from=dependency_cache_layer $CARGO_HOME $CARGO_HOME
RUN cargo make build --profile production

FROM scratch
COPY --from=build_layer /build/target/x86_64-unknown-linux-musl/release/server /
EXPOSE 80/tcp
ENTRYPOINT ["/server"]

Another Hand-Rolled Example

Jacob showed an example Dockerfile with a similar process of caching except done manually (not using cargo-chef):

FROM rust as dependencies

WORKDIR /build

# Create new fake project ($USER is needed by `cargo new`)
RUN USER=root cargo new app

WORKDIR /build/app

# Copy real app dependencies
COPY Cargo.* ./

# Build fake project with real dependencies
RUN cargo build --release

# Remove the fake app build artifacts
#
# NOTE If your application name contains `-` (`foo-bar` for example)
# then the correct command to remove build artifacts looks like:
#
# RUN rm -rf target/release/foo-bar target/release/deps/foo_bar-*
#                              ^                           ^
RUN rm -rf target/release/hello* target/release/deps/hello-*

Yet Another Similar Example

Chris also had found a similar example in a blog post on How to Package Rust Applications Into Minimal Docker Containers

# Dockerfile for creating a statically-linked Rust application using docker's
# multi-stage build feature. This also leverages the docker build cache to avoid
# re-downloading dependencies if they have not changed.
FROM rust:1.35.0 AS build
WORKDIR /usr/src

# Download the target for static linking.
RUN rustup target add x86_64-unknown-linux-musl

# Create a dummy project and build the app's dependencies.
# If the Cargo.toml or Cargo.lock files have not changed,
# we can use the docker build cache and skip these (typically slow) steps.
RUN USER=root cargo new url-shortener
WORKDIR /usr/src/url-shortener
COPY Cargo.toml Cargo.lock ./
RUN cargo build --release

# Copy the source and build the application.
COPY src ./src
RUN cargo install --target x86_64-unknown-linux-musl --path .

# Copy the statically-linked binary into a scratch container.
FROM scratch
COPY --from=build /usr/local/cargo/bin/url-shortener .
USER 1000
CMD ["./url-shortener"]

Mocking in Rust and Rust in the Linux Kernel via @mysteriouspants

Mocking with Mockall

Chris was looking at mockall for isolating some code and making it more testable. Mocking is tradionally done in more dynamic languages, but that doesn't stop people from trying to do it in Rust. There are two ways to use Mockall. The easiest is to use #[automock]. It can mock most traits, or structs that only have a single impl block. For things it can't handle, there is mock!.

Linux Kernel

Chris was also excited by news that Linus Torvalds, responding to an RFC that adds support for Rust to the Linux kernel, said:

on the whole I don't hate it.

The conversation went on to other developments in the Linux kernel space. A few links from the discussion are below:

Conversations around WebAssembly via @jacobrosenthal, @BlaineBublitz, and @PeterKehl

Wizer

Jacob was watching the WebAssembly Summit. He thought that the presentation on Wizer, a WebAssembly Pre-Initializer, was interesting. From the Wizer README:

Wizer instantiates your WebAssembly module, executes its initialization function, and then snapshots the initialized state out into a new WebAssembly module. Now you can use this new, pre-initialized WebAssembly module to hit the ground running, without making your users wait for that first-time set up code to complete.

OCaml-rs

Another presentation from the WebAssembly Summit was on Grain, a WebAssembly-First Programming Language. Blaine contributes to Grain and was looking at ocaml-rs as part of his work on the project as a way to avoid writing more C bindings to OCaml.

Watt

Peter brought up Watt, a crate providing a runtime for executing Rust procedural macros compiled as WebAssembly. There is a cargo subcommand, cargo watt, which aims to improve the Watt tooling by:

  • Compiling existing proc-macro crates without manual intervention for the watt runtime
  • Verifying that a wasm file is compiled from a particular source

Some Blockchain Stuff via @jacobrosenthal

Jacob also shared some links on Blockchain:

  • Fe: Fe is a statically typed language for the Ethereum Virtual Machine (EVM)inspired by Rust.

  • uint is a crate providing facilities to construct big unsigned integer types which use no allocations (stack-based, fixed bit length). They also have primitive types and implement math on top of it. This is what Jacob used to get an advantage playing the Blockchain-based game, Dark Forest, building a miner that was 10x faster than the competition.

Crates You Should Know

  • mockall: A powerful mock object library for Rust
  • glommio: A set of utilities to allow one to write thread per core applications
  • ocaml: OCaml bindings for Rust
  • watt: Runtime for executing Rust procedural macros compiled as WebAssembly
  • uint: Large fixed-size integer arithmetic
  • rmp: Pure Rust MessagePack serialization implementation