Capistrano を使用した EC2 へのデプロイ作業メモ

9 min read読了の目安(約8500字

まえおき

先日 Rails のデプロイ作業を行なったので、その時のメモ。Capistrano の設定とか忘れがちなのでメモしとく

前提条件

  • EC2, RDS(postgress), セキュリティグループなどの諸々の環境は構築済み
  • Nginx は使わない

今回使用する各環境

OS: Amazon Linux2
DB: RDS (Postgres)
Rails: 6.1.3
Ruby: 3.0.1
Capistrano:

  • capistrano: 3.16
  • capistrano-rails: 1.6
  • capistrano3-puma: 5.0
  • capistrano-rbenv: 2.2

EC2

ユーザー作成

  1. ec2-userでログイン
  2. sudo adduser deploy
  3. sudo visudo で以下を記述
    root  ALL=(ALL)       ALL
    
    # 上記の行の下に、以下を追記
    # デプロイ時の puma 再起動時にパスワードなしで実行できるようにする
    deploy  ALL=NOPASSWD: /bin/systemctl
    
  4. sudo su - deploy
  5. mkdir .ssh -m 700
  6. vim .ssh/authorized_keys で ssh接続に使う公開鍵を登録
  7. chmod 600 .ssh/authorized_keys

これで deploy ユーザーでログインできるようになる。

各種必要なプラグインのインストール

postgresql-devel の部分は使用するDB毎に必要なものを入れる

$ sudo yum install -y git gcc openssl-devel readline-devel zlib-devel gcc-c++ postgresql-devel

rbenv

# 以下は必ずデプロイ時のユーザー(例: deploy, ec2-user)で行うこと
$ git clone https://github.com/rbenv/rbenv.git ~/.rbenv
$ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile
$ echo 'eval "$(rbenv init -)"' >> ~/.bash_profile
$ source ~/.bash_profile
$ mkdir -p "$(rbenv root)"/plugins
$ git clone https://github.com/rbenv/ruby-build.git "$(rbenv root)"/plugins/ruby-build

ruby

インストールする ruby バージョンは環境毎に変更すること

  1. rbenv install 3.0.1
  2. rbenv global 3.0.1
  3. rbenv rehash
  4. rbenv versions

Capistrano

前提条件

  • 上記に書いてある EC2 の設定を終えていること
  • master.key, credentials.yml.enc を作成済であること
    • まだの場合はローカルで bin/rails credentials:edit を実行
  • master.key, database.yml/home/deploy/アプリ名/shared/config/ においてあること

導入

  1. Gemfile 更新

    Gemfile
        --- 省略 ---
    group :development do
        gem "capistrano", "~> 3.16", require: false
        gem "capistrano-rails", "~> 1.6", require: false
        gem 'capistrano3-puma', "~> 5.0", require: false
        gem 'capistrano-rbenv', '~> 2.2', require: false
    end
        --- 省略 ---
    
  2. bundle install

  3. bundle exec cap install で必要なファイルを作成し、設定を書き換える。以下設定例

    Capfile
    # Load DSL and set up stages
    require "capistrano/setup"
    
    # Include default deployment tasks
    require "capistrano/deploy"
    
    # Load the SCM plugin appropriate to your project:
    #
    # require "capistrano/scm/hg"
    # install_plugin Capistrano::SCM::Hg
    # or
    # require "capistrano/scm/svn"
    # install_plugin Capistrano::SCM::Svn
    # or
    require "capistrano/scm/git"
    require 'capistrano/puma'
    # install_plugin Capistrano::Puma::Systemd
    
    # Include tasks from other gems included in your Gemfile
    #
    # For documentation on these, see for example:
    #
    #   https://github.com/capistrano/rvm
    #   https://github.com/capistrano/rbenv
    #   https://github.com/capistrano/chruby
    #   https://github.com/capistrano/bundler
    #   https://github.com/capistrano/rails
    #   https://github.com/capistrano/passenger
    #
    # require "capistrano/rvm"
    # require "capistrano/chruby"
    # require "capistrano/passenger"
    
    # require "capistrano/rails/assets"
    require "capistrano/rbenv"
    require "capistrano/bundler"
    require "capistrano/rails/migrations"
    
    install_plugin Capistrano::SCM::Git
    install_plugin Capistrano::Puma
    install_plugin Capistrano::Puma::Systemd # 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
    # config valid for current version and patch releases of Capistrano
    lock "~> 3.16.0"
    
    # アプリ名を記述
    set :application, "test_app"
    
    # クローンするリモートリポジトリを記述
    set :repo_url, "git@xxxxxxxx/xxx.git"
    
    # Default branch is :master
    # ask :branch, `git rev-parse --abbrev-ref HEAD`.chomp
    # set :branch, :main
    
    # Default deploy_to directory is /var/www/my_app_name
    # デプロイ先を記述
    set :deploy_to, "/home/deploy/test_app"
    
    # Default value for :scm is :git
    # set :scm, :git
    
    # Default value for :format is :airbrussh.
    set :format, :airbrussh
    set :log_level, :debug
    
    # You can configure the Airbrussh format using :format_options.
    # These are the defaults.
    # set :format_options, command_output: true, log_file: "log/capistrano.log", color: :auto, truncate: :auto
    
    # Default value for :pty is false
    set :pty, true
    
    # Default value for :linked_files is []
    # デプロイ対象から外すファイルを記述
    append :linked_files, "config/master.key", "config/database.yml"
    
    # Default value for linked_dirs is []
    # デプロイ対象から外すディレクトリを記述
    append :linked_dirs, "log", "tmp/pids", "tmp/cache", "tmp/sockets", "public/system", 'vender/bundle'
    
    # Default value for default_env is {}
    set :default_env, { path: "/opt/ruby/bin:$PATH" }
    
    # Default value for local_user is ENV['USER']
    # set :local_user, -> { `git config user.name`.chomp }
    
    # Default value for keep_releases is 5
    set :keep_releases, 5
    
    # Uncomment the following to require manually verifying the host key before first deploy.
    # set :ssh_options, verify_host_key: :secure
    
    # rbenv
    set :rbenv_type, :user
    set :rbenv_ruby, '3.0.1'
    
    # 以下二つは正しく設定しないと gem を読み込めなくなるかも
    set :rbenv_path, '/home/ユーザー名(例: deploy, ec2-user .etc)/.rbenv'
    set :bundle_path, -> { shared_path.join('vendor/bundle') }
    
    config/deploy/production.rb
    server "デプロイ先のドメイン or ip", roles: %w{app web db}
    set :rails_env, "production"
    set :app_env, "production"
    set :branch, :main
    set :puma_service_unit_env_file, '/etc/environment'
    
    set :ssh_options, {
        user: 'deploy',   # rbenv_path で設定したユーザー名と合わせる
        keys: %w(/home/user_name/.ssh/id_rsa),  # ssh 接続に使う秘密鍵のパスを記述
        forward_agent: true,
        auth_methods: %w(publickey)
     }
    
  4. bundle exec cap production puma:config

  5. bundle exec cap production puma:systemd:config puma:systemd:enable を実行し、pumaの自動起動設定用ファイルが /etc/systemd/system 配下に作られる。デフォルトだと多分うまくいかないので、以下のように書き換える
    4. sudo 権限で弾かれる場合はこの記事を参考に、一時的にパスワードなしで実行できるようにする。(やり方は雑だけど)もちろん、終わったら元に戻す。

    /etc/systemd/system/puma_test_app_production.service
    [Unit]
    Description=Puma HTTP Server for test_app (production)
    After=network.target
    
    [Service]
    Type=simple
    User=デプロイ先のユーザー(例: ec2-user, deploy)
    WorkingDirectory=/home/deploy/test_app/current
    ExecStart=/home/Userと同じ名前/.rbenv/bin/rbenv exec bundle exec puma -C   /home/deploy/test_app/shared/puma.rb
    ExecReload=/bin/kill -TSTP $MAINPID
    StandardOutput=append:/home/deploy/test_app/shared/log/puma_access.log
    StandardError=append:/home/deploy/test_app/shared/log/puma_error.log
    EnvironmentFile=/etc/environment
    
    Restart=always
    RestartSec=1
    
    SyslogIdentifier=puma
    
    [Install]
    WantedBy=multi-user.target
    
  6. bundle exec cap production deploy:check

  7. bundle exec cap production deploy

Tips

  • デプロイ中に以下のような警告が出た場合は、EC2 内でsudo systemctl daemon-reload を実行する。(理由)

    00:15 puma:restart
      01 sudo /bin/systemctl restart puma_test_app_production
      01 Warning: puma_test_app_production.service changed on disk. Run 'systemctl daemon-reload' to reload units.
    ✔ 01 deploy@3.113.245.65 0.151s
    
  • gem 'capistrano3-puma', '~> 5.0', require: false のような書き方をしていると、bundle exec cap production puma:status のようなコマンドが使えなくなる。ただ環境が整えば打てる必要はないので、require つけてもいいと思う。

  • デプロイ時にpuma を再起動できるように Capfile にinstall_plugin Capistrano::Puma::Daemon追加したが、 invalid option: --daemon (OptionParser::InvalidOption) となってしまった。どうも README に書かれているように、5系から daemon は廃止になってしまったらしい。

  • puma の ログファイルが作られない場合はtouch /home/deploy/test_app/shared/log/puma_access.log, touch /home/deploy/test_app/shared/log/puma_error.log で作成する

感想

普通に苦戦した。特にcapistrano。何回やっても全然覚えられない。ただその分学びは多かった気がする。知らんけど。

参考資料

https://qiita.com/take18k_tech/items/5710ad9d00ea4c13ce36#新規ユーザーアカウントの作成