[Linode] Deploy Rails app to Linode by Capistrano ( Ubuntu 16.04/nginx/puma/MySQL/rvm )
Ubuntu 16.04 / Ruby 2.3.1 / Rails 5.0.0
Create a Linode
- plan:
- Ubuntu 16.04 LTS Disk
-
256MB Swap Image
- login user:
- root 123456
ssh to remote side and build environment
$ ssh root@xxx.xxx.xxx.xxx
#=> Linode > dashboard > remote access
1) update all packages
$ sudo apt-get -y update;sudo apt-get -y upgrade;sudo apt-get -y autoremove;
2) setup time zone
$ sudo dpkg-reconfigure
or
$ sudo timedatectl set-timezone Asia/Taipei
3) Generate locales
$ sudo locale-gen zh_TW zh_TW.UTF-8 zh_CN.UTF-8 en_US.UTF-8;
4) install MySQL
setup root pw: 1234
$ sudo debconf-set-selections <<< 'mysql-server mysql-server/root_password password 1234';sudo debconf-set-selections <<< 'mysql-server mysql-server/root_password_again password 1234';sudo apt-get -y install mysql-server mysql-common mysql-client libmysqlclient-dev;
5) install necessary packages
$ sudo apt-get install -y build-essential git-core bison openssl libreadline6-dev curl zlib1g zlib1g-dev libssl-dev libyaml-dev libsqlite3-0 libsqlite3-dev sqlite3 autoconf libc6-dev libpcre3-dev curl libcurl4-nss-dev libxml2-dev libxslt-dev imagemagick nodejs libffi-dev
6) install rvm & ruby 2.3.1
$ sudo \curl -sSL https://get.rvm.io | bash;source ~/.rvm/scripts/rvm;rvm install 2.3.1;
if Mac $ sudo \curl -sSL https://get.rvm.io | bash;source /etc/profile.d/rvm.sh;rvm install 2.3.1;
7) install nginx
$ sudo apt-get -y install nginx;
8) install Rails & bundler
$ gem install rails -v '5.0.0' -V --no-ri --no-rdoc;gem install bundler -V --no-ri --no-rdoc;
check your settings
$ date
# Wed Aug 6 10:05:17 CST 2016
$ rvm list
# =* ruby-2.3.1
$ ruby -v
# ruby 2.3.1
$ rails -v
# Rails 5.0.0
$ nginx -v
# nginx version: nginx/1.4.6 (Ubuntu)
$ mysql -u root -p
# enter pw 1234, there will goes `mysql>`, and you can type `exit` to leave
Create database
1) login
$ mysql -u root -p
2) create database ( replace YOUR_DATABASE_NAME )
mysql> create database YOUR_DATABASE_NAME default character set utf8mb4;
3) make a check
mysql> show databases;
setup Nginx symlinks (linked to your project)
1) delete default setting
$ sudo rm /etc/nginx/sites-enabled/default
2) add our symlink ( replace YOUR_PEOJECT_NAME )
$ sudo ln -nfs "/home/deploy/YOUR_PROJECT_NAME/current/config/nginx.conf" "/etc/nginx/sites-enabled/YOUR_PROJECT_NAME"
3) make a check
$ ls -la /etc/nginx/sites-enabled/
# =>lrwxrwxrwx 1 root root 45 Aug 5 12:06 kixERP -> /home/deploy/YOUR_PROJECT_NAME/current/config/nginx.conf
add a deploy user
1) setup your password (other information you can just enter to pass )
$ adduser deploy
2) Add user deploy to group sudo
$ gpasswd -a deploy sudo
close root login
1) find “PremitRootLogin” , change from yes to no
$ vim /etc/ssh/sshd_config
2) restart ssh service
$ service ssh restart
login by user deploy
1) setup ssh-key login
ssh deploy@xxx.xxx.xxx.xxx 'mkdir -p ~/.ssh;cat >> ~/.ssh/authorized_keys' < ~/.ssh/id_rsa.pub
If not work.. Maybe you should login first (
ssh deploy@xxx.xxx.xxx.xxx
) And answer ‘yes’ to add the host fingerprint to the~/.ssh/known_hosts
Then try again..
2) login
ssh deploy@xxx.xxx.xxx.xxx
ssh-key generate
1) generate ssh-key
$ ssh-keygen
2) copy the public key
$ cat ~/.ssh/id_rsa.pub
3) paste the key into your github or bitbucket
- github
- Bitbucket - YOUR_PROJECT_NAME / Settings / Deployment keys
Setup Capistrano
1) setup Gemfile
- gem 'sqlite3'
+ gem 'sqlite3', group: :development
- # gem 'therubyracer', platforms: :ruby
+ gem 'therubyracer', platforms: :ruby
+ group :development do
+ gem 'capistrano', '~> 3.6.0', require: false
+ gem 'capistrano-rvm', '~> 0.1', require: false
+ gem 'capistrano-rails', '~> 1.1.7', require: false
+ gem 'capistrano-bundler', '~> 1.1.4', require: false
+ gem 'capistrano3-puma', '~> 1.2.1', require: false
+ end
+ group :production do
+ gem "mysql2"
+ end
$ bundle install
$ cap install
# => ask for installing other package? no
2) modify Capfile
# Load DSL and set up stages
require "capistrano/setup"
# Include default deployment tasks
require "capistrano/deploy"
require 'capistrano/rails'
require 'capistrano/rvm'
require 'capistrano/bundler'
require 'capistrano/rails/assets'
require 'capistrano/rails/migrations'
require 'capistrano/puma'
# Load custom tasks from `lib/capistrano/tasks` if you have any defined
Dir.glob("lib/capistrano/tasks/*.rake").each { |r| import r }
3) replace config/deploy.rb
# config valid only for current version of Capistrano
set :repo_url, 'git@github.com: YOUR_GIT_ACCOUNT/YOUR_PROJECT_NAME'
set :application, 'YOUR_PROJECT_NAME'
set :user, 'deploy'
set :puma_threads, [4, 16]
set :puma_workers, 0
# Don't change these unless you know what you're doing
set :pty, true
set :use_sudo, false
set :stage, :production
set :deploy_via, :remote_cache
set :deploy_to, "/home/#{fetch(:user)}/#{fetch(:application)}"
set :puma_bind, "unix://#{shared_path}/tmp/sockets/#{fetch(:application)}-puma.sock"
set :puma_state, "#{shared_path}/tmp/pids/puma.state"
set :puma_pid, "#{shared_path}/tmp/pids/puma.pid"
set :puma_access_log, "#{release_path}/log/puma.error.log"
set :puma_error_log, "#{release_path}/log/puma.access.log"
set :ssh_options, { forward_agent: true, user: fetch(:user), keys: %w(~/.ssh/id_rsa.pub) }
set :puma_preload_app, true
set :puma_worker_timeout, nil
set :puma_init_active_record, true # Change to false when not using ActiveRecord
## Defaults:
# set :scm, :git
# set :branch, :master
# set :format, :pretty
# set :log_level, :debug
# set :keep_releases, 5
## Linked Files & Directories (Default None):
set :linked_files, %w{config/database.yml config/secrets.yml}
set :linked_dirs, %w{bin log tmp/pids tmp/cache tmp/sockets vendor/bundle public/system}
namespace :puma do
desc 'Create Directories for Puma Pids and Socket'
task :make_dirs do
on roles(:app) do
execute "mkdir #{shared_path}/tmp/sockets -p"
execute "mkdir #{shared_path}/tmp/pids -p"
end
end
before :start, :make_dirs
end
namespace :deploy do
desc "Make sure local git is in sync with remote."
task :check_revision do
on roles(:app) do
unless `git rev-parse HEAD` == `git rev-parse origin/master`
puts "WARNING: HEAD is not the same as origin/master"
puts "Run `git push` to sync changes."
exit
end
end
end
desc 'Initial Deploy'
task :initial do
on roles(:app) do
before 'deploy:restart', 'puma:start'
invoke 'deploy'
end
end
desc 'Restart application'
task :restart do
on roles(:app), in: :sequence, wait: 5 do
invoke 'puma:restart'
end
end
desc 'Upload to shared/config'
task :upload do
on roles (:app) do
upload! "config/database.yml", "#{shared_path}/config/database.yml"
upload! "config/secrets.yml", "#{shared_path}/config/secrets.yml"
end
end
before :starting, :check_revision
after :finishing, :compile_assets
after :finishing, :cleanup
after :finishing, :restart
end
desc "Run rake db:seed on a remote server."
task :seed do
on roles (:app) do
within release_path do
with rails_env: fetch(:rails_env) do
execute :rake, "db:seed"
end
end
end
end
# ps aux | grep puma # Get puma pid
# kill -s SIGUSR2 pid # Restart puma
# kill -s SIGTERM pid # Stop puma
4) modify config/deploy/production.rb
set :stage, :production
set :branch, :master
role :app, %w(deploy@xxx.xxx.xxx.xxx)
role :web, %w(deploy@xxx.xxx.xxx.xxx)
role :db, %w(deploy@xxx.xxx.xxx.xxx)
set :rails_env, "production"
set :puma_env, "production"
set :puma_config_file, "#{shared_path}/config/puma.rb"
set :puma_conf, "#{shared_path}/config/puma.rb"
5) create config/nignx.conf
upstream puma-YOUR_PROJECT_NAME {
server unix:///home/deploy/YOUR_PROJECT_NAME/shared/tmp/sockets/YOUR_PROJECT_NAME-puma.sock;
}
server {
listen 80 default_server deferred;
# server_name example.com;
root /home/deploy/YOUR_PROJECT_NAME/current/public;
access_log /home/deploy/YOUR_PROJECT_NAME/current/log/nginx.access.log;
error_log /home/deploy/YOUR_PROJECT_NAME/current/log/nginx.error.log info;
location ^~ /assets/ {
gzip_static on;
expires max;
add_header Cache-Control public;
}
try_files $uri/index.html $uri @puma;
location @puma {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://puma-YOUR_PROJECT_NAME;
}
error_page 500 502 503 504 /500.html;
client_max_body_size 10M;
keepalive_timeout 10;
}
6) modify .gitignore
+ /config/database.yml
+ /config/secrets.yml
- copy to *.sample
- config/database.yml -> config/database.yml.sample
- config/secrets.yml -> config/secrets.yml.sample
- delete
- config/database.yml
- config/secrets.yml
7) first commit
$ git add .
$ git commit -m "ready to deploy"
$ git push origin master
8) add ymls back and modify
* copy
- config/database.yml.sample -> config/database.yml
- config/secrets.yml.sample -> config/secrets.yml
config/database.yml
production:
- <<: *default
- database: db/production.sqlite3
+ adapter: mysql2
+ encoding: utf8mb4
+ database: YOUR_PROJECT_NAME
+ pool: 5
+ username: root
+ password:
+ socket: /var/run/mysqld/mysqld.sock
config/secrets.yml
- secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
+ secret_key_base: e4a98dd1063a6765xxxxxxx # copy from development or test secret_key_base
First Deploy
1) check deploy
$ cap production deploy:check
if it shows database.yml does not exist, don't worry about that. just go next step to copy yml.
2) upload database.yml and secrets.yml from ssh( not git )
$ cap production deploy:upload
3) initial deploy
$ cap production deploy:initial
4) rake db:seed
$ cap production seed
Normal deploy
$ git add . $ git commit -m "commit message" $ git push origin master $ cap production deploy
Open your website
open your site by ip
http://xxx.xxx.xxx.xxx
if failure, just ssh to Server for log
$ ssh deploy@xxx.xxx.xxx.xxx
$ tail -f /home/deploy/YOUR_PROJECT_NAME/current/log/production.log
參考
- kakas/CapistranoDeployTest
[(Mini Course) Deploy Rails Project to Linux Server Growth School](http://courses.growthschool.com/courses/deploy-rails-project-to-linux-server)
[Deploying a Rails App on Ubuntu 14.04 with Capistrano, Nginx, and Puma DigitalOcean](https://www.digitalocean.com/community/tutorials/deploying-a-rails-app-on-ubuntu-14-04-with-capistrano-nginx-and-puma)
留言