[DigitalOcean] 使用 Capistrano Deploy Rails App (二)
本文使用 Ubuntu 14.04 LTS / Ruby 2.3.0 / Rails 5.0.0
前言
續前篇建立Droplet及環境安裝,本文將講解專案如何透過Capistrano Deploy至我們準備好的production環境。 本文重點如下:
- Server部分
- MySQL 設定
- Nginx 設定
- Server安全性調整
- Host部分(本機)
- Capistrano安裝及設定
- deploy流程
流程講解
再設定之前,先進行流程的講解, 這邊將主機分為Host(本機)、Git主機(github或bitbucket)、Server主機(production伺服器)來講解
Deply流程
- 在Host主機,將完成的專案(master branch) push上git主機
- 使用ssh連到Server主機,將剛剛上傳的master branch從git主機clone或pull下來
- 再回到Host主機,將yml檔透過ssh丟給Server。(因為有安全性問題,不透過git傳)
- 最後如果有調整一些設定值,我們還要至server主機重啟http server。
雖然上述流程感覺不難,但是真正操作起來指令也是很繁瑣,秉持『工欲善其事,必先利其器』的原則,我們請出Capistrano這個自動化部署工具,來幫我們處理這些繁複卻一貫的作業。
Capistrano的好處就是在於你只需要一個指令,就可以完成上述deploy的動作,甚至還可以客製化自己想要的指令,並且可以同時開啟多個Deploy狀態,並且同時部署至多台機器。
Server部分設定
請先連線至 ssh root@xxx.xxx.xxx.xxx
再開始以下動作
建立專案用資料庫
$ mysql -u root -p
#=> 輸入密碼1234
mysql > create database YOUR_DATABASE_NAME default character set utf8;
mysql > show databases;
#=> 確認是否有建立了
mysql > exit
設定 Nginx 的 config symlinks到你的專案
sudo rm /etc/nginx/sites-enabled/default
sudo ln -nfs "/home/deploy/YOUR_PROJECT_NAME/current/config/nginx.conf" "/etc/nginx/sites-enabled/YOUR_PROJECT_NAME"
註:因為nginx.conf裡面有設定會include /etc/nginx/sites-enabled/中所有設定symlinks的檔案,所以我們以後每增加一個project,你都可以多設定一個symlinks來指定一個虛擬伺服器
新增deploy帳號
$ adduser deploy
# =>設定密碼,其他資訊都enter跳過即可
$ gpasswd -a deploy sudo
# => 將deploy加入sudo群組
關閉root遠端登入
$ vim /etc/ssh/sshd_config
#=> 找到 PermitRootLogin 將yes 改為no
$ service ssh restart
建立server自動git pull需要的金鑰
$ ssh-keygen
#=> 建立金鑰
$ cat ~/.ssh/id_rsa.pub
#=> 複製公鑰
到 github 或 Bitbucket開一個新專案
- github 到 SSH and GPG keys 新增貼上
- Bitbucket 到 專案 / Settings / Deployment keys 新增貼上
host部分設定
請輸入exit跳回自己電腦
新增git origin位置,並首次push
git remote add origin git@bitbucket.org:anxgang/kixerp.git
git push -u origin --all
設定deploy免輸入密碼登入
執行此指令將本機公鑰複製到伺服器
ssh deploy@xxx.xxx.xxx.xxx 'mkdir -p ~/.ssh;cat >> ~/.ssh/authorized_keys' < ~/.ssh/id_rsa.pub
設定Capistrano自動化部署
修改 Gemfile
# [path] 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
# => 會詢問要不要安裝額外的套件,選NO
修改 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 }
覆寫 config/deploy.rb
並設定deploy.rb
# [path] config/deploy.rb
# config valid only for current version of Capistrano
set :repo_url, 'git@github.com:YOUR_USERNAME/YOUR_PROJECT_NAME.git' # 改成你的
set :application, 'YOUR_PROJECT_NAME' # 改成你的 appname
set :user, 'deploy' # 這個對應到我們剛剛增加的 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
修改 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"
修改 config/nginx.conf
在專案新增 config/nginx.conf
// [path] config/nginx.conf
upstream puma {
server unix:///home/deploy/YOUR_PROJECT_NAME/shared/tmp/sockets/KaohsiungRubbishTruck-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;
}
error_page 500 502 503 504 /500.html;
client_max_body_size 10M;
keepalive_timeout 10;
}
調整 .gitignore
加入以下兩行
# [path] .gitignore
/config/database.yml
/config/secrets.yml
複製
- config/database.yml -> config/database.yml.sample
- config/secrets.yml -> config/secrets.yml.sample
刪除
- config/database.yml
- config/secrets.yml
將剛剛做的修改 commit 起來,push 到遠端
git add .
git commit -m "ready to deploy"
git push origin master
修改 config/database.yml
production:
- <<: *default
- database: db/production.sqlite3
+ adapter: mysql2
+ encoding: utf8
+ database: YOUR_PROJECT_NAME
+ pool: 5
+ username: root
+ password: 1234
+ socket: /var/run/mysqld/mysqld.sock
修改 config/secrets.yml
- secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
+ secret_key_base: e4a98dd1063a6765xxxxxxx # 這一串從上面的 test 或是 development 複製過來即可
首次 deploy
$ cap production deploy:check
$ cap production deploy:upload
#=> 把 database.yml 跟 secrets.yml 丟到 Ubuntu(之後如果有更改也可以這樣丟)
$ cap production deploy:initial
#=> ssh 登入,重開 nginx:$ sudo service nginx restart
$ cap production seed
#=> 有寫seed可以順便丟
其他指令可以這樣查
cap -T
如果用 Capistrano 的時候他還是叫你打密碼, 修正方法如下:
eval `ssh-agent -s`
ssh-add ~/.ssh/id_rsa
如果你是用 zsh,請再做以下修改
vi ~/.zshrc
plugins=(git ssh-agent) # 加上這一行
Normal deploy
git commit -m "commit message"
git push origin master
cap production deploy
查看網站是否Deploy成功
http:/xxx.xxx.xxx.xxx/
若失敗的話,可以進入Server查看log
ssh deploy@xxx.xxx.xxx.xxx
tail -f /home/deploy/YOUR_PROJECT_NAME/current/log/production.log
留言