Yuto Blog

Yuto Blog

Ruby on Rails + Docker + Capistrano + Puma + Nginx AWS EC2へデプロイ方法のメモ

Ruby on Rails + Docker + Capistrano + Puma + Nginx AWS EC2へデプロイ方法のメモ

AWS EC2 インスタンスの準備

AWS コンソールにアクセスする

「Launch Instance」をクリックする

Step 1: Choose an Amazon Machine Image (AMI)

「Ubuntu Server 16.04 LTS (HVM), SSD Volume Type 64bit (x86」を選択する
スクリーンショット 2020-05-03 2.35.38.png

Step 2: Choose an Instance Type

「t2.micro」を選択する
スクリーンショット 2020-05-03 2.36.09.png

Step 3: Configure Instance Details

そのまま次へ進む
スクリーンショット 2020-05-03 2.37.05.png

Step 4: Add Storage

無料利用枠だと最大30GBまで
スクリーンショット 2020-05-03 2.38.43.png

Step 5: Add Tags

そのまま次へ進む
スクリーンショット 2020-05-03 2.41.16.png

Step 6: Configure Security Group

こちらは、SSHでは「My IP」、HTTPでは「Anywhere」を設定すればいいと思います。
スクリーンショット 2020-05-03 2.42.37.png

そして、「Review and Launch」をクリックして、情報確認終わったら「Launch」をクリックする

キーペアをダウンロードしてから「Launch Instances」をクリックすれば終了です。
スクリーンショット 2020-05-03 2.46.00.png

インスタンスへアクセス

先ほどダウンロードしたキーペアを「.ssh/key.pem」とします。

権限を与える

Copy
1
sudo chmod 400 .ssh/key.pem
サーバーにアクセス
Copy
1
ssh -i .ssh/key.pem ubuntu@52.196.67.76
52.196.67.76は先作成したインスタンスのIPv4 Public IPです。

デプロイ用のユーザーを作成

「deploy」というユーザー名とすると

Copy
1
sudo adduser deploy
ユーザーの権限を与える
Copy /etc/sudoers
1
%deploy ALL=(ALL) NOPASSWD:ALL

deployユーザーに切り替える

Copy
1
sudo su - deploy
authorized_keysを追加
Copy
1
2
3
4
mkdir .ssh
sudo chmod 700 .ssh
touch ~/.ssh/authorized_keys
sudo chmod 600 ~/.ssh/authorized_keys
Copy ~/.ssh/authorized_keys
1
2
<ローカル環境で「cat ~/.ssh/id_rsa.pub」の内容>
もしまだなければ「ssh-keygen -t rsa -C」で作成する

*これからローカル環境でssh deploy@52.196.67.76でインスタンスにアクセスできます。

サーバーで環境構築

Ruby、Rails、Gitなどインストール

rbenv
ruby
rails
libmysqlclient-dev / libpq-dev
git
bundler

上のものをインストールする方法は以下のリンクにあります。
インストール方法

Docker、docker-composeインストール

インストール方法は以下のリンクにあります。

インストール方法

デプロイ用フォルダとファイルを作成

例えばアプリ名は「todoapp」とすると:

Copy
1
2
3
4
5
6
7
8
9
10
sudo mkdir /deploy
sudo mkdir /deploy/apps
sudo mkdir /deploy/apps/todoapp
sudo mkdir /deploy/apps/todoapp/shared
sudo mkdir /deploy/apps/todoapp/shared/config

sudo touch /deploy/apps/todoapp/shared/config/application.yml
sudo touch /deploy/apps/todoapp/shared/config/database.yml

sudo chown -R deploy:deploy /deploy/apps
dockerビルド
Copy
1
cd /deploy/apps/todoapp/shared
Dockerfileとdocker-compose.ymlをこちらにペースト

そして、ビルド!

Copy
1
docker-compose build
起動する

Copy
1
docker-compose up -d
データベース設定
Copy /deploy/apps/todoapp/shared/config/database.yml
1
2
3
4
5
6
7
8
production:
  adapter: mysql2
  encoding: utf8mb4  
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  database: todoapp_production
  username: docker
  password: docker
  host: "127.0.0.1"

秘密キー設定

ローカル環境で秘密キーを取得する:

Copy
1
RAILS_ENV=production rake secret
Copy /deploy/apps/todoapp/shared/config/application.yml
1
SECRET_KEY_BASE: "<secret_key>"

ENV定数管理Gemをインストール

figaro を使うと application.yml が使えます。

Githubページ

Copy Gemfile
1
gem "figaro"
Copy
1
figaro install
Capistrano設定

Gem追加

Copy Gemfile
1
2
3
4
5
6
7
8
9
10
group :development do
  gem 'capistrano'
  gem 'capistrano3-puma'
  gem 'capistrano-rails'
  gem 'capistrano-rails-collection' # 例: rails:rake:db:migrate など
  gem 'capistrano-rails-console' # cap production rails:c でコンソールにアクセスしたい場合
  gem 'capistrano-rake' # 例: cap production invoke:rake TASK=paperclip:refresh
  gem 'capistrano-rbenv'
  gem 'capistrano-sidekiq', '1.0.2' # sidekiqを使う場合
end

インストール

Copy
1
cap install
Capfile設定
Copy Capfile
1
2
3
4
5
6
7
8
9
10
11
12
require 'capistrano/rails'
require 'capistrano/rails/collection'
require 'capistrano/rails/console'
require 'capistrano/rake'
require 'capistrano/rbenv'
require 'capistrano/puma'
require 'capistrano/sidekiq'

# Install puma plugins
install_plugin Capistrano::Puma
install_plugin Capistrano::Puma::Systemd
install_plugin Capistrano::Puma::Nginx

デプロイファイル設定

Copy config/deploy.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
lock '~> 3.17.1'

set :application, 'todoapp'
set :repo_url, 'git@github.com:xxxxxxxxxxxxx.git'
set :deploy_to, '/deploy/apps/todoapp'
set :pty, true
set :init_system, :systemd
set :keep_releases, 3

# rbenv
set :rbenv_type, :user # or :system, depends on your rbenv setup
set :rbenv_ruby, File.read('.ruby-version').strip # set ruby version from the file
set :rbenv_prefix,
    "RBENV_ROOT=#{fetch(:rbenv_path)} RBENV_VERSION=#{fetch(:rbenv_ruby)} #{fetch(:rbenv_path)}/bin/rbenv exec"
set :rbenv_map_bins, fetch(:rbenv_map_bins).to_a.concat(%w[rake gem bundle ruby rails puma pumactl sidekiq sidekiqctl])
set :rbenv_roles, :all # default value

# bundle
set :bundle_flags, '--deployment' # Show log when install gems

# puma
set :puma_init_active_record, true

append :linked_files, 'config/database.yml', 'config/application.yml'
append :linked_dirs, 'log', 'tmp/pids', 'tmp/cache', 'tmp/sockets', 'public/system', 'node_modules'
Copy config/deploy/production.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
set :stage, :production
set :rails_env, :production
set :branch, 'main'
server 'xxx.xx.xx.xxx', user: 'deploy', roles: %w[web app db]

# nginx
set :nginx_config_name, 'todoapp.com' # アプリのドメイン

# SSLを設定する場合
set :nginx_server_name, "localhost #{fetch(:nginx_config_name)}"
set :nginx_ssl_certificate, "/etc/letsencrypt/live/#{fetch(:nginx_config_name)}/fullchain.pem"
set :nginx_ssl_certificate_key, "/etc/letsencrypt/live/#{fetch(:nginx_config_name)}/privkey.pem"
set :nginx_use_ssl, true

nginx設定

nginxをインストール

Copy
1
sudo apt-get install nginx
因みに、nginxを完全に削除するコマンドは:

Copy
1
2
sudo apt-get purge nginx nginx-common
sudo apt-get autoremove
Puma設定ファイルをアップロードする
Copy
1
2
cap production puma:config
cap production puma:systemd:config
サーバーでPuma設定ファイルを修正する
Copy /etc/systemd/system/puma_todoapp_production.service
1
ExecStart=/home/deploy/.rbenv/shims/bundle exec --keep-file-descriptors puma -C /deploy/apps/ityogo/shared/puma.rb

もし以下のエラーが発生した場合:

Copy
1
2
3
4
5
6
7
8
(Backtrace restricted to imported tasks)
cap aborted!
NotImplementedError: OpenSSH keys only supported if ED25519 is available
net-ssh requires the following gems for ed25519 support:
 * ed25519 (>= 1.2, < 2.0)
 * bcrypt_pbkdf (>= 1.0, < 2.0)
See https://github.com/net-ssh/net-ssh/issues/565 for more information
Gem::LoadError : "ed25519 is not part of the bundle. Add it to your Gemfile."
必要なGemを追加大丈夫だと思います。

Copy Gemfile
1
2
3
4
group :development do 
  gem 'ed25519'
  gem 'bcrypt_pbkdf'
end

platforms [x86_64-darwin-20] みたいなエラーが発生する場合:

Copy
1
2
3
remote:        Your bundle only supports platforms ["x86_64-darwin-20"] but your local platform
remote:        is x86_64-linux. Add the current platform to the lockfile with `bundle lock
remote:        --add-platform x86_64-linux` and try again.
解決方法:

Copy
1
bundle lock --add-platform x86_64-linux
Puma Nginx設定ファイルをアップロードする
Copy
1
cap production puma:nginx_config
Nginxを再起動する
Copy
1
sudo service nginx restart
デプロイ!
Copy
1
cap production deploy
コマンド一覧

Copy
1
cap --tasks
もしassets precompile が失敗したら、以下の記事に参考してみてください

assets precompileがメモリ不足で失敗する

カスタムRakeタスク

assetsをローカルでコンパイルする

デプロイする時、assetsをコンパイルするのに凄く時間がかかる為、ローカルでコンパイルしておいてからサーバーにアップロードするのがおすすめです。

Viteの場合

Copy lib/capistrano/tasks/assets_precompile.rake
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# frozen_string_literal: true

namespace :deploy do
  namespace :assets do
    Rake::Task['deploy:assets:precompile'].clear_actions

    desc 'Precompile assets locally and upload to servers'
    task :precompile do
      on roles(fetch(:assets_roles)) do
        # Local assets precompile
        run_locally do
          with rails_env: fetch(:rails_env) do
            execute 'rm -rf public/assets'
            execute 'rm -rf public/vite-dev'
            execute 'rm -rf public/vite-test'
            execute 'rails assets:precompile RAILS_ENV=production'
          end
        end

        # Upload to server
        within release_path do
          with rails_env: fetch(:rails_env) do
            old_manifest_path = "#{shared_path}/public/assets/manifest*"
            execute :rm, old_manifest_path if test "[ -f #{old_manifest_path} ]"
            upload!('./public/assets/', "#{shared_path}/public/", recursive: true)
            upload!('./public/vite/', "#{release_path}/public/", recursive: true)
          end
        end

        puts "\e[32mLocal assets clean\e[0m"
        run_locally do
          with rails_env: fetch(:rails_env) do
            execute 'rm -rf public/assets'
            execute 'rm -rf public/vite-dev'
            execute 'rm -rf public/vite-test'
          end
        end
      end
    end
  end
end

Webpackの場合

Copy lib/capistrano/tasks/assets_precompile.rake
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# frozen_string_literal: true

namespace :deploy do
  namespace :assets do
    Rake::Task['deploy:assets:precompile'].clear_actions

    desc 'Precompile assets locally and upload to servers'
    task :precompile do
      on roles(fetch(:assets_roles)) do
        # Local assets precompile
        puts "\e[32mLocal precompile\e[0m"
        run_locally do
          with rails_env: fetch(:rails_env) do
            execute 'rails assets:clobber'
            execute 'rails assets:precompile'
            execute 'bin/webpack'
          end
        end

        # Upload to server
        puts "\e[32mUpload to server\e[0m"
        within release_path do
          with rails_env: fetch(:rails_env) do
            old_manifest_path = "#{shared_path}/public/assets/manifest*"
            execute :rm, old_manifest_path if test "[ -f #{old_manifest_path} ]"
            upload!('./public/assets/', "#{shared_path}/public/", recursive: true)
            upload!('./public/packs/', "#{release_path}/public/", recursive: true)
          end
        end
      end
    end
  end
end

デプロイの前に自動Gitプッシュ

デプロイの前に毎回Gitにプッシュするのが面倒なので、タスクを作ります。

Copy lib/capistrano/tasks/github_push.rake
1
2
3
4
5
6
7
8
desc 'Push source code to Github'
task :github_push do
  run_locally do
    execute "git push origin #{fetch(:branch)}"
  end
end

before 'deploy:starting', :github_push
Updated by Yuto at 2022-10-09 06:00
1
Like this article
Le Minh Thien Toan

Tác giả:Yuto Yasunaga

Xin chào các bạn. Mình là kỹ sư IT đang làm việc ở Nhật Bản. Mình tạo blog này để chia sẻ về cuộc sống và những kinh nghiệm trong quá trình học tập và làm việc.
Hy vọng bài viết này sẽ có ích cho bạn.