幫Capistrano加上自動化Rails指令


Deploy Rails至VPS當中我使用capistrano進行自動化佈署,非常快速簡便。

但接下來會遇到一個問題:裡頭並沒有其他自動化指令,因此每次佈署,後續都會有一些必須執行的指令。例如標準動作bundlerake db:migrate等等,如果每次佈署後都可以自動化執行,就可以省下非常多功夫。

Rails 版本 4.2.0
Capistrano 版本 3.4.0

在capistrano當中可以設定不同的task,就像一般Rakefile當中所使用的task一樣,佈署後可直接指定執行。

1. 佈署後自動更新gem

可以在deploy檔案的namespace :deploy底下另外定義我們要執行的task。

/config/deploy.rb

namespace :deploy do 
  # ...原本的code

  task :bundle do
    on roles(:web) do
      execute "cd /var/www/rails/current; bundle install"
    end
  end

  after :deploy, "deploy:bundle"
end

其中web是你所設定的角色名稱,我是使用預設的web角色,詳情可看前一篇的設定內容。

路徑的部份,原本capistrano在官網上提供的寫法是within current_path do ... end的block寫法,但我不管怎麼試都不成功,於是直接土法煉鋼的把路徑打上去。rails是我們設定的app名稱,設定內容同樣在前一篇可看到。

最後加上的after指令,就會在執行完一般的deploy程序之後執行,因此邏輯就是在deploy之後執行bundle install

2. 加上其他自動化項目

除了bundle以外,一般佈署也需要執行以下指令:

$rake db:migrate
$rake assets:clean
$rake assets:precompile

可以照上面同樣的方法將指令寫在capistrano當中。

/config/deploy.rb

namespace :deploy do 
  # ...原本的code

  path = "cd /var/www/rails/current;"
  task :migrate do
    on roles(:web) do
      execute "#{path}rake db:migrate RAILS_ENV=production"
    end
  end

  task :precompile do
    on roles(:web) do
      execute "#{path}rake assets:clean"
      execute "#{path}rake assets:precompile"
    end
  end

  after :deploy, "deploy:migrate"
  after :deploy, "deploy:precompile"
end

這邊將路徑抽出來放在path變數當中,比較容易整理。另外migrate時將環境指定到production,才不會只migrate到development環境。

3. 連結public資料夾

許多網站都會在public資料夾當中放入user上傳的圖片、文件等等,或是有一些不會常去動到的靜態資料,這些資料在每次deploy時會被蓋掉,必須重新建立連結。

因此,我們將這些資料放到shared/public資料夾當中(佈署不會蓋掉),並且每次佈署完到current/public資料夾來建立連結。

/config/deploy.rb

namespace :deploy do 
  # ...原本的code

  task :symlink do
    on roles(:web) do
      execute "cd /var/www/rails/current/public; ln -s /var/www/rails/shared/public/uploads"
    end
  end

  after :deploy, "deploy:symlink"
end

如此一來,原本放在shared/public/uploads底下的檔案,就會每次佈署完後建立連結至current/public當中,可經由一般正常的網頁路徑存取。

4. 重啟server

要能夠在重啟遠端server會稍微麻煩一些,因為重啟需要sudo權限,但capistrano沒辦法在本機將sudo密碼送到遠端主機上。一種解法是在遠端機器上建立passwordless sudo指令,讓佈署的使用者不需要打密碼就可以利用sudo指令重啟。

但這一方面較危險,另一方面是本人嘗試一直失敗(主要原因),因此後來改用sshkit-sudo這個gem來完成。

首先在本機進行安裝:

gem 'sshkit-sudo'

並執行bundle。接下來要在capistrano當中require:

Capfile

require 'sshkit/sudo'

這樣就算設定好了,直接在deploy設定檔當中寫上重啟指令:

/config/deploy.rb

namespace :deploy do 
  # ...原本的code

  task :server_restart do
    on roles(:web) do
      execute! :sudo, :service, :nginx, :restart
    end
  end

  after :deploy, "deploy:server_restart"
end

這樣在佈署指令執行到最後,就會要求輸入密碼,這時輸入就會正常了!

5. 啟用public key認證

這跟capistrano本身較沒關係,而是跟機器設定較有關係,如果我們可用public key直接在遠端機器上驗證,這樣連ssh的密碼都免打,capistrano的佈署也能更方便。

更重要的,capistrano不知為何在我們輸入ssh密碼時,一定會明文顯示,這在公共場合進行佈署時,就顯得非常危險。

首先取得本機的ssh public key:

$ ssh-add -L

將顯示出來的一大串key複製起來,接著到遠端機器上將key加入白名單:

$ cd .ssh
$ echo "key" >> authorized_keys
$ sudo service ssh restart

其中key是你在本機上複製起來的那串,記得加上""

重啟之後,可以嘗試離線再連線,正確的話應該就不需要密碼登入了,capistrano也一樣。

假如因此有把密碼登入給取消的話,記得在其他地方將這份key保留起來,如果電腦爆了,才不致於會無法登入遠端機器。

總結

當然,本篇算是一個經驗談,code有點亂,有許多值得改進的地方,如果各位有更棒的寫法,歡迎留言或來信跟我說!

最後附上本篇文章所有自動化code都寫在一起的檔案

圖片來源

Capistrano官網