📜 ⬆️ ⬇️

Deploying Rails Applications with Docker

Introduction


This post is about how I deployed a Ruby On Rails application to the DigitalOcean server so that it runs in a separate Docker container. For simplicity, I'm going to explain in great detail the process of deploying a Rails application inside a Docker container.
In this post:

Let's start by installing on the server.

Installing Docker on the server


First of all, I downloaded the new Ubuntu 14.04 on DigitalOcean and installed the Docker:
workstation $ ssh root@178.62.232.206 server $ apt-get install docker.io server $ docker -v Docker version 1.0.1, build 990021a 

Dockerfile and nginx.conf


Now we need to build a Docker image from a Rails application. It so happened that Jeroen (Jeroen van Baarsen, approx. Transl.) Wrote about this last week: How I assembled a Docker image for a Rails application . I will use his post as a basis for further steps.
I will build an image on the same server where I want to host the application later. I decided to do this because I want the application not to be in public access, so a public Docker repository is a bad option for this. I could set up a private repository for myself, but then I would have to maintain it, which I don’t want to do at the moment. In this post, I consider the easiest way to use Docker to host an application.
I have added the following Dockerfile and nginx.conf configuration files to my intercity-website project:

Dockerfile

 FROM phusion/passenger-ruby21 MAINTAINER Firmhouse "hello@firmhouse.com" ENV HOME /root ENV RAILS_ENV production CMD ["/sbin/my_init"] RUN rm -f /etc/service/nginx/down RUN rm /etc/nginx/sites-enabled/default ADD nginx.conf /etc/nginx/sites-enabled/intercity_website.conf ADD . /home/app/intercity_website WORKDIR /home/app/intercity_website RUN chown -R app:app /home/app/intercity_website RUN sudo -u app bundle install --deployment RUN sudo -u app RAILS_ENV=production rake assets:precompile RUN apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* 

As you can see, Dockerfile uses the base image of phusion / passenger-ruby21. It adds the Nginx config, application code, runs the bundler to install gems, and also precompiles assets.
')
nginx.conf

 # This is the server block that serves our application. server { server_name intercityup.com; root /home/app/intercity_website/public; passenger_enabled on; passenger_user app; passenger_ruby /usr/bin/ruby2.1; } # This is the server block that redirects www to non-www. server { server_name www.intercityup.com; return 301 $scheme://intercityup.com$request_uri; } 

Build the image for the application container


I added these files to my repository. Now I am going to upload it to the server and collect the container:
 my_workstation $ git archive -o app.tar.gz --prefix=app/ master my_workstation $ scp app.tar.gz root@178.62.232.206: my_workstation $ ssh root@ 178.62.232.206 server $ tar zxvf app.tar.gz server $ docker build --tag="intercity-website" app/ 

This command displays a lot of results and does a lot of things. When I first started docker build, it took a few minutes. This is because the Docker has to download the base phusion / passenger-ruby21 image. This is done only once. After loading the base image, the process will continue in accordance with my Dockerfile.

Now the docker images command shows my image:
 server $ docker images REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE intercity-website latest 629f05f42915 3 minutes ago 1.011 GB 

Running the container for the first time


It's time to start the application.
 server $ docker run --rm -p 80:80 intercity-website 

This command starts the container, prints some data, and finally displays the following line:
 [ 2014-09-23 11:23:11.9005 113/7fb22942b780 agents/Watchdog/Main.cpp:728 ]: All Phusion Passenger agents started! 

Now let's see if the application works correctly. Execute the query using curl:
 server $ curl -H "Host: intercityup.com" http://localhost/ <!DOCTYPE html> <html> <head> <title>We`re sorry, but something went wrong (500)</title> ... 

Oops, something went wrong. Judging by the log inside the container (for access to which I used docker-bash from Phusion), I forgot to create a database. So now I am going to install on the MySQL server.

Database installation


I will use the standard MySQL server available in Ubuntu 14.04:
 server $ apt-get install mysql-server 

After installing and setting the administrator password, I can create a database for the application:
 server $ mysql -u root -p mysql> create database intercity_website_production; Query OK, 1 row affected (0.00 sec) mysql> grant all on intercity_website_production.* to 'intercity' identified by 'rwztBtRW6cFx9C'; Query OK, 0 rows affected (0.00 sec) 

