Recipe for deploying Rails apps on AWS EC2

Introduction

When I create a personal app in Rails and then deploy it on AWS, I always forget how to do it, so I'd like to write it down here.

Premise

--Assuming Rails 5.2 or higher

1. EC2 instance creation / key pair creation

image.png

image.png

image.png

image.png You can create a new key pair or an existing key pair. The key pair must not contain spaces.

2. Linking with Elastic IP

image.png

image.png

image.png

image.png

3. Open the port

image.png

image.png

image.png

Set the type to "HTTP", the protocol to "TCP", the port range to "80", and the source to "Custom / 0.0.0.0/0, :: / 0".

The following is also released so that HTTP connection can be made

image.png

image.png

image.png

4. Log in as EC2-user

local


$ cd ~

$ mkdir ~/.ssh
# .Create a directory called ssh
#Even if you get an error saying File exists.It means that the ssh directory exists, so let's proceed as it is.

$ mv Downloads/The name of the downloaded key.pem .ssh/
#Use the mv command to download the pem file from the download directory..Change to the ssh directory.

$ cd .ssh/

$ ls
#Check if the pem file exists

$chmod 600 The name of the downloaded key.pem

$ ssh -i Downloaded key name.pem ec2-user@Elastic IP associated with the created EC2 instance
#(For example, Elastic IP is 123.456.If it's 789, ssh-i Downloaded key name.pem [email protected] command is 789)
#(Using the downloaded key, ec2-Login as user)

5. Required packages ・ Install Ruby

server


[ec2-user@ip-172-31-25-189 ~]$ sudo yum -y update
[ec2-user@ip-172-31-25-189 ~]$ sudo yum -y install git make gcc-c++ patch libyaml-devel libffi-devel libicu-devel zlib-devel readline-devel libxml2-devel libxslt-devel ImageMagick ImageMagick-devel openssl-devel libcurl libcurl-devel curl
[ec2-user@ip-172-31-25-189 ~]$ sudo curl -sL https://rpm.nodesource.com/setup_6.x | sudo bash -
[ec2-user@ip-172-31-25-189 ~]$ sudo yum -y install nodejs
[ec2-user@ip-172-31-25-189 ~]$ git clone https://github.com/sstephenson/rbenv.git ~/.rbenv 
#Pass through
[ec2-user@ip-172-31-25-189 ~]$ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile 
#Description for calling rbenv
[ec2-user@ip-172-31-25-189 ~]$ echo 'eval "$(rbenv init -)"' >> ~/.bash_profile
#.bash_Load profile
[ec2-user@ip-172-31-25-189 ~]$ source .bash_profile
#ruby-install build
[ec2-user@ip-172-31-25-189 ~]$ git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build
#Do rehash
[ec2-user@ip-172-31-25-189 ~]$ rbenv rehash  
[ec2-user@ip-172-31-25-189 ~]$ rbenv install 2.5.1
[ec2-user@ip-172-31-25-189 ~]$ rbenv global 2.5.1
[ec2-user@ip-172-31-25-189 ~]$ rbenv rehash  #Do rehash
[ec2-user@ip-172-31-25-189 ~]$ ruby -v #Check version

#MySQL installation
[ec2-user@ip-172-31-25-189 ~]$ sudo yum -y install mysql56-server mysql56-devel mysql56
[ec2-user@ip-172-31-25-189 ~]$ sudo service mysqld start
[ec2-user@ip-172-31-25-189 ~]$ sudo /usr/libexec/mysql56/mysqladmin -u root password 'Password you want to set here'

#Check if you can enter with the PW set in MySQL
[ec2-user@ip-172-31-25-189 ~]$ mysql -u root -p

6. Register SSH key on Github

EC2 server


[ec2-user@ip-172-31-23-189 ~]$ ssh-keygen -t rsa -b 4096

You may be asked to enter about 3 steps such as passphrase on the way, but please proceed with the Enter key without entering anything.

EC2 server


[ec2-user@ip-172-31-23-189 ~]$ cat ~/.ssh/id_rsa.pub

