This tutorial is an extraction from a talk I recently presented about docker and rails apps at the Barcelona on Rails user group. I’ll explain how to integrate docker into an existing rails app workflow.
Dockerizing our dependencies
The first step to integrate docker into our workflow is to start with our application dependencies.
As an example I’ll use the open source app fulcrum. I assume this application is running locally as would be the case with an existing rails app. For this example we’ll use the postgresql database.
So the first thing is to initialize our database container. For that we execute the following command.
docker run -d --name fulcrum-postgres \ -e POSTGRES_USER=fulcrum -e POSTGRES_PASSWORD=fulcrum \ -p 5432:5432 postgres
So we’re creating a container and run it as a daemon (-d), we setup some environment variables (-e) for the username and password and make the ports available (-p HOSTPORT:CONTAINERPORT).
Now we need to adjust our config/database.yml, it should look something like this:
development: adapter: postgresql encoding: unicode database: fulcrum_development pool: 5 host: <%= ENV['POSTGRES_HOST'] %> username: <%= ENV['POSTGRES_USER'] %> password: <%= ENV['POSTGRES_PASSWORD'] %> test: adapter: postgresql encoding: unicode database: fulcrum_test pool: 5 host: <%= ENV['POSTGRES_HOST'] %> username: <%= ENV['POSTGRES_USER'] %> password: <%= ENV['POSTGRES_PASSWORD'] %>
And now we run our rails app
# Initialize the db POSTGRES_HOST=`boot2docker ip` \ POSTGRES_USER=fulcrum \ POSTGRES_PASSWORD=fulcrum \ bundle exec rake db:setup # Run the app POSTGRES_HOST=`boot2docker ip` \ POSTGRES_USER=fulcrum \ POSTGRES_PASSWORD=fulcrum \ bundle exec rails s
Please note that for the value for the POSTGRES_HOST I’m using boot2docker, if you’re running on linux or against another docker host replace this accordingly.
Now when we visit localhost:3000 our app will be running against our database in a docker container.
For a more complicated app, we’d do the same for the rest of our dependencies. By starting with the dependencies, we are preparing the road so we can have a more decoupled environment while maintaining our regular workflow.
Dockerizing the app
The next step is to run the application itself inside a container. For this we’ll need to create our Dockerfile.
There are many different places to start from. We’ll use the official ruby 2.1.2 container since this is the ruby version our app needs, but defining our own from scratch is quite easy as well.
Our Dockerfile should look like this
FROM ruby:2.1.5 RUN apt-get update && \ DEBIAN_FRONTEND=noninteractive apt-get install -y \ nodejs \ && rm -rf /var/lib/apt/lists/* WORKDIR /app COPY Gemfile* /app/ RUN bundle install -j4 ADD . /app # Let's create a user to run the app that is not root RUN usr/sbin/useradd --create-home --home-dir /app --shell /bin/bash fulcrum RUN chown -R fulcrum:fulcrum /app USER fulcrum CMD ["rails", "server"]
We also need to tell docker certain files we don’t want as part of our container, we create a .dockerignore file like this:
.git .bundle vendor/bundle
After creating the files in the root of our app we execute:
docker build -t fulcrum-web .
Once that is finised we can run our app like this:
docker run --rm -p 3000:3000 --link fulcrum-postgres:postgres \ -e POSTGRES_HOST=postgres \ -e POSTGRES_USERNAME=fulcrum \ -e POSTGRES_PASSWORD=fulcrum \ fulcrum-web
So what is this doing exactly? We’re linking our database container to our web container (–link), we’re exposing the port 3000 (-p) and we’re also setting environment variables (-e) with the values we used when creating our db container. After executing this we can go visit our app running on http://DOCKER_HOST:3000
The first thing we can see is that we have to set many duplicate environment variables from the database when running our container, fortunately docker allows us to have access to a linked container environment variables.
We can change now our config/database.yml replacing the username and password like this:
username: <%= ENV['POSTGRES_USER'] || ENV['POSTGRES_ENV_POSTGRES_USER'] %> password: <%= ENV['POSTGRES_PASSWORD'] || ENV['POSTGRES_ENV_POSTGRES_PASSWORD'] %>
We rebuild our container again, and now we can start it simply like:
docker run --rm -p 3000:3000 --link fulcrum-postgres:postgres \ -e POSTGRES_HOST=postgres \ fulcrum-web
Now, recreating our container every time we make a file change is going the get very annoying quite soon. For this we can also leverage another docker facility called volumes. This will allow our container to have access to our local files. To do this simply we start our container like this:
docker run --rm -p 3000:3000 --link fulcrum-postgres:postgres \ -e POSTGRES_HOST=postgres \ -v $PWD:/app \ fulcrum-web
Note: Please make sure you delete your .bundle/config local file to avoid issues with bundler inside the container.
Automating our setup with docker compose
At this point we have our app running successfully inside a docker container, but having to be typing this commands constantly is not fun.
To finish our setup we’ll use docker compose.
So let’s create our docker-compose.yml.
web: build: . links: - db:postgres environment: POSTGRES_HOST: postgres ports: - "3000:3000" volumes: - .:/app db: image: postgres environment: POSTGRES_USER: fulcrum POSTGRES_PASSWORD: fulcrum
Now to run our app we simply do:
The first time we access our app it will complain our db is not created we can simply fix this by running:
docker-compose run web rake db:setup
And last but not least if you want to run your tests then you can simply do:
docker-compose run web rake db:test:prepare spec
So that’s it, I hope this tutorial helps you get started with docker with your existing rails applications. If you want more info about docker compose, check out my article about fig (docker compose’s predecesor) here
19 Dec 2014
During the talk I presented a tool that is helping me focus on developing the different containers and make the changes to the Dockerfile and the apps themselves and leave out the details of using fig.
09 Dec 2014
It’s been over 3 months since my change to the site operations team at Xing and time has really been flying by.
Everything is new and I really don’t know what to do many times and that I find very delightful. The investigation, learning, figuring out how to achieve something, reading forums or stack overflow and trying to fit a puzzle into your head. I think this is what I love the most about computers, that moment when you understand the machine, what it does and why it does it. It’s the hack, making the computer do what you want. Getting out of my comfort zone is helping me appreciate the basics more. [...] read more
03 Jul 2014
A couple months ago I started growing a vegetable garden at home with my girlfriend. It’s been a great experience. She’s always been into plants, we have lots of them around the house but never had we tried to grow food. I think the fact that I can get something out of the experience that is more than just aesthetic but also functional (I can eat the vegetables) has made it more appealing for me.
There have been some experiences so far that I somehow can relate with developing a software project. [...] read more
12 Oct 2013
I was invited with my friend Jean Carlos Meninno to give a presentation on the GDG DevFest Barcelona 2013. It was a great opportunity to talk about the work we’re doing recently for XING and the things we’ve been learning about developing large scale backbone applications.
Here you can find the slides: http://diasjorge.github.io/google-dev-fest-slides/
Hope you like them. [...] read more
10 Sep 2012
During my time working at XING I believe my single biggest contribution for the company is a side project I’ve developed called Xing scripts. This project started with a personal need for working with our development environment in a more automated way. I’m a big proponent of automating everything you can and so when I started working I realized that there were these tasks that I would do over and over again. Since I couldn’t bare doing all this manual work I started writing my own scripts. [...] read more
11 Jul 2012
Yesterday I was at work with a colleague and we wanted to merge a long-running branch we had. This branch was full of useless commits so we wanted to clean it up. We tried an interactive rebase but we got a lot of conflicts since git doesn’t know how to resolve merge conflicts that we had previously fixed. As you probably know this is no fun, so we did what any sane person would do and found a nice solution for this called git-rerere. [...] read more
11 May 2012
Recently I had to reinstall my computer at work since I had to update to Lion and I could only do a fresh install, so I decided to try to automate the installation process since some of my colleagues are going through the same and it seems like every time we have to waste many hours or days to solve the same issues over and over. [...] read more
21 Dec 2011
It is perhaps my experience but I’ve hardly had the opportunity to work on green field projects, but rather worked on legacy ones where most of the original developers were no longer part of the team or even none of them. Projects with little to no documentation and in some cases no tests at all. You probable know this feeling, it sucks, you want to do things but everything you touch breaks something else, where you obviously see that there was lack of care. [...] read more
29 Aug 2011
It’s been some agitated months lately for me, I quitted working at JustLanded after almost two years there and then went working for some consulting, the experience was not so great, actually it was really bad, the kind of experience that has made me learn to choose very carefully my future career moves and never again believe in marketing people. Fortunately I got an offer to work at XING offices in Barcelona, so I packed my stuff and moved there. Now this is a really good place to work, everything was as we talked, they’ve been very helpful with my relocation and the environment is great, lots of smart people that want to do a good job, so nowadays I’m very happy and enjoying my new city. [...] read more
13 Jan 2011
17 Nov 2010
When using passenger with rvm I’ve had some issues with project specific gemsets, where bundler was unable to find the gems. After searching a lot I found out about using the “config/setup_load_paths.rb” file to tell passenger where to locate your gems, but then I had a new issue with rvm trying to use the system ruby instead of the ruby version of my .rvmrc file.
After going to the irc channel, I got some help that help me fixed my problem. The culprit was my rvmrc file. [...] read more
04 Nov 2010
As promised here are the slides for the “Conferencia Rails” workshop on process automation. Thanks to all the people that were there. I’m also releasing the redmine CLI I’ve created along with the CLI twitter client.
The presentation was created using the slideshow gem which generates an html document for you.
Hope you enjoy it.[...] read more
25 May 2010
Today I spent several hours with my friend Gleb trying to find a weird bug we we’re having importing some rss feeds.
We have a rake task that will grab an xml feed and import it to our system. When we call this rake task from the command line it would run fine, but if we run it from inside our application, we would get some wrong characters (you know, the usual ???) in the imported items. [...] read more
11 Apr 2010
If you’re using emacs to write your jekyll blog posts, there is a mode to help you with some common tasks. It is originally from metajack. Recently I thought it could be a nice addition to have syntax highlight support for jekyll posts, so I got my hands dirty and after some hours of lisp hacking (this was my first attempt at lisp programming) it was a reallity. It is based on nxhtml so you need it to work. [...] read more
If you’re using capistrano-ext to deploy to a different server, using a custom environment, you’ve probably noticed that it always tries to run the migrations for the production environment, like this:
cd path_to_app/deploy/releases/20100309152738; rake RAILS_ENV=production db:migrate
Digging through capistrano’s source I found the solution is really simple, just set the rails_env variable to the environment you want, in this example staging. So inside config/deploy/staging.rb
set :rails_env, "staging"
Then when migrations get executed they’ll have RAILS_ENV=staging.[...] read more
08 Mar 2010
Recently I moved my blog to Jekyll, while being able to write stuff directly in my favorite editor EMACS, there was some functionality that I was missing from my previous custom blog engine, such as archives. Looking at how I could achieve this, I found Raoul Felix approach to the problem. Instead of patching jekyll, he wrote a small library that wraps around it, called jekyll_ext. Using it was really easy, and based on some of the extensions he created, I was able to provide this functionality in my site.
Although I had archives generated for me, I was still missing a way to display this information on my site, so I decided to create my own extension. [...] read more
02 Mar 2010
23 Feb 2010
If you ever run into the situation where one migration doesn’t complete sucessfully, and you’re stuck with a column in a table or a new table, so you can’t drop the migration or execute the migration again, you can always call the migration methods from the console like this: [...] read more
16 Dec 2008
I recently had to implement some ajax pagination for a site. After googling for a while I found a solution, but I couldn’t customize the pagination url’s or I had to specify the paginator to use (will paginate’s default or mine for ajax), so I came up with this solution which fulfils all my needs. [...] read more