Blog

Docker for Rails Development

Placeholder Avatar
Lucas Caton
June 27, 2018

Developing Ruby on Rails applications in large teams can be frustrating when team members use different operative systems, languages, timezones, etc.

Docker is a great tool in these situations because it synchronises your team with the same setup for everyone collaborating on your project. That’s fantastic!

Why?

  • Your team will have a stable development environment
  • Development is a mirror of Production
  • No bugs caused by environment
  • New team members will love it!

How?

Let’s break this into multiple sections, we will cover everything needed to build awesome Rails applications in Dockerized environments.

Rails in a Docker Container

Running Rails in a Docker Container is very straightforward. Let’s begin by creating a Rails application and then configure a custom Docker Image so we can run the Rails application from there.

Rails application

Start by creating a new Rails application. > I’m assuming you have Rails 5.2 installed if not please follow one of these Guides. $ rails new myapp --skip-test --skip-bundle

Docker Image

Now that we have a Rails application, let’s create a simple Dockerfile in our Rails application root. This will help us running our Rails application in Docker. Dockerfile

FROM ruby:2.5 RUN apt-get update -yqq RUN apt-get install -yqq --no-install-recommends nodejs COPY . /usr/src/app/ COPY Gemfile* /usr/src/app/ WORKDIR /usr/src/app RUN bundle install

Line #1 Sets Ruby 2.5 as the Ruby version for our custom Docker Image Line #2 Update the Package list Line #3 Install Node.js (needed for the Asset Pipeline) Line #4 Copy our Rails application files from our local directory into the container Line #5 Gemfile caching Line #6 Sets the working directory for the Docker Image Line #7 Installs the Ruby gems > Please make sure you are in the Rails application root directory. $ docker build . ... Removing intermediate container 487d39dad5ff ---> c2183f884d23 Successfully built c2183f884d23 > Take a look at the custom image identifier (c2183f884d23) because we will use it next. Note: Your identifier will be different. We should now be able to list all of our available Docker Images. $ docker images ```

REPOSITORY TAG IMAGE ID CREATED SIZE

c2183f884d23 About a minute ago 381MB ``` As you can see the custom Docker Image is available. Now that we have created our own Docker Image, it's time to run the application. #### Running Rails To get the application up and running we will execute the following command which basically says "start a container out of our image (c2183f884d23) and then run rails s -b 0.0.0.0 inside it". The -b tells our Rails server to bind all IP addresses not just localhost. `$ docker run -p 3000:3000 c2183f884d23 rails s -b 0.0.0.0` The Rails server boots up, ``` => Booting Puma => Rails 5.2.0 application starting in development => Run `rails server -h` for more startup options Puma starting in single mode... * Version 3.11.4 (ruby 2.5.1-p57), codename: Love Song * Min threads: 5, max threads: 5 * Environment: development * Listening on tcp://0.0.0.0:3000 ``` Visit `localhost:3000` and you should see Rails welcome page. **Yay! You're on Rails!**. ### Improve the Docker Image Using the custom image ID as a reference to start our Rails application can be problematic because it is not easy to remember such a number. Instead, let's `tag` this image so it is easy to remember: `$ docker tag c2183f884d23 app` If we list our Docker images: ``` REPOSITORY TAG IMAGE ID CREATED SIZE app latest c2183f884d23 5 days ago 1.03GB ``` You can use tags to version your custom Docker Image. All about **Docker tag command** [here](https://docs.docker.com/engine/reference/commandline/tag/). With the new Docker Image name **app** we can simply run the Rails application as follows: `$ docker run -p 3000:3000 app rails s -b 0.0.0.0` > Notice we are no longer using the Docker Image ID but instead the tag name. Now let's add a default command to out image, using CMD: **Dockerfile** ``` FROM ruby:2.5 RUN apt-get update -yqq RUN apt-get install -yqq --no-install-recommends nodejs COPY . /usr/src/app/ COPY Gemfile* /usr/src/app/ WORKDIR /usr/src/app RUN bundle install CMD ["rails", "s", "-b", "0.0.0.0"] ``` There are some files we can ignore in our Docker Image using `.dockerignore`. Feel free to add more here as required: **.dockerignore** ``` .git .gitignore log/* tmp/* ``` Next, rebuild the Docker Image with the new CMD instruction: `$ docker build -t app .` Start the Rails application without the `rails s -b 0.0.0.0` using the new tag name `app`: `$ docker run -p 3000:3000 app` So far we have covered `Dockerfile`, `.dockerignore` and `docker tag`. Next we will switch to Docker Compose! ### Rocking with Docker Compose With Docker Compose you describe each of your application services. Introducing `docker-compose.yml` ``` version: '3' services: web: build: . ports: - "3000:3000" volumes: - .:/usr/src/app command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'" ``` We will remove the `CMD ["rails", "s", "-b", "0.0.0.0"]` line from our `Dockerfile` since we are running the same command from the `docker-compose.yml`. **This step is very important since if we don't do it we could get this error:** `A server is already running. Check /usr/src/app/tmp/pids/server.pid` **Dockerfile** ``` FROM ruby:2.5 RUN apt-get update -yqq RUN apt-get install -yqq --no-install-recommends nodejs COPY . /usr/src/app/ COPY Gemfile* /usr/src/app/ WORKDIR /usr/src/app RUN bundle install ``` Now, let's build the Docker Image with Docker Compose: `$ docker-compose up --build` Our Rails application is up and running, thanks to Docker Compose, YAY! #### Docker Compose useful commands This Docker Compose commands will help you during development and you will use them everyday! Builds, (re)creates, starts, and attaches to containers for a service: `$ docker-compose up` Lists containers: `$ docker-compose ps` Managing containers lifecycle: `$ docker-compose [start|stop|kill|restart|pause|unpause|rm] SERVICE` Displays log output from services.: `$ docker-compose logs [SERVICE...]` Run arbitrary commands in your services: `$ docker-compose exec SERVICE COMMAND` Runs a one-time command against a service.: `$ docker-compose run SERVICE [COMMAND]` Rebuilding a Docker Image: `$ docker-compose build [SERVICE...]` ### Summary We learned how to use Docker for Rails development, and covered some key concepts like: Dockerfile and Docker Compose. Now you should be ready to start developing Rails applications in a Dockerized environment!