Access the public key displayed on cat to Github and register it. https://github.com/settings/keys

image.png

After registering the key on Github, check if you can connect with SSH with the following command

EC2 server


[ec2-user@ip-172-31-23-189 ~]$ ssh -T [email protected]

7. Capistrano preparation

Gemfile


group :development, :test do
  gem 'capistrano'
  gem 'capistrano-rbenv'
  gem 'capistrano-bundler'
  gem 'capistrano-rails'
  gem 'capistrano3-unicorn'
end

local


$ bundle install
$ bundle exec cap install

Capfile editing


require "capistrano/setup"
require "capistrano/deploy"
require 'capistrano/rbenv'
require 'capistrano/bundler'
require 'capistrano/rails/assets'
require 'capistrano/rails/migrations'
require 'capistrano3/unicorn'

Dir.glob("lib/capistrano/tasks/*.rake").each { |r| import r }

config/deploy/production.rb


server '<Prepared Elastic IP>', user: 'ec2-user', roles: %w{app db web}

config/deploy.rb


# config valid only for current version of Capistrano
#Described the version of capistrano. Continue to use the fixed version and prevent troubles due to version change
lock '<Capistrano version>'

#Used to display Capistrano logs
set :application, '<Own application name>'

#Specify from which repository you want to pull your app
set :repo_url,  '[email protected]:<Github username>/<Repository name>.git'

#Specify a directory to be referenced in common even if the version changes
set :linked_dirs, fetch(:linked_dirs, []).push('log', 'tmp/pids', 'tmp/cache', 'tmp/sockets', 'vendor/bundle', 'public/system', 'public/uploads')

set :rbenv_type, :user
set :rbenv_ruby, '<The version of ruby used in this app>' 

#Which public key to use for deployment
set :ssh_options, auth_methods: ['publickey'],
                  keys: ['<Ssh key for EC2 instance on local PC(pem)Path to (eg~/.ssh/key_pem.pem)>'] 

#Location of the file containing the process number
set :unicorn_pid, -> { "#{shared_path}/tmp/pids/unicorn.pid" }

#Location of Unicorn configuration files
set :unicorn_config_path, -> { "#{current_path}/config/unicorn.rb" }
set :keep_releases, 5

set :linked_files, %w{ config/master.key }

after 'deploy:publishing', 'deploy:restart'
namespace :deploy do
  task :restart do
    invoke 'unicorn:stop'
    invoke 'unicorn:start'
  end

  desc 'upload master.key'
  task :upload do
    on roles(:app) do |host|
      if test "[ ! -d #{shared_path}/config ]"
        execute "mkdir -p #{shared_path}/config"
      end
      upload!('config/master.key', "#{shared_path}/config/master.key")
    end
  end
  before :starting, 'deploy:upload'
  after :finishing, 'deploy:cleanup'
end

config/database.yml


production:
  <<: *default
  database: application_name_production
  username: root
  password: <%= ENV['DATABASE_PASSWORD'] %>
  socket: /var/lib/mysql/mysql.sock

8. Application server (unicorn) settings

Gemfile


group :production do
  gem 'unicorn', '5.4.1'
end

local


bundle install

config/unicorn.rb


app_path = File.expand_path('../../../', __FILE__)

worker_processes 1
#Specify current
working_directory "#{app_path}/current"

#Changed to refer to each in shared
listen "#{app_path}/shared/tmp/sockets/unicorn.sock"
pid "#{app_path}/shared/tmp/pids/unicorn.pid"
stderr_path "#{app_path}/shared/log/unicorn.stderr.log"
stdout_path "#{app_path}/shared/log/unicorn.stdout.log"

timeout 60

#The following is an applied setting, so the explanation is omitted.

preload_app true
GC.respond_to?(:copy_on_write_friendly=) && GC.copy_on_write_friendly = true

check_client_connection false

run_once = true

