Build and Run Rails Application with Docker and Docker Compose

Docker compose is a tool for running multi-container docker application. Container configuration are maintained easily with docker compose. Before proceed, know basics of docker (how to build and run simple container application).

For docker compose installation help click here https://docs.docker.com/compose/install/. Lets create simple rails application and run it with docker compose.

Define Dockerfile

Check the docker and docker-compose are installed properly.

$ docker --version
Docker version 17.03.1-ce, build c6d412e
$ docker-compose --version
docker-compose version 1.12.0, build b31ff33

Lets build rails container by defining the Dockerfile and place it in the application root. Overall file structure.

Dockerfile

FROM ruby:2.2.0
COPY . /my-rails-app
RUN apt-get update -qq && apt-get install -y build-essential
RUN apt-get install -y nodejs npm
# for postgres
RUN apt-get install -y libpq-dev
ENV APP_HOME /my-rails-app
WORKDIR $APP_HOME
ADD . $APP_HOME/
RUN bundle install

This will create container with ruby, nodejs and pg-dev package, application will be copied to container /my-rails-app directory

Lets create nginx upstream to listen application port 3000. Create nginx folder in application root. It will contain two files nginx.conf and Dockerfile.

Docker file (For Nginx)

FROM nginx
COPY nginx.conf /etc/nginx/nginx.conf

nginx.conf (To listen application port 3000)

worker_processes 4;
events { worker_connections 1024; }
http {
upstream web-app {
least_conn;
server app:3000 weight=10 max_fails=3 fail_timeout=30s;
}

server {
listen 80;

location / {
proxy_pass http://web-app;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
}

Note the upstream, its listening web container port 3000.

Define Docker Compose file

Lets define web, db and app containers and link them to make it work together.

docker-compose.yml

webserver:
build: ./nginx
ports:
- "80:80"
links:
- app:app
db:
image: postgres:9.4.1
ports:
- "5432:5432"
environment:
- POSTGRES_PASSWORD=password
volumes:
- /home/user/pgdata/etc:/etc/postgresql
- /home/user/pgdata/log:/var/log/postgresql
- /home/user/pgdata/lib:/var/lib/postgresql
app:
build: .
command: bin/rails server --port 3000 --binding 0.0.0.0
links:
- db
volumes:
- .:/my-rails-app
volumes_from:
- bundle
ports:
- "3000"
bundle:
image: ubuntu
volumes:
- /home/user/volumes/appbundle:/bundle

webserver- Nginx Container configuration. Its going to expose port 80

db - Database Postgresql Container configuration. Its going to expose port 5432

app - Rails application configuration. Its going to start webrick server on port 3000. Note the db volumes, it will share the postgres data with host machine, so that data will be persist even after re-create the container.

Find the container environment variables.

docker-compose run app env

This will list all environment variables. Database host has to be copied to database.yml. Update database.yml


development:
adapter: postgresql
encoding: unicode
database: demo_db01
username: postgres
password: postgres
pool: 5
host: <%= ENV['DB_1_PORT_5432_TCP_ADDR']%>

Build and Run the Container

Done with configuration. Lets build and run the container.

docker-compose build
docker-compose up

Navigate to http://localhost to see you the application home page.

DB migration tasks can be run inside the app container. Lets connect with app container.

docker-compose run app bash
rake db:migrate

Thanks for reading. :)