capistranoでデプロイをしてもRailsアプリのコードが反映されない!とハマったので備忘録
状況は以下の通り
- デプロイはcapistranoで行っている(リリースディレクトリに対するsymlinkの切り替えがリリースとなっている)
- Railsアプリはpumaで動かしている
- pumaはsupervisordで管理している
- デプロイの際、pumaのpidに対してSIGUSR2を飛ばしてリスタートをかけている
- が、Railsアプリのコードが反映されていない
[program:my_app]
command=bundle exec puma -C config/puma.rb
directory=/root/current
process_name=%(program_name)s
stdout_logfile=/var/log/supervisor/supervisord.log
stdout_logfile_maxbytes=1MB
stdout_logfile_backups=5
stdout_capture_maxbytes=1MB
redirect_stderr=true
pumaの設定はファイルはこんな感じ。
threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }
threads threads_count, threads_count
port ENV.fetch("PORT") { 3000 }
environment ENV.fetch("RAILS_ENV") { "development" }
原因
supervisordのdirectoryでsymlinkを指定すると、起動するプロセスのディレクトリはsymlinkを展開した実際のディレクトリにcdします。つまり、/root/currentを指定すると/root/app1のディレクトリにcdすることになります。コードで追うと以下の部分。supervisordのバージョンは3.3.4です。
class Subprocess(object):
def _spawn_as_child(self, filename, argv):
options = self.config.options
# ...
try:
cwd = self.config.directory
if cwd is not None:
options.chdir(cwd)
except OSError, why:
# ...
# set umask, then execve
try:
if self.config.umask is not None:
options.setumask(self.config.umask)
options.execve(filename, argv, env)
except OSError, why:
指定したdirectoryの値でcdしてからexecveしています。
一方、pumaの起動ディレクトリは設定ファイルで指定されていない場合、カレントディレクトリになります。
module Puma
class Launcher
def initialize(conf, launcher_args={})
# ...
generate_restart_data
# ...
Dir.chdir(@restart_dir)
# ...
def generate_restart_data
if dir = @options[:directory]
@restart_dir = dir
# ...
end
@restart_dir ||= Dir.pwd
capistranoはsymlinkでリリースを切り替えますが、@restart_dirは古いディレクトリの状態で切り替わらず変更が反映されないことになります。
解決策としては、設定ファイルのdirectoryにsymlinkのパスを設定するだけでOKです。これによって、pumaが起動されるときにdirectoryのsymlinkを解決することになります。再起動時も必ずsymlinkを経由してcdするため、新しくなったcurrentのリンク先を参照することになります。
directory '/root/current'