Deploying to AWS EC2 using Capistrano
This guide walks through the process of creating a cloud server using Amazon Web Services, configuring it to run as a deployment server and setting up a deployment workflow using Capistrano
1. Create and login to a new EC2 instance
Create an AWS account (you will be eligible for one year free-tier). Navigate to the EC2 Dashboard in Singapore region
In local machine Terminal enter the following command and copy the result. If you do not have a default SSH key, follow the steps here to generate one with the identity
id_rsa
cat ~/.ssh/id_rsa.pub
From the AWS EC2 dashboard click ‘Key Pairs’, then ‘Import Key Pairs’. Specify the name
id_rsa
and paste the key you just copied from your local machine. Then click ‘Import’.Click on Instances in the sidebar and 'Launch Instance'. Select Ubuntu Server 14.04 LTS (HVM), SSD Volume Type - ami-25c00c46
Select General purpose: t2.micro and click 'Review and Launch'
At the review page, click 'Edit Security Groups'.
Create a security group named merchant and Add Rule HTTP to allow incoming HTTP connections on port 80 (see image):
Click 'Review and Launch' to go back to the review page. Click 'Launch'
To associate an SSH key to enable secure remote login, select 'Choose an existing key pair' and select
id_rsa
Check the check-box and click 'Launch Instances'. You will be brought to the post-launch page
Click on View Instances to be taken back to the AWS EC2 console to view your new instance. After a short initialization time (about 30 seconds), the instance should have passed status checks
When Capistrano is run on the local machine, it will connect to your EC2 server via SSH to remotely pull down the latest code from GitHub and deploy it into production. To enable this, we need to allow the local machine to forward SSH requests
On local machine, open the file
~/.ssh/config
and add the following (NOTE: replace the IP address with that of your EC2 instance)Host 52.77.234.2 ForwardAgent yes
Next, you need to add your local private key to the
ssh-agent
- reference heremhan@Mingdings-MacBook-Air:~$ eval "$(ssh-agent -s)" Agent pid 29576 mhan@Mingdings-MacBook-Air:~$ ssh-add ~/.ssh/id_rsa Identity added: /Users/mhan/.ssh/id_rsa (/Users/mhan/.ssh/id_rsa)
Login to your EC2 instance
ssh -i ~/.ssh/id_rsa [email protected]
(note the IP address). You should see the following:The authenticity of host '52.77.234.2 (52.77.234.2)' can't be established. RSA key fingerprint is fc:ed:63:62:dc:48:e7:a8:c7:0e:20:75:3f:48:76:55. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added '52.77.234.2' (RSA) to the list of known hosts. Welcome to Ubuntu 14.04.3 LTS (GNU/Linux 3.13.0-74-generic x86_64) * Documentation: https://help.ubuntu.com/ System information as of Sat Feb 20 11:21:36 UTC 2016 System load: 0.16 Memory usage: 5% Processes: 81 Usage of /: 9.9% of 7.74GB Swap usage: 0% Users logged in: 0 Graph this data and manage this system at: https://landscape.canonical.com/ Get cloud support with Ubuntu Advantage Cloud Guest: http://www.ubuntu.com/business/services/cloud 0 packages can be updated. 0 updates are security updates.
The programs included with the Ubuntu system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by
applicable law.
ubuntu@ip-172-30-0-195:~$
2. Setup EC2 instance with Ruby and RVM
Step by step instructions to setup your EC2 server for deploying a Rails application. Reference here
Login to your EC2 instance
ubuntu@ip-172-30-0-195:~$ pwd /home/ubuntu
Switch to root user privileges to update system and install Ubuntu packages
# from ubuntu@ip-172-30-0-195:~$ sudo -s # from root@ip-172-30-0-195:~# apt-get update apt-get -y upgrade apt-get -y install build-essential libmagickcore-dev imagemagick libmagickwand-dev libxml2-dev libxslt1-dev git-core nginx redis-server curl nodejs htop # Set Rails environment echo "RAILS_ENV=production" >> /etc/environment # Add a gemrc file to ubuntu user echo -e "verbose: true\nbulk_threshold: 1000\ninstall: --no-ri --no-rdoc --env-shebang\nupdate: --no-ri --no-rdoc --env-shebang" > /home/ubuntu/.gemrc chmod 644 /home/ubuntu/.gemrc chown ubuntu:ubuntu /home/ubuntu/.gemrc exit ubuntu@ip-172-30-0-195:~$
Install RVM and Ruby - reference here
\curl -sSL https://get.rvm.io | bash -s source ~/.rvm/scripts/rvm rvm install 2.2.2 rvm use 2.2.2 --default # Using /home/ubuntu/.rvm/gems/ruby-2.2.2 ruby -v # ruby 2.2.2p95 (2015-04-13 revision 50295) [x86_64-linux] gem install bundler # Fetching: bundler-1.11.2.gem (100%) # Successfully installed bundler-1.11.2 # 1 gem installed
3. Install and configure Nginx, Passenger and PostGreSQL
Switch to root user privileges
# from ubuntu@ip-172-30-0-195:~$ sudo -s
Install Git + Passenger + Nginx
apt-get install -y git apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 561F9B9CAC40B2F7 apt-get install -y apt-transport-https ca-certificates sh -c 'echo deb https://oss-binaries.phusionpassenger.com/apt/passenger trusty main > /etc/apt/sources.list.d/passenger.list' # update ubuntu sources apt-get update apt-get install -y nginx-extras passenger
Enable the Passenger Nginx module
vi /etc/nginx/nginx.conf # uncomment the following lines # passenger_root /usr/lib/ruby/vendor_ruby/phusion_passenger/locations.ini; # passenger_ruby /usr/bin/passenger_free_ruby;
Restart Nginx and validate the installation
service nginx restart passenger-config validate-install # * Checking whether this Passenger install is in PATH... ✓ # * Checking whether there are no other Passenger installations... ✓ # Everything looks good. :-) exit ubuntu@ip-172-30-0-195:~$
To ensure Nginx knows where to find the application code after we deploy, create a new conf file in
/etc/nginx/sites-enabled
with the application namemerchant
cd /etc/nginx/sites-enabled sudo vi merchant.conf
Add the following Nginx server configuration
server { listen 80; server_name 52.77.234.2; #replace with your server ip or domain # specify the application's 'public' directory root /home/ubuntu/apps/merchant/current/public; # Turn on Passenger passenger_enabled on; passenger_ruby /home/ubuntu/.rvm/gems/ruby-2.2.2/wrappers/ruby; }
As mentioned in the reference, the last line
passenger_ruby
should be replaced by the path to the correct Ruby interpreter to usepassenger-config about ruby-command
Restart Nginx
sudo service nginx restart
Setting Up PostgreSQL
sh -c "echo 'deb http://apt.postgresql.org/pub/repos/apt/ precise-pgdg main' > /etc/apt/sources.list.d/pgdg.list" wget --quiet -O - http://apt.postgresql.org/pub/repos/apt/ACCC4CF8.asc | sudo apt-key add - sudo apt-get update sudo apt-get install -y postgresql-common sudo apt-get install -y postgresql-9.3 libpq-dev
Test that pg gem can install properly with native extensions
gem install pg -v '0.18.3' # Fetching: pg-0.18.3.gem (100%) # Building native extensions. This could take a while... # Successfully installed pg-0.18.3 # 1 gem installed
Configure PostgreSQL to accept all connections
ls /etc/postgresql sudo vim /etc/postgresql/9.3/main/pg_hba.conf # change the following lines to METHOD 'trust', and save # ======================= # TYPE DATABASE USER ADDRESS METHOD # "local" is for Unix domain socket connections only # local all all trust # IPv4 local connections: # host all all 127.0.0.1/32 trust # IPv6 local connections: # host all all ::1/128 trust sudo service postgresql reload sudo -u postgres createuser ubuntu -s
4. Setup Capistrano on local machine
Switch to a new code branch:
git checkout -b capistrano
Update Gemfile for development environment and
bundle install
group :development do gem 'capistrano', '~> 3.4' gem 'capistrano-rails', '~> 1.1' gem 'capistrano-bundler' gem 'capistrano-passenger' gem 'capistrano-rvm' end # Capistrano 3.1 has some breaking changes. Please check the CHANGELOG: http://goo.gl/SxB0lr # If you're upgrading Capistrano from 2.x, we recommend to read the upgrade guide: http://goo.gl/4536kB # The `deploy:restart` hook for passenger applications is now in a separate gem called capistrano-passenger. Just add it to your Gemfile and require it in your Capfile.
Enable your project to use Capistrano
bundle exec cap install STAGES=production # mkdir -p config/deploy # create config/deploy.rb # create config/deploy/production.rb # mkdir -p lib/capistrano/tasks # create Capfile # Capified
In the
Capfile
uncomment these lines:# Capfile require 'capistrano/rvm' require 'capistrano/bundler' require 'capistrano/rails/assets' require 'capistrano/rails/migrations' require 'capistrano/passenger'
Specify Capistrano configuration for deployment instance
# config/deploy.rb set :application, 'merchant' set :repo_url, '[email protected]:hanmd82/merchant.git' ask :branch, `git rev-parse --abbrev-ref HEAD`.chomp set :deploy_to, '/home/ubuntu/apps/merchant' set :scm, :git set :format, :pretty set :log_level, :info set :pty, true set :linked_dirs, fetch(:linked_dirs, []).push('log', 'tmp/pids', 'tmp/cache', 'tmp/sockets', 'vendor/bundle', 'public/system') set :keep_releases, 5
Configure Capistrano to get app repo from Github and deploy
# config/deploy/production.rb server '52.77.234.2', user: 'ubuntu', roles: %w{web app db} set :ssh_options, { forward_agent: true }
5. Deploy the app with Capistrano
Ensure that these two gems are in your Gemfile and
bundle install
gem 'therubyracer', platforms: :ruby gem 'execjs'
Export IP address of EC2 instance as local environment variable
export PRODUCTION_SERVER_IP_ADDR=54.169.70.160
Deploy from local machine with Capistrano
cap production deploy
Create production database if you see the following error
ActiveRecord::NoDatabaseError: FATAL: database "merchant_app_production" does not exist
# from ubuntu@ip-172-30-0-195:~$ cd ~/apps/merchant/releases/${CURRENT_VERSION} RACK_ENV=production rake db:create
SSH into the EC2 instance to generate and copy new secret key
# from ubuntu@ip-172-30-0-195:~$ cd ~/apps/merchant/current bundle exec rake secret
Set the newly generated secret key as an environment variable using
sudo vi ~/.profile
export SECRET_KEY_BASE=${NEW_SECRET_KEY}
Check that you can access your application server
curl 52.77.234.2 # replace with your IP address or domain # you should see HTML markup corresponding to your index/html
Run database seeding tasks (if any)
# ubuntu@ip-172-30-0-195:~/apps/merchant/current$ RACK_ENV=production bundle exec rake db:seed
Navigate to
http://52.77.234.2
in a web browser window to view your application