|  | 
| 1 |  | -# We split the Dockerfile into two stages: | 
| 2 |  | -# Stage 1: We copy all the Cargo.toml files and create empty lib.rs files. | 
| 3 |  | -# Stage 2: | 
| 4 |  | -#   * We copy the files from the first stage | 
| 5 |  | -#   * We compile all the crates. | 
| 6 |  | -#   * We copy the rest of the files and compile again. | 
| 7 |  | -# The reason we compile twice is to allow caching for the first compilation (that compiles all the | 
| 8 |  | -# dependency crates) if no Cargo.toml files were changed. | 
| 9 |  | -# The reason we split it into two stages is to first copy all the files and then erase all | 
| 10 |  | -# non-Cargo.toml files. This way, non-Cargo.toml files won't affect the cache of the second stage | 
| 11 |  | -# (For more on docker stages, read https://docs.docker.com/build/building/multi-stage/). | 
| 12 |  | -FROM rust:1.78 AS copy_toml | 
|  | 1 | +# Dockerfile with multi-stage builds for efficient dependency caching and lightweight final image. | 
|  | 2 | +# For more on Docker stages, visit: https://docs.docker.com/build/building/multi-stage/ | 
| 13 | 3 | 
 | 
| 14 |  | -COPY crates/ /app/crates/ | 
| 15 |  | -COPY Cargo.toml /app/ | 
| 16 |  | - | 
| 17 |  | -WORKDIR /app/ | 
| 18 |  | - | 
| 19 |  | -# Erase all non-Cargo.toml files. | 
| 20 |  | -RUN find /app \! -name "Cargo.toml" -type f -delete ; \ | 
| 21 |  | -    find /app -empty -type d -delete; \ | 
| 22 |  | -    # Create empty lib.rs files. | 
| 23 |  | -    # In order for cargo init to work, we need to not have a Cargo.toml file. In each crate, we rename | 
| 24 |  | -    # Cargo.toml to another name and after running `cargo init` we override the auto-generated | 
| 25 |  | -    # Cargo.toml with the original. | 
| 26 |  | -    mv Cargo.toml _Cargo.toml && \ | 
| 27 |  | -    # TODO: Consider moving to a script. | 
| 28 |  | -    for dir in crates/*; do \ | 
| 29 |  | -        if [ -f "$dir/Cargo.toml" ]; then \ | 
| 30 |  | -            mv $dir/Cargo.toml $dir/_Cargo.toml \ | 
| 31 |  | -            && cargo init --lib --vcs none $dir \ | 
| 32 |  | -            && mv -f $dir/_Cargo.toml $dir/Cargo.toml; \ | 
| 33 |  | -        else \ | 
| 34 |  | -            for subdir in $dir/*; do \ | 
| 35 |  | -                mv $subdir/Cargo.toml $subdir/_Cargo.toml \ | 
| 36 |  | -                && cargo init --lib --vcs none $subdir \ | 
| 37 |  | -                && mv -f $subdir/_Cargo.toml $subdir/Cargo.toml; \ | 
| 38 |  | -            done; \ | 
| 39 |  | -        fi; \ | 
| 40 |  | -    done && \ | 
| 41 |  | -    mv _Cargo.toml Cargo.toml | 
| 42 |  | - | 
| 43 |  | -COPY Cargo.lock /app/ | 
| 44 |  | - | 
| 45 |  | -# Starting a new stage so that the first build layer will be cached if a non-Cargo.toml file was | 
| 46 |  | -# changed. | 
| 47 |  | -# Use this image to compile the project to an alpine compatible binary. | 
| 48 |  | -FROM clux/muslrust:1.78.0-stable AS builder | 
| 49 |  | -WORKDIR /app/ | 
|  | 4 | +# We use Cargo Chef to compile dependencies before compiling the rest of the crates. | 
|  | 5 | +# This approach ensures proper Docker caching, where dependency layers are cached until a dependency changes. | 
|  | 6 | +# Code changes in our crates won't affect these cached layers, making the build process more efficient. | 
|  | 7 | +# More info on Cargo Chef: https://github.com/LukeMathWalker/cargo-chef | 
| 50 | 8 | 
 | 
|  | 9 | +# We start by creating a base image using 'clux/muslrust' with additional required tools. | 
|  | 10 | +FROM clux/muslrust:1.78.0-stable AS chef | 
|  | 11 | +WORKDIR /app | 
| 51 | 12 | RUN apt update && apt install -y clang unzip | 
|  | 13 | +RUN cargo install cargo-chef | 
| 52 | 14 | ENV PROTOC_VERSION=25.1 | 
| 53 | 15 | RUN curl -L "https://github.com/protocolbuffers/protobuf/releases/download/v$PROTOC_VERSION/protoc-$PROTOC_VERSION-linux-x86_64.zip" -o protoc.zip && unzip ./protoc.zip -d $HOME/.local &&  rm ./protoc.zip | 
| 54 | 16 | ENV PROTOC=/root/.local/bin/protoc | 
| 55 |  | - | 
| 56 |  | - | 
| 57 |  | -# Copy all the files from the previous stage (which are Cargo.toml and empty lib.rs files). | 
| 58 |  | -COPY --from=copy_toml /app . | 
| 59 |  | - | 
| 60 |  | -# Add the proc_macro code since cargo init puts autogenerated code in lib.rs file that breaks the build. | 
| 61 |  | -COPY crates/papyrus_proc_macros /app/crates/papyrus_proc_macros | 
| 62 |  | - | 
| 63 |  | -RUN rustup target add x86_64-unknown-linux-musl && \ | 
| 64 |  | -    CARGO_INCREMENTAL=0 cargo build  --target x86_64-unknown-linux-musl --release --package papyrus_node | 
| 65 |  | - | 
| 66 |  | -# Copy the rest of the files. | 
| 67 |  | -COPY crates/ /app/crates | 
| 68 |  | - | 
| 69 |  | -# Touching the lib.rs files to mark them for re-compilation. Then re-compile now that all the source | 
| 70 |  | -# code is available | 
| 71 |  | -RUN touch crates/*/src/lib.rs; \ | 
| 72 |  | -    CARGO_INCREMENTAL=0 cargo build --release --package papyrus_node --bin papyrus_node; | 
| 73 |  | - | 
| 74 |  | -# Starting a new stage so that the final image will contain only the executable. | 
|  | 17 | +# Add the x86_64-unknown-linux-musl target to rustup for compiling statically linked binaries. | 
|  | 18 | +# This enables the creation of fully self-contained binaries that do not depend on the system's dynamic libraries, | 
|  | 19 | +# resulting in more portable executables that can run on any Linux distribution. | 
|  | 20 | +RUN rustup target add x86_64-unknown-linux-musl | 
|  | 21 | + | 
|  | 22 | +##################### | 
|  | 23 | +# Stage 1 (planer): # | 
|  | 24 | +#####################  | 
|  | 25 | +FROM chef AS planner | 
|  | 26 | +COPY . . | 
|  | 27 | +# * Running Cargo Chef prepare that will generate recipe.json which will be used in the next stage. | 
|  | 28 | +RUN cargo chef prepare | 
|  | 29 | + | 
|  | 30 | +##################### | 
|  | 31 | +# Stage 2 (cacher): # | 
|  | 32 | +#####################  | 
|  | 33 | +# Compile all the dependecies using Cargo Chef cook. | 
|  | 34 | +FROM chef AS cacher | 
|  | 35 | + | 
|  | 36 | +# Copy recipe.json from planner stage | 
|  | 37 | +COPY --from=planner /app/recipe.json recipe.json | 
|  | 38 | + | 
|  | 39 | +# Build dependencies - this is the caching Docker layer! | 
|  | 40 | +RUN cargo chef cook --target x86_64-unknown-linux-musl --release --package papyrus_node | 
|  | 41 | + | 
|  | 42 | +###################### | 
|  | 43 | +# Stage 3 (builder): # | 
|  | 44 | +######################  | 
|  | 45 | +FROM chef AS builder | 
|  | 46 | +COPY . . | 
|  | 47 | +COPY --from=cacher /app/target target | 
|  | 48 | +# Disable incremental compilation for a cleaner build. | 
|  | 49 | +ENV CARGO_INCREMENTAL=0 | 
|  | 50 | + | 
|  | 51 | +# Compile the papyrus_node crate for the x86_64-unknown-linux-musl target in release mode, ensuring dependencies are locked. | 
|  | 52 | +RUN cargo build --target x86_64-unknown-linux-musl --release --package papyrus_node --locked | 
|  | 53 | + | 
|  | 54 | +########################### | 
|  | 55 | +# Stage 4 (papyrus_node): # | 
|  | 56 | +########################### | 
|  | 57 | +# Uses Alpine Linux to run a lightweight and secure container. | 
| 75 | 58 | FROM alpine:3.17.0 AS papyrus_node | 
| 76 | 59 | ENV ID=1000 | 
| 77 |  | - | 
| 78 | 60 | WORKDIR /app | 
| 79 |  | -# Copy the node executable and its config. | 
|  | 61 | + | 
|  | 62 | +# Copy the node executable and its configuration. | 
| 80 | 63 | COPY --from=builder /app/target/x86_64-unknown-linux-musl/release/papyrus_node /app/target/release/papyrus_node | 
| 81 |  | -COPY config/ /app/config | 
|  | 64 | +COPY config config | 
| 82 | 65 | 
 | 
