Deploying with Docker
Last weekend I deployed an app using Vagrant, so this weekend I decided to deploy the same app with Docker, to see the differences. I’ll talk through that a bit, and then the specifics of Docker.
Vagrant vs. Docker
That image sums it up pretty well (Vagrant on the left, Docker on the right). Basically, when using Vagrant, each container has its own Linux operating system, and requires all dependencies to be installed on each machine. Using Docker, each container can use the host’s operating system and can share dependencies. For example, if the host already has Rails installed on it, and you create a new Docker container that also requires Rails, it won’t download another copy, making it much more lightweight than Vagrant VMs.
- Docker: Mac install instructions here
Create a file in the root directory of your app called Dockerfile. Mine contains exactly one line of code: FROM rails:onbuild. This resource contains general information about building a rails app, but also says this about the onbuild command: This image includes multiple ONBUILD triggers which should cover most applications. The build will COPY . /usr/src/app, RUN bundle install, EXPOSE 3000, and set the default command to rails server.
This is where the meat of it happens. This file also goes in the root directory of your app. Mine looks like this:
command: bundle exec rake db:create db:migrate
command: bundle exec rails s -p 3000 -b '0.0.0.0'
Here’s what all that means:
- Under services, there are two sections called web and db respectively. This means that I am building two containers, one which will hold my app, and the other will hold my database.
- Within the db section, the image: postgres tells Docker that PostgreSQL is a dependency of my app, and it will install it if it’s not already on the host.
- The portscommand in both sections exposes the ports for the database and the application — Docker doesn’t do this autmatically. You can expose the internal ports externally as any port you want. For example, you could say ports:- “80:3000”, and what is port 3000 inside the container (Rails’ default port), would be exposed as port 80.
- The commands lines are fairly obvious: they run those commands, creating and migrating my database, and starting the server. These need to be done in that order.
- A volume is basically a directory, and tells Docker where to save my app.
- The links and depends_on sections allow the web and db containers to communicate, which they can’t automatically do. The depends_on section will also determine the order in which the containers are started — the database will be started prior to the application.
- The environments section contains your environmental variables. My real ones aren’t included here, obviously, but the formatting shown is what it should look like.
Making it all happen!
All that’s left to do is run two commands:
- docker-compose build
- docker-compose up
You can then run docker-machine ip and navigate to that IP address at the port you specified to see your app up and running!