From 1fc61c1aa764e8344ece4f0079b40dbee861a4d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9F=91=A8=F0=9F=8F=BB=E2=80=8D=F0=9F=92=BB=20Romain=20M?= =?UTF-8?q?arcadier?= Date: Thu, 12 Aug 2021 17:32:21 +0200 Subject: [PATCH] polish up build a bit further --- superchain/Dockerfile | 219 +++++++++++++++++++++++++++----------- superchain/build-local.sh | 10 +- 2 files changed, 159 insertions(+), 70 deletions(-) diff --git a/superchain/Dockerfile b/superchain/Dockerfile index f5dd610d50..7af24ab59b 100644 --- a/superchain/Dockerfile +++ b/superchain/Dockerfile @@ -1,42 +1,120 @@ +######################################################################################################################## +# Introduction +######################################################################################################################## +# This is a multi-sage build, which eventually results in a squashed image. +# The image intended for release/use is the one obtained using `docker build --target=superchain ...`. +# +# Stages are as follows +# 1. [bindist] Prepare a bunch of binary distributions +# 2. [staging] Prepare the superchain staging image +# 3. [superchain] Squash into a single layer +# 4. [test] Run tests on the superchain stage +# +# Multi-stage build allows several of these steps to run in parallel up to some point. It makes the build cache usage +# more efficient, and allows us to run cross-architecture builds in the most efficient possible manner, by running the +# "platform-independent" elements within the build platform, which does not require any kind of emulation. + + +######################################################################################################################## +# Prepare install images of "manual" binary distributions (runs on BUILD platform for speed) +######################################################################################################################## +FROM --platform=${BUILDPLATFORM} ${REGISTRY}/debian:10 as bindist + +# Build & target platforms, they are provided by buildx and will look like "linux/amd64" or "linux/arm64" +ARG BUILDPLATFORM +ARG TARGETPLATFORM +# So we can use a different registry than public ECR if needed +ARG REGISTRY="public.ecr.aws/debian" + +# We require a couple of tools to be available in order to work here... +RUN apt-get update && apt-get install -y curl gpg tar zsh + +# We'll be using zsh substitutions, so ensuring this is the shell we use +SHELL ["/bin/zsh", "-c"] + +# Prepare maven binary distribution +ARG M2_VERSION="3.6.3" +ENV M2_DISTRO="https://www.apache.org/dist/maven/maven-3" +RUN set -eo pipefail \ + && curl -sL "${M2_DISTRO}/${M2_VERSION}/binaries/apache-maven-${M2_VERSION}-bin.tar.gz" \ + -o /tmp/apache-maven.tar.gz \ + && curl -sL "${M2_DISTRO}/${M2_VERSION}/binaries/apache-maven-${M2_VERSION}-bin.tar.gz.asc" \ + -o /tmp/apache-maven.tar.gz.asc \ + && mkdir -p /tmp/gpg-maven && chmod go-rwx /tmp/gpg-maven \ + && curl -sL "https://www.apache.org/dist/maven/KEYS" | gpg --homedir /tmp/gpg-maven --import \ + && gpg --homedir /tmp/gpg-maven --verify /tmp/apache-maven.tar.gz.asc /tmp/apache-maven.tar.gz \ + && mkdir -p /opt/apache/maven && tar xzf /tmp/apache-maven.tar.gz -C /opt/apache/maven --strip-components=1 + +# Prepare .NET Core distribution +ARG DOTNET_CHANNEL="3.1" +ENV DOTNET_FEED="https://dotnetcli.azureedge.net/dotnet" +RUN DOTNET_VERSION=$(curl -fSsL "${DOTNET_FEED}/Sdk/${DOTNET_CHANNEL}/latest.version") \ + && DOTNET_ASSET="dotnet-sdk-${DOTNET_VERSION}-linux-${${TARGETPLATFORM#linux/}/amd64/x64}.tar.gz" \ + && curl -fSsL "${DOTNET_FEED}/Sdk/${DOTNET_VERSION}/${DOTNET_ASSET}" \ + -o /tmp/dotnet.tar.gz \ + && mkdir -p /opt/microsoft/dotnet \ + && tar zxf /tmp/dotnet.tar.gz -C /opt/microsoft/dotnet + +# Prepare PowerShell LTS distribution +ENV POWERSHELL_RELEASES="https://github.com/PowerShell/PowerShell/releases" +RUN POWERSHELL_RELEASE=$(curl -fSsL "https://aka.ms/powershell-release?tag=lts" -o /dev/null -w %{url_effective}) \ + && POWERSHELL_VERSION=${POWERSHELL_RELEASE#${POWERSHELL_RELEASES}/tag/v} \ + && ASSET="powershell-${POWERSHELL_VERSION}-linux-${${TARGETPLATFORM#linux/}/amd64/x64}.tar.gz" \ + && curl -fSsL "${POWERSHELL_RELEASES}/download/v${POWERSHELL_VERSION}/${ASSET}" \ + -o /tmp/powershell.tar.gz \ + && mkdir -p /opt/microsoft/powershell \ + && tar zxf /tmp/powershell.tar.gz -C /opt/microsoft/powershell \ + && chmod +x /opt/microsoft/powershell/pwsh + +# Prepare Go distribution +ARG GO_VERSION="1.16.7" +RUN curl -fSsL "https://golang.org/dl/go${GO_VERSION}.linux-${TARGETPLATFORM#linux/}.tar.gz" -o /tmp/go.tar.gz \ + && mkdir -p /opt/golang/go && tar -xzf /tmp/go.tar.gz -C /opt/golang/go + ######################################################################################################################## # Set up the image ######################################################################################################################## -FROM public.ecr.aws/debian/debian:10-slim as superchain +FROM ${REGISTRY}/debian:10-slim as staging + +# Build & target platforms, they are provided by buildx and will look like "linux/amd64" or "linux/arm64" +ARG BUILDPLATFORM +ARG TARGETPLATFORM +# So we can use a different registry than public ECR if needed +ARG REGISTRY="public.ecr.aws/debian" + +SHELL ["/bin/bash", "-c"] # Set locale and some other interesting environment variables ENV LANG="C.UTF-8" \ LC_ALL="C.UTF-8" \ CHARSET="UTF-8" \ \ - DOTNET_CLI_TELEMETRY_OPTOUT="true" \ DOTNET_RUNNING_IN_CONTAINER="true" \ - DOTNET_NOLOGO="true" \ DOTNET_USE_POLLING_FILE_WATCHER="true" \ NUGET_XMLDOC_MODE="skip" \ \ - GEM_HOME="/usr/local/bundle" \ - BUNDLE_SILENCE_ROOT_WARNING="1" \ + M2_HOME="/opt/apache/maven" \ + M2="/opt/apache/maven/bin" \ \ - M2_VERSION="3.6.3" \ - M2_HOME="/usr/local/apache-maven" \ - M2="/usr/local/apache-maven/bin" \ - MAVEN_OPTS="-Xms256m -Xmx512m" \ - \ - GOROOT="/usr/local/go" \ - GITHUB_CLI_VERSION="1.13.1" + GOROOT="/usr/local/go" -# Build & target platforms, they are provided by buildx and will look like "linux/amd64" or "linux/arm64" -ARG BUILDPLATFORM -ARG TARGETPLATFORM - -# Installing tar as it's needed for installing Maven, curl as it's needed to download a bunch of things during the -# build, build-essentials as it would be needed to install any dependency including native components, and a couple of -# generally useful/pervasive packages. +# Installing the system dependencies we need... RUN apt-get update \ - && apt-get -y install apt-transport-https dirmngr gnupg ca-certificates \ - tar curl \ - build-essential \ - git gzip libicu63 libssl-dev openssl rsync unzip zip \ + && apt-get -y install \ + apt-transport-https \ + build-essential \ + ca-certificates \ + curl \ + dirmngr \ + git \ + gnupg \ + gzip \ + libicu63 \ + libssl-dev \ + openssl \ + rsync \ + unzip \ + zip \ && rm -rf /var/lib/apt/lists/* # Install mono @@ -49,27 +127,10 @@ RUN apt-key add /tmp/mono.asc && rm /tmp/mono.asc && rm -rf /var/lib/apt/lists/* # Install .NET Core and PowerShell -RUN ARCH="${TARGETPLATFORM#linux/}" && ARCH="${ARCH/amd64/x64}" \ - && FEED_URL="https://dotnetcli.azureedge.net/dotnet" \ - && DOTNET_CHANNEL=3.1 \ - && DOTNET_VERSION=$(curl -fSsL "${FEED_URL}/Sdk/${DOTNET_CHANNEL}/latest.version") \ - && curl -fSsL "${FEED_URL}/Sdk/${DOTNET_VERSION}/dotnet-sdk-${DOTNET_VERSION}-linux-${ARCH}.tar.gz" \ - -o /tmp/dotnet.tar.gz \ - && mkdir -p /opt/microsoft/dotnet/${DOTNET_CHANNEL} \ - && tar zxf /tmp/dotnet.tar.gz -C /opt/microsoft/dotnet/${DOTNET_CHANNEL} \ - && rm /tmp/dotnet.tar.gz \ - && ln -s /opt/microsoft/dotnet/${DOTNET_CHANNEL}/dotnet /usr/bin/dotnet \ - && POWERSHELL_RELEASES="https://github.com/PowerShell/PowerShell/releases" \ - && POWERSHELL_RELEASE=$(curl -fSsL "https://aka.ms/powershell-release?tag=lts" -o /dev/null -w %{url_effective}) \ - && POWERSHELL_VERSION=${POWERSHELL_RELEASE#${POWERSHELL_RELEASES}/tag/v} \ - && ASSET="powershell-${POWERSHELL_VERSION}-linux-${ARCH}.tar.gz" \ - && curl -fSsL "${POWERSHELL_RELEASES}/download/v${POWERSHELL_VERSION}/${ASSET}" \ - -o /tmp/powershell.tar.gz \ - && mkdir -p /opt/microsoft/powershell/lts \ - && tar zxf /tmp/powershell.tar.gz -C /opt/microsoft/powershell/lts \ - && rm /tmp/powershell.tar.gz \ - && chmod +x /opt/microsoft/powershell/lts/pwsh \ - && ln -s /opt/microsoft/powershell/lts/pwsh /usr/bin/pwsh +COPY --from=bindist /opt/microsoft/dotnet /opt/microsoft/dotnet +RUN ln -s /opt/microsoft/dotnet/dotnet /usr/bin/dotnet +COPY --from=bindist /opt/microsoft/powershell /opt/microsoft/powershell +RUN ln -s /opt/microsoft/powershell/pwsh /usr/bin/pwsh # Install Python 3 RUN apt-get update \ @@ -82,7 +143,8 @@ RUN apt-get update # Install Rust (required for https://pypi.org/project/cryptography/ in certain circumstances... like ARM64 arch) ENV RUSTUP_HOME=/usr/local/rustup \ CARGO_HOME=/usr/local/cargo -RUN curl -fSsL "https://sh.rustup.rs" | sh -s -- -y --no-modify-path --profile=minimal \ +RUN set -eo pipefail \ + && curl -fSsL "https://sh.rustup.rs" | sh -s -- -y --no-modify-path --profile=minimal \ && echo "source ${CARGO_HOME}/env" >> /etc/profile.d/cargo.sh ENV PATH=$PATH:${CARGO_HOME}/bin @@ -97,25 +159,15 @@ RUN apt-key add /tmp/corretto.asc && rm /tmp/corretto.asc && rm -rf /var/lib/apt/lists/* # Install Maven -RUN curl -sL "https://www.apache.org/dist/maven/maven-3/${M2_VERSION}/binaries/apache-maven-${M2_VERSION}-bin.tar.gz" \ - -o /tmp/apache-maven.tar.gz \ - && curl -sL "https://www.apache.org/dist/maven/maven-3/${M2_VERSION}/binaries/apache-maven-${M2_VERSION}-bin.tar.gz.asc"\ - -o /tmp/apache-maven.tar.gz.asc \ - && mkdir -p /tmp/gpg-maven && chmod go-rwx /tmp/gpg-maven \ - && curl -sL "https://www.apache.org/dist/maven/KEYS" | gpg --homedir /tmp/gpg-maven --import \ - && gpg --homedir /tmp/gpg-maven --verify /tmp/apache-maven.tar.gz.asc /tmp/apache-maven.tar.gz \ - && mkdir -p /usr/local && (cd /usr/local && tar xzf /tmp/apache-maven.tar.gz) \ - && mv /usr/local/apache-maven-${M2_VERSION} /usr/local/apache-maven \ - && for BIN in $(find /usr/local/apache-maven/bin -type f -executable); \ - do \ - ln -s $BIN /usr/local/bin/$(basename $BIN); \ - done \ - && rm -rf /tmp/apache-maven.tar.gz /tmp/apache-maven.tar.gz.asc /tmp/gpg-maven +COPY --from=bindist /opt/apache/maven ${M2_HOME} +RUN for BIN in $(find ${M2} -type f -executable) \ + ;do \ + ln -s $BIN /usr/local/bin/$(basename $BIN) \ + ;done COPY superchain/m2-settings.xml /root/.m2/settings.xml # Install Go -RUN curl -fSsL "https://golang.org/dl/go1.16.7.linux-${TARGETPLATFORM#linux/}.tar.gz" -o /tmp/go.tar.gz \ - && mkdir -p /usr/local && (cd /usr/local && tar -xzf /tmp/go.tar.gz) +COPY --from=bindist /opt/golang/go ${GOROOT} ENV PATH="$GOROOT/bin:$PATH" # Install Docker @@ -128,7 +180,10 @@ RUN apt-key add /tmp/docker.asc && rm /tmp/docker.asc VOLUME /var/lib/docker # Install GitHub CLI -RUN curl -fSsL "https://github.com/cli/cli/releases/download/v${GITHUB_CLI_VERSION}/gh_${GITHUB_CLI_VERSION}_linux_${TARGETPLATFORM#linux/}.deb"\ +ARG GITHUB_CLI_VERSION="1.13.1" +RUN BASE="https://github.com/cli/cli/releases/download" \ + && echo "${BASE}/v${GITHUB_CLI_VERSION}/gh_${GITHUB_CLI_VERSION}_linux_${TARGETPLATFORM#linux/}.deb" \ + && curl -fSsL "${BASE}/v${GITHUB_CLI_VERSION}/gh_${GITHUB_CLI_VERSION}_linux_${TARGETPLATFORM#linux/}.deb" \ -o /tmp/gh.deb \ && apt-get update \ && apt-get -y install /tmp/gh.deb \ @@ -138,7 +193,7 @@ RUN curl -fSsL "https://github.com/cli/cli/releases/download/v${GITHUB_CLI_VERSI # Install Node 10+ (configurable with '--build-arg NODE_MAJOR_VERSION=xxx') and yarn # (Put this as late as possible in the Dockerfile so we get to reuse the layer cache # for most of the multiple builds). -ARG NODE_MAJOR_VERSION=12 +ARG NODE_MAJOR_VERSION="12" COPY superchain/gpg/nodesource.asc /tmp/nodesource.asc COPY superchain/gpg/yarn.asc /tmp/yarn.asc RUN apt-key add /tmp/nodesource.asc && rm /tmp/nodesource.asc \ @@ -155,6 +210,30 @@ COPY superchain/ssh_config /root/.ssh/config RUN chmod 600 /root/.ssh/config COPY superchain/dockerd-entrypoint.sh /usr/local/bin/ +CMD ["/bin/bash"] + +######################################################################################################################## +# Creating squashed image +######################################################################################################################## +FROM scratch as superchain +# Set locale and some other interesting environment variables +ENV LANG="C.UTF-8" \ + LC_ALL="C.UTF-8" \ + CHARSET="UTF-8" \ + \ + DOTNET_CLI_TELEMETRY_OPTOUT="true" \ + DOTNET_RUNNING_IN_CONTAINER="true" \ + DOTNET_NOLOGO="true" \ + DOTNET_USE_POLLING_FILE_WATCHER="true" \ + NUGET_XMLDOC_MODE="skip" \ + \ + M2_HOME="/opt/apache/maven" \ + M2="/opt/apache/maven/bin" \ + MAVEN_OPTS="-Xms256m -Xmx512m" \ + \ + GOROOT="/usr/local/go" +COPY --from=staging / / + # Add the source used to build this Docker image (to facilitate re-builds, forensics) COPY superchain /docker-source @@ -172,8 +251,18 @@ LABEL org.opencontainers.image.created=${BUILD_TIMESTAMP} CMD ["/bin/bash"] ######################################################################################################################## -# Running tests now +# Running tests ######################################################################################################################## FROM superchain as test +# Set up a volume, so we can possibly inspect the data after the fact (esp. in case of problems) +VOLUME /tmp/source COPY . /tmp/source -RUN cd /tmp/source && yarn install --frozen-lockfile && yarn build && yarn test +WORKDIR /tmp/source +# Make sure we start fresh (symlinks from outside the build may cause issues, e.g: python venvs) +RUN git clean -fqdx +# Install all dependencies again +RUN yarn install --frozen-lockfile +# Build the code +RUN yarn build +# Test the code +RUN yarn test diff --git a/superchain/build-local.sh b/superchain/build-local.sh index 5e3639e472..053a87288f 100755 --- a/superchain/build-local.sh +++ b/superchain/build-local.sh @@ -20,13 +20,13 @@ else fi # Now on to building the image -docker buildx build \ - --load \ - --pull \ - --target superchain \ +docker build \ + --target test \ + --build-arg BUILDPLATFORM=linux/amd64 \ + --build-arg TARGETPLATFORM=linux/amd64 \ --build-arg BUILD_TIMESTAMP=$(date -u +'%Y-%m-%dT%H:%M:%SZ') \ + --build-arg REGISTRY="docker.io/library" \ --build-arg COMMIT_ID=${COMMIT_ID} \ - --build-arg NODE_MAJOR_VERSION=10 \ -t "jsii/superchain:local" \ -f ${PWD}/Dockerfile \ ..