4 分鐘閱讀

本文使用 Ubuntu 14.04 LTS / Ruby 2.3.0 / Rails 5.0.0

接續上篇 DigitalOcean - 使用 Capistrano Deploy Rails App (一)

前言

續前篇建立Droplet及環境安裝,本文將講解專案如何透過Capistrano Deploy至我們準備好的production環境。 本文重點如下:

  • Server部分
    • MySQL 設定
    • Nginx 設定
    • Server安全性調整
  • Host部分(本機)
    • Capistrano安裝及設定
    • deploy流程

流程講解

再設定之前,先進行流程的講解, 這邊將主機分為Host(本機)、Git主機(github或bitbucket)、Server主機(production伺服器)來講解

Deply流程

  1. 在Host主機,將完成的專案(master branch) push上git主機
  2. 使用ssh連到Server主機,將剛剛上傳的master branch從git主機clone或pull下來
  3. 再回到Host主機,將yml檔透過ssh丟給Server。(因為有安全性問題,不透過git傳)
  4. 最後如果有調整一些設定值,我們還要至server主機重啟http server。

螢幕快照 2016-08-06 下午2.03.14.png

雖然上述流程感覺不難,但是真正操作起來指令也是很繁瑣,秉持『工欲善其事,必先利其器』的原則,我們請出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

參考

  1. kakas/CapistranoDeployTest
  2. (Mini Course) Deploy Rails Project to Linux Server - Growth School
  3. Deploying a Rails App on Ubuntu 14.04 with Capistrano, Nginx, and Puma - DigitalOcean

更新時間:

留言