before_fork do |server, worker|
  defined?(ActiveRecord::Base) &&
    ActiveRecord::Base.connection.disconnect!

  if run_once
    run_once = false # prevent from firing again
  end

  old_pid = "#{server.config[:pid]}.oldbin"
  if File.exist?(old_pid) && server.pid != old_pid
    begin
      sig = (worker.nr + 1) >= server.worker_processes ? :QUIT : :TTOU
      Process.kill(sig, File.read(old_pid).to_i)
    rescue Errno::ENOENT, Errno::ESRCH => e
      logger.error e
    end
  end
end

after_fork do |_server, _worker|
  defined?(ActiveRecord::Base) && ActiveRecord::Base.establish_connection
end

config/environments/production.rb (after modification)


# config.assets.js_compressor = :uglifier

9. Clone code from Github

First, let's commit and push from GitHub Desktop. Be sure to do it on the master branch at this time.

ec2 server


#Create a new directory with the mkdir command
[ec2-user@ip-172-31-23-189 ~]$ sudo mkdir /var/www/
#Ec2 permissions on the created www directory-Change to user
[ec2-user@ip-172-31-23-189 ~]$ sudo chown ec2-user /var/www/

image.png

ec2 server


[ec2-user@ip-172-31-23-189 ~]$ cd /var/www/
[ec2-user@ip-172-31-23-189 www]$ git clone https://github.com/<username>/<Repository name>.git

10. Nginx settings

ec2 server


#Installation
[ec2-user@ip-172-31-25-189 ~]$ sudo yum -y install nginx

#Start configuration file
[ec2-user@ip-172-31-25-189 ~]$ sudo vim /etc/nginx/conf.d/rails.conf

rails.conf


upstream app_server {
  #Changed to refer to in shared
  server unix:/var/www/<Application name>/shared/tmp/sockets/unicorn.sock;
}

server {
  listen 80;
  server_name <Fill in Elastic IP>;

#Set the maximum size of files uploaded from the client to 2 giga. The default is 1 mega, so keep it large
  client_max_body_size 2g;

  #Changed to refer to in current
  root /var/www/<Application name>/current/public;

  location ^~ /assets/ {
    gzip_static on;
    expires max;
    add_header Cache-Control public;
    #Changed to refer to in current
    root   /var/www/<Application name>/current/public;
  }

  try_files $uri/index.html $uri @unicorn;

  location @unicorn {
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_redirect off;
    proxy_pass http://app_server;
  }

  error_page 500 502 503 504 /500.html;
}

ec2 server


#Change nginx permissions
[ec2-user@ip-172-31-25-189 ~]$ cd /var/lib
[ec2-user@ip-172-31-25-189 lib]$ sudo chmod -R 775 nginx  
[ec2-user@ip-172-31-25-189 lib]$ cd ~
[ec2-user@ip-172-31-25-189 ~]$ sudo service nginx restart
#nginx reboot
[ec2-user@ip-172-31-25-189 ~]$ sudo service nginx reload
[ec2-user@ip-172-31-25-189 ~]$ sudo service nginx restart

11. EC2 capacity expansion / bundler install

ec2 server


[ec2-user@ip-172-31-25-189 ~]$ cd
[ec2-user@ip-172-31-25-189 ~]$ sudo dd if=/dev/zero of=/swapfile1 bs=1M count=512
[ec2-user@ip-172-31-25-189 ~]$ sudo chmod 600 /swapfile1
[ec2-user@ip-172-31-25-189 ~]$ sudo mkswap /swapfile1
[ec2-user@ip-172-31-25-189 ~]$ sudo swapon /swapfile1
[ec2-user@ip-172-31-25-189 ~]$ sudo sh -c 'echo "/swapfile1  none        swap    sw              0   0" >> /etc/fstab'

[ec2-user@ip-172-31-23-189 <Repository name>]$ gem install bundler -v x.x.x
#Introduce the locally confirmed version of bundler
[ec2-user@ip-172-31-23-189 <Repository name>]$ bundle install
#The above command may take several minutes or more.

12. Setting environment variables

ec2 server


