Categories
Uncategorized

Docker, Gemfile, Gemfile.lock, rbenv, rubygems, bundler or why good planning can often beat evolution

Rbenv → specifies a Ruby version

Ruby → loads some gems that are part of the base Ruby distribution (bundler, rubygems, irb, etc)

Rubygems →loads other gems (for the system or user) → GEM_HOME and GEM_PATH

Bundler → loads other gems (for the current project) → BUNDLE_PATH

So basically Ruby ships with a bunch of gems, rubygems manages most of the gems you install, and bundler manages all of the gems for a specific project, and both rubygems and bundler ship with Ruby.

The initial release of Docker was done at PyCon in 2013, but I wonder how much Ruby and the various shades of gem catostrophe helped inspire Docker adoption?

If you're using Docker and thus only running a single set of gems at a time, I think you should simplify the models as much as possible. For a ruby focused docker container, just skip rbenv and similar tools, try to bypass the system gems as much as possible, and try to get bundler / rubygems to force everything into /gems.

You'll want to check what bundler and rubygems detect as the environment via:

bundler env
gems env

Basically, inside of your docker-compose.yml and Dockerfile and perhaps even your dip.yml you want to set

HOME:        /app
BUNDLE_PATH: /gems
GEM_HOME:    /gems
GEM_PATH:    /gems
PATH:        /gems/ruby/<ruby-version>/bin:/gems/bin:/app/bin:$PATH

Also, I found my local docker build environment to be far less annoying when I figured out how to run everything as my local user... Ultimately I need to move to actually using a text template system to process all of the required variables (it's pretty lame that the Docker people make permissions that match the developer impossible without your own wrappers) but if it bothers you enough you could always fork docker.

ARG UID=<your id -u>
ARG GID=<your id -g>
ARG APPUSER=appuser

RUN set -x \
    && addgroup --system --gid $GID $APPUSER || true \
    && adduser  --system --disabled-login --ingroup $APPUSER --no-create-home --home /app --gecos "$APPUSER" --shell /bin/false --uid $UID $APPUSER || true

USER $APPUSER

# Why on earth does Docker's COPY always copy as root instead of the USER: you set? Only the Docker people know...
COPY --chown=$APPUSER: Gemfile* /app/

Then in your dip.yml makle sure to add run_options for your user...

interaction:
  shell:
    description: Open the Bash Shell in Container
    service: web
    command: bash
    compose:
      run_options: ["no-deps", "user=<your id -u>"]