Docker Community Forums

Share and learn in the Docker community.

Caching Build Stages with Docker Hub and/or Docker Cloud

automatedbuilds
dockercloud
dockerhub

(Mbklein) #1

I have a Rails application that uses the following multi-stage Dockerfile:

#################################
# Build the support container
FROM ruby:2.4.2 as base
LABEL tld.where.i.work.app=my_app \
      tld.where.i.work.role=build
ENV BUILD_DEPS="build-essential libpq-dev tzdata locales unzip" \
    DEBIAN_FRONTEND="noninteractive"
RUN useradd -m -U app && \
    su -s /bin/bash -c "mkdir -p /home/app/current" app
RUN apt-get update -qq && \
    apt-get install -y $BUILD_DEPS --no-install-recommends

RUN # code to build a bunch of statically-linked executable 
    # dependencies and put them in /tmp/stage/bin
    
WORKDIR /home/app/current

COPY Gemfile /home/app/current/
COPY Gemfile.lock /home/app/current/

RUN chown -R app:app /home/app/current && \
    su -c "bundle install --jobs 20 --retry 5 --without development:test --path vendor/gems" app && \
    rm -rf vendor/gems/ruby/*/cache/* vendor/gems/ruby/*/bundler/gems/*/.git

#################################
# Build the Application container
FROM ruby:2.4.2 as app
LABEL tld.where.i.work.app=my_app \
      tld.where.i.work.role=runtime
ENV RUNTIME_DEPS="libpq5" \
    DEBIAN_FRONTEND="noninteractive" \
    RAILS_ENV="production"

RUN useradd -m -U app && \
    su -s /bin/bash -c "mkdir -p /home/app/current/vendor/gems" app

RUN apt-get update -qq && \
    apt-get install -y $RUNTIME_DEPS --no-install-recommends && \
    apt-get clean -y && \
    rm -rf /var/lib/apt/lists/*

COPY --from=base /tmp/stage/bin/* /usr/local/bin/
COPY --from=base /usr/local/bundle /usr/local/bundle
COPY . /home/app/current/

RUN chown -R app:staff /usr/local/bundle && \
    chown -R app:app /home/app/current && \
    mkdir /var/log/puma && chown root:app /var/log/puma && chmod 0775 /var/log/puma && \
    mkdir /var/run/puma && chown root:app /var/run/puma && chmod 0775 /var/run/puma

USER app
WORKDIR /home/app/current

COPY --from=base /home/app/current/vendor/gems/ /home/app/current/vendor/gems/

RUN bundle exec rake assets:precompile SECRET_KEY_BASE=$(ruby -r 'securerandom' -e 'puts SecureRandom.hex(64)')

EXPOSE 3000
CMD bin/boot_container
HEALTHCHECK --start-period=60s CMD curl -f http://localhost:3000/

The resulting runtime container works perfectly, and the multi-stage build has made it much, much smaller than my old monolithic build. It also builds in 1/3 of the time, because it only has to re-run the very time-consuming bundle install when either Gemfile or Gemfile.lock changes (which isn’t often).

However, whenever I push to my master branch and my automated Docker Hub build runs, it rebuilds both the build container and the runtime container every time. I tried creating a repo from within Docker Cloud as well, and while the logs say some stuff about pulling caches, both stages run there, too.

Obviously, only the final stage actually gets tagged and pushed anywhere. Is there a way to get Docker’s infrastructure (either Hub or Cloud) to save my first stage and take advantage of it on subsequent builds to make the runtime build faster?


(Mbklein) #2

P.S. I know I could be using COPY --chown instead of all those RUN chown -R directives, and I was – until I found that Docker Hub doesn’t support --chown yet. :wink: