Dockerizing an Existing Rails Application

Today we’re going to take an existing Ruby on Rails application and convert it so that can be developed and deployed using Docker. Up to this point we’ve been using mostly “hello world” applications we’ve been using Docker outside of our Python series we’re just using a Python image in order to run our code but today we’re going to take some non-trivial code, an application that actually exists already and we’re going to convert it from being a non-dockerized application to working inside of Docker with all the services that it needs the application that we’re going to be working with is called a happy hours it’s a happyhours.io and this is an open-source rails application and the entire application is packaged up so it’s it’s something that we can actually work with that is pretty substantial. So the first thing we’re going to do is we’re going to come over here or going to copy this and clone this to our repository let’s git clone paste that in now if we cd into the Hours directory and just take a look at what’s here we’ll just open it up in atom. Just skimming down the side here we can see that there are quite a few different services that use being an open-source application and can use a lot of services for free so we have hound for doing some style checking that’s not going to affect us uses Travis it has a sample.env file which means it probably uses Dotenv, there’s an app.json and a Procfile which would indicate that it’s using Foreman and probably heroku so if we take a look at this Procfile you can see that they’re going to be two processes running we’re going to have web and worker so that’s not too bad this job’s work this yeah this will be the background jobs and it uses unicorn as its app server and if we go to this app.json and read this a little bit you can see that there are some environment variables that we’re definitely going to need so that work out for us. Last thing we want to check out before we get too far as let’s look at the Gemfile and see what we have to install we’re gonna need quite a few different things here we’re going to be using postgres, so we’ll create a database image or database container using the postgres image that we used before and then this coffee-rails means we’re going to need a JavaScript runtime so we’ll install node. [If we] keep on going here we’ve got a ‘dolli’ which means we’re going to be using memcache so we’re going to have a cache container which we haven’t come up to up to this point and then in their testing you can see that they’re using capybara-webkit which means we’re gonna have to install a few extra things in here too namely Qt. So that we can run our tests in the browser without having to launch an actual browser now that we’ve seen the lay of the land of this application let’s first start out by creating our docker-compose.yml file to sort of sketch out what we want our services to be and we’ll build on top of that to have our custom docker image for our application afterwards it’s a new file this is just docker-compose.yml and this is going to be kind of similar in a way to what we’ve done before using volumes we’re going to call db-data and this is going to be on the same machine while we’re in development so it’s not external we’re going to have services also so we’re gonna have a cache service this is going to use an image and we know that uses memcached so we’re going to say memcached and then I happen to know that we want 1.4-alpine and the reason we want alpine here as the image tag is alpine is a really small linux distro so it just means that this image is going to be really small ideally that’s kinda what you want use a fewer or uses up a less of your storage on your your server that way next up we’re going to have our db which has a few environment variables going to have POSTGRES_USER and POSTGRES_PASSWORD and then we’re also going to use an image for this we’re going to use postgres:9.5 and then we’ll map that volume so db-data is going to go to /usr/local/pgsql/data whoops… data okay next up we’re going to have a jobs container and this is going to run our background jobs and will run it in a separate process that way we don’t have to block i’m going to assume that their background jobs don’t yeah they actually run a separate process but I haven’t really looked at the code yet but we’re gonna have an env

file and this is going to be .env which this’ll… could potentially cause some issues with the fact that this application uses Dotenv the the Ruby tool and but we’ll get into that a little bit we’re going to build based on the current docker file and then we’re going to map through our volumes just mapping our local directory into /usr/src/app this one we’re going to actually give a separate command so we’re going to a ‘bundle exec rake jobs:work’ which if you remember looking at the Procfile this is the exact command that they were using there and this depends on the database alright then we’re going to have an app also and this is going to be pretty similar so we’re actually just going to copy this paste it down here, change a few things so it’s gonna be app this is all the same. We can get rid of this command but the difference here is going to be that we’re going to do some port mapping so ports and we’re going to map from 8080 8080 to 8080 so we’ll be able to access this at localhost:8080 and jobs and app need to have the exact same code so they’re going to use the same Dockerfile but we’ll we’ll package in the normal unicorn starting command inside of our Dockerfiles and that’s why we don’t have specified command for app alright we’ll save this and then we’ll come over and we’ll create a new file for a Dockerfile so Dockerfile and this is almost identical to some of the previous Ruby ones we’ve done so we’ll say from ruby:2.1, 2.3.1 actually and then we need to run apt-get update and then we always say yes and then we make it quiet just so it doesn’t have to spit out as much we also want to run apt-get install also quiet and yes we’re going to do –no-install-recommends just so we can install a few things as possible this might leave this open running into problems if there’s something that was recommended we didn’t install it but we actually needed it but we’ll cross that bridge when we come to it we’re gonna install the postgresql-client, we also need nodejs, we need qt5-default and this is for capybara-webkit and we also need libqt oops if I could spell libqt5webkit5-dev little bit of a mouthful but this is just that development library that we’re going to need in order to integrate with capybara-webkit and then from there we’re going to apt-get clean and finally rm -rf /var/lib/apt/lists just to clean up all the extras we’re gonna set are working directory to /usr/src/app just so we don’t have to cd every time we use our container going to copy over our Gemfile related stuff into the our working directory, we’ll run “bundle install” and lastly will copy over the rest of our application. Our final thing here is going to be to set our command which is going to be “bundle exec unicorn -c” so we can pass the config file and that’s going to be in our config directory as a unicorn.rb so if we save that. Hopefully I didn’t fly through that too fast but we need to more or less install of our dependencies here since Ruby installs quite a few of those things we just need postgres for a database, node for our JavaScript runtime, and then Qt for some of our testing libraries and then the rest of this is the same basic approach that we’ve taken every time we’ve wanted to load a rails application into a Docker image alrighty… if we move over to config there’s probably gonna be some different things in here just because I know about heroku and that is… yeah it doesn’t come with the database.yml by default it has one for Travis and then it also has one for, as an example file here. So we’re going to end up doing is we are going to going to take one of these will probably take this on and will duplicate this and we’ll just make this the normal database.yml and since we’re going to configure this with environment variables it’s not going to be a big deal so we’re going to have the database this will be default not development there we go then we’ll get rid of this database line and we have our adapter we also need a host we’re going to pull from an environment variable (POSTGRES_HOST) and then we’ll do a similar thing for

username, this’ll be “username” and what this will be POSTGRES_USER that’s just because it’s what’s required from the postgres image so we’re gonna we’re gonna follow suit here and copy this again and do the password i can select that there we go password on this back up, save it ok so test is going to use this, we want development to do its own thing too development it’s going to look very much the same inherit from default it’s sets database to hours_development. There we go and that should be good to go but just for safe measure in case we ever did want to deploy this to a production setting we can do the same thing here so we can do production set this default and the database to hours_production alright save that. Looking over in the sidebar here there is no secrets.yml which is also something that we’re going to need so we might as well create that while we’re at it so add a new file this is secrets with an s .yml and this takes a few things so it’s set up very much the same way as other one but it’s going to have a secret_key_base for every section here so we’ll copy these. We’ll have test and then we’ll also do one for production too and for these just so it’s easier for us and we don’t have to package up in kind of secrets we’re actually going to use an environment variable for this too so this will just be SECRET_KEY_BASE and now that I think about it I should’ve just copy pasted this for each of these there we go. So we’ll save that off look there ok that’s gonna that’s pretty close to what we need but the next thing we’re going to have is this .env file. So we know that they need environment variables and we’ve added some ourselves so we are going to copy the sample.env file into a normal .env file which is going to be tracked in the repository it’s just going to be for our sake and then we’ll add the extras that we need so if we come down here to the sample.env and we duplicate it. We’ll just duplicate it as .env i’m going to get rid of this the RACK environments not really necessary for us to set here… it has all these extra settings and then the ones that we added are going to be POSTGRES_USER which we’re just going to set as ours POSTGRES_PASSWORD super_secure and then also set a POSTGRES_HOST up here which is going to be ‘db’ and that’s so that it can route within, from container to container, db will make sense to our application container we’ll also set a SECRET_KEY_BASE in here… there other already is one it’s just a really short so that’s fine for development sake we don’t need to do this if we were to deploy this to production we would be in big trouble but right now I’m not super worried about that alright let’s save this ok I think I have everything that I need so we’ll bounce back over to our terminal and we’ll just build some things so we can spin up our db and cache right away because these are based on images already downloaded the image is to this machine so that I don’t have to waste as much time the next thing that we’re going to do is we’re going to build our app and jobs containers we can build those at the same time because they’re going to be based on the same Dockerfile so jobs will be built near instantly because the layers were cached when building app. So we’ll “docker-compose build app jobs” This is going to take a while so I’m gonna stop while it’s doing this and we’ll bounce back when it’s done now that our images have finished building successfully we can go ahead and we can prep our database so that it can run with our application so we’ll run “docker-compose run –rm” so removes the container “app rake db:create and db:migrate” now our database’s up to speed I did forget one extra thing in the environment variable file so if we