|  | 66 | +# Install tini, a lightweight init system, to call our executable. | 
| 83 | 67 | RUN set -ex; \ | 
| 84 | 68 |     apk update; \ | 
| 85 | 69 |     apk add --no-cache tini; \ | 
| 86 | 70 |     mkdir data | 
| 87 | 71 | 
 | 
|  | 72 | +# Create a new user "papyrus". | 
| 88 | 73 | RUN set -ex; \ | 
| 89 | 74 |     addgroup --gid ${ID} papyrus; \ | 
| 90 | 75 |     adduser --ingroup $(getent group ${ID} | cut -d: -f1) --uid ${ID} --gecos "" --disabled-password --home /app papyrus; \ | 
| 91 | 76 |     chown -R papyrus:papyrus /app | 
| 92 | 77 | 
 | 
|  | 78 | +# Expose RPC and monitoring ports. | 
| 93 | 79 | EXPOSE 8080 8081 | 
| 94 | 80 | 
 | 
|  | 81 | +# Switch to the new user. | 
| 95 | 82 | USER ${ID} | 
| 96 | 83 | 
 | 
|  | 84 | +# Set the entrypoint to use tini to manage the process. | 
| 97 | 85 | ENTRYPOINT ["/sbin/tini", "--", "/app/target/release/papyrus_node"] | 
0 commit comments