普段作るRailsのプロジェクトはGitで管理しているので、アプリケーションのアップデート手順というと、sshでログインしてgit pull。あと必要に応じてbundle installやrake db:migrateして、touch tmp/restart.txtで完了。
たいした手間ではないし、昔のように更新されたファイルを選んでFTPでアップロードしてた頃に比べたら格段に楽だし確実になった。
とはいえ今までは複数のサーバを一斉にアップデートする。ということはあまりなかったのでこれで良かったんだけど、今やってるプロジェクトはローンチして間もないため頻繁にアップデートがある上、複数に適用する必要があるため、微妙に手間がかかるし、たまにcache:clearやassets:precompileを忘れて変なことになる。
ということで遅ればせながらRuby界隈のデプロイツールとして定番のCapistranoを使ってみることにした。
Capistranoは最初に生成されるファイルがコメント沢山の大きなファイルで(まあRails自体がそんな感じなんだけど)、学習コストが高そうな印象を受けるんだけど、思い切ってコメントを全部消してシンプルな設定ファイルにしてみると、それほど大変ではなさそうという気がしてくる。特にRakeのタスクを書き慣れている人なら馴染みやすい感じ。一度書いてしまえば後々のプロジェクトでも流用できるし。
主に書かなければいけないのがデプロイ手順を書いたconfig/deploy.rbで、こんな感じになった。
lock '3.2.1' set :application, 'Project' set :repo_url, 'git://github.com/miyamae/xxxxx.git' set :keep_releases, 5 set :bundle_path, -> { shared_path.join('vendor/bundle') } set :linked_files, %w{ config/database.yml config/application.yml config/newrelic.yml } set :linked_dirs, %w{ log tmp vendor/bundle public/system } namespace :deploy do desc 'Upload sample configuration files' task :config do on roles(:app) do |host| if test("[ ! -d #{shared_path}/config ]") execute("mkdir -p #{shared_path}/config") end if test("[ ! -e #{shared_path}/config/database.yml ]") upload!('config/database.yml.sample', "#{shared_path}/config/database.yml") end if test("[ ! -e #{shared_path}/config/application.yml ]") upload!('config/application.yml.sample', "#{shared_path}/config/application.yml") end if test("[ ! -e #{shared_path}/config/newrelic.yml ]") upload!('config/newrelic.yml.sample', "#{shared_path}/config/newrelic.yml") end end end desc 'Restart application' task :restart do on roles(:app), in: :sequence, wait: 5 do execute :touch, release_path.join('tmp/restart.txt') end end after :restart, :clear_cache do on roles(:app), in: :groups, limit: 3, wait: 10 do within current_path do with rails_env: fetch(:rails_env) do execute :rake, 'tmp:cache:clear' end end end end after :restart, :assets_precompile do on roles(:app), in: :groups, limit: 3, wait: 10 do within release_path do with rails_env: :production do execute :rake, 'assets:precompile' end end end end before :starting, :config after :publishing, :restart end
まだ理解不十分のため最適化できていないと思います。きもちいい!って感じではないけど、一応やっていることは理解できるコードかな。database.ymlなどの設定ファイルサンプルのコピーをアップロードするconfigタスクがちょっと汚い。あと、capistrano-railsを使ってるけど、なぜかdeploy:compile_assetsが動いてくれなかったので、自前でタスクを定義している。たぶん正しい方法ではないけど、自前で定義しても大したコストではない。
今回はRuby環境がターゲットによってRVMだったりrbenvだったりするため、capistrano-rvmやcapistrano-rbenvは使わなかった。
そしてデプロイ先の設定はconfig/deploy/*.rb。これはdeploy.rbの内容をデプロイ先ごとに細かく設定できるファイルですね。最小限だとsshの接続先とデプロイ先パスを書くだけでシンプル。
server 'www.example.com', user: 'web', roles: %w{ app db } set :rails_env, :production set :deploy_to, '/var/www/application'
これで、Ansibleでサーバをセットアップして、Capistranoでアプリケーションのデプロイ、という感じで意外にコストのかかる日常業務がほぼ自動化されました。多少ターゲットの台数が増えてもそれほど手間が大きくならずに済みます。