Dockerize and Deploy to AWS EC2

Published Sep 06, 2017Last updated Sep 07, 2017

Hi! Currently I needed to the set up a new staging server, so this was the best opportunity to start with Docker

My project has the following configuration:

Configuration

First, we need to create the dockerfile at the root of the project.

# We set the base image to be ruby 2.3.1
FROM ruby:2.3.1

# The author of the file
MAINTAINER Mariano Matayoshi <matayoshi.mariano@gmail.com>

# Things we will need.
RUN apt-get update && \apt-get install -y nodejs \vim \less \mysql-client \imagemagick \--no-install-recommends && \rm -rf /var/lib/apt/lists/*

# Init the root path
RUN mkdir -p /home/ubuntu/myProject

# We set it as env variable
ENV RAILS_ROOT /home/ubuntu/myProject

# We set /home/ubuntu/myProject to be the root of the project
WORKDIR $RAILS_ROOT

# The log of shoryuken will be in shared/log
RUN mkdir -p shared/log && touch shared/log/shoryuken.log

# We add the gemfiles of the project
ADD Gemfile $RAILS_ROOT/Gemfile
ADD Gemfile.lock $RAILS_ROOT/Gemfile.lock

# We copy all the project to the root path
ADD . $RAILS_ROOT

# We install bundler
RUN gem install bundler

# Run bundle install to install al the gems
RUN bundle install

# Precompile the assets
RUN bundle exec rake assets:precompile

Then we need to create the .dockerignore file. Thsi is used to ignore all the files that don’t need to be copied, like temp files or logs.

.git
.gitignore
.rbenv-vars
README.md

.byebug_history
log/*
tmp/*
public/system
spec/tmp
.bundle

.ruby-version
.ruby-gemset

.bundle/*
.capistrano/*
.bundle/*
.elasticbeanstalk/*

Lastly we create the docker-compose.yml, in this file we define all the services to be executed: web, db and worker.

# We set the version of the file
version: '3.1'
# This part is explained below the file
services:
  web:
    build: .
    command: /bin/sh -c "rm -f /home/ubuntu/myProject/tmp/pids/server.pid && bundle exec rake db:create db:migrate && rails server puma -p 80"
    volumes:
      - $PWD:/myProject
    ports:
      - "80:80"
    links:
      - "db:database"
    env_file:
      - .env.stagingv2
  db:
    image: mysql:5.6
    environment:
      MYSQL_ROOT_PASSWORD: root
  worker:
    build: .
    command: /bin/sh -c "bundle exec bin/delayed_job -n 1 --log-dir=/home/ubuntu/myProject/shared/log/delayed_job.log --pool='notifications-poller:1' --pool='broadcast,default,elasticsearch,firebase:2' restart && bundle exec shoryuken --logfile '/home/ubuntu/myProject/shared/log/shoryuken.log' --config '/home/ubuntu/myProject/config/shoryuken_staging.yml' -R"
    links:
      - "db:database"
    volumes:
      - $PWD:/myProject
    env_file:
      - .env.stagingv2

Each service will be a different container. I will be describing each container next.

Web container

  web:
    build: .
    command: /bin/sh -c "rm -f /home/ubuntu/myProject/tmp/pids/server.pid && bundle exec rake db:create db:migrate && rails server puma -p 80"
    volumes:
      - $PWD:/myProject
    ports:
      - "80:80"
    links:
      - "db:database"
    env_file:
      - .env.stagingv2
  • Volumes: we are setting where the project will be written. In this case the path is: PWD:/myProject(PWD:/myProject (PWD is the P athname of the current W orking D irectory).
  • Command: rm -f /home/ubuntu/previando/tmp/pids/server.pid && rails db:migrate && rails server puma -p 80. Before it execute the puma, we shut it down with the rm command and then run the migrations, then we run puma.
  • Links: this container has the ability to connect to the db container
  • env_file: in the .env.stagingv2 file are all the environment variables needed to run the project

DB container

db:
    image: mysql:5.6
    environment:
      MYSQL_ROOT_PASSWORD: root
  • image: mysql:5.6 is the base image.
  • enviroment: here we set the environment variables. In this link are all the available ones.

Worker container

worker:
    build: .
    command: /bin/sh -c "bundle exec bin/delayed_job -n 1 --log-dir=/home/ubuntu/myProject/shared/log/delayed_job.log --pool='notifications-poller:1' --pool='broadcast,default,elasticsearch,firebase:2' restart && bundle exec shoryuken --logfile '/home/ubuntu/myProject/shared/log/shoryuken.log' --config '/home/ubuntu/myProject/config/shoryuken_staging.yml' -R"
    links:
      - "db:database"
    volumes:
      - $PWD:/myProject
    env_file:
      - .env.stagingv2

command: here I am executing delayed_jobs and shoryuken, but you can execute whatever process you are using to run asyn jobs, such as sidekiq.

The other items are similar to the web container.

Once we have all these files, we can run myProject in our local machine, running the following commands:

docker-compose builddocker-compose up

Deploy

Now the interesting part, deploying to AWS.

  1. Set the following environment variables:
export AWS_DEFAULT_REGION="region"
export AWS_ACCESS_KEY_ID="key_id"
export AWS_SECRET_ACCESS_KEY="access_key"

Preferably, put them in your .bashrc.zshrc, etc.

  1. Install docker-machine.

  2. Now, the only thing left is to mount up the server in AWS, to do that we run the following command.

docker-machine create --driver amazonec2 --amazonec2-instance-type "t2.medium" staging2

This command will mount an EC2 server t2.medium in the region defined by AWS_DEFAULT_REGION called staging2.

  1. To deploy the project we need to run the following command:
docker-machine env staging2
eval $(docker-machine env staging2)

Now, any docker command executed after those command, it will be executed in the new staging2 EC2 machine, because the staging2 machine is the active one. To verify that we can run, docker-machine ls

The output will be:

The * under the ACTIVE column, tell us that staging2 is the active machine.

So, now we can really do the deploy. Executing the same command to run docker localy, we execute them but to make the deploy to the EC2 machine.

docker-compose builddocker-compose up -d

The last command is executed with the -d option because that make the containers to be running in the background.

Lastly, if you want to connect to a container, you can do it like this:

First run docker ps, and you will se something like this:

This show us all the containers running on the EC2 machine.

And then to connect to the web container, we run: docker exec -i -t my_worker_1 /bin/bash

And that’s all! If you have any doubt feel free to ask or write to me!

Discover and read more posts from Mariano Matayoshi
get started