幫Capistrano加上自動化Rails指令
在Deploy Rails至VPS當中我使用capistrano進行自動化佈署,非常快速簡便。
但接下來會遇到一個問題:裡頭並沒有其他自動化指令,因此每次佈署,後續都會有一些必須執行的指令。例如標準動作bundle
、rake 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都寫在一起的檔案。