After that, I changed /etc/mysql/my.cnf and bind-address from 127.0.0.1 to my external IP address, 178.62.232.206. So Rails in my container can now use MySQL. In /etc/mysql/my.cnf, I replaced the line with bind-address with the following:
 bind-address = 178.62.232.206 

And restarted MySQL:
 server $ /etc/init.d/mysql restart 


Using environment variables to set up a database


I'm going to use environment variables so that my container can use them for authorization in MySQL. To do this, I need to do two things: 1) Prepare the database.yml file in the repository for using environment variables. and 2) configure Nginx to transfer these variables to the passenger process.

Here is my new database.yml, prepared for environment variables:
 production: adapter: mysql2 host: <%= ENV['APP_DB_HOST'] %> port: <%= ENV['APP_DB_PORT'] || "3306" %> database: <%= ENV['APP_DB_DATABASE'] %> username: <%= ENV['APP_DB_USERNAME'] %> password: <%= ENV['APP_DB_PASSWORD'] %> 

In order for these environment variables to work for my Rails application, I need to configure Nginx. This is due to the fact that Nginx resets all environment variables, except for those that you define.

I added the rails-env.conf file to the Rails application:
 env APP_DB_HOST; env APP_DB_PORT; env APP_DB_DATABASE; env APP_DB_USERNAME; env APP_DB_PASSWORD; 

And also corrected the Dockerfile to add the rails_env file when building the container:
 FROM phusion/passenger-ruby21 MAINTAINER Firmhouse "hello@firmhouse.com" ENV HOME /root ENV RAILS_ENV production CMD ["/sbin/my_init"] RUN rm -f /etc/service/nginx/down RUN rm /etc/nginx/sites-enabled/default ADD nginx.conf /etc/nginx/sites-enabled/intercity_website.conf # Add the rails-env configuration file ADD rails-env.conf /etc/nginx/main.d/rails-env.conf ADD . /home/app/intercity_website WORKDIR /home/app/intercity_website RUN chown -R app:app /home/app/intercity_website RUN sudo -u app bundle install --deployment RUN sudo -u app RAILS_ENV=production rake assets:precompile RUN apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* EXPOSE 80 


Building an image with support for environment variables


I added a new Nginx config to the repository. Now I intend to rebuild the new version of the container:
 workstation $ git archive -o app.tar.gz --prefix=app/ master workstation $ scp app.tar.gz root@178.62.232.206: workstation $ ssh root@178.62.232.206 server $ tar zxvf app.tar.gz server $ docker build --tag="intercity-website" app/ 

Run rake with environment variables


After assembling the container, I can configure the database. In the next command, I use environment variables to pass information about the database connection to run rake db: setup. Notice that I added the -u app argument to the command. This argument is needed to make sure that rake db: setup is run on behalf of the app user inside the container.
 server $ docker run --rm -e "RAILS_ENV=production" -e "APP_DB_HOST=178.62.232.206" -e "APP_DB_DATABASE=intercity_website_production" -e "APP_DB_USERNAME=intercity" -e "APP_DB_PASSWORD=rwztBtRW6cFx9C" -e "APP_DB_PORT=3306" -u app intercity-website rake db:setup intercity_website_production already exists -- create_table("invite_requests", {:force=>true}) -> 0.0438s -- initialize_schema_migrations_table() -> 0.1085s 

Wow! It worked!


Run an application with environment variables


Now I can run the container with the same environment variables and try to access it from the browser to check if it works:
 server $ docker run --rm -p 80:80 -e "RAILS_ENV=production" -e "APP_DB_HOST=178.62.232.206" -e "APP_DB_DATABASE=intercity_website_production" -e "APP_DB_USERNAME=intercity" -e "APP_DB_PASSWORD=rwztBtRW6cFx9C" -e "APP_DB_PORT=3306" intercity-website 

When I open 178.62.232.206 , I see a Rails application that connects to the database, and I also see that the assets have been compiled and everything works. Victory!

Conclusion


This concludes the post where we:
  1. Install Docker on the server
  2. Configured Dockerfile and build container image
  3. Set up a database using environment variables

What's next?


I still have questions to answer. I and other developers at Intercity will still write about them. Here are some of the questions that need to be addressed:

I hope you enjoyed this post. I will be glad to advice and questions!

Thank you very much for your attention.

Source: https://habr.com/ru/post/238069/


All Articles