bounce back over here and take a look at our env file there’s a single-tenant option here and we’re going to set this to true and that’s because we’re going to run this as though we want to do time tracking for ourselves not necessarily that we want to have the whole sales portion of it with the homepage and different plans and stuff this is a flag that allows you to toggle those two individual features so if we come back over to our terminal we actually have one more thing that we want to run and that is “docker-compose run –rm app rake” and then they have a custom rake task called “create user” which is going to set up our single-tenant so we’ll use coder last name journey keith@coderjourney.com and then my password you won’t see it… it’s very secure i’m sure. All right everything is created so let’s spin up our application and our jobs containers so we’ll do ‘docker-compose up -d app jobs’ if we look at our processes looks like they’re all in the Up state so we should be ok there oops and now we’ll go over to our browser and we’ll try to reload this so we’re going to go to localhost:8080 it’s taking its sweet time so that kind of makes me wonder if we crashed talk. docker ps no it’s like we’re still oh hey there we go, it worked so we know my email here is keith@coderjourney.com and my password alright it signs us in. It gives us a lot of messages because we’re missing some of the extra things here so we have no projects we have no categories so we’re just going to click through and see we can do you so we’re gonna have a development category there’s a design category that worked out great so now if we go back to our projects, you’ll see that we don’t have any projects will create one this’ll be build a course I don’t know that we need a description or budget alright there we go so we were able to convert this entire application to run inside of docker without needing to install any of the dependencies on our machine if we go back over here there was one thing we did all those extra things related to tests so if we ‘docker-compose exec app bash’ this will take us into are running app container and we just want to try to run our tests since this is using Travis i’m going to assume that rake is how you run these tests looks like it’s running factory specs right now ok looks like we did get a failed test and this would… is because it’s looking at seeing a sign in email password when it’s expecting to see something else so that tells me that odds are that single-tenant flag which we did cause a problem if we look at env right now you can see that these are really set as environment variable so POSTGRES_PASSWORD is actually in here the way Dotenv the works is it doesn’t actually load them into the env, or the environment for your shell it loads them into the ENV object inside of Ruby but it will not overwrite those things so since we set them explicitly in the shell Dotenv won’t override them so that means we’re gonna have to make a change to the spec helper in order to get these things to work so if we go into spec/spec_helper.rb and we come in here before we load the actual application environment we can require Dotenv the tool and then we will tell it to overload and we want to point it at the sample.env file and the reason I know to do this is because I’ve already looked at the travis.yml file and you’ll see that they do a “cp sample.env .env” and this is what sets the environment that they want to use running tests so we will do that same thing in our spec helper always and that would actually allow that travis.yml file thing to be removed because it would be handled right here but so if we save this it’ll get loaded back over and then if we run rake again we’ll make sure that this test suite runs and if this runs that means we successfully, without breaking the application, converted it to run in a different sort of environment Success! The entire test suite ran and even the brakemen report ran which is the last step so we have gone from

taking an application that we didn’t even write to converting its running inside of Docker with all the test suite running too so that was a pretty big ordeal but it’s something that doesn’t require that many steps and you just need to know sort of what you’re looking for in the application a lot of it involves making sure the application can work off of environment variables so keep that in mind when you go to convert your own application around inside of docker i hope you enjoyed this video we’re gonna have some more Docker related content coming up in the future and but leave me a comment down below tell me what you thought about this if the pacing was bad or if there was something else that you would like me to show you i want to know what you want to see that way I can make you some high-quality content but if you’d be so kind of like the video and give it a thumbs up and subscribe to the channel and share this with your friends I’d really appreciate the exposure really helps