Running Rails applications in a container makes managing configurations easy on my device and cross platform compatibility really easy for code reviews.
This post goes into containerizing a new rails applicaiton. It is a very “in the weeds” techincal post. Also after looking at the way I formatted this post, I would not recommend using this kind of setup for a new rails setup. I would use chruby or rvm. I have used both for development and they are both good, but currently I prefer chruby. There are benefits and drawbacks to everything.
Rails in a Container from Scratch
From my linux machine I ran mkdir dds
and then created a Dockerfile with touch Dockerfile
.
The contents of the Dockerfile look like this:
FROM ruby:2.6.0
RUN apt-get update && apt-get install -y \
build-essential
RUN curl -sL https://deb.nodesource.com/setup_10.x | bash - \
&& apt-get install -y nodejs
RUN mkdir -p /app
WORKDIR /app
RUN gem install rails
RUN rails new .
I then built the dockerfile into an image and tagged it using docker build -t rails:dds .
: note -t
for tag and the .
for using this folders Dockerfile.
I then created a container named with docker create --name dds_rails rails:dds
with the docker image I just created. This command then returned a container id for the container. The container id is a UUID generated by the Docker application.
I used docker container ls -a
to find the container id, as docker container ls
only shows the running containers.
I then ran docker cp -a container_id:/app .
to copy the application code down into a folder on my host named app
. I refreshed the folder on my local machine and the application code from the intermediary container copied into a new folder named ./app
on my host machine.
Back to Dockerfile
Now that I had the start to a rails project on my computer, I went back to my Dockerfile and made some revisions. The dockerfile from above was more of a big bang and was and wil only be used once. It needs to be revised to host the application.
I changed the Dockerfile to look like:
FROM ruby:2.6.0
RUN apt-get update && apt-get install -y \
build-essential vim
RUN curl -sL https://deb.nodesource.com/setup_10.x | bash - \
&& apt-get install -y nodejs
RUN mkdir -p /app
WORKDIR /app
ENV EDITOR=vim
COPY Gemfile Gemfile.lock ./
RUN gem install bundler && bundle install --jobs 20 --retry 5
COPY . ./
EXPOSE 3000
CMD ["bundle", "exec", "rails", "server", "-b", "0.0.0.0"]
Then I moved this Dockerfile
into the app
directory that has just been copied into your working directory.
Change directories into the app
folder and docker build -t rails:app-name .
to create the image. You will then need to create the container like we did before with docker create --name dds-app rails:app-name
, note we used the rails:app-name
tag because this is the image we gave to our new Dockerfile image.
Then running docker run -it -p 3000:3000 rails:dds
we will be running our application on our host machine from a temporary container on port 3000.
A quick update is the ENV EDITOR=vim line and the ..apt-get build-essential vim
with the vim package added/ installed.
This will allow us to edit secret credentials using the /usr/local/bundle/bin/rails credentials:edit
command.
Run Rails Commands from the host using commands from the Container with a Volume
To run all rails commands and create controllers and models, you will need to be in the container and copy down the changes as you make them. This is a little inconvient for now, but I will be using the container’s /usr/local/bundle/bin/rails
command from the host machine and a bash alias to copy the changes down.
I crafted up this docker run -it -p 3000:3000 -v $(pwd):/app rails:dds /usr/local/bundle/bin/rails generate controller static
to generate a static controller as a test, but I think I will alias the first part of this command, docker run -it -v $(pwd):/app rails:dds /usr/local/bundle/bin/rails
to something easier on my local development machine like drails
.. Note this drails
will only work from the present working directory of the rails application.. From my bash terminal in my Mac, I ran alias drails=docker run -it -v $(pwd):/app rails:dds /usr/local/bundle/bin/rails
to create the drails alias. Note the $(pwd)
will give you trouble if you have a file or directory that has spaces. You will have to swap the $(pwd)
with the full location of the app
directory.
With this alias configured, we can run drails g model test
and this will create a new model. With this we have access to all the normal rails
commands.
I then created essentially the same alias for the rest of the container binaries, but I may go back and write a bash function to run anything from the container.
We are now able to run everything from our local machine without having to install ruby or rails on our machine. We can then run the rails application with docker run -t -p 3000:3000 rails:dds
from the host machine and our host will have our rails application running on port 3000.
I opened my browser and everything appeared to be working well.
Rework
If I were to redo this, I think I would have modified the second dockerfile in this post to allow me to attach to the application container and run the commands in the container and using the attached host:container
volume I would be able to modify the files on my host machine.
More
It is really inconvienent to run rails commands from the host to the container using the drails
alias I created, but I am going to work on a simple bash function to run all the container binaries (everything in the /usr/local/bundle/bin/
folder). This will be covered in the second part of this post.
More
Checkout Part 2 for more on bash scripts around docker. Part 3 ofwill deal with deploying a containerized rails site to production.
I also made a youtube video for our metrics application (under development) dealing with containers and how to get started.