-
Notifications
You must be signed in to change notification settings - Fork 2.9k
feat: Refactor Dockerfile
#3372
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
39fcccb
063a9be
a314ed9
2d4e856
d352b09
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -1,45 +1,86 @@ | ||||||
| FROM --platform=$BUILDPLATFORM ubuntu as build | ||||||
| # syntax=docker/dockerfile:1 | ||||||
|
|
||||||
| FROM --platform=${BUILDPLATFORM} ubuntu:24.04 AS builder-base | ||||||
| # Configure the shell to exit early if any command fails, or when referencing unset variables. | ||||||
| # Additionally `-x` outputs each command run, this is helpful for troubleshooting failures. | ||||||
| SHELL ["/bin/bash", "-eux", "-o", "pipefail", "-c"] | ||||||
|
|
||||||
| RUN \ | ||||||
| --mount=target=/var/lib/apt/lists,type=cache,sharing=locked \ | ||||||
| --mount=target=/var/cache/apt,type=cache,sharing=locked \ | ||||||
| <<HEREDOC | ||||||
| # https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/reference.md#example-cache-apt-packages | ||||||
| # https://stackoverflow.com/questions/66808788/docker-can-you-cache-apt-get-package-installs#comment135104889_72851168 | ||||||
| rm -f /etc/apt/apt.conf.d/docker-clean | ||||||
| echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' > /etc/apt/apt.conf.d/keep-cache | ||||||
|
|
||||||
| apt update && apt install -y --no-install-recommends \ | ||||||
| build-essential \ | ||||||
| curl \ | ||||||
| python3-venv \ | ||||||
| cmake | ||||||
| HEREDOC | ||||||
|
|
||||||
| ENV HOME="/root" | ||||||
| ENV PATH="$HOME/.venv/bin:$PATH" | ||||||
| WORKDIR $HOME | ||||||
|
|
||||||
| RUN apt update \ | ||||||
| && apt install -y --no-install-recommends \ | ||||||
| build-essential \ | ||||||
| curl \ | ||||||
| python3-venv \ | ||||||
| cmake \ | ||||||
| && apt clean \ | ||||||
| && rm -rf /var/lib/apt/lists/* | ||||||
|
|
||||||
| # Setup zig as cross compiling linker | ||||||
| RUN python3 -m venv $HOME/.venv | ||||||
| RUN .venv/bin/pip install cargo-zigbuild | ||||||
| ENV PATH="$HOME/.venv/bin:$PATH" | ||||||
| # Setup zig as cross compiling linker: | ||||||
| RUN <<HEREDOC | ||||||
| python3 -m venv $HOME/.venv | ||||||
| .venv/bin/pip install --no-cache-dir cargo-zigbuild | ||||||
| HEREDOC | ||||||
|
|
||||||
| # Install rust | ||||||
| ARG TARGETPLATFORM | ||||||
| RUN case "$TARGETPLATFORM" in \ | ||||||
| "linux/arm64") echo "aarch64-unknown-linux-musl" > rust_target.txt ;; \ | ||||||
| "linux/amd64") echo "x86_64-unknown-linux-musl" > rust_target.txt ;; \ | ||||||
| *) exit 1 ;; \ | ||||||
| esac | ||||||
| # Update rustup whenever we bump the rust version | ||||||
| COPY rust-toolchain.toml rust-toolchain.toml | ||||||
| RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --target $(cat rust_target.txt) --profile minimal --default-toolchain none | ||||||
| # Install rust: | ||||||
| ENV PATH="$HOME/.cargo/bin:$PATH" | ||||||
| # Installs the correct toolchain version from rust-toolchain.toml and then the musl target | ||||||
| RUN rustup target add $(cat rust_target.txt) | ||||||
|
|
||||||
| # Build | ||||||
| COPY crates crates | ||||||
| COPY ./Cargo.toml Cargo.toml | ||||||
| COPY ./Cargo.lock Cargo.lock | ||||||
| RUN cargo zigbuild --bin uv --target $(cat rust_target.txt) --release | ||||||
| RUN cp target/$(cat rust_target.txt)/release/uv /uv | ||||||
| # TODO(konsti): Optimize binary size, with a version that also works when cross compiling | ||||||
| # RUN strip --strip-all /uv | ||||||
| COPY rust-toolchain.toml . | ||||||
| RUN <<HEREDOC | ||||||
| # Install rustup, but skip installing a default toolchain as we only want the version from `rust-toolchain.toml`: | ||||||
| curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal --default-toolchain none | ||||||
|
|
||||||
| # When rustup installs the toolchain ensure it actually uses the minimal profile, avoiding excess layer weight: | ||||||
| # https://github.com/rust-lang/rustup/issues/3805#issuecomment-2094066914 | ||||||
| echo 'profile = "minimal"' >> rust-toolchain.toml | ||||||
| echo 'targets = [ "aarch64-unknown-linux-musl", "x86_64-unknown-linux-musl" ]' >> rust-toolchain.toml | ||||||
| # Add the relevant musl target triples (for a building binary with static linking): | ||||||
| # Workaround until `ensure` arrives: https://github.com/rust-lang/rustup/issues/2686#issuecomment-788825744 | ||||||
| rustup show | ||||||
| HEREDOC | ||||||
|
Comment on lines
+37
to
+48
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When installing Likewise there was a mishap with the The next line added to
|
||||||
|
|
||||||
| # Handle individual images differences for ARM64 / AMD64: | ||||||
| FROM builder-base AS builder-arm64 | ||||||
| ENV CARGO_BUILD_TARGET=aarch64-unknown-linux-musl | ||||||
|
|
||||||
| FROM builder-base AS builder-amd64 | ||||||
| ENV CARGO_BUILD_TARGET=x86_64-unknown-linux-musl | ||||||
|
Comment on lines
+50
to
+55
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is effectively what you were doing earlier with the bash conditional statement on This actually removes the need for the The next stage ( It'll remain as being built from the same A prior commit in this PR history had an alternative approach where both targets were built, and these separate stages were located at the end of the |
||||||
|
|
||||||
| # Build app: | ||||||
| FROM builder-${TARGETARCH} AS builder-app | ||||||
| COPY crates/ crates/ | ||||||
| COPY Cargo.toml Cargo.lock . | ||||||
| ARG APP_NAME=uv | ||||||
| ARG CARGO_HOME=/usr/local/cargo | ||||||
| ARG RUSTFLAGS="-C strip=symbols -C relocation-model=static -C target-feature=+crt-static -C opt-level=z" | ||||||
|
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've used the
|
||||||
| ARG TARGETPLATFORM | ||||||
| RUN \ | ||||||
| --mount=type=cache,target="/root/.cache/zig",id="zig-cache" \ | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Worth using |
||||||
| # Cache mounts (dirs for crates cache + build target): | ||||||
| # https://doc.rust-lang.org/cargo/guide/cargo-home.html#caching-the-cargo-home-in-ci | ||||||
| # CAUTION: As cargo uses multiple lock files (eg: `${CARGO_HOME}/{.global-cache,.package-cache,.package-cache-mutate}`), do not mount subdirs individually. | ||||||
| --mount=type=cache,target="${CARGO_HOME}",id="cargo-cache" \ | ||||||
| # This cache mount is specific enough that you may not have any concurrent builds needing to share it, communicate that expectation explicitly: | ||||||
| --mount=type=cache,target="target/",id="cargo-target-${APP_NAME}-${TARGETPLATFORM}",sharing=locked \ | ||||||
| # These are redundant as they're easily reconstructed from cache above. Use TMPFS mounts to exclude from cache mounts: | ||||||
| # TMPFS mount is a better choice than `rm -rf` command (which is risky on a cache mount that is shared across concurrent builds). | ||||||
| --mount=type=tmpfs,target="${CARGO_HOME}/registry/src" \ | ||||||
| --mount=type=tmpfs,target="${CARGO_HOME}/git/checkouts" \ | ||||||
|
Comment on lines
+64
to
+76
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The cache mounting here is a bit excessive, I've added some context with comments. You're not really going to need to maintain much here AFAIK, so it's mostly out of sight/mind in practice. If you're not familiar with this feature, the default We have 3 caches here,
The last two If you do want something that looks simpler, Earthly has tooling to simplify rust builds and cache management. However |
||||||
| <<HEREDOC | ||||||
| cargo zigbuild --release --bin "${APP_NAME}" --target "${CARGO_BUILD_TARGET}" | ||||||
| cp "target/${CARGO_BUILD_TARGET}/release/${APP_NAME}" "/${APP_NAME}" | ||||||
| HEREDOC | ||||||
|
Comment on lines
+77
to
+80
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is using |
||||||
|
|
||||||
| # Final stage - Image containing only uv + empty /io dir: | ||||||
| FROM scratch | ||||||
| COPY --from=build /uv /uv | ||||||
| COPY --from=builder-app /uv /uv | ||||||
| WORKDIR /io | ||||||
| ENTRYPOINT ["/uv"] | ||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The cache mount here (and workaround for
docker-cleanin this base image used), doesn't bring any savings to disk usage of this layer. It does however keep the cache external instead of the automatic clean, which is useful when the layer is invalidated, as it speeds up the build time.