I want to compile my Rust program depending on the used host machine. We are using a test server with linux/amd64
and deploy it on a Raspberry Pi 4 which uses linux/arm64
. We build the docker image on our local gitlab Docker registry. How can I use the cross-compiling mechanism?
Have you read this? If so, what's missing for your use case?
In addition, check out cross-rs
which is a more powerful and complete cross compilation system that uses Docker internally. It can do more than the built-in rustup target support can, if that's not enough.
Maybe I'm looking more for a Docker solution instead of rust compiling solution. Since we are building the Docker image on gitlab's Docker registry I need to figure out a way to handle multiple architectures on this step because it only compiles my program for linux/amd64
. Maybe compiling multiple times is the only way?
Docker supports multi platform images, see if that's useful
I created something like below. It builds docker image for arm64 & amd64 platforms.
You can create multi platform image by calling docker build this way:
docker buildx build --platform=linux/arm64,linux/amd64 --tag your-app:v1 --file services/some-service/docker/Dockerfile.unified .
You can also use the --platform switch when running the image, enabling you to test the arm64 version on an amd64 host to ensure it functions as expected.
Additionally, structuring the build process in this manner significantly enhances efficiency. Whereas a traditional build might typically take about 1.5 hours (in my case), the method described here reduces that time to approximately 10-15 minutes..
Dockerfile recipe:
FROM --platform=linux/amd64 lukemathwalker/cargo-chef:latest-rust-latest AS amd64-chef
FROM --platform=linux/arm64 lukemathwalker/cargo-chef:latest-rust-latest AS arm64-chef
# Base image for the build stage - this is a multi-stage build that uses cross-compilation (thanks to --platform switch)
FROM --platform=$BUILDPLATFORM lukemathwalker/cargo-chef:latest-rust-latest AS chef
WORKDIR /app
# Planner stage
FROM chef AS planner
COPY . .
RUN cargo chef prepare --recipe-path recipe.json
# Builder stage
FROM chef AS builder
COPY --from=planner /app/recipe.json recipe.json
ARG TARGETPLATFORM
ARG TARGETARCH
# Copy runtime dependencies for specific target platform/architecture
# ARM specific folders
WORKDIR /all-files/linux/arm64/lib/aarch64-linux-gnu
# AMD64 specific folders
WORKDIR /all-files/linux/amd64/lib/x86_64-linux-gnu
WORKDIR /all-files/linux/amd64/lib64
# Common folders
WORKDIR /all-files/${TARGETPLATFORM}/etc/ssl/certs
WORKDIR /all-files/${TARGETPLATFORM}/app
# ARM64
COPY --from=arm64-chef \
/lib/aarch64-linux-gnu/libssl.so.3 \
/lib/aarch64-linux-gnu/libcrypto.so.3 \
/lib/aarch64-linux-gnu/libgcc_s.so.1 \
/lib/aarch64-linux-gnu/libm.so.6 \
/lib/aarch64-linux-gnu/libc.so.6 \
/all-files/linux/arm64/lib/aarch64-linux-gnu/
COPY --from=arm64-chef \
/lib/ld-linux-aarch64.so.1 \
/all-files/linux/arm64/lib
# AMD64
COPY --from=amd64-chef \
/lib/x86_64-linux-gnu/libssl.so.3 \
/lib/x86_64-linux-gnu/libcrypto.so.3 \
/lib/x86_64-linux-gnu/libgcc_s.so.1 \
/lib/x86_64-linux-gnu/libm.so.6 \
/lib/x86_64-linux-gnu/libc.so.6 \
/all-files/linux/amd64/lib/x86_64-linux-gnu/
COPY --from=amd64-chef \
/lib64/ld-linux-x86-64.so.2 \
/all-files/linux/amd64/lib64/
# Common files - certs
COPY --from=amd64-chef \
/etc/ssl/certs/ca-certificates.crt \
/all-files/linux/amd64/etc/ssl/certs/
COPY --from=arm64-chef \
/etc/ssl/certs/ca-certificates.crt \
/all-files/linux/arm64/etc/ssl/certs/
WORKDIR /app
# Install dependencies for cross-compilation and protobuf
RUN dpkg --add-architecture arm64 \
&& apt-get update \
&& apt-get install -y \
protobuf-compiler \
g++-aarch64-linux-gnu \
libc6-dev-arm64-cross \
libssl-dev:arm64 \
ca-certificates \
&& rustup target add aarch64-unknown-linux-gnu \
&& rustup toolchain install stable-aarch64-unknown-linux-gnu \
&& rm -rf /var/lib/apt/lists/*
# Build dependencies - this is the caching Docker layer!
RUN case ${TARGETARCH} in \
arm64) PKG_CONFIG_SYSROOT_DIR=/ CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc cargo chef cook --target=aarch64-unknown-linux-gnu --release --recipe-path recipe.json ;; \
amd64) cargo chef cook --release --recipe-path recipe.json ;; \
*) exit 1 ;; \
esac
# Copy the source code
COPY . /app
# Build application - this is the caching Docker layer!
RUN case ${TARGETARCH} in \
arm64) PKG_CONFIG_SYSROOT_DIR=/ CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc cargo build --target=aarch64-unknown-linux-gnu --release ;; \
amd64) cargo build --release ;; \
*) exit 1 ;; \
esac
# Copy all the dependencies to a separate folder
RUN set -ex; \
# Determine target (source folder for the binary and env files)
case ${TARGETARCH} in \
arm64) target='/app/target/aarch64-unknown-linux-gnu/release';; \
amd64) target='/app/target/release';; \
*) exit 1 ;; \
esac; \
# Copy files from the target folder to app folder
cp $target/*-env.json /all-files/${TARGETPLATFORM}/app && \
cp $target/your-app /all-files/${TARGETPLATFORM}/app
# # Create a single layer image
FROM scratch AS runtime
# Make build arguments available in the runtime stage
ARG TARGETPLATFORM
ARG TARGETARCH
WORKDIR /app
# Copy the binary and the environment files from the pre-runtime stage as a single layer
COPY --from=builder /all-files/${TARGETPLATFORM} /
# Expose the port that the application listens on.
EXPOSE 8080
# Run the application
ENTRYPOINT ["/app/your-app"]
#ENTRYPOINT ["bash"]
This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.