Unverified Commit 520d073c authored by Aaron Todd's avatar Aaron Todd Committed by GitHub
Browse files

optimize docker image layers (#4271)

## Description
[Adding/fixing caching of Gradle
wrapper](https://github.com/smithy-lang/smithy-rs/pull/4270) to our
build image increased it's size beyond 2GB. We started seeing issues
with dry run release and download artifact [silently
failing](https://github.com/actions/download-artifact/issues/396). This
PR attempts to optimize the image layers to reduce it's overall size and
hopefully work past these issues.

### Key Optimizations

Multi-stage consolidation:
• Combined all cargo tool installations into a single `cargo_tools`
stage instead of separate stages per tool
• Created dedicated `gradle_wrapper` stage that reuses install_rust base
to avoid duplicating Java installation

Layer reduction:
• Combined RUN commands with cleanup operations (yum clean all && rm -rf
/var/cache/yum)
• Added cargo cache cleanup (rm -rf /opt/cargo/registry/src && rm -rf
/opt/cargo/git/db) after installations
• Consolidated AWS CLI installation and cleanup in single layer

Gradle wrapper optimization:
• Removed checkout_smithy_rs_tools conditional logic - now always
fetches gradle wrapper
• Uses sparse checkout to fetch only required files (gradlew,
gradle/wrapper, gradle.properties)


Before and after:

```
> finch images
REPOSITORY                          TAG                                            IMAGE ID        CREATED         PLATFORM       SIZE       BLOB SIZE
smithy-rs-build-image               latest                                         449b886b6045    2 days ago      linux/arm64    6.663GB    2.223GB
smithy-rs-base-image                local                                          2c1a924209b3    2 days ago      linux/arm64    6.663GB    2.223GB
smithy-rs-base-image                ci-d4053cbdd4e82adff099eb2f4e6cbaae2139d577    2c1a924209b3    2 days ago      linux/arm64    6.663GB    2.223GB
ghcr.io/github/github-mcp-server    latest                                         9cd2504664e1    2 months ago    linux/arm64    46.25MB    13.08MB
todaaron at 842f577d1917 in ~/sandbox/rs/smithy-rs on aajtodd/optimize-dockerfile ✗                                                                                  [91e76ab4]  10:22
> finch images
REPOSITORY                          TAG                                            IMAGE ID        CREATED           PLATFORM       SIZE       BLOB SIZE
smithy-rs-base-image                local                                          811268820b1a    48 seconds ago    linux/arm64    4.57GB     1.564GB
smithy-rs-base-image                ci-ea6828b430e9f84941104db55cb6d58d201ad145    811268820b1a    59 seconds ago    linux/arm64    4.57GB     1.564GB
smithy-rs-build-image               latest                                         b66608bb54c3    3 days ago        linux/arm64    4.571GB    1.564GB
smithy-rs-base-image                ci-d4053cbdd4e82adff099eb2f4e6cbaae2139d577    2c1a924209b3    3 days ago        linux/arm64    6.663GB    2.223GB
ghcr.io/github/github-mcp-server    latest                                         9cd2504664e1    2 months ago      linux/arm64    46.25MB    13.08MB
```

----

_By submitting this pull request, I confirm that you can use, modify,
copy, and redistribute this contribution, under the terms of your
choice._
parent 91e76ab4
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -249,7 +249,7 @@ dependencies = [

[[package]]
name = "aws-smithy-eventstream"
version = "0.60.10"
version = "0.60.11"
dependencies = [
 "aws-smithy-types",
 "bytes",
+92 −83
Original line number Diff line number Diff line
@@ -10,14 +10,15 @@ ARG rust_stable_version=1.86.0
ARG rust_nightly_version=nightly-2025-05-04

FROM ${base_image} AS bare_base_image
RUN yum -y updateinfo
RUN yum -y updateinfo && yum clean all && rm -rf /var/cache/yum

FROM bare_base_image as musl_toolchain
RUN yum -y install --allowerasing tar gzip gcc make
RUN curl https://musl.libc.org/releases/musl-1.2.5.tar.gz -o musl-1.2.5.tar.gz \
    && ls \
    && tar xvzf musl-1.2.5.tar.gz \
    && (cd musl-1.2.5 && ./configure && make install)
RUN yum -y install --allowerasing tar gzip gcc make && \
    curl https://musl.libc.org/releases/musl-1.2.5.tar.gz -o musl-1.2.5.tar.gz && \
    tar xzf musl-1.2.5.tar.gz && \
    (cd musl-1.2.5 && ./configure && make install) && \
    rm -rf musl-1.2.5* && \
    yum clean all && rm -rf /var/cache/yum

#
# Rust & Tools Installation Stage
@@ -66,8 +67,8 @@ RUN yum -y install --allowerasing \
        pkgconfig \
        tar \
        xz && \
    yum clean all
RUN set -eux; \
    yum clean all && rm -rf /var/cache/yum && \
    set -eux; \
    if [[ "$(uname -m)" == "aarch64" || "$(uname -m)" == "arm64" ]]; then \
        curl https://static.rust-lang.org/rustup/archive/1.24.3/aarch64-unknown-linux-gnu/rustup-init --output rustup-init; \
        echo "32a1532f7cef072a667bac53f1a5542c99666c4071af0c9549795bbdb2069ec1 rustup-init" | sha256sum --check; \
@@ -88,64 +89,67 @@ RUN set -eux; \
    cargo --version; \
    cargo +${rust_nightly_version} --version;

FROM install_rust AS local_tools
ARG rust_nightly_version
ENV GRADLE_USER_HOME=/home/build/.gradle
COPY . tools/ci-build
# when `checkout_smithy_rs_tools` is set to true, this commit will be checked out
FROM install_rust AS gradle_wrapper
# Commit/branch to checkout for gradle wrapper
ARG smithy_rs_commit_hash=main
# If the `checkout_smithy_rs_tools` arg is set to true, then the Dockerfile will acquire the tools
# source code by checking out smithy-lang/smithy-rs/main rather than copying them from the local directory.
# If it is false we still clone the smithy-rs repo beecause we need the gradle wrapper to install the gradle
# binary. But in this case we delete the repo before installing the tools and use the local versions from
# the build context.
ARG checkout_smithy_rs_tools=false
ENV GRADLE_USER_HOME=/opt/gradle
WORKDIR /opt/gradle
RUN set -eux; \
    git clone https://github.com/smithy-lang/smithy-rs.git --depth 1; \
    cd smithy-rs; \
    if [[ "${checkout_smithy_rs_tools}" == "true" ]]; then \
        git checkout ${smithy_rs_commit_hash}; \
    else \
    source /etc/profile.d/java_home.sh; \
    git init smithy-rs && cd smithy-rs; \
    git remote add origin https://github.com/smithy-lang/smithy-rs.git; \
    git sparse-checkout init --cone; \
    git sparse-checkout set gradlew gradle/wrapper gradle.properties; \
    git fetch --depth=1 origin ${smithy_rs_commit_hash}; \
    git checkout FETCH_HEAD; \
    # Run the gradle wrapper with no args to download the gradle binary and cache it in the image
    ./gradlew; \
    cd ..; \
        rm -rf smithy-rs; \
    fi; \
    cargo install --locked --path tools/ci-build/changelogger; \
    cargo install --locked --path tools/ci-build/crate-hasher; \
    cargo install --locked --path tools/ci-build/difftags; \
    cargo install --locked --path tools/ci-build/publisher; \
    cargo install --locked --path tools/ci-build/runtime-versioner; \
    cargo install --locked --path tools/ci-build/sdk-lints; \
    cargo install --locked --path tools/ci-build/sdk-lockfiles; \
    cargo install --locked --path tools/ci-build/sdk-versioner; \
    chmod g+rw -R /opt/cargo/registry
    rm -rf smithy-rs

ARG cargo_deny_version=0.16.4 \
cargo_udeps_version=0.1.56 \
cargo_hack_version=0.6.27 \
cargo_minimal_versions_version=0.1.27 \
cargo_check_external_types_version=0.2.0 \
maturin_version=1.5.1 \
wasm_pack_version=0.13.1 \
cargo_wasmtime_version=34.0.1 \
cargo_component_version=0.20.0 \
cargo_semver_checks_version=0.41.0 \
cargo_mdbook_version=0.4.37 \
cargo_mdbook_mermaid_version=0.13.0
FROM install_rust AS local_tools
ARG rust_nightly_version
COPY . tools/ci-build
RUN cargo install --locked --path tools/ci-build/changelogger && \
    cargo install --locked --path tools/ci-build/crate-hasher && \
    cargo install --locked --path tools/ci-build/difftags && \
    cargo install --locked --path tools/ci-build/publisher && \
    cargo install --locked --path tools/ci-build/runtime-versioner && \
    cargo install --locked --path tools/ci-build/sdk-lints && \
    cargo install --locked --path tools/ci-build/sdk-lockfiles && \
    cargo install --locked --path tools/ci-build/sdk-versioner && \
    chmod g+rw -R /opt/cargo/registry && \
    rm -rf /opt/cargo/registry/src && \
    rm -rf /opt/cargo/git/db

RUN cargo install cargo-deny --locked --version ${cargo_deny_version} \
&& cargo +${rust_nightly_version} install cargo-udeps --locked --version ${cargo_udeps_version} \
&& cargo install cargo-hack --locked --version ${cargo_hack_version} \
&& cargo install cargo-minimal-versions --locked --version ${cargo_minimal_versions_version} \
&& cargo install cargo-check-external-types --locked --version ${cargo_check_external_types_version} \
&& cargo install maturin --locked --version ${maturin_version} \
&& cargo install wasm-pack --locked --version ${wasm_pack_version} \
&& cargo install wasmtime-cli --features="component-model" --locked --version ${cargo_wasmtime_version} \
&& cargo +${rust_nightly_version} install cargo-component --locked --version ${cargo_component_version} \
&& cargo install cargo-semver-checks --locked --version ${cargo_semver_checks_version} \
&& cargo install mdbook --locked --version ${cargo_mdbook_version} \
&& cargo install mdbook-mermaid --locked --version ${cargo_mdbook_mermaid_version}
FROM install_rust AS cargo_tools
ARG cargo_deny_version=0.16.4
ARG cargo_udeps_version=0.1.56
ARG cargo_hack_version=0.6.27
ARG cargo_minimal_versions_version=0.1.27
ARG cargo_check_external_types_version=0.2.0
ARG maturin_version=1.5.1
ARG wasm_pack_version=0.13.1
ARG cargo_wasmtime_version=34.0.1
ARG cargo_component_version=0.20.0
ARG cargo_semver_checks_version=0.41.0
ARG cargo_mdbook_version=0.4.37
ARG cargo_mdbook_mermaid_version=0.13.0
ARG rust_nightly_version
RUN cargo install cargo-deny --locked --version ${cargo_deny_version} && \
    cargo +${rust_nightly_version} install cargo-udeps --locked --version ${cargo_udeps_version} && \
    cargo install cargo-hack --locked --version ${cargo_hack_version} && \
    cargo install cargo-minimal-versions --locked --version ${cargo_minimal_versions_version} && \
    cargo install cargo-check-external-types --locked --version ${cargo_check_external_types_version} && \
    cargo install maturin --locked --version ${maturin_version} && \
    cargo install wasm-pack --locked --version ${wasm_pack_version} && \
    cargo install wasmtime-cli --features="component-model" --locked --version ${cargo_wasmtime_version} && \
    cargo +${rust_nightly_version} install cargo-component --locked --version ${cargo_component_version} && \
    cargo install cargo-semver-checks --locked --version ${cargo_semver_checks_version} && \
    cargo install mdbook --locked --version ${cargo_mdbook_version} && \
    cargo install mdbook-mermaid --locked --version ${cargo_mdbook_mermaid_version} && \
    rm -rf /opt/cargo/registry/src && \
    rm -rf /opt/cargo/git/db

# nodejs needed by internal release process
FROM install_rust AS nodejs
@@ -207,30 +211,36 @@ RUN set -eux; \
        shadow-utils \
        cmake \
        tar \
        unzip; \
    yum clean all; \
    rm -rf /var/cache/yum; \
    groupadd build; \
    useradd -m -g build build; \
    chmod 775 /home/build;
RUN set -eux; cd /tmp; curl 'https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip' -o awscliv2.zip && unzip awscliv2.zip && ./aws/install
        unzip && \
    yum clean all && \
    rm -rf /var/cache/yum && \
    groupadd build && \
    useradd -m -g build build && \
    chmod 775 /home/build && \
    cd /tmp && \
    curl 'https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip' -o awscliv2.zip && \
    unzip awscliv2.zip && \
    ./aws/install && \
    rm -rf awscliv2.zip aws && \
    pip3 install --no-cache-dir mypy==0.991

COPY --chown=build:build --from=local_tools /opt/cargo /opt/cargo
COPY --chown=build:build --from=local_tools /opt/cargo/bin/cargo-deny /opt/cargo/bin/cargo-deny
COPY --chown=build:build --from=local_tools /opt/cargo/bin/cargo-udeps /opt/cargo/bin/cargo-udeps
COPY --chown=build:build --from=local_tools /opt/cargo/bin/cargo-hack /opt/cargo/bin/cargo-hack
COPY --chown=build:build --from=local_tools /opt/cargo/bin/cargo-minimal-versions /opt/cargo/bin/cargo-minimal-versions
COPY --chown=build:build --from=local_tools /opt/cargo/bin/cargo-check-external-types /opt/cargo/bin/cargo-check-external-types
COPY --chown=build:build --from=local_tools /opt/cargo/bin/maturin /opt/cargo/bin/maturin
COPY --chown=build:build --from=local_tools /opt/cargo/bin/wasm-pack /opt/cargo/bin/wasm-pack
COPY --chown=build:build --from=local_tools /opt/cargo/bin/wasmtime /opt/cargo/bin/wasmtime
COPY --chown=build:build --from=local_tools /opt/cargo/bin/cargo-component /opt/cargo/bin/cargo-component
COPY --chown=build:build --from=gradle_wrapper /opt/gradle /home/build/.gradle
COPY --chown=build:build --from=cargo_tools /opt/cargo/bin/cargo-deny /opt/cargo/bin/cargo-deny
COPY --chown=build:build --from=cargo_tools /opt/cargo/bin/cargo-udeps /opt/cargo/bin/cargo-udeps
COPY --chown=build:build --from=cargo_tools /opt/cargo/bin/cargo-hack /opt/cargo/bin/cargo-hack
COPY --chown=build:build --from=cargo_tools /opt/cargo/bin/cargo-minimal-versions /opt/cargo/bin/cargo-minimal-versions
COPY --chown=build:build --from=cargo_tools /opt/cargo/bin/cargo-check-external-types /opt/cargo/bin/cargo-check-external-types
COPY --chown=build:build --from=cargo_tools /opt/cargo/bin/maturin /opt/cargo/bin/maturin
COPY --chown=build:build --from=cargo_tools /opt/cargo/bin/wasm-pack /opt/cargo/bin/wasm-pack
COPY --chown=build:build --from=cargo_tools /opt/cargo/bin/wasmtime /opt/cargo/bin/wasmtime
COPY --chown=build:build --from=cargo_tools /opt/cargo/bin/cargo-component /opt/cargo/bin/cargo-component
COPY --chown=build:build --from=install_rust /opt/rustup /opt/rustup
COPY --chown=build:build --from=local_tools /opt/cargo/bin/cargo-semver-checks /opt/cargo/bin/cargo-semver-checks
COPY --chown=build:build --from=local_tools /opt/cargo/bin/mdbook /opt/cargo/bin/mdbook
COPY --chown=build:build --from=local_tools /opt/cargo/bin/mdbook-mermaid /opt/cargo/bin/mdbook-mermaid
COPY --chown=build:build --from=cargo_tools /opt/cargo/bin/cargo-semver-checks /opt/cargo/bin/cargo-semver-checks
COPY --chown=build:build --from=cargo_tools /opt/cargo/bin/mdbook /opt/cargo/bin/mdbook
COPY --chown=build:build --from=cargo_tools /opt/cargo/bin/mdbook-mermaid /opt/cargo/bin/mdbook-mermaid
COPY --chown=build:build --from=nodejs /opt/nodejs /opt/nodejs
COPY --chown=build:build --from=musl_toolchain /usr/local/musl/ /usr/local/musl/
COPY --chown=build:build --from=local_tools /home/build/.gradle /home/build/.gradle
ENV PATH=/opt/nodejs/bin:/opt/cargo/bin:$PATH:/usr/local/musl/bin/ \
    NODE_HOME=/opt/nodejs \
    CARGO_HOME=/opt/cargo \
@@ -245,7 +255,6 @@ ENV PATH=/opt/nodejs/bin:/opt/cargo/bin:$PATH:/usr/local/musl/bin/ \
# This is used primarily by the `build.gradle.kts` files in choosing how to execute build tools. If inside the image,
# they will assume the tools are on the PATH, but if outside of the image, they will `cargo run` the tools.
ENV SMITHY_RS_DOCKER_BUILD_IMAGE=1
RUN pip3 install --no-cache-dir mypy==0.991
WORKDIR /home/build
# RUSTUP_TOOLCHAIN takes precedence over everything except `+<toolchain>` args. This will allow us to ignore the toolchain
# file during CI, avoiding issues during Rust version upgrades.