If you're hacking on your Gemfile and using Docker, you know the pain of having the bundle install
command run after you've added or removed a gem. Using docker-compose
you could mount a volume and stage your gems there, but this adds additional complexity and doesn't always really solve the problem.
Enter this imperfect solution:
What if we installed every gem into it's own Docker layer which would be happily cached for us?
gem-inject-docker
does just that. It takes the list of gems used by your app via bundle list
and transforms it into a list of RUN gem install <your gem> -v <gem version>
statements and injects them into the Dockerfile at a point of your choosing.
It additionally caches this list, and only appends new gems to the end to prevent a full rebuild for all previously installged gems. If a gem is removed it is removed from the list of instructions and the cache. This does not seem to trigger Docker to rebuild all layers.
Pros
- Will absolutely increase the speed of your gem-update related Docker builds.
Cons
- Requires you to use a build script (see
build.sh
) - Requires you to add ./tmp/docker-cache to your
.gitignore
file. If you do not do this, other people's builds might not benefit from the speed improvement - Creates a bunch of Docker layers in the local cache
- Add a
bin
folder to your project - Add
gem-inject-docker.rb
andbuild.sh
to that path - Make them executable:
chmod +x bin/gem-inject-docker.rb bin/build.sh
- Instead of running
docker build . [...]
runbin/build
The first build will take longer than you are used to as a new layer is created for every gem. This is normal (for this hack). Subsequent builds will benefit from this step, however.