Cache Rails bundle when working with Docker Compose

Tram Ho

Applying Docker to the project must not be too strange for the developer. Docker helps us optimize the installation environment on machines that need to run the project, from development to production. However, using Docker properly and optimally is not possible for everyone. Today I introduce how to cache the Bundle of the Rails application when you configure Docker using Docker Compose

Question

Every time Gemfile changes, it means that the Docker container must be rebuilt to load those changes, which means that each rebuild will have to run bundle install again from the beginning, because Docker will build the new container rather than overwrite it. Changes on container are available. When your app uses few gems and your gems take a little time to install, you may feel no problem. However, when the app gets bigger, more gems are added or the version is changed, each rebuilding the container means you will have to wait for a century, while if you do not use Docker, you will only have to wait for the gems to install. change. So how does this work when you work with Docker?

Solution

When you rebuild a Container, everything will be refreshed, so the first thing to do is to store the gems that have been installed in a different place than the container. When building a container, just check Gemfile if anything changes, if any, just reinstall those gems, ignoring the gems already installed.

So where do we store the installed gems? In a certain folder of the server running Docker? No, Docker provides us with a tool called Docker Volume. Docker Volume can imagine it as a standalone database, regardless of the container’s lifecycle, it will always be there when we initialize, save all the data we need and just disappear. when we actually delete it.

Starting our app’s Docker config with Dockerfile is as simple as:

The most important thing here is to put the environment variables BUNDLE_PATH , BUNDLE_BIN , GEM_HOME into the folder /bundle , to tell Ruby that you want to store the gems installed in the container into this folder. Or if you don’t want to, it will be saved in /usr/local/bundle default. The path to this folder is important for the next step.

With Dockerfile above, we can completely build an image to help our app run on it. Next we will define Docker Compose to run the app on the image created by Dockerfile just written.

We notice the following config:

We declare to Docker that we will use the Volume, the Volume defined under the bundle name will be mounted in the folder /bundle in the container, which is the folder that we have defined in Dockerfile. With this config every rebuild container, the previously installed gems will be mounted from the Volume into the container, so we will not take time to reinstall again.

So, where does the check bundle already exist and where the new bundle install gem will be?

There are 2 files here:

  • wait-for-it.sh : This is just a wait file, the purpose is to wait for the db in port 3306 to be successfully created to run the file docker/app/entrypoint.sh
  • entrypoint.sh : this file will contain commands that each deploy will automatically run, such as: migrate db, generate API docs, bundle install , … and start the rails server

On the third command line, we will perform the bundle check , now the installed gems are mounted from the volume to the folder / bundle in the container so they will not install these gems anymore. After performing the check that has changed, we will perform bundle install the changed gems.

With the above settings, we only need to build images once using the docker-compose build command.

And then each time deploy just docker-compose down to finish the old build and docker-compose up to initialize the build with the new code. And of course, there will be no more scenes of waiting for bundle to reinstall from the beginning as the problem I raised before. Hope the article will help you 😄

Share the news now

Source : Viblo