[ec2-user@ip-172-31-23-189 <Repository name>]$ rake secret
69619d9a75b78f2e1c87ec5e07541b42f23efeb6a54e97da3723de06fe74af29d5718adff77d2b04b2805d3a1e143fa61baacfbf4ca2c6fcc608cff8d5a28e8d
#Copy string

ec2 server


[ec2-user@ip-172-31-23-189 ~]$ sudo vim /etc/environment

/etc/environment


DATABASE_PASSWORD='MySQL root user password'
SECRET_KEY_BASE='The secret you copied earlier_key_base'

After writing, press esc (escape key) and enter: wq to save the contents. Once saved, log out to apply the environment variables.

local


$ ssh -i [The name of the downloaded key].pem ec2-user@[Elastic IP associated with the created EC2 instance]
(Using the downloaded key, ec2-Login as user)

ec2 server


[ec2-user@ip-172-31-23-189 ~]$ env | grep SECRET_KEY_BASE
SECRET_KEY_BASE='secret_key_base'

[ec2-user@ip-172-31-23-189 ~]$ env | grep DATABASE_PASSWORD
DATABASE_PASSWORD='MySQL root user password'

ec2 server


#Production environment database creation
[ec2-user@ip-172-31-23-189 <Repository name>]$ cd /var/www/app name/releases
[ec2-user@ip-172-31-23-189 <Repository name>]$ ll 
#A folder with the name of the number representing the date is displayed Example: 20200218063515
cd bottom number#=>Example cd 20200218063515
[ec2-user@ip-172-31-23-189 <Repository name>]$ rails db:create RAILS_ENV=production
# => Created database '<Database name>'
[ec2-user@ip-172-31-23-189 <Repository name>]$ rails db:migrate RAILS_ENV=production

13. Automatic deployment

Push all local changes to master

local


#Run in the application directory
$ bundle exec cap production deploy

How to check the error log On the server side Check / var / www / <repository name> /current/log/unicorn.stderr.log with the less or cat command and check if there are any errors (the lower the log, the more up-to-date the log is. Note that it is UTC)

Recommended Posts

Recipe for deploying Rails apps on AWS EC2
Launch Rails on EC2
Deploy RAILS on EC2
Install docker on AWS EC2
For beginners! Automatic deployment with Rails6 + CircleCI + Capistrano + AWS (EC2)
[Rails] DB design for EC site
Error when deploying EC2 on CircleCI
Launch Rails on EC2 (manual deployment)
[EC2 / Vue / Rails] EC2 deployment procedure for Vue + Rails
[Rails] AWS EC2 instance environment construction
Tutorial 1 for building apps with Rails (First steps to Yay! You're on Rails!)
EFS mount on AWS Ubuntu EC2 (amazon-efs-utils)
Deployed using Docker + Rails + AWS (EC2 + RDS)
Easy deployment with Capistrano + AWS (EC2) + Rails
SCSS doesn't work when deploying Rails6 AWS
Try deploying a Rails app on EC2-Part 1-
Knowledge required to bring Rails apps to AWS
[Rails] Image posting by CarrierWave [AWS EC2]
Explanation of Ruby on rails for beginners ①
[Personal memo] Summary of stumbling blocks when deploying Rails apps to AWS
Validation settings for Ruby on Rails login function
[Ruby on Rails] Select2 introduction memo for Webpacker
[Rails] Procedure for linking databases with Ruby On Rails
Tailwind on Rails
EC2 on Docker-compose
Install docker-compose on a Graviton 2 instance of AWS EC2
How to install Ruby on an EC2 instance on AWS
Memo to build a Servlet environment on AWS EC2
Build a Ruby on Rails development environment on AWS Cloud9
Deploy laravel using docker on EC2 on AWS ① (Create EC2 instance)
Challenge the settings for developing with vue.js on Rails 6
Explanation of Ruby on rails for beginners ⑥ ~ Creation of validation ~
A high bill from AWS during development on rails ...
Explanation of Ruby on rails for beginners ② ~ Creating links ~
Explanation of Ruby on rails for beginners ⑦ ~ Flash implementation ~