1
\$\begingroup\$

I wrote this Dockerfile (multi-stage) where the first stage builds the project and in the second stage we just copy the stuff over. I am wondering can I please get some feedback? more specifically the use of apk add --no-cache --virtual= and how I install packages and whether I am following the best practices. Thank you

ARG SCALA_VERSION="2.12.15"
ARG BISON_VERSION="1.875"
ARG SPIM_VERSION="8.0"
ARG TOOLSPATH="/usr/lib/tools"

FROM azul/zulu-openjdk-alpine@sha256:a75ab3f1021fdd206e94b5b17a0f4e8813acca5f3953db540e2f1cd2f42d6bfe AS build

ARG SCALA_VERSION
ARG BISON_VERSION
ARG SPIM_VERSION
ARG TOOLSPATH

WORKDIR /usr/lib

# Install basics
RUN apk add --no-cache bash make gcompat build-base m4 \
  && apk add --no-cache --virtual=build-dependencies wget ca-certificates

# Install Scala
RUN wget -q --no-cookies "https://downloads.lightbend.com/scala/${SCALA_VERSION}/scala-${SCALA_VERSION}.tgz" -O - | gunzip | tar x \
  && apk del build-dependencies \
  && rm -rf /tmp/*

ENV SCALAHOME="/usr/lib/scala-$SCALA_VERSION"
ENV PATH="$SCALAHOME/bin:$PATH"

# Build and install Bison
RUN wget -q "https://ftp.gnu.org/gnu/bison/bison-${BISON_VERSION}.tar.gz" -O - | gunzip | tar x \
  && cd "bison-${BISON_VERSION}" \
  && ./configure \
  && make \
  && make install

# Build and install Spim
RUN apk add --no-cache flex \
  && wget -q "http://pages.cs.wisc.edu/~larus/SPIM/spim-${SPIM_VERSION}.tar.gz" -O - | gunzip | tar x \
  && cd "spim-${SPIM_VERSION}/spim" \
  && make -j5 \
  && make -j5 install

# Build Scala-Bison
RUN apk add --no-cache git \
  && git clone https://github.com/boyland/scala-bison scala-bison \
  && cd scala-bison \
  && wget -q "https://github.com/boyland/scala-bison/releases/download/v1.1/scala-bison-2.12.jar" -O "scala-bison.jar" \
  && make boot \
  && make compile \
  && make \
  # Repeat for bootstraping
  && make boot \
  && make compile \
  && make \
  && mkdir -p "${TOOLSPATH}" \
  && cp /usr/lib/scala-bison/scala-bison.jar "${TOOLSPATH}/lib"

WORKDIR ${TOOLSPATH}

COPY . .

ENV TOOLSPATH="$TOOLSPATH"
ENV PATH="$TOOLSPATH/bin:$PATH"
ENV PATH="$TOOLSPATH/cmd:$PATH"

RUN find ./cmd ./bin -type f -print0 | xargs -0 chmod +x

RUN make \
  && make install-docker

FROM azul/zulu-openjdk-alpine@sha256:a75ab3f1021fdd206e94b5b17a0f4e8813acca5f3953db540e2f1cd2f42d6bfe AS base

ARG SCALA_VERSION
ARG BISON_VERSION
ARG SPIM_VERSION
ARG TOOLSPATH

LABEL maintainer="foo@bar.edu"
LABEL version="1.0.0"

WORKDIR /usr/lib

# Install basics
RUN apk add --no-cache bash make gcompat build-base m4 \
  && apk add --no-cache --virtual=build-dependencies wget ca-certificates

# Install Scala
RUN wget -q --no-cookies "https://downloads.lightbend.com/scala/${SCALA_VERSION}/scala-${SCALA_VERSION}.tgz" -O - | gunzip | tar x \
  && apk del build-dependencies \
  && rm -rf /tmp/*

ENV SCALAHOME="/usr/lib/scala-$SCALA_VERSION"
ENV PATH="$SCALAHOME/bin:$PATH"

# Build and install Bison
RUN wget -q "https://ftp.gnu.org/gnu/bison/bison-${BISON_VERSION}.tar.gz" -O - | gunzip | tar x \
  && cd "bison-${BISON_VERSION}" \
  && ./configure \
  && make \
  && make install

RUN apk add --no-cache flex \
  && wget -q "http://pages.cs.wisc.edu/~larus/SPIM/spim-${SPIM_VERSION}.tar.gz" -O - | gunzip | tar x \
  && cd "spim-${SPIM_VERSION}/spim" \
  && make -j5 \
  && make -j5 install

WORKDIR ${TOOLSPATH}

COPY --from=build "${TOOLSPATH}/lib" "${TOOLSPATH}/lib"
COPY --from=build "${TOOLSPATH}/bin" "${TOOLSPATH}/bin"
COPY --from=build "${TOOLSPATH}/cmd" "${TOOLSPATH}/cmd"

ENV TOOLSPATH="$TOOLSPATH"
ENV PATH="$TOOLSPATH/bin:$PATH"
ENV PATH="$TOOLSPATH/cmd:$PATH"

RUN find ./cmd ./bin -type f -print0 | xargs -0 chmod +x

WORKDIR /root
\$\endgroup\$
1
\$\begingroup\$

This seems like a very packed dockerfile to me, I personally prefer to keep the dockerfile itself as simple as possible and to move large parts of the installation setup into a script that I can COPY to the build stage. This allows me to keep the dockerfile basically only for actual container setup and the script for the building step, which I personally find a bit cleaner.

Note that I don't have a lot of experience maintaining Dockerfiles, so most of this is coming from a very amateur perspective and you may have very good reasons to ignore most of what I will say.

All that said, there are some things that I noticed:

  • sha256 version hashes are meaningless. They do not communicate semantically meaningful information to a reader of the Dockerfile about which tag of the container is actually pulled. Instead of a sha hash, I'd expect a version number like azul/zulu-openjdk-alpine:11.0.12.

  • I dislike that you are deleting build-dependencies before continuing to use wget in the next installation step. In addition those build-dependencies are not actually related to the build, but to network interactions, so download-dependencies might be a better virtual name.

  • Docker caches layers and this could make the build process quicker if you move the least-often changed steps to the top. I don't see a particular reason to change the order of your installations, but I don't know how often these dependencies each change, it's just something to be aware of.

  • It's not obvious within the Dockerfile why you download the scala-bison-2.12.jar. The comment four lines later gives a hint (and reading the README of the source you get it from makes it clear), but it feels curious when reading it the first time.

  • You seem to rely on the docker build caching mechanism to speed up the creation of the result image by having the exact same sequence of steps in the setup before the WORKDIR ${TOOLSPATH} instruction. I very much dislike that, because it breaks somewhat easily, even with just reordering some instructions... The easy fix for that is to create a separate stage as a base and basing both the builder stage and the result stage off of that:

    FROM azul/zulu-openjdk-alpine:11 as base
    
    WORKDIR /usr/lib
    RUN apk add ...
    ...
      && make -j5 install
    
    FROM base as builder
    
    WORKDIR /usr/lib
    RUN apk add --no-cache git \
      ...
    
    FROM base
    
    WORKDIR ${TOOLSPATH}
    
    COPY --from=build "${TOOLSPATH}/lib" "${TOOLSPATH}/lib"
    ...
    
\$\endgroup\$
1
  • \$\begingroup\$ this was incredibly helpful. I learned something new. Thank you \$\endgroup\$
    – Node.JS
    Jan 12 at 18:17

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.