20201126のRailsに関する記事は22件です。

Rails結合テスト ログイン処理まとめ

はじめに

Railsでオリジナルアプリ制作を終えました。モデルの単体テストは完了したものの、結合テストは未実施でした。ユーザーがログインしないと使えない機能テストにおいて、ログイン処理を繰り返しテストコードに記述するのは避けたいと思います。より簡素な記述をするため、書き記します。

開発環境
ruby 2.6.5
Rails 6.0.3.4
Gem : gem 'rspec-rails'

目次

1.ログイン処理
2.簡素なログイン処理の記述

1.ログイン処理

ログインするにあたり、email及びパスワードが必要だと仮定する。テストコードにおける記述は以下のようになる。問題は機能テストごとにログイン処理を書く必要がある。これを簡略化する方法を次の節で説明する。

spec/system/○○_spec.rb
visit root_path
fill_in 'user_email', with: user.email
fill_in 'user_password', with: user.password
click_on("Log in")
expect(current_path).to eq root_path
#以下ログインした状態での機能テストの記述を書く
visit new_desk_path

※なお、email入力フォームのidはuser_email、パスワードはuser_passwordとする。

2.簡素なログイン処理の記述

specディレクトリの直下にsupportディレクトリを作成する。さらにその直下にsign_in_support.rbファイルを作成し、下記ログイン処理のメソッドを作成する。

spec/supports/sign_in_support.rb
module SignInSupport
  def sign_in(user)
    visit new_user_session_path
    fill_in 'email', with: user.email
    fill_in 'password', with: user.password
    find('input[name="commit"]').click
    expect(current_path).to eq root_path
  end
end

ここで定義したsign_inメソッドをテストコードのファイルで使用できるように下記を設定する。

spec/rails_helper.rb
# コメントアウトを外す
Dir[Rails.root.join('spec', 'support', '**', '*.rb')].sort.each { |f| require f }

# 中略

RSpec.configure do |config|
  # 追記
  config.include SignInSupport

以上によりsign_inメソッドを使用できるようになった。下記に使用例を示す。

spec/system/○○_spec.rb
before do
    #テスト用のユーザーダミーデータを生成する。
    @user = FactoryBot.create(:user)
  end
  context '画像投稿ができるとき'do
    it 'ログインしたユーザーは新規投稿できる' do
      # ログインする
      sign_in(@user)
#以下省略

以上

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

deviseのgemで、ユーザーの詳細ページや編集ページは作れないかなぁ…

はじめに

 deviseをを使ってユーザー管理機能を実装した。マイページを作って編集をできるようにしたい。しかし、deviseのコントローラーでできるのか、不安だったので、deviseで作ったuserディレクトリの配下には作らず、別でuserコントローラーを作った。似たようなディレクトリとファイルがあるので、なんかモヤモヤする。

userコントローラー

ターミナルで、

rails g controller users

を行うと、controllers/users_controller.rbファイルが生成される。

users_controller.rb
class UsersController < ApplicationController
  def show
    @user = User.find(params[:id])
  end
end

まあ、こうすれば詳細ページを作れるのだが…

終わりに

 deviseのコントローラーにはコメントアウトされている部分がたくさんあるが、この中に詳細を表示させたり、編集したりすることは

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

cloud9でrails sを終了せずに閉じてしまった時の対処法

はじめに

cloud9でrails sを終了せずにターミナルを閉じてしまいました...
再度rails sをするとこんなエラーが...

 A server is already running. Check /home/ubuntu/environment/[フォルダ名]/tmp/pids/server.pid.

pidファイルを開いてみるも5桁の数字が記載されているだけ...
下記の記事を見つけて再起動するも上手く行かず。

A server is already running 対処方法【Rails】

pidファイルを削除しようとするもファイルツリーから見つけられず...
上記エラー文から開いたpidファイルの数字を削除して再度rails sしました。
すると今度は違うエラー文...

Address already in use - bind(2)

解決法

下記の記事通り
Cloud9上でRails sをしてA server is already runningが出た時の対処法

$ kill -9 数字(pidファイルに記載)

で済むはずなんですが、今回は数字を消してしまったので...

$ killall -9 ruby

で解決しました?

参考:Address already in use - bind(2) when starting server in Cloud9 IDE

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Ruby ハッシュとシンボルの記法 まとめ

Railsチュートリアル
ハッシュとシンボルの書き方

user = {} # {}は空のハッシュ
=> {}
user["first_name"] = "hurihata" # キーが "first_name" で値が "hurihata"
=> "hurihata"
user["last_name"] = "koyo" # キーが "last_name" で値が "koyo"
=> "koyo"
user["first_name"] # 要素へのアクセスは配列の場合と似ている
=> "hurihata"
user # ハッシュのリテラル表記
=> {"last_name"=>"koyo", "first_name"=>"hurihata"}

ハッシュのキーとしてシンボルを採用する場合、user のハッシュは次のように定義できる

一つめの記法

user = { :name => "koyo", :email => "kkkk@XXX.com" }
=> {:name=>"koyo", :email=>"kkkk@XXX.com"}
user[:name] # :name に対応する値にアクセスする
=> "koyo"
user[:password] # 未定義のキーに対応する値にアクセスする
=> nil

二つめの記法

{ name: "koyo", email: "kkkk@XXX.com" }

自分的にはハッシュロケット(=>)を使う一つ目の記法より二つ目のシンボルの:を後ろにつける記法の方が見やすく使いやすいと感じた。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ancestryはawsにデプロイしただけでは使えない

 1.どんな状態だったか

ancestryを利用して、カテゴリー機能を実装、ローカルでは動くのに本番環境で反映されないという状態

2原因

ancestryを使うためには、本番環境でもローカルと同じ様に
db migrateし、seedを読み込まなくてはならない

3解決方法

まずdb migrateします

$ cd var/www/app名/current
$ rake db:migrate RAILS_ENV=production

上手くいかない時はdbをドロップしもう一度クリエイトし直す

$ RAILS_ENV=production DISABLE_DATABASE_ENVIRONMENT_CHECK=1 bundle exec rake db:drop

$ rake db:create RAILS_ENV=production

もう一度migrateします。

次にseedを反映させます。

$ rake db:seed RAILS_ENV=production

以上になります。

4最後に

自動デプロイをしている場合はcurrentディレクトリで操作を行う必要があるそうです
そうでない場合は~ディレクトリで大丈夫みたいです

参考にした記事
[aws,rails]ancestryをawsにデプロイした時に反映されない状況の解決方法
本番環境でrake db:seedを実行する際の注意点

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

MySQL 5.5から5.7へのバージョンアップと、libmysqlclient.so.18: cannot open shared object file: No such file or directoryの解消

はじめに

前回の記事 Cloud9でRuby on Railsの環境構築 ~インストールから起動まで~ で「Yay! You’re on Rails!」を表示し、最低限の環境構築までは完了したが、MySQLのバージョンが古かったためバージョンアップをすることに。

その過程でいくつかエラーが発生したので、備忘録のためにカキカキしていきます。

目標

MySQL 5.5 からMySQL 5.7にアップデートし、「Yay! You’re on Rails!」を表示させる

環境

Amazon linux
ruby 2.6.3
Rails 6.0.3.4
MySql 5.5.62

やっていきましょう

MySQLのバージョンアップ

まずはバージョンを確認しておきます。

$ mysql -uroot -p
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 2
Server version: 5.5.62-log MySQL Community Server (GPL)
~略~

そうしたら"yum"を使ってMySQL関連のモジュールをアンインストール。
その後使用するバージョンを指定して、MySQLのモジュールをインストールします。

$ yum remove mysql*
$ yum install mysql57 mysql57-devel mysql57-server

ここで一度バージョンを確認してみます。

$ mysql -u root -p
Enter password:
ERROR 2002 (HY000): Can't connect to local MySQL server through socket'/var/lib/mysql/mysql.sock' (2)

エラーが発生。前回の記事と同じ内容です。

socketファイルが無いためMySQLに接続できていないので、MySQLを再起動してsoketファイルを自動作成させます。
その後もう一度バージョンを確認。

$ sudo /etc/init.d/mysqld restart
$ mysql -u root -p
Enter password: 
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 4
Server version: 5.7.31 MySQL Community Server (GPL)

これでうまく5.7.31がインストールできました。

サーバー起動

サーバーを起動して接続できるか確認します。

$ rails s
~略~
libmysqlclient.so.18: cannot open shared object file: No such file or directory
~略~

エラーです。
ディレクトリとかファイルがないから開けませんって言われています。

今回はDBにMySQLを使用するため、プロジェクト作成時に"-d mysql"オプションを追加しました(前回の記事参照)。
この時にmysql2というgemがインストールされたのですが、これを再インストールすれば解消されるっぽい。

そんなわけでmysql2をインストールしなおします。

$ gem uninstall mysql2
$ gem install mysql2

これで起動してみましょう。

$ rails s
Mysql2::Error::ConnectionError
Can't connect to local MySQL server through socket '/var/lib/mysql/mysql.sock' (2)

またsoketファイルか・・・
MySQLを再起動してから再びサーバーを起動。

$ sudo /etc/init.d/mysqld restart
$ rails s

スクリーンショット 2020-11-26 184833.png

成功しました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

サルでもできる!? Rails6アプリをAWS EC2にデプロイするまでの全手順【後半】(独自ドメイン, HTTPS化, S3, CloudFront)

この記事では,画像投稿機能の付いた Rails 6 のアプリを AWS EC2 にデプロイするまでの全過程を解説します。

【前半】 でRailsアプリのデプロイはひとまず完了しましたが,まだまだすべきことがたくさんあります。続きも気を抜かずに頑張りましょう!

タイトル
【前半】
1章 はじめに
2章 VPC
3章 RDS
4章 EC2
5章 サーバー構築
6章 デプロイ(Capistrano)
【後半】 <-- こちら
7章 独自ドメイン
8章 HTTPS化(ACM, ALB)
9章 デプロイ関連事項
10章 S3
11章 CloudFront
12章 削除方法

7. 独自ドメイン

Railsアプリを公開することができましたが,現状では2つ大きな問題を抱えています。

  • サイトの URL が不自然
    • google.com のような 独自ドメイン ではない
  • HTTPSではない
    • Google Chrome で「このサイトへの接続は保護されていません」という警告が出る

まずは前者から解決していきましょう。

aws-sample-app.com のような URL でアクセスできるようにするには, ドメイン を取得する必要があります。

ドメイン は有料ですが,こだわらなければ初年度は数百円程度で取得できます。

この記事では昔から代表的な レジストラ である「お名前.com」で取得する前提で解説します。

7.1 ドメインの購入

  • 「お名前.com」にアクセス

  • 検索窓に取得したいドメイン名を入力し,「検索」ボタンをクリック

    • ドメインに使用できる文字は「半角英数字」と「ハイフン -」です
    • 「アンダースコア _」 は使用できません

7_1a.png

  • .com などのドメインを選択し,「料金確認へ進む」ボタンをクリック

    • 自動選択されているチェックを外し忘れないように注意しましょう
    • こだわりがなければ初年度の安いドメインを取得してもよいでしょう
  • 購入処理を行って下さい。「Whois情報公開代行」は必須ですが,その他のオプションは全て不要です

    • 「サーバー」は「利用しない」をチェック
    • 「Whois情報公開代行メール転送オプション」などは全てチェック不要
  • 「お申し込みを受け付けました。」と表示されたら「ドメインの設定はこちら」のリンクをクリック

【備考】後に続きの操作をおこないますので,他のタブで続きの作業を進めて下さい

自動更新の解除

初期設定では,ドメインの更新期限日を過ぎますと自動更新・自動課金されます。

2年目以降は料金が高くなるドメインが多いですので,自動更新を解除したい場合は以下の操作を行ってください。

  • 「お名前.com」のページ上側にあるタブの「ドメイン」を選択

  • 「更新画面から移動する」をクリック

  • アプリで使用するドメインを選択

7_1b.png

  • 「自動更新」ボタンをクリック

7_1c.png

  • 「確認画面へ進む」をクリック

  • 「自動更新設定申込内容」の項目が「解除する」になっていることを確認の上,「規約に同意し、上記内容を申し込む」ボタンをクリック

7_1d.png

  • 「解除する」ボタンをクリック

  • 「ドメイン一覧」のタブをクリック

【備考】後に続きの操作をおこないますので,続きの作業は他のタブで進めて下さい

7.2 IPアドレスとドメインの関連付け

Route 53 を利用することで,先ほど購入した ドメインIPアドレス に変換することができます。

通常の使用範囲内ならば月100円もかかりません。

  • AWS の画面左上の「サービス」を開き、検索欄に「route」と入力し、「Route 53」を選択

  • 「ホストゾーンの作成」ボタンをクリック

キー
ドメイン名 取得したドメインを記載
説明 - オプション アプリ名_domain
タイプ パブリックホストゾーン

7_2a.png

  • 「ホストゾーンの作成」ボタンをクリック

  • タイプが「NS」の方の「値/トラフィックのルーティング先」に表示されている4つを ネームサーバー としてメモしておいて下さい

    • 【要注意】メモする際に,それぞれの最後についている「ドット .」は削除して下さい!

7_2b.png

7.3 ネームサーバーの変更

先ほど残しておいた「お名前.com」に移動して下さい

(タブを閉じてしまった場合は,再度「お名前.com」にアクセスし,ログインして下さい)

  • 「ドメイン一覧」の中から取得した「ドメイン名」を選択

  • 「ネームサーバー情報」の項目の「ネームサーバーの変更」をクリック

7_3a.png

  • 「2.ネームサーバーの選択」をクリック

  • 「その他」タブをクリックし,「その他のネームサーバーを使う」の方にチェックを入れ,先ほどメモした4つの ネームサーバー をコピペして下さい

    • 先ほど注意しました通り,最後についている「ドット .」は入力しないで下さい

7_3b.png

7.4 ネームサーバーの確認

ローカル環境のターミナルから以下を実行して下さい。

ローカル環境のターミナル
dig ドメイン名 NS +short

登録した4つのドメインが入っていればOKです。(順番が入れ替わっていてもOK)

デフォルトの2つしか入っていない場合は,しばらく待ってから再度先ほどのコマンドを実行しましょう。

7.5 ドメインの適用

  • AWSRoute 53 の先ほどの続きの画面から,「レコードを作成」をクリック

  • 「シンプルルーティング」にチェックを入れた状態で「次へ」ボタンをクリック

  • 「シンプルなレコードを定義」ボタンをクリック

キー
レコード名 ※入力しない
値/トラフィックのルーティング先 「レコードタイプに応じた IP アドレス または別の値」をクリックし, Elastic IP を入力
レコードタイプ A

7_5.png

これで準備が完了です。まずは,ローカル環境のターミナルから以下を実行して下さい。

ローカル環境のターミナル
curl -I ドメイン名

最初に HTTP/1.1 200 OK もしくは HTTP/1.1 302 が出れば正常です。

前回は Elastic IP でなければアクセスできませんでしたが,今度は ドメイン名 でアクセスできる状態になっています。

ブラウザから ドメイン名 でアクセスし, スーパーリロード して下さい。(ショートカットキーは command + shift + r

8. HTTPS化(ACM, ALB)

次はこちらの問題に対処していきましょう。

  • HTTPSではない
    • Google Chrome で「このサイトへの接続は保護されていません」という警告が出る

【備考】 ELBApplication Load Balancer(ALB) を利用しますので,月2000円程度の費用がかかります。

8_0.png

8.1 ACM

HTTPS化 には,まず, SSL/TLS サーバー証明書 を取得する必要があります。

Let's Encrypt で取得する方法が有名ですが,AWSを利用する場合は SSL/TLS サーバー証明書ACM で取得し, ALB に関連付けるのが一般的な手法です。作業が楽で,更新の手間も不要になります。

ALB と関連付けますので, ACM 自体は無料で利用できます。

  • AWS の画面左上の「サービス」を開き、検索欄に「acm」と入力し、「Certificate Manager」を選択

  • 「証明書のプロビジョニング」の「今すぐ始める」ボタンをクリック

  • 「パブリック証明書のリクエスト」を選択した状態で「証明書のリクエスト」ボタンをクリック

  • 「この証明書に別の名前を追加」をクリック

  • 取得した「ドメイン名」と「*.ドメイン名」を入力

  • 「次へ」ボタンをクリック

8_1.png

  • 「DNS の検証」を選択した状態で「次へ」ボタンをクリック
タグ名
Name アプリ名_alb
  • 「確認」ボタンをクリック

  • 「確定とリクエスト」ボタンをクリック

  • 「続行」ボタンをクリック

8.2 DNS検証用の CNAME レコード追加

  • ドメインの欄のプルダウンを開き,「Route 53 でのレコードの作成」ボタンをクリック
    • 2つ表示されていますが,どちらか片方のみでOKです
    • プルダウンが表示されない場合は,一度リロードして下さい

8_2.png

  • 「作成」ボタンをクリック

【注意】 8.3 は先に行っても問題ありませんが, 8.4 以降の作業は,状況が「検証保留中」から「発行済み」に変わってから行って下さい

8.3 ALB 用のセキュリティグループの作成

【構成内容】 クライアント --(HTTP or HTTPS)--> ALB --(HTTP)--> EC2

ELB の内の ALB (Application Load Balancer) を使用し,先ほど取得した SSL/TLS サーバー証明書 を使用できるように設定していきましょう。

ALBEC2 の前にリクエストを受け,(一般には複数の) EC2 に分散します。このことを踏まえたセキュリティグループを作成しましょう。

  • 画面左上の「サービス」を開き、検索欄に「ec2」と入力し、「EC2」を選択

  • EC2 画面左のメニューバーの「セキュリティグループ」を選択し,「セキュリティグループを作成」ボタンをクリック

キー
セキュリティグループ名 アプリ名_alb_security_group
説明 アプリ名_alb_security_group
VPC アプリ名_vpc

「インバウンドルール」の「ルールを追加」を2回クリック

キー
タイプ HTTP
プロトコル TCP ※自動選択
ポート範囲 80 ※自動選択
ソース 任意の場所 (0.0.0.0/0 と ::/0 が表示)
説明 ※空白でOK
キー
タイプ HTTPS
プロトコル TCP ※自動選択
ポート範囲 443 ※自動選択
ソース 任意の場所 (0.0.0.0/0 と ::/0 が表示)
説明 ※空白でOK

8_3a.png

「アウトバウンドルール」は デフォルトの設定を削除した後 「ルールを追加」をクリックし,以下を追加

8_3b.png

キー
タイプ HTTP
プロトコル TCP ※自動選択
ポート範囲 80 ※自動選択
ソース カスタム: アプリ名_ec2_security_group
説明 ※空白でOK

8_3c.png

作成後に次の図のようにルールが定まっているかどうかを確認して下さい。

8_3d.png
8_3e.png

8.4 ロードバランサーの設定

  • 画面左のメニューバーの「ロードバランサー」を選択

  • 「ロードバランサーの作成」ボタンをクリック

  • 「HTTP, HTTPS」の方の「作成」ボタンをクリック

■ 手順 1: ロードバランサーの設定

  • 【基本的な設定】
キー
名前 アプリ名-alb ※ ハイフンに変更

他はデフォルト

  • 【リスナー】

「リスナーの追加」ボタンをクリックし,「HTTPS」を選択

  • 【アベイラビリティゾーン】
キー
VPC アプリ名_vpc
アベイラビリティーゾーン 「ap-northeast-1a」を選択し「アプリ名_public_1a_subnet」を選択
アベイラビリティーゾーン 「ap-northeast-1c」を選択し「アプリ名_public_1c_subnet」を選択

「次の手順: セキュリティ設定の構成」ボタンをクリック

8_4a.png

■ 手順 2: セキュリティ設定の構成

  • 「証明書の名前」がアプリ用のドメインであることを確認し,「セキュリティグループの設定」をクリック

8_4b.png

■ 手順 3: セキュリティグループの設定

  • 「アプリ名_alb_security_group」を選択し,「次の手順: ルーティングの設定」ボタンをクリック

8_4c.png

■ 手順 4: ルーティングの設定

  • ターゲットグループ
キー
ターゲットグループ 新しいターゲットグループ
名前 アプリ名-target-group ※ ハイフンに変更
ターゲットの種類 インスタンス ※デフォルト
プロトコル HTTP ※自動設定
ポート 80 ※自動設定
  • ヘルスチェック

デフォルト設定でOK

「次の手順: ターゲットの登録」ボタンをクリック

8_4d.png

■ 手順 5: ターゲットの登録

  • 「インスタンス」欄から、「アプリ名_instance」にチェックを入れた状態で「登録済みに追加」ボタンをクリック

8_4e.png

  • 「次の手順: 確認」をクリック

  • 「作成」ボタンをクリック

8.5 Route 53の設定を修正

ドメインを EC2 から先ほど作成した ALB に割り当てるように設定を変更しましょう。

  • AWS の画面左上の「サービス」を開き、検索欄に「route」と入力し、「Route 53」を選択

  • 「ホストゾーン」をクリック

  • アプリで使用するドメインをクリック

8_5a.png

  • 「タイプA」の「ドメイン名」のレコードをクリックし,「編集」ボタンをクリック

8_5b.png

  • 「値/トラフィックのルーティング先」の箇所を変更して下さい
    • 「Application Load Balancer と Classic Load Balancer へのエイリアス」を選択
    • 「アジアパシフィック (東京) [ap-northeast-1]」を選択
    • 「アプリ名-alb」を含むロードバランサーを選択

8_5c.png

  • 「変更を保存」ボタンをクリック

8.6 セキュリティグループの修正

ALB 経由でアクセスされるように変更したため, EC2ALB からのアクセスに限定すべきです。そのため, EC2 用のセキュリティグループを修正します。

  • AWS の画面左上の「サービス」を開き、検索欄に「ec2」と入力し、「EC2」を選択

  • 画面左のメニューバーの「セキュリティグループ」を選択

  • 「アプリ名_ec2_security_group」にチェックを入れ,「アクション」ボタンをクリックし,「インバウンドルールを編集」ボタンをクリック

8_6a.png

  • タイプが HTTP のものを全て削除し,「ルールを追加」をクリックし,以下を設定
    • SSH は削除しないこと

8_6b.png

キー
タイプ HTTP
ソース カスタム: アプリ名_alb_security_group
説明 ※空白でOK

8_6c.png

  • 「ルールを保存」ボタンをクリック

8.7 Nginx の設定ファイルを修正

ALB への接続に使用するプロトコルが HTTPS であることを識別できるよう,Nginx の設定ファイルを修正し,再起動しておきましょう。

(こちらを実行しないと,例えば POST リクエスト時にエラーが発生します)

サーバー環境のターミナル
sudo sed -i s/X-Forwarded-Proto\ http/X-Forwarded-Proto\ https/g /etc/nginx/conf.d/$APP_NAME.conf
sudo service nginx restart

それでは, HTTPS でアクセスできるかを確認しましょう。まずはターミナルから以下を実行して下さい。

ターミナル(ローカル側でもサーバー側でもOK)
curl -I https://ドメイン名

最初に HTTP/2 200 もしくは HTTP/2 302 が表示されれば正常です。次にブラウザから https://ドメイン名 にアクセスし, スーパーリロード して下さい。(ショートカットキーは command + shift + r

ブラウザのURLの左隣に「鍵マーク」が表示され,アプリが問題なく開けばOKです!

8_7.png

8.8 HTTPS にリダイレクト

【参考】 https://aws.amazon.com/jp/premiumsupport/knowledge-center/elb-redirect-http-to-https-using-alb/

最後に, HTTP でのアクセスを HTTPS にリダイレクトするように設定しましょう。

  • EC2の画面左のメニューバーの「ロードバランサー」をクリック

  • (「アプリ名-alb」を選択した状態で)「リスナー」タブをクリック

  • HTTP : 80 の方の 「ルールの表示/編集」をクリック

8_8a.png

  • 上の「鉛筆」マークをクリックし,下に出現した「鉛筆」マークをクリック

8_8b.png

  • 「THEN」の項目の転送先にある「ゴミ箱」マークをクリックして削除

8_8c.png

  • 「アクションの追加」を選択し,「リダイレクト先」をクリック

  • 「HTTPS」になっていることを確認し,ポートに 443 を入力

  • 「チェックマーク」をクリック

  • 右上の「更新」ボタンをクリック

8_8d.png

これで http でアクセスした場合も https に自動変換されるようになりました。ターミナルから以下を実行してみましょう。

ターミナル(ローカル側でもサーバー側でもOK)
curl -I http://ドメイン名

最初に HTTP/1.1 301 Moved Permanently が表示され,最後に Location: https://アプリ名:443/ が表示されていればOKです。

ブラウザでも http://ドメイン名 でアクセスしてみましょう。URLの左隣に鍵マークが付いていればOKです!

お疲れ様でした!

8.9 最後に

グローバル変数の削除

作業が終わりましたので,ローカル環境で定義したグローバル変数 $APP_NAME は消しておくこととしましょう。

ローカル環境のターミナル
# zsh の場合
vi ~/.zshrc

# bash の場合
vi ~/.bash_profile

一番最後の行にある export APP_NAME=アプリ名 を削除して下さい。

EC2のパスワード設定

次に,EC2のユーザーにパスワードを設定し, sudo コマンド実行時にパスワードを求められるように変更しましょう。

(作業を楽にするため,EC2のユーザーはパスワード無しで sudo コマンドを使用できる状態にしていました)

サーバー環境のターミナル
sudo passwd `whoami`
設定したいEC2のパスワードを入力
EC2のパスワードを再入力

【注意】「EC2のパスワード」は必ずメモしておいて下さい。

サーバー環境のターミナル
sudo visudo

Vim で開かれたら,一番最後の行の NOPASSWD: の箇所のみ削り,次の状態にして下さい。

sudoers
ユーザー名 ALL=(ALL) ALL

【注意】ユーザー名 ALL=(ALL) NOPASSWD: ALL を「全て」削ってはいけません!編集を間違えると,二度と sudo コマンドを実行できなくなる恐れがありますので,慎重に行ってください。

これで sudo コマンドを実行した際に今後はパスワードを尋ねられるようになります。

以下を実行し,パスワードを求められるかどうか,パスワードを入力してコマンドを実行できるかどうかを確かめて下さい。

サーバー環境のターミナル
sudo ls

9. デプロイ関連事項

この章は必要な場合のみご覧ください。

9.1 アプリ修正時の反映方法

以下のコマンドで「GitHub」の master ブランチをAWS側に反映させることができます。

ローカル環境のターミナル
# Railsアプリのルートディレクトリに移動してから
bundle exec cap production deploy

【注意】 Heroku のデプロイで使用する git push heroku master は「ローカル」の master ブランチを反映させるコマンドです。 bundle exec cap production deploy コマンドは「ローカル」ではなく「GitHub」である点に注意しましょう。

9.2 master以外のブランチを反映させたい

config/deploy.rb
set :branch, ENV['BRANCH'] || "master"

を追加すれば,例えば以下のコマンドで「GitHub」の hoge ブランチを反映できます。

ローカル環境のターミナル
bundle exec cap production deploy BRANCH=hoge

例えば,現在のブランチをAWS側で動作確認したい場合は次の手順になります。

ローカル環境のターミナル
# add, commit の実行後
git push origin HEAD
bundle exec cap production deploy BRANCH=HEAD

9.3 AWS側で Rails コマンドや rake タスクを実行する方法

サーバー側のアプリは /var/www/$APP_NAME/current に入っています。

まずはここまで移動し,必要に応じて yarn install --check-files を実行して下さい。

サーバー環境のターミナル
cd /var/www/$APP_NAME/current
yarn install --check-files

その上で,必要なコマンドを実行しましょう。コマンド例を紹介します。

  • サーバー側で rails db:seed を実行したい場合
サーバー環境のターミナル(/var/www/$APP_NAME/current)
bin/rails db:seed RAILS_ENV=production

bin/railsbundle exec rails でも問題ありませんが, rails ではダメです。また, RAILS_ENV=production が必要である点に注意して下さい。

  • サーバー側で rails db:migrate:reset を実行したい場合
サーバー環境のターミナル(/var/www/$APP_NAME/current)
bin/rails db:migrate:reset RAILS_ENV=production DISABLE_DATABASE_ENVIRONMENT_CHECK=1

本番環境のデータベースを削除(リセットを含む)したい場合は, DISABLE_DATABASE_ENVIRONMENT_CHECK=1 が必要です。

  • サーバー側で rails console を実行したい場合
サーバー環境のターミナル(/var/www/$APP_NAME/current)
bin/rails c -e production

オプションが RAILS_ENV=production ではなく -e production の書き方になります。

  • サーバー側で rake タスクを実行したい場合
サーバー環境のターミナル(/var/www/$APP_NAME/current)
bundle exec rake タスク名 RAILS_ENV=production

タスク名は bundle exec rake -T で確認できます。bundle exec rakebin/rails でもOKですが rake ではダメです。

9.4 サーバー側で ログ を確認する方法

サーバー側の ログ/var/www/$APP_NAME/shared/log に入っています。

サーバー環境のターミナル
cd /var/www/$APP_NAME/shared/log
ls

でログファイルの一覧を確認できます。

エラーが出た場合は,nginx.error.logpuma_error.log を確認しましょう。

サーバー環境のターミナル(/var/www/$APP_NAME/shared/log)
# ログを全て表示したい場合
cat nginx.error.log
# 最後の30行を表示したい場合
tail -n 30 nginx.error.log
# ログの追加分のみを自動表示したい場合(control + c で終了)
tail -f nginx.error.log

10. S3

【注意】10章, 11章は, CarrierWaveActiveStorage などで画像の投稿機能を実装している場合のみ,実行して下さい。

現状でも画像の投稿は可能ですが, AWS S3 を使用することで,Webサーバー(EC2)のストレージが画像で圧迫されるのを防いだり,負荷分散ができるなど複数のメリットがあります。

さらに, CloudFront を用いることで画像転送を高速化するところまでを解説します。

10.1 S3バケットの作成

S3 は,クラウド型のオブジェクトストレージサービスです。画像に限らずいろいろなデータを保存することができ,簡単なWebサイトの公開にも使用できます。

セキュリティ・耐久性共に優れ,価格も安いのが特徴です。ポートフォリオの画像投稿機能に使用する程度であれば,大抵の場合は無料利用枠内でおさまるでしょう。

S3 を利用するには,まず, バケット を作成する必要があります。

  • 画面左上の「サービス」を開き、検索欄に「s3」と入力し、「S3」を選択

  • 「バケットの作成」を選択

  • 一般的な設定

タイトル 内容
バケット名 任意 ※世界ですでに存在する名前は付けられません
リージョン アジアパシフィック (東京) ap-northeast-1

【注】 バケット名とリージョンap-northeast-1を忘れないようにメモしておいて下さい。

  • ブロックパブリックアクセスのバケット設定

下の2つ(ACL以外)のみをチェックし,注意喚起にもチェック

10_1a.png
10_1b.png

「バケットを作成」をクリック

10.2 アプリ用のIAMユーザーを作成

次に,アプリ側から S3 にアクセスするための アクセスキー などを入手しましょう。

  • 画面左上の「サービス」を開き、検索欄に「iam」と入力し、「IAM」を選択

  • 左のメニューから「ユーザー」を選択

  • 青いボタン「ユーザーを追加」をクリック

タイトル 内容
ユーザ名 アプリ名_user
アクセスの種類 「プログラムによるアクセス」にチェック

10_2a.png

  • 「次のステップ:アクセス権限」をクリック

  • 「既存のポリシーを直接アタッチ」ボタンをクリック

  • AmazonS3FullAccess をチェックして,「次のステップ: タグ」ボタンをクリック

    • 「参考」のようにより権限を厳しくしたポリシーをアタッチするとより安全です

10_2b.png

  • 「次のステップ: 確認」ボタンをクリック

  • 「ユーザーの作成」をクリック

  • 「.csvのダウンロード」ボタンをクリック

「ユーザー名」「アクセスキーID」「シークレットアクセスキー」をメモしておいて下さい。

  • 「閉じる」ボタンをクリック

参考: 権限の少ないポリシーを作成

AmazonS3FullAccessS3 の全ての操作ができる権限ですので,アプリに持たせる権限としては過剰すぎます。

最低限必要な権限をもつポリシーを作成し,AmazonS3FullAccess の代わりにこちらをIAMユーザーにアタッチしておくと,万一の場合の被害を軽減することができます。

参考: CarrierWave

IAMポリシー
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "s3:PutObject",
                "s3:GetObject",
                "s3:DeleteObject",
                "s3:PutObjectAcl",
                "s3:ListAllMyBuckets"
            ],
            "Resource": "*"
        }
    ]
}

10_2c.png
10_2d.png

さらに安全を求めるならば,バケットまで制限することも可能ですが,ここでは省略します。

10.3 CarrierWaveの設定

※ 以下,画像投稿機能に CarrierWave を使用していることを前提とします

まず,Railsアプリに fog-awsGemfile に追加し,インストールしておきましょう。

ローカル環境のターミナル
bundle add fog-aws

本番環境のみで AWS S3 を使用する場合は次のように修正しましょう。

(開発環境でも使用したい場合は,条件分岐せず storage :fog のみにして下さい)

app/uploaders/image_uploader.rb
# 「storage :file」を以下に置き換える
  if Rails.env.production?
    storage :fog
  else
    storage :file
  end

次に, S3 の「アクセスキーID」などを読み込む設定を追加しましょう。

特に「シークレットアクセスキー」は,GitHubに公開してはならないので, credentials.yml.enc から読み込む形式とします。

「リージョン」と「バケット名」は公開しても問題ありませんので,直接記載することとします。

ローカル環境のターミナル
touch config/initializers/carrier_wave.rb

(開発環境でもS3を使用したい場合は,最初の行 if Rails.env.production? と最後の行 end を削りましょう)

config/initializers/carrier_wave.rb
if Rails.env.production?
  CarrierWave.configure do |config|
    config.fog_credentials = {
      provider: "AWS",
      aws_access_key_id: Rails.application.credentials.dig(:aws, :access_key_id),
      aws_secret_access_key: Rails.application.credentials.dig(:aws, :secret_access_key),
      region: "ap-northeast-1"
    }
    config.fog_directory = "S3のバケット名"
  end
end
  • credentials.yml.encを開くため,次のコマンドを実行
ターミナル
EDITOR=vi rails credentials:edit
  • アプリが S3 にアクセスするために必要な情報を credentials.yml.enc に記載しましょう
    • yaml形式なので,インデント幅にはくれぐれも注意して下さい
    • 例えば,access_key_id:の後に「半角スペース1個」がないだけでエラーになります
config/credentials.yml.enc
db:
  password: RDSのパスワード
  hostname: RDSのエンドポイント
# 以下を追加
aws:
  access_key_id: IAMユーザーのアクセスキーID
  secret_access_key: IAMユーザーのシークレットアクセスキー

10.4. 動作確認

念のため以下を実行し,「アクセスキーID」などが取得できることを確認しておいた方がよいでしょう。

ローカル環境のターミナル
rails c
# コンソール起動後
Rails.application.credentials.dig(:aws, :access_key_id)
Rails.application.credentials.dig(:aws, :secret_access_key)
# IAMユーザーのアクセスキーID, シークレットアクセスキーが表示されることを確認後
exit

コミット・プッシュ(必要があればプルリク・マージ)などを行い,GitHub の master ブランチに変更を反映した上で,AWS にデプロイを行いましょう。

ローカル環境のターミナル
bundle exec cap production deploy

【補足】 9.2 に記載した方法で,masterブランチにマージする前に確認するのが理想的です

これで本番環境では S3 に画像を投稿し,読み込みができるようになりました。

ブラウザから https://ドメイン名 にアクセスし,画像を投稿してみましょう。問題なく画像が投稿・表示できていればOKです。

念のため,AWSにログインし S3 のバケットに画像が保存されているかどうかも確認しておくとよいでしょう。

バケットを選択し,ディレクトリを選択していき,「オブジェクト URL」をクリックして投稿した画像が表示されていればOKです。

11. CloudFront

最後に CloudFront を用いて画像の表示を高速化できるようにし,さらに画像のURLを独自ドメインに変更しましょう。

CloudFront も1年間は無料利用枠があり,料金も安いのであまり気にしなくてよいでしょう。

11.1 ACM

CloudFront で ACM 証明書を使用するには、「米国東部」リージョンで証明書をリクエストする必要があります。

  • AWS の画面左上の「サービス」を開き、検索欄に「acm」と入力し、「Certificate Manager」を選択

  • 【重要!】右上の「東京」を「米国東部 (バージニア北部)us-east-1」に変更

11_1a.png

  • 「証明書のプロビジョニング」の「今すぐ始める」ボタンをクリック

  • 「パブリック証明書のリクエスト」を選択した状態で「証明書のリクエスト」ボタンをクリック

  • static.ドメイン名 を入力

    • static の箇所は任意ですが,変更する場合は以降も合わせて変更して下さい

11_1b.png

  • 「次へ」ボタンをクリック

  • 「DNS の検証」を選択した状態で「次へ」ボタンをクリック

タグ名
Name アプリ名_cloudfront

11_1c.png

  • 「確認」ボタンをクリック

  • 「確定とリクエスト」ボタンをクリック

  • 「続行」ボタンをクリック

  • DNS検証用の CNAME レコードを追加(8.2参照)

11.3 の作業は,状況が「検証保留中」から「発行済み」に変わってから行う必要があるため,ブラウザのタブは残しておきましょう

11.2 CarrierWaveの設定

次に,Railsアプリ側のCarrierWaveの設定に追記しましょう。

config/initializers/carrier_wave.rb
if Rails.env.production?
  CarrierWave.configure do |config|
    config.fog_credentials = {
      provider: "AWS",
      aws_access_key_id: Rails.application.credentials.dig(:aws, :access_key_id),
      aws_secret_access_key: Rails.application.credentials.dig(:aws, :secret_access_key),
      region: "ap-northeast-1"
    }
    config.fog_directory = "S3のバケット名"
    # ***** 以下を追加 *****
    config.asset_host = "https://static.ドメイン名"
    # ***** 以上を追加 *****
  end
end

コミット・プッシュ(必要があればプルリク・マージ)などを行い,GitHub の master ブランチに変更を反映した上で,AWS にデプロイを行いましょう。

ローカル環境のターミナル
bundle exec cap production deploy

11.3 CloudFront

【注意】 ACM の状況が「検証保留中」から「発行済み」に変わってから行って下さい

  • 画面左上の「サービス」を開き、検索欄に「cloudfront」と入力し、「CloudFront」を選択

  • 「Create Distribution」ボタンをクリック

  • 「Web」の方の「Get Started」を選択

【Origin Settings】

キー
Origin Domain Name S3のバケット名を選択
Restrict Bucket Access Yes

11_3a.png

【Default Cache Behavior Settings】

キー
Viewer Protocol Policy Redirect HTTP to HTTPS

11_3b.png

【Distribution Settings】

キー
Alternate Domain Names static.ドメイン名
SSL Certificate Custom SSL Certificate をチェックし,自分のドメインを選択

11_3c.png

他はデフォルト設定のまま,右下の「Create Distribution」ボタンをクリック

【注意】 続きの作業は「Status」が「Deployed」になってから行ってください。

11.4 Route 53

最後に CloudFront 用のAレコードを作成しましょう。

  • AWS の画面左上の「サービス」を開き、検索欄に「route」と入力し、「Route 53」を選択

  • 「ホストゾーン」をクリック

  • アプリで使用するドメインをクリック

  • 「レコードを作成」ボタンをクリック

  • 「シンプルルーティング」にチェックを入れた状態で「次へ」ボタンをクリック

  • 「シンプルなレコードを定義」ボタンをクリック

キー
レコード名 static
値/トラフィックのルーティング先 CloudFront ディストリビューションへのエイリアス
米国東部 (バージニア北部)
CloudFrontのドメイン名を選択
レコードタイプ A

11_4.png

  • 「シンプルなレコードを定義」ボタンをクリック

これで全ての設定が完了です。

ブラウザから https://ドメイン名 にアクセスし,再度画像を投稿してみましょう。問題なく画像が投稿・表示できていればOKです。

念のため,AWSにログインし S3 のバケットに画像が保存されているかどうかも確認しておくとよいでしょう。

「無事全てが完了した!!」という方は是非 LGTM もお願いいたします!

12. 削除方法

AWS は有料ですので,アプリの公開が不要となったタイミングで削除することをお勧めします。

以下,削除の手順を解説しますが,特に有料となっている以下の削除を忘れないようにしましょう。

  • RDSインスタンス【高額!】
  • ALB(ロードバランサ)【高額!】
  • EC2インスタンス【高額!】
  • Elastic IP(EC2を削除すると課金対象)
  • Route 53
  • ACM
  • S3(画像投稿機能を付けた場合)
  • CloudFront(画像投稿機能を付けた場合)

12.1 Route 53 の削除

  • Route 53 ダッシュボードに移動

    • (画面左上の「サービス」を開き、検索欄に「route」と入力し、「Route 53」を選択)
  • 画面左のメニューバーの「ホストゾーン」をクリック

  • アプリで使用したドメインをクリック

  • タイプが ACNAME のものにチェックを入れ,「削除」ボタンをクリックし,さらに「削除」ボタンをクリック

  • 右上の「削除」ボタンをクリック

  • 「削除」と入力し,「削除」ボタンをクリック

12.2 ALB の削除

  • EC2 ダッシュボードに移動

    • (画面左上の「サービス」を開き、検索欄に「ec2」と入力し、「EC2」を選択)
  • 画面左のメニューバーの「ロードバランサー」をクリック

  • 「アクション」ボタンをクリックし,「削除」をクリック

    • 使用しているロードバランサーが表示されない場合は,右上のリージョンが「東京」になっているか確認
  • 「削除」をクリック

12.3 CloudFront の削除

【備考】画像投稿機能を付けていない場合は,次に進んで下さい。

  • CloudFront ダッシュボードに移動

    • (画面左上の「サービス」を開き、検索欄に「cloudfr」と入力し、「CloudFront」を選択)
  • 使用している Distribution にチェックを入れて,「Disable」ボタンをクリック

  • 「Yes, Disabled」ボタンをクリック

「Status」が「Disabled」になるまで待ちましょう

  • 使用している Distribution にチェックを入れて,「Delete」ボタンをクリック

  • 「Yes, delete」ボタンをクリック

  • 「Close」ボタンをクリック

12.4 SSL証明書 の削除

  • Certificate Manager ダッシュボードに移動

    • (画面左上の「サービス」を開き、検索欄に「acm」と入力し、「Certificate Manager」を選択)
  • アプリで使用したドメインを選択し,「アクション」ボタンをクリックし,「削除」をクリック

  • 「削除」ボタンをクリック

【備考】画像投稿機能を付けている場合は,「東京」リージョンだけでなく「バージニア北部」リージョンのACMも削除して下さい。

12.5 EC2 の削除

  • EC2 ダッシュボードに移動
    • (画面左上の「サービス」を開き、検索欄に「ec2」と入力し、「EC2」を選択)

ターゲットグループの削除

  • 画面左のメニューバーの「ターゲットグループ」をクリック

  • アプリで使用していたものを選択した状態で「Action」のプルダウンの「Delete」をクリック

  • 「Yes, delete」ボタンをクリック

EC2 インスタンスの削除

  • 画面左のメニューバーの 「インスタンス」をクリック
  • 削除したいインスタンスを選択した状態で「アクション」ボタンをクリックし,「インスタンスの状態」「インスタンスを終了」をクリック(「停止」ではない)
  • 「終了」ボタンをクリック

Elastic IP の解放

  • 画面左のメニューバーの Elastic IP をクリック
  • 削除したい Elastic IP を選択した状態で「アクション」ボタンをクリックし,「Elastic IPアドレスの解放」をクリック
    • EC2終了後少し待たないと解放できません
  • 削除したい Elastic IP が消えたことを確認

その他

  • 「キーペア」も削除しましょう
  • 「セキュリティグループ」は VPC 削除時に自動的に削除されます

12.6 RDS の削除

  • RDS ダッシュボードに移動
    • (画面左上の「サービス」を開き、検索欄に「rds」と入力し、「RDS」を選択)

s RDS インスタンスの削除

  • 画面左のメニューバーの「データベース」をクリック
  • 削除したい DB を選択し,「アクション」ボタンをクリックし,「削除」をクリック
  • 「最終スナップショットを作成しますか?」のチェックを外し,「インスタンスの削除後、システムスナップショットとポイントインタイムの復元を含む自動バックアップが利用不可となることを了承しました。」にチェックを入れ,フィールドに「delete me」を入力して「削除」ボタンをクリック

その他

  • 「サブネットグループ」を削除
    • RDSが削除されるまで削除できません
  • 「パラメータグループ」は残してよいでしょう。

12.7 VPC の削除

  • VPC ダッシュボードに移動

    • (画面左上の「サービス」を開き、検索欄に「vpc」と入力し、「VPC」を選択)
  • 削除したい VPC を選択し,「アクション」ボタンをクリックし,「VPC を削除」をクリック

  • フィールドに「削除」と記入して「削除」ボタンをクリック

サブネット,インターネットゲートウェイ,ルートテーブルも同時に削除されます。

12.8 S3/IAM の削除

【備考】画像投稿機能を付けていない場合は,次に進んで下さい。

S3 の削除

  • S3 ダッシュボードに移動

    • (画面左上の「サービス」を開き、検索欄に「s3」と入力し、「S3」を選択)
  • アプリで使用したバケットを選択し,「空にする」ボタンをクリック

  • 「完全に削除」と入力し「空にする」ボタンをクリック

  • 「終了」ボタンをクリック

  • 「削除」ボタンをクリック

  • 「バケット名」を入力し,「バケットを削除」ボタンをクリック

IAM の削除

  • IAM ダッシュボードに移動

    • (画面左上の「サービス」を開き、検索欄に「iam」と入力し、「IAM」を選択)
  • 画面左のメニューバーの「ユーザー」をクリック

  • 「アプリ名_user」にチェックを入れ,「ユーザーの削除」ボタンをクリック

    • アプリと関係ないものを削除しないように注意!
  • 「はい,削除します」ボタンをクリック

12.9 キーペアの削除

GitHub の公開鍵を削除

  • ブラウザでGitHubにアクセス
  • 右上のサムネイル画像をクリック
  • 「Settings」をクリック
  • 左メニューバーから「SSH and GPG keys」をクリック
  • 「アプリ名_git_rsa/pub」の箇所で「Delete」ボタンをクリック
  • 「I understand, please delete this SSH key」ボタンをクリック

ローカル環境のキーペアの削除

最後に,ローカル環境の秘密鍵と設定を削除しておきましょう。

ローカル環境のターミナル
cd ~/.ssh
rm -f アプリ名.pem
vi config
# Host アプリ名 の設定を削除
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

サルでもできる!? Rails6アプリをAWS EC2にデプロイするまでの全手順【前半】(VPC, RDS, EC2, Capistrano)

この記事では,画像投稿機能の付いた Rails 6 のアプリを AWS EC2 にデプロイするまでの全過程を解説します。

Railsアプリのデプロイに関する記事はたくさん見かけますが,初学者にとっては

「具体的に何をすればよいかが分からない:sweat:

記事がほとんどで,ましてや「独自ドメインの設定方法」や「投稿画像を独自ドメインでCloudFrontから配信する方法」まで網羅的に解説を行っている記事は見当たらない状態です。

そこで,「Rails初学者が初めてAWSにデプロイする際に本当に参考となる記事」を目指して書かせていただきました。

Heroku にデプロイしたことはあるけれど,AWS にデプロイしたことがない」という方を対象としますので,AWS の操作は,直感的に操作できる AWS マネジメントコンソール で行います。

AWS CLITerraform などのツールは使用しませんので,あらかじめご了承下さい。

タイトル
【前半】 <-- こちら
1章 はじめに
2章 VPC
3章 RDS
4章 EC2
5章 サーバー構築
6章 デプロイ(Capistrano)
【後半】
7章 独自ドメイン
8章 HTTPS化(ACM, ALB)
9章 デプロイ関連事項
10章 S3
11章 CloudFront
12章 削除方法

1. はじめに

1.1 注意事項

この記事における注意事項を列記します。

料金

AWS は有料のサービスで,いわゆる従量課金制です(サービスの使用量に応じて料金が決まります)。

この記事の構成で AWS を利用した場合,ポートフォリオのようにあまりアクセスが来ないアプリであっても,(無料利用枠を使用する前提で)月におよそ3000円前後かかります。

RDS, ALB などを使用せず, EC2 にデータベースシステムをインストールする構成にすればもっと安く済ませることができますが,ポートフォリオのアピール材料としては弱くなるでしょう。

なお,エラーが発生した際に原因を特定しやすくするため,AWS へのデプロイはアプリの 完成後ではなく開発初期の段階 から行うことをお勧めします。

ただ,料金の問題がありますので,アプリの開発初期は Heroku にデプロイし,仕上げの段階で AWS にデプロイするという手法もありでしょう。

Railsアプリの条件

AWSにデプロイ予定のアプリが,以下の条件を満たすことを前提とします。

  • アプリの Rails のバージョンが 6 であること(5でも多分OK)

  • データベースは MySQL 5.7 もしくは PostgreSQL を使用

  • 画像投稿機能がある場合は CarrierWave を使用

6.1 に画像投稿機能の付いたサンプルアプリを作成する方法を紹介しておりますので,必要がございましたらご利用下さい。

構成図

以下のような一般的な構成を目指します。(料金の都合上,冗長化は行いません)

1_1.png

サーバー環境

  • 【Web サーバー】 Nginx
  • 【アプリケーションサーバー】 Puma

使用する AWS のサービス

  • VPC(Amazon Virtual Private Cloud)
  • RDS(Amazon Relational Database Service)
  • EC2(Amazon Elastic Compute Cloud)
  • Route 53
  • ACM(Certificate Manager)
  • ALB(Application Load Balancer)
  • S3(Amazon Simple Storage Service)
  • IAM(AWS Identity and Access Management)
  • CloudFront

基礎知識の解説

この記事は,「画像投稿機能の付いた Rails アプリを AWS EC2 にデプロイし,独自ドメインでアクセスできるようにする」ことを主目的としております。

最低限度知っておくべき内容は触れますが,インフラの基礎知識は解説しません。

AWS に必要な基礎知識を学べる本はたくさんありますし,Udemyにも丁寧な解説動画が存在します。デプロイの完了後でもよいですので,是非学習されることをお勧めします。

注意点

理解が不十分な状態で進められる場合は, 絶対にミスをしない ように気を付けて下さい。1つのミスで最初からやり直すことになり,数時間の作業が無駄になる可能性があります。

コメントについて

記事内の改善点・間違いなどございましたら,遠慮なくご指摘下さい。可能な限りで記事にも反映させていただきます。

Qiita上の質問には原則お答えする時間が取れませんので,あらかじめ了解いただいた上でご質問下さい。

メモ

作業中にパスワードなど各自設定が異なる部分が複数存在します。メモ帳に以下を貼り付け,決まり次第埋めていくようにされるとよいでしょう。

メモ
【アプリ名】



【RDS】

●マスターユーザー名



●マスターパスワード


●エンドポイント


【EC2】

●Elastic IP


●sshでサインインするときのコマンド

ssh アプリ名

●ユーザー名



●パスワード

【ドメイン】

●ドメイン名


●ネームサーバー情報

【S3】

●バケット名


●リージョン


【IAM】

●ユーザー名


●アクセスキーID


●シークレットアクセスキー


【CloudFront】

static.ドメイン名

まずは,AWSで使用する アプリ名 を決め,上記にメモして下さい。ただし,「aws_sample_app」のように スネークケース 表記であることを前提とします。(アルファベット大文字は使わないで下さい)

「(AWSで使用する)アプリ名」は,「Railsのアプリ名」「GitHubのリポジトリ名」と合わせる方が混乱しづらいと思いますが,同じでなくても問題ありません。

以下, アプリ名 と記載している箇所は,全てこのメモ通りにして下さい。

1.2 AWSアカウントの作成・初期設定

要点のみ記載します。

  • AWSアカウントの作成

    • https://portal.aws.amazon.com/billing/signup
    • メールアドレス・パスワードは超重要です。絶対に忘れないようにし,また他人に知られないように注意しましょう。不正利用されると高額の料金を請求される可能性があります。
    • パスワードは,大文字・小文字・数字・記号を含む16文字以上の強固なものを設定しましょう。
    • サポートプランは特に用事がなければ「ベーシックプラン」でOK
  • 「セキュリティステータス」に全てチェックが入るように設定を進めましょう。

【お勧め参考記事】 https://qiita.com/tmknom/items/303db2d1d928db720888

  • ルートアカウントの MFA を有効化
    • セキュリティ向上のため2段階認証を導入
  • 個々の IAM ユーザーの作成
    • 最初に発行される「ルートアカウント」は,セキュリティの都合上,今後サインインに使用しないようにします
    • 普段のサインインに使用する IAM ユーザーを作成
    • 「パスワードのリセットが必要」は不要なのでチェックを外しましょう
    • 今後のサインインに必要となる「アカウントID」「ユーザー名」「パスワード」を忘れないようにしましょう
  • グループを使用してアクセス許可を割り当て
    • ほぼ全ての権限を持つ「AdministratorAccess」のポリシーを持つグループを作成し,作成した IAM ユーザーを追加
  • IAMパスワードポリシーの適用

最後に「請求アラーム」を作成し,課金状況を確認できるようにしておくことをお勧めします。

【お勧め参考記事】 https://www.kakiyoro.com/archives/2198

2. VPC

Amazon VPC とは Amazon Virtual Private Cloud の略称で,ユーザー専用のプライベートなネットワーク空間を構築できるサービスです。

RailsアプリをAWSで動作させるために,Webサーバーを配置する EC2 とデータベースシステムを動かす RDS を使用しますが,これらを配置するには先に VPC を設定する必要があります。

Amazon VPC は追加料金なしで使用できます。(参考:https://docs.aws.amazon.com/ja_jp/vpc/latest/userguide/what-is-amazon-vpc.html

2.1 VPC の作成

2章では以下の部分を作成していきます。

2_0.png

【注意】 メニューバー右側の真ん中に「米国」などと表示されている場合は,「東京(アジアパシフィック(東京) ap-northeast-1)」に変更して下さい

2_1a.png

  • 画面左上の「サービス」を開き、検索欄に「VPC」と入力し、「VPC」を選択

2_1b.png

【注意】 以下,左上の「New VPC Experience」をONにしていることを前提とします。

  • 画面左のメニューバー(もしくは,ダッシュボード内の)の「VPC」をクリック

2_1c.png

  • 「VPCを作成」ボタンをクリック
キー
名前タグ アプリ名_vpc
IPv4 CIDR ブロック 10.0.0.0/16

【注意】1章で注意しましたとおり,「アプリ名」の箇所はあらかじめ メモ しておいた「アプリ名」を使用して下さい

  • 他はデフォルトのままで「VPCを作成」ボタンをクリック

2_1d.png

2.2 サブネット の作成

VPCの中に,「インターネットから直接通信できる EC2用 のサブネット」と「インターネットから直接通信できない RDS用 のサブネット」を2つずつ作成していきましょう。(許可する接続方法は後に設定します)

  • 画面左のメニューバーから「サブネット」を選択

EC2 用のサブネット

HTTPS化 する際に ALB を使用するため,異なる アベイラビリティーゾーン に属するサブネットを 2つ 用意する必要があります。

  • 「サブネットの作成」ボタンをクリック
キー
VPC アプリ名_vpc ※リストから選択

2_2a.png

キー
名前タグ アプリ名_public_1a_subnet
アベイラビリティーゾーン ap-northeast-1a ※リストから選択
IPv4 CIDR ブロック 10.0.0.0/24

2_2b.png

  • 「新しいサブネットを追加」ボタンをクリック
キー
名前タグ アプリ名_public_1c_subnet
アベイラビリティーゾーン ap-northeast-1c ※リストから選択
IPv4 CIDR ブロック 10.0.1.0/24

※ まだ作成しますので,「サブネットを作成」ボタンをクリックしないこと!

RDS 用のサブネット

RDS を利用するには異なる アベイラビリティーゾーン に属するサブネットを 2つ 用意する必要があります。

  • 「新しいサブネットを追加」ボタンをクリック
キー
名前タグ アプリ名_private_1a_subnet
アベイラビリティーゾーン ap-northeast-1a
IPv4 CIDR ブロック 10.0.10.0/24
  • 「新しいサブネットを追加」ボタンをクリック
キー
名前タグ アプリ名_private_1c_subnet
アベイラビリティーゾーン ap-northeast-1c
IPv4 CIDR ブロック 10.0.11.0/24
  • 「サブネットを作成」ボタンをクリック

  • 下図の状態になっていることを確認しておきましょう

2_2c.png

2.3 インターネットゲートウェイ の作成

VPC 内から インターネット に接続するための「出入口」に相当する インターネットゲートウェイ を作成しましょう。

  • 画面左のメニューバーから「インターネットゲートウェイ」を選択
  • 「インターネットゲートウェイの作成」ボタンをクリック
キー
名前タグ アプリ名_gateway

2_3a.png

  • 他はデフォルトのままで「インターネットゲートウェイの作成」ボタンをクリック
  • フラッシュで表示されている「VPCへアタッチ」をクリック
    • (「アクション」を選択し、「VPCにアタッチ」を選択してもOK)
キー
使用可能なVPC アプリ名_vpc
  • 「インターネットゲートウェイのアタッチ」ボタンをクリック

2_3b.png

2.4 ルートテーブル の作成

インターネットゲートウェイ をアタッチしただけでは,サブネットからインターネットにアクセスすることはできません。
インターネットゲートウェイ へ転送する経路である ルートテーブル を設定する必要があります。

  • 画面左のメニューバーから「ルートテーブル」を選択
  • 「ルートテーブルの作成」ボタンをクリック
キー
名前タグ アプリ名_table
VPC アプリ名_vpc

2_4a.png

  • 「作成」ボタンをクリック
  • 「閉じる」ボタンをクリック

  • 作成したルートテーブルを選択し,「アクション」ボタンをクリックし,「ルートの編集」を選択

2_4b.png

  • 「ルートの追加」ボタンをクリック
    • local を消さないこと!
キー
送信先 0.0.0.0/0
ターゲット 「Internet Gateway」を選択し,先ほど作成した「アプリ名_gateway」を選択

2_4c.png

2_4d.png

  • 「ルートの保存」をクリック
  • 「閉じる」ボタンをクリック

  • (作成したルートテーブルを選択したまま)「アクション」ボタンをクリックし,「サブネットの関連付けの編集」を選択

  • 「アプリ名_public_1a_subnet」「アプリ名_public_1c_subnet」のみを選択し,「保存」ボタンをクリック

2_4e.png

2.5 セキュリティグループ の作成

インスタンスごとに許可する通信を設定するため, セキュリティグループ を作成しておきましょう。

EC2RDS に適用する作業は,後で行います)

EC2 用のセキュリティグループ

【構成内容】 クライアント --(HTTP or SSH)--> EC2

  • 画面左のメニューバーの「セキュリティグループ」を選択

2_5a.png

  • 「セキュリティグループを作成」ボタンをクリック
キー
セキュリティグループ名 アプリ名_ec2_security_group
説明 アプリ名_ec2_security_group
VPC アプリ名_vpc

「インバウンドルール」の「ルールを追加」を2回クリック

キー
タイプ HTTP ※「HTTPS」ではありません!
プロトコル TCP ※自動選択
ポート範囲 80 ※自動選択
ソース 任意の場所 (0.0.0.0/0 と ::/0 が表示)
説明 ※空白でOK
キー
タイプ SSH
プロトコル TCP ※自動選択
ポート範囲 22 ※自動選択
ソース マイIP (自分のグローバルIPが表示)
説明 ※空白でOK

2_5b.png

【注意】ソースを マイIP とすることで,SSH接続できるIPアドレスを制限できます。ただし,自宅以外で EC2 にアクセスする可能性がある場合は,他のIPアドレスも登録が必要です。(意味が分からない場合は, SSH のソースも 任意の場所 に設定して下さい)

RDS 用のセキュリティグループ

【構成内容】 EC2 --(TCP)--> RDS

データベースは EC2 からのリクエストのみを受け付けるように設定しておきます。

画面左のメニューバーの「セキュリティグループ」を選択し,「セキュリティグループを作成」ボタンをクリック

キー
セキュリティグループ名 アプリ名_db_security_group
説明 アプリ名_db_security_group
VPC アプリ名_vpc

「インバウンドルール」の「ルールを追加」をクリック

【注意】アプリで使用するデータベースシステムを確認し,「いずれか一方」のルールを追加して下さい

  • MySQL の場合
キー
タイプ MySQL/Aurora
プロトコル TCP ※自動選択
ポート範囲 3306 ※自動選択
ソース カスタム : アプリ名_ec2_security_group
説明 ※空白でOK

2_5c.png

「セキュリティグループを作成」をクリック

  • PostgreSQL の場合
キー
タイプ PostgreSQL
プロトコル TCP ※自動選択
ポート範囲 5432 ※自動選択
ソース カスタム : アプリ名_ec2_security_group
説明 ※空白でOK

2_5d.png

「セキュリティグループを作成」をクリック

3. RDS

Amazon RDS とは Amazon Relational Database Service の略称で,MySQLPostgreSQL のようなデータベース管理システム(DBMS)を最適な動作条件で利用できるサービスです。

RDS は運用負荷が低く非常に便利なのですが,コストが高い ため注意が必要です。

AWSアカウント作成後,1年間は一定の条件で無料となりますが,不要になった時点で削除することをお勧めします。

3.1. サブネットグループの作成

2.2 で作成した RDS 用のサブネット2つをグループ化しましょう。

  • 画面左上の「サービス」を開き、検索欄に「RDS」と入力し、「RDS」を選択

  • 画面左のメニューバーの「サブネットグループ」を選択し,右上の「DB サブネットグループを作成」ボタンをクリック

  • サブネットグループの詳細

キー
名前 アプリ名_subnet_group_db
説明 アプリ名_subnet_group_db
VPC アプリ名_vpc
  • サブネットの追加
    • 異なる アベイラビリティーゾーン に属するサブネットを2つ以上選択する必要があります
    • マルチ AZ 構成時に必要となるため,今回のように マルチ AZ を使用しない場合にも必須となります
キー
アベイラビリティーゾーン ap-northeast-1a, ap-northeast-1c
サブネット 10.0.10.0/24, 10.0.11.0/24 の2つを選択

3_1.png

「作成」ボタンをチェック

3.2 パラメータグループの作成(MySQL限定)

【注意】PostgreSQL の場合はスキップして下さい

RDSMySQL のデフォルト文字コードは latin1 であり,日本語に対応していません。

文字コードを変更するためのパラメータグループを先に作成しておきましょう。

(1度作成すれば使い回しが可能です)

  • 画面左のメニューバーの「パラメータグループ」を選択

「パラメータグループの作成」ボタンをクリック

  • パラメータグループの詳細
キー
パラメータグループファミリー mysql5.7
グループ名 mysql57-supported-in-ja
説明 parameter group for mysql5.7 supported in ja
  • 作成した「mysql57-supported-in-ja」をクリック

  • 「パラメータの編集」ボタンをクリック

  • 検索窓にキーをコピペして値を変更

キー
character_set_client utf8
character_set_connection utf8
character_set_database utf8mb4
character_set_results utf8
character_set_server utf8mb4
skip-character-set-client-handshake 1
  • 「変更の保存」ボタンをクリック

3.3 DBインスタンスの作成

画面左のメニューバーの「データベース」を選択

「データベースを作成」ボタンをクリック

「MySQL」の場合と「PostgreSQL」の場合で設定が異なりますので,いずれか片方を実行して下さい。

3.3.1 MySQLの場合

  • データベース作成方法を選択

「標準作成」を選択

  • エンジンのオプション
キー
エンジンのタイプ MySQL
バージョン MySQL 5.7.* (*の箇所は最新のものを推奨)
  • テンプレート

「無料利用枠」を選択

3_3ma.png

  • 設定

【注意】ここの「アプリ名」の _ は ハイフン - とすること
【注意】マスターユーザー名・パスワードをメモしておくこと

キー
DB インスタンス識別子 アプリ名-db
マスターユーザー名 root
マスターパスワード 任意
  • DB インスタンスサイズ

db.t2.micro(デフォルト)

3_3mb.png

  • ストレージ

※デフォルト設定で

  • 可用性と耐久性

※デフォルト設定で

3_3mc.png

  • 接続

「アプリ名_vpc」を選択

  • 「追加の接続設定」のドロップダウンをクリック
キー
サブネットグループ アプリ名_subnet_group_db ※2.1で作成
セキュリティグループ アプリ名_db_security_group ※1.5で作成, default は外す
アベイラビリティーゾーン ap-northeast-1a

3_3md.png

  • データベース認証

※デフォルト設定で

  • 「追加設定」をクリック

  • データベースの選択肢

キー
最初のデータベース名 アプリ名_production
DB パラメータグループ mysql57-supported-in-ja
オプショングループ ※デフォルト設定
  • バッグアップ以降

※デフォルト設定で

3_3me.png

3_3f.png

  • 「データベースの作成」ボタンをクリック

3.3.2 PostgreSQLの場合

まず,ターミナルでローカルで使用している PostgreSQL のバージョンを確認しておきましょう。

psql -V
  • データベース作成方法を選択

「標準作成」を選択

  • エンジンのオプション

PostgreSQL のバージョンは,上記で確認したローカルのバージョンに合わせた方が無難です。

メジャーバージョンを合わせれば十分なので,例えばローカルが 12.3 ならば,12.* (*の箇所は最新のもの) をお勧めします。

2020年11月時点ではバージョンが 13 に対応していないので,ローカルのバージョン 13 の場合は最新のバージョンでよいでしょう。

キー
エンジンのタイプ PostgreSQL
バージョン ※上記を参考に設定
  • テンプレート

「無料利用枠」を選択

3_3pa.png

  • 設定

【注意】ここの「アプリ名」の _ は ハイフン - とすること
【注意】マスターユーザー名・パスワードをメモしておくこと

キー
DB インスタンス識別子 アプリ名-db
マスターユーザー名 postgres
マスターパスワード 任意
  • DB インスタンスサイズ

db.t2.micro(デフォルト)

3_3pb.png

  • ストレージ

※デフォルト設定で

  • 可用性と耐久性

※デフォルト設定で

3_3pc.png

  • 接続

「アプリ名_vpc」を選択

  • 「追加の接続設定」のドロップダウンをクリック
キー
サブネットグループ アプリ名_subnet_group_db ※2.1で作成
セキュリティグループ アプリ名_db_security_group ※1.5で作成, default は外す
アベイラビリティーゾーン ap-northeast-1a

3_3pd.png

  • データベース認証

※デフォルト設定で

  • 「追加設定」をクリック

  • データベースの選択肢

キー
最初のデータベース名 アプリ名_production
DB パラメータグループ ※デフォルト設定
オプショングループ ※デフォルト設定
  • バッグアップ以降

※デフォルト設定で

3_3pe.png

3_3pf.png

  • 「データベースの作成」ボタンをクリック

3.4 エンドポイントの確認

  • アプリ名を選択

  • 「接続とセキュリティ」の「エンドポイント」をメモ

    • データベースが作成されるまで待つ必要がありますので,別タブで次の EC2 の設定に進めるとよいでしょう

3_4.png

4. EC2

Amazon EC2 とは Amazon Elastic Compute Cloud の略称で,コンピューティング性能をクラウド内で提供するサービスです。サーバーに必要なものをクラウド内で借りることができます。

Railsのアプリをこの EC2 にデプロイし,ブラウザからアクセスできるように準備を行います。

EC2 も運用に料金がかかりますので,不要になったインスタンスなどは削除するようにしましょう。

4.1 インスタンスの作成

  • 画面左上の「サービス」を開き、検索欄に「EC2」と入力し、「EC2」を選択

  • 画面左のメニューバーの「インスタンス」を選択し,「インスタンスを起動」ボタンをクリック

ステップ 1: Amazon マシンイメージ (AMI)

「無料利用枠の対象」タグの付いた「Amazon Linux 2 AMI (HVM), SSD Volume Type」「64ビット (x86)」の「選択」ボタンをクリック

4_1a.png

ステップ 2: インスタンスタイプの選択

  • 「無料利用枠の対象」タグの付いた「t2.micro」が選択されていることを確認した上で,「次のステップ」ボタンをクリック
    • 「確認と作成」ボタンをクリックしないこと。クリックした場合は,「戻る」ボタンをクリックしてステップ3まで移動しましょう

4_1b.png

ステップ 3: インスタンスの詳細の設定

キー
ネットワーク アプリ名_vpc
サブネット アプリ名_public_1a_subnet

他はデフォルト設定のまま「次のステップ」ボタンをクリック

4_1c.png

ステップ 4: ストレージの追加

「次のステップ」ボタンをクリック

ステップ 5: タグの追加

「タグの追加」ボタンをクリック

キー
Name 「アプリ名_instance」を選択

4_1d.png

「次のステップ」ボタンをクリック

ステップ 6: セキュリティグループの設定

「既存のセキュリティグループを選択する」にチェック

「アプリ名_ec2_security_group」にチェック

4_1e.png

「確認と作成」ボタンをクリック

ステップ 7: インスタンス作成の確認

  • セキュリティグループの「名前」が「アプリ名_ec2_security_group」になっていることを確認した上で,「起動」ボタンをクリック

既存のキーペアを選択するか、新しいキーペアを作成します。

  • 「新しいキーペアの作成」を選択

  • 「キーペア名」は「アプリ名」を入力

    • メモ しているアプリ名と全く同じにして下さい(ハイフン - を使用せず,アンダースコア _ を使用)
  • 「キーペアのダウンロード」を選択

    • アプリ名.pem をダウンロードできます。後に EC2 のログインで利用します

4_1f.png

「インスタンスの作成」ボタンをクリック

作成ステータス

「インスタンスの表示」ボタンをクリック

4.2 Elastic IPの作成、紐付け

インターネットに接続するには グローバルIPアドレス が必須です。

現状ではこの グローバルIPアドレス は固定化されておらず EC2インスタンス を再起動した際に変更されてしまいます。(URLが変化してしまう!)

Elastic IP 設定することで,グローバルIPアドレス を固定化することができます。

Elastic IP はこれから行う EC2インスタンス との関連付けを行っている状態では無料ですが,関連付けた EC2インスタンス を削除したり停止させると料金が発生するようになります。

EC2インスタンス を削除する際は Elastic IP の開放を忘れないように注意しましょう。

Elastic IPの作成

  • 画面左のメニューバーの「Elastic IP」を選択

  • 「Elastic IP アドレスの割り当て」ボタンをクリック

  • 「割り当て」ボタンをクリック

Elastic IPの紐付け

  • 先ほど割り当てた Elastic IP を選択した状態で、「アクション」ドロップダウンを選択し、「Elastic IP アドレスの関連付け」をクリック

4_2a.png

ここで,表示されている Elastic IP は必ずメモしておいて下さい。

キー
インスタンス アプリ名_instance
プライベート IP アドレス ※クリックして表示されるIPを選択
再関連付け チェックを入れる

4_2b.png

  • 「関連付ける」ボタンをクリック

4.3 EC2へのログイン

アプリ名を定義(ローカル環境)

作業ミスを減らすため,ターミナルで頻出する アプリ名 を一時的にグローバル変数として定義しておきましょう。

アプリ名 の箇所は,各自がメモしているものに置き換えて実行して下さい。

ターミナル
APP_NAME=アプリ名
echo $APP_NAME
(例)
APP_NAME=aws_sample_app
echo $APP_NAME

最後のコマンドで,「アプリ名」が正しく表示されていることを確認した上で,以下を実行して下さい。

【補足】間違った場合は再度上記を実行し直してから進めて下さい。また,コマンドは原則コピペして下さい。1文字間違っただけで大きく時間をロスする可能性があります。

ターミナル
# zsh の場合
echo "export APP_NAME=$APP_NAME" >> ~/.zshrc
source ~/.zshrc

# bash の場合
echo "export APP_NAME=$APP_NAME" >> ~/.bash_profile
source ~/.bash_profile

【備考】 使用しているシェルスクリプトが zshbash か分からない場合は echo $SHELL を実行して確認して下さい

4_3a.png

キーペアの移動・アクセス権の付与

【参考】 https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/ec2-key-pairs.html

4.1 でダウンロードしたキーペア アプリ名.pem.ssh ディレクトリに移動し,利用できる状態にしておきましょう。

ターミナル
cd
mkdir .ssh
mv Downloads/$APP_NAME.pem .ssh
chmod 400 .ssh/$APP_NAME.pem
ls -l .ssh/$APP_NAME.pem

最後のコマンドで -r-------- (略) .ssh/アプリ名.pem が表示されていればOKです。

【補足】mv コマンド実行時に No such file or directory が表示された場合は,Google Chrome の下に現れている アプリ名.pem のドロップダウンメニューから,キーペアの場所を確認して,直接,ホームディレクトリの .ssh ディレクトリに移動させて下さい。キーペアのファイル名が アプリ名.pem になっているかどうかも確認しましょう。

EC2 インスタンスの状態を確認

  • EC2 の画面左のメニューバーの「インスタンス」をクリックし,作成した EC2インスタンスの「インスタンスの状態」が 実行中 となっているか確認して下さい
    • 実行中 になっていない場合はしばらく待ってからブラウザをリロードして下さい

【補足】4.2で Elastic IP をメモし忘れた人は,作成した EC2インスタンス を選択し, Elastic IP をメモして下さい

4_3b.png

ssh接続の設定ファイルを作成

EC2インタスタンス にログインするには,「ホスト名」「ユーザー名」「AWSからダウンロードしたキーペア(秘密鍵)」が必要になります。

これらをログインの度に毎回記述するのは面倒なので,設定ファイルに記載し,使える状態にしましょう。

ローカル環境のターミナル
vi ~/.ssh/config

Vim エディタでファイルが開かれますので,以下を記述して下さい。(デフォルトで ec2-user という名前のユーザーが用意されています)

ローカル環境のターミナル(~/.ssh/config)
Host アプリ名
  Hostname ElasticIPを記入
  User ec2-user
  IdentityFile ~/.ssh/アプリ名.pem

【参考】 「アプリ名」が aws_sample_app, 「Elastic IP」が 172.217.161.46 の場合は,次の形式となります。

(例)
Host aws_sample_app
  Hostname 172.217.161.46
  User ec2-user
  IdentityFile ~/.ssh/aws_sample_app.pem

【補足】 Vimの基本

一般のテキストエディタは,ファイルを開いてすぐに文字が入力できます。 Vimインサートモード に変更しなければキー入力ができません。

ここは初学者が非常に混乱しやすいところですので,くれぐれもご注意下さい。

以下は最低限覚えておきましょう。

  • Vim の起動時は「ノーマルモード」
  • 文字をキー入力したいときは i を入力し,「インサートモード」に変更する必要がある
    • 「かな」入力モードでは i を入力しても効果が出ないので,「英数」入力モードに切り替えてから行うこと
  • 「ノーマルモード」への移行は,esc
  • 保存終了は「ノーマルモード」にしてから :wq
  • (保存せずに)強制終了は「ノーマルモード」にしてから :q!

EC2にログイン

それでは, EC2インタスタンス にログインしてみましょう。

ターミナル
ssh アプリ名

Are you sure you want to continue connecting (yes/no/[fingerprint]) と表示されたら,yes と入力してEnterキーを押しましょう。

これで, EC2インタスタンス にログインできます。


【備考】ここでSSH接続できない場合は,以下の可能性があります。

  • 先ほど作成した ~/.ssh/config にミスがある

  • 4.3で,キーペアが指定箇所に移動できていない or パーミッションを適切に変更できていない(ls -l ~/.ssh/$APP_NAME.pem で確認)

  • Elastic IP をメモし間違えている

  • 2.5で作成した「EC2 用のセキュリティグループ(アプリ名_ec2_security_group)」の設定を間違えている

    • SSHのソースが マイIP の場合は,IPアドレスが変わっていないかも確認しましょう(不明な場合は 任意の場所 に変更)
  • 4.1で設定したEC2 用のセキュリティグループが「アプリ名_ec2_security_group」になっていない

  • 2.4で作成したルートテーブルの「サブネットの関連付け」を間違えている


4.4 EC2の初期設定

(参考)Amazon Linux インスタンスでユーザーアカウントを管理する


要注意

今後,EC2にログインした状態の サーバー環境のターミナル と,通常の ローカル環境のターミナル のどちらで操作するかを「必ず」確認して下さい。1度でも間違えると,エラー解決に大きく時間を取られる可能性があります。

なお,一定時間が経過しますと,EC2からログアウトしますので,その点にも注意しましょう。


新規ユーザーアカウントの作成

今後 EC2 での作業や Capistrano で利用するユーザーを作成しましょう。作業を楽にするため,しばらくパスワード無しで sudo コマンドを使える状態にしておきます。

EC2にログインした状態の サーバー環境のターミナル で以下を実行しましょう。

「ユーザー名」の箇所は自由に決め, メモ しておいて下さい。

サーバー環境のターミナル
APP_USER=ユーザー名
echo $APP_USER

最後のコマンドで,「ユーザー名」が正しく表示されていることを確認した上で,以下を実行して下さい。

サーバー環境のターミナル
sudo adduser $APP_USER
echo "$APP_USER ALL=(ALL) NOPASSWD: ALL" | sudo EDITOR='tee -a' visudo > /dev/null
sudo su - $APP_USER
sudo ls

最初の表示が ec2-user から「自分の決めたユーザー名」に変更され,最後のコマンドでパスワードを求められなければOKです。

【補足】2番目の操作は sudo visudo 実行後,一番最後の行に ユーザー名 ALL=(ALL) NOPASSWD: ALL を追加する操作です

アプリ名を定義(サーバー環境)

作業ミスを減らすため,サーバー環境でも「アプリ名」をグローバル変数として定義しておきましょう。

サーバー環境のターミナル
APP_NAME=アプリ名
echo $APP_NAME

最後のコマンドで,「アプリ名」が正しく表示されていることを確認した上で,以下を実行して下さい。

(間違っている場合は再度上記を実行し直してから進めて下さい)

サーバー環境のターミナル
echo "export APP_NAME=$APP_NAME" >> ~/.bash_profile
source ~/.bash_profile

新規ユーザーでssh接続するための設定

新規ユーザーでssh接続できるようにするには,アプリ名.pem (秘密鍵)に対応する 公開鍵EC2~/.ssh/authorized_keys に配置し,適切な権限を与えておく必要があります。

サーバー環境のターミナル
mkdir .ssh -m 700
vi .ssh/authorized_keys

サーバー側でVimエディタが開かれます。

ここで,新しく ローカル 環境のターミナルを用意して下さい。( command + n もしくは command + t

そして, ローカル 環境のターミナルで 秘密鍵(アプリ名.pem) から 公開鍵 をコピーして下さい。

ローカル環境のターミナル
ssh-keygen -y -f ~/.ssh/$APP_NAME.pem | pbcopy

(コマンドを実行するだけでコピーされますので,command + c は不要です。もし pbcopy: command not found が表示される場合は | pbcopy を削って実行し,直接コピーして下さい)

その後,サーバー環境のターミナルに戻って以下の操作を行ってください。

サーバー環境のターミナル
# 公開鍵を貼り付け,保存終了
chmod 600 .ssh/authorized_keys
exit
exit

これで,新しく作成したユーザーでもログインができるようになりました。

ログイン時のユーザー名を変更しましょう。

ローカル環境のターミナル
vi ~/.ssh/config
  • 「ユーザー名」を ec2-user から メモしたユーザー名 に変更しましょう。
ローカル環境のターミナル(~/.ssh/config)
Host アプリ名
  Hostname ElasticIP
  User ユーザー名 ★ここを変更★
  IdentityFile ~/.ssh/アプリ名.pem

yml:(ユーザー名が take の場合)
Host aws_sample_app
Hostname 172.217.161.46
User take
IdentityFile ~/.ssh/aws_sample_app.pem

4.5 今後のEC2のログイン方法

再度,以下のコマンドで EC2インスタンス にログインしてみましょう。

ローカル環境のターミナル
ssh アプリ名

ec2-user ではなく各自が決めたユーザー名でログインできるはずです。

デフォルトユーザーは必要なくなりましたので,削除しておきましょう。

サーバー環境のターミナル
sudo userdel -r ec2-user

5. サーバー構築

Railsアプリを実行できるようにするための環境構築を行っていきます。

EC2インスタンス にログインしていない場合は,次でログインして下さい。

ローカル環境のターミナル
ssh アプリ名

5.1 Nginx の設定

以下の順序で実行していきます。

  • サーバーをアップデート
  • Nginx のインストール
  • Nginx の起動とインスタンス起動時自動起動の設定
サーバー環境のターミナル
sudo yum -y update
sudo amazon-linux-extras install -y nginx1
sudo systemctl start nginx
sudo systemctl enable nginx

ブラウザに Elastic IP を入力してアクセスしましょう。

Welcome to nginx on Amazon Linux! が表示されればOKです。

【補足】ここでアクセスできない場合は,以下の可能性があります

  • 2.5で作成した「EC2 用のセキュリティグループ(アプリ名_ec2_security_group)」の設定を間違えている
    • 「タイプが HTTP, ソースが 0.0.0.0/0::/0」のものが含まれているかを確認。「HTTP」を「HTTPS」にしているミスを見かけます
  • 4.1で設定したEC2 用のセキュリティグループが「アプリ名_ec2_security_group」になっていない

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

  • 各種プラグインのインストール
    • 1行のコマンドです
サーバー環境のターミナル
sudo yum -y install git make gcc-c++ patch openssl-devel libyaml-devel libffi-devel libicu-devel libxml2 libxslt libxml2-devel libxslt-devel zlib-devel readline-devel ImageMagick ImageMagick-devel

【注意】上記はあくまで「画像投稿可能なアプリ」のデプロイを想定したものです。アプリに導入されている gem や ライブラリ次第では,依存関係にある他のプラグインもインストールしておく必要があります。

5.3 データベースにログイン

まず,EC2 にプリインストールされている MariaDB を削除しておきましょう。

サーバー環境のターミナル
sudo yum -y remove mariadb-libs

MySQLの場合

以下の順序で実行していきます。

  • MySQL のリポジトリを追加
  • MySQL の5.7がインストールされるように変更
  • MySQL をインストール
サーバー環境のターミナル
sudo yum localinstall -y https://dev.mysql.com/get/mysql80-community-release-el7-3.noarch.rpm
sudo yum-config-manager --disable mysql80-community
sudo yum-config-manager --enable mysql57-community
sudo yum -y install mysql-community-client mysql-server mysql-devel

次のコマンドでRDSの MySQL にログインできます。

サーバー環境のターミナル
mysql -h エンドポイント -u root -p
# RDS の パスワード を入力

mysql> という表示が出た後に次を実行して下さい。

サーバー環境のターミナル(mysql)
show databases;
# 表示されたデータベース一覧に,「アプリ名_production」が存在していればOK
exit

【参考】「エンドポイント」は 2.4 で確認したものです。確認できていない場合は RDS の「データベース」をクリックし,アプリを選択して出てくる「エンドポイント」を確認しましょう。


【補足】もし,データベース一覧に,「アプリ名_production」が存在しない場合は,exit; で終了する前に以下を実行してデータベースを作成して下さい。

サーバー環境のターミナル(mysql)
# 「アプリ名_production」が存在しない場合のみ,次を実行して下さい
CREATE DATABASE アプリ名_production;

【参考】 Sequel Proで MySQL に接続する方法

Sequel Pro (nightly バージョン) をインストールしていない場合は,次を実行してインストールしましょう。

ローカル環境のターミナル
brew cask install homebrew/cask-versions/sequel-pro-nightly

これで,アプリケーションディレクトリに Sequel Pro.app が入ります。

  • SSHのタブを選択
キー
MySQL Host RDSのエンドポイントを記載
Username root
Password RDSのパスワードを記載
Database アプリ名_production
Port 3306
SSH Host Elastic IPを記載
SSH User EC2のユーザー名を記載
SSH Password 右側の鍵マークボタンをクリックし「~/.ssh/アプリ名.pem」を選択

【参考】 .ssh は隠しファイルです。隠しファイルを表示するショートカットキーは command + shift + . です。

5_3.png

PostgreSQLの場合

単純にインストールするとバージョンが低すぎるので,amazon-linux-extras リポジトリから PostgreSQL のバージョン11を入れることにしましょう。

サーバー環境のターミナル
sudo amazon-linux-extras install -y postgresql11
yum list | grep postgresql
sudo yum -y install postgresql-devel

【補足】PostgreSQLのバージョン12以上を強引に入れることも可能ですが,postgresql-devel のインストールが難しいのでお勧めしません。なお,インストール可能な最新バージョンは, amazon-linux-extras | grep postgresql で確認できます。


次のコマンドでRDSの PostgreSQL にログインできます。

サーバー環境のターミナル
psql -h エンドポイント -U postgres
# RDS の パスワード を入力

postgres=> という表示が出た後に次を実行して下さい。

サーバー環境のターミナル(PostgreSQL)
\l
# 表示されたデータベース一覧に,「アプリ名_production」が存在していればOK
exit;

【補足】もし,データベース一覧に,「アプリ名_production」が存在しない場合は,exit; で終了する前に以下を実行してデータベースを作成して下さい。

サーバー環境のターミナル(PostgreSQL)
# 「アプリ名_production」が存在しない場合のみ,次を実行
CREATE DATABASE アプリ名_production;

【参考】 Postico で PostgreSQL に接続する方法

https://eggerapps.at/postico

インストールしていない場合は,上記サイトの Download ボタンをクリックし,zipファイルを解凍して出てくる Postico をアプリケーションに移動した上で,実行して下さい。

  • Postico を起動し,左下の「New Favorite」をクリック

  • Options のドロップダウンを開き, Connect via SSH をクリック

キー
Nickname ※任意 (アプリ名_production など)
Host RDS のエンドポイントを記載
Port ※不要
User postgres
Password RDSのパスワードを記載
Database アプリ名_production
SSH Host Elastic IPを記載
User EC2のユーザー名を記載
Password ※不要
Private Key 「Choose」をクリックし,「~/.ssh/アプリ名.pem」を選択

「Connect」を選択し,再度「Connect」を選択

5_3b.png

5.4 Node, Yarn のインストール

サーバー環境のターミナル
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.34.0/install.sh | bash
. ~/.nvm/nvm.sh
nvm install node
node -e "console.log('Running Node.js ' + process.version)"

最後のコマンドで Running Node.js バージョン が表示されればOKです。

  • Yarn のインストール
サーバー環境のターミナル
curl -o- -L https://yarnpkg.com/install.sh | bash

5.5 Rubyのインストール

  • rbenv のインストール
サーバー環境のターミナル
git clone https://github.com/sstephenson/rbenv.git ~/.rbenv
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile
echo 'eval "$(rbenv init -)"' >> ~/.bash_profile
source ~/.bash_profile
  • ruby-build のインストール
サーバー環境のターミナル
git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build
  • Ruby のインストール

まず,デプロイするアプリの Ruby のバージョンを確認して下さい。

ローカル環境のターミナル でアプリのディレクトリまで移動し ruby -v で確認できます)

以下は,バージョンが 2.6.6 の場合の例です。バージョンはアプリに「必ず」合わせて下さい。

サーバー環境のターミナル
rbenv install 2.6.6
rbenv global 2.6.6
rbenv rehash

Ruby のインストールには時間がかかります。

  • Bundler のインストール
サーバー環境のターミナル
gem install bundler

完了後に以下を実行し,全てバージョンが表示されればOKです。

サーバー環境のターミナル
rbenv -v
ruby -v
yarn -v
bundler -v

5.6 デプロイ先ディレクトリの設定

(デフォルト設定の場合) Capistrano によるアプリのデプロイ先は /var/www/アプリ名 ディレクトリになります。

そこで,このディレクトリをあらかじめ作成しておき,必要な権限を与えておきましょう。

サーバー環境のターミナル
sudo mkdir -p /var/www/$APP_NAME
sudo chown `whoami`:`whoami` /var/www/$APP_NAME
ls -l /var/www

最後のコマンドで drwxr-xr-x 2 ユーザー名 ユーザー名 日付 アプリ名 が表示されればOKです。

5.7 Git の初期設定

次に,サーバー環境で GitHub の操作ができるようにしていきましょう。

まずは,サーバー環境の Git の初期設定を行いましょう。ここでは,ローカル環境と同じ「ユーザー名」「メールアドレス」を登録することとしておきます。

まず, Git に登録した名前とメールアドレスを確認しましょう。

ローカル環境のターミナル
cat ~/.gitconfig

次の箇所をコピーして下さい。

出力内容の一部
[user]
    name = ~~~~~
  email = ~~~~~@~~~.~~~
  • .gitconfig ファイルの生成
サーバー環境のターミナル
cd
vi .gitconfig
  • Vimが開いたら先ほどコピーしたものをペーストしましょう。

5.8 GitHub連携

サーバー環境に GitHub へのアクセス権限を持たせるため,必要な公開鍵・秘密鍵を生成しましょう。

サーバー環境のターミナル
cd .ssh
ssh-keygen -t rsa -f "${APP_NAME}_git_rsa" -N ""
  • これで,以下のファイルが作成されます

    • 公開鍵(GitHubに配置する鍵) アプリ名_git_rsa.pub
    • 秘密鍵(EC2に残しておく鍵) アプリ名_git_rsa
  • config ファイルの生成

サーバー環境のターミナル
vi config
  • Vimでファイルが開かれたら次を追加
    • 「アプリ名」の箇所は各自の設定に合わせること
~/.ssh/config
Host github github.com
  Hostname github.com
  User git
  IdentityFile ~/.ssh/アプリ名_git_rsa
  • GitHubに公開鍵を登録

以下を実行し,値をコピーしておきましょう。

サーバー環境のターミナル(~/.ssh)
cat "${APP_NAME}_git_rsa.pub"

5_8a.png

  1. ブラウザでGitHubにアクセス
  2. 右上のサムネイル画像をクリック
  3. 「Settings」をクリック
  4. 左メニューバーから「SSH and GPG keys」をクリック
  5. 「SSH Keys」の「New SSH key」をクリック

5_8b.png
5_8c.png

キー
Title アプリ名_git_rsa.pub
Key サーバー環境のターミナルからコピーした値をペースト

5_8d.png

「Add SSH key」ボタンをクリック

  • サーバーから GitHub に接続できるか確認
サーバー環境のターミナル(~/.ssh)
chmod 600 config
ssh github

yes を入力し,次の赤枠の内容が出力されればOKです。

5_8e.png

6. デプロイ(Capistrano)

Capistrano を使い,1コマンドで Rails アプリを AWS EC2 にデプロイできる状態にしていきましょう。

サンプルのアプリをデプロイされたい場合は 6.1 から進めて下さい。オリジナルのアプリをデプロイされる場合は 6.2 から進めて下さい。

なお,オリジナルアプリのデプロイについては,以下の条件を改めて確認しておいて下さい。

  • Rails 6 であること
  • アプリで使用するデータベースシステムの指定が RDS と一致していること
    • 他のデータベースを指定している場合は config/database.yml を修正して下さい。
    • rails db:system:change --to=mysql もしくは rails db:system:change --to=postgresql コマンドが便利です
  • アプリで使用する gem やライブラリ次第では EC2 に他にもあらかじめインストールしておく必要がある可能性があります

6.1 Railsのサンプルアプリを作成

【注】 「サンプルのアプリ」で AWS のデプロイに挑戦されたい方以外はスキップして下さい。

CarrierWave を用いた画像投稿機能を持つアプリを簡単に実装しましょう。

アプリの作成(MySQLの場合)

ターミナル
rails new aws_sample_app -d mysql -C -M -T --skip-active-storage

アプリの作成(PostgreSQLの場合)

ターミナル
rails new aws_sample_app -d postgresql -C -M -T --skip-active-storage

※ 以下は MySQL, PostgreSQL 共通です

ターミナル
cd aws_sample_app
git add .
git commit -m "init"
bundle remove jbuilder
bundle add carrierwave
rails g scaffold Post title:string image:string --skip-assets --skip-helper
rails db:migrate:reset
rails g uploader Image
echo "/public/uploads/" >> .gitignore
  • トップページを設定
config/routes.rb
Rails.application.routes.draw do
  # ********** 以下を追加 **********
  root to: 'posts#index'
  # ********** 以上を追加 **********
  resources :posts
end
  • 画像投稿機能を付けるための準備
app/models/post.rb
class Post < ApplicationRecord
  # ********** 以下を追加 **********
  mount_uploader :image, ImageUploader
  # ********** 以上を追加 **********
end
  • 投稿する画像の選択ボタンを作成
app/views/posts/_form.html.erb
<!-- 略 -->
  <div class="field">
    <%= form.label :image %>
  <!-- ********** 「<%= form.text_field :image %>」を変更 ********** -->
    <%= form.file_field :image, accept: "image/png,image/jpeg,image/gif" %>
  <!-- ********** 以上を変更 ********** -->
  </div>
<!-- 略 -->
  • 投稿詳細ページで,投稿した画像が表示されるように設定
app/views/posts/show.html.erb
<p id="notice"><%= notice %></p>

<p>
  <strong>Title:</strong>
  <%= @post.title %>
</p>

<!-- ********** 以下を編集 ********** -->
<% if @post.image? %>
  <p>
    <%= image_tag @post.image.url %>
  </p>
<% end %>
<!-- ********** 以上を編集 ********** -->

<%= link_to 'Edit', edit_post_path(@post) %> |
<%= link_to 'Back', posts_path %>

動作確認を行った上で,GitHubにリポジトリを作成し,add, commit を行い, GitHub に push しましょう。

さらに,画像を AWS S3 に保存できるようにすべきですが,ひとまず画像投稿は可能ですのでデプロイ完了後の10章・11章で設定を行うこととしましょう。

6.2. データベースの設定

※以下は自作アプリの場合の場合も実行して下さい。必要があれば作業前にブランチを切るようにしましょう。

ファイル名 ファイルのイメージ
config/master.key
config/credentials.yml.enc 錠付きファイル

まず,本番環境用の config/database.yml を修正しておきましょう。

RDS のパスワードとエンドポイントは GitHub から見られないようにするため, credentials.yml.enc に記載し,これを読み込むように設定することとします。

dotenv-rails を導入し .env にパスワードなどを記載する方法もあります)

MySQLの場合

config/database.yml
production:
  <<: *default
  # ***** 以下を修正 *****
  database: アプリ名_production
  password: <%= Rails.application.credentials.db[:password] %>
  host: <%= Rails.application.credentials.db[:hostname] %>

PostgreSQLの場合

config/database.yml
production:
  <<: *default
  # ***** 以下を修正 *****
  database: アプリ名_production
  username: postgres
  password: <%= Rails.application.credentials.db[:password] %>
  host: <%= Rails.application.credentials.db[:hostname] %>

※ 以下は MySQL, PostgreSQL 共通です

  • database.yml で読み込むパスワードとエンドポイントを credentials.yml.enc に記載しましょう
    • yaml 形式ですので,インデント幅にはくれぐれも注意して下さい
    • 例えば,password:の後に「半角スペース1個」がないだけでエラーになります
ローカル環境のターミナル
EDITOR=vi rails credentials:edit
config/credentials.yml.enc
db:
  password: RDSのパスワード
  hostname: RDSのエンドポイント

さらに,本番環境で master.key が存在しない場合はエラーが出るように設定しておきます。

config/environments/production.rb
# 次のコメントアウトを解除
config.require_master_key = true

念のため以下を実行し,パスワードとエンドポイントが取得できることを確認しておいた方がよいでしょう。

ローカル環境のターミナル
rails c
# コンソール起動後
Rails.application.credentials.db[:password]
Rails.application.credentials.db[:hostname]
# RDSのパスワード, エンドポイントが表示されることを確認後
exit

確認ができれば,add, commit を行い, GitHub に push しましょう。

master(main) ブランチ以外で作業をされている場合は,プルリクを出し,GitHubの master(main) ブランチにマージするところまでは行ってください。

6.3. Capistrano

1コマンドで Rails アプリをデプロイできるよう Capistrano を導入しましょう。

Gemfile
group :development do
  # 略
  # ***** 以下を追加 *****
  gem "capistrano", "~> 3.10", require: false
  gem "capistrano-rails", "~> 1.6", require: false
  gem 'capistrano-rbenv', '~> 2.2'
  gem 'capistrano-rbenv-vars', '~> 0.1'
  gem 'capistrano3-puma'
  # ***** 以上を追加 *****
end
ローカル環境のターミナル
bundle install
bundle exec cap install STAGES=production

Capfile# require "capistrano/passenger" の下に以下を追加

Capfile
require 'capistrano/rbenv'
require 'capistrano/bundler'
require 'capistrano/rails/assets'
require 'capistrano/rails/migrations'
require 'capistrano/puma'
install_plugin Capistrano::Puma
install_plugin Capistrano::Puma::Nginx
  • デプロイの設定ファイルを修正・追記しましょう
    • 「アプリ名」「GitHubリポジトリURL」は各自のものに置き換えて下さい
    • 「GitHubリポジトリURL」は HTTPS でなく SSH のURLをコピペして下さい
config/deploy.rb
# config valid for current version and patch releases of Capistrano
lock "~> 3.14.1"

# ***** 以下を修正 *****
set :application, "アプリ名"
set :repo_url, "GitHubのリポジトリURL"
set :rbenv_ruby, File.read('.ruby-version').strip

# Nginxの設定ファイル名と置き場所を修正
set :nginx_config_name, "#{fetch(:application)}.conf"
set :nginx_sites_enabled_path, "/etc/nginx/conf.d"

append :linked_files, "config/master.key"
append :linked_dirs, "log", "tmp/pids", "tmp/cache", "tmp/sockets", "node_modules"
# ***** 以上を追加 *****
  • production.rb の一番上に,EC2サーバーにログインするユーザー名、サーバーのロールを記述
    • 「ElatsticIP」「アプリ名」「ユーザー名」は各自のものに置き換えること
config/deploy/production.rb
server "ElatsticIP", user: "ユーザー名", roles: %w{app db web}

set :ssh_options, {
  keys: %w(~/.ssh/アプリ名.pem),
  forward_agent: true,
  auth_methods: %w(publickey),
}

master.key の配置と Rails アプリのデプロイ

準備が整いましたので,Railsアプリのデプロイを行いましょう。

初回の実行では master.key がないためデプロイに失敗しますが, master.key を配置するために必要なディレクトリが生成されます。

デプロイ失敗後に scp コマンドで master.key をサーバー側に追加し,再度実行すればデプロイに成功するでしょう。

ローカル環境のターミナル
# Railsアプリのルートディレクトリに移動してから
bundle exec cap production deploy

# 「ERROR linked file /var/www/アプリ名/shared/config/master.key does not exist on ElasticIP」が出ればOK

scp config/master.key $APP_NAME:/var/www/$APP_NAME/shared/config
bundle exec cap production deploy

最後から2行目に Command restart sent success が表示されていればOKです。

【備考】ここでデプロイに失敗した場合は,まずエラー文を確認しましょう。いろいろなケースがありますが,アプリで使用されている gem 次第では,先に依存関係にあるプラグインを EC2 にインストールしておく必要があります。

Nginx の設定

Nginx 用の設定ファイルを追加するため,先に必要なディレクトリをサーバー側で作成しておきましょう。

サーバー環境のターミナル
cd /etc/nginx
sudo mkdir sites-available

ローカル環境 のターミナルで,Railsアプリのルートディレクトリまで移動した後,以下を実行しましょう。
これで Nginx 用の設定が生成されます。

ローカル環境のターミナル
# Railsアプリのルートディレクトリに移動してから
bundle exec cap production puma:nginx_config

これで,サーバー側の /etc/nginx/conf.d に設定ファイル アプリ名.conf が入ります。

Nginx を再起動しましょう。

サーバー環境のターミナル(/etc/nginx)
sudo service nginx restart

ブラウザのアドレスバーに Elastic IP を入力してアクセスし, スーパーリロード して下さい。(ショートカットキーは command + shift + r

アプリが表示されればOKです!

【補足】アプリが表示されない場合は,以下を試して下さい

  • ローカル環境のターミナルから curl -I ElasticIP を実行したとき,最初に HTTP/1.1 301 Moved Permanently が出力される場合
    • config/environments/production.rbconfig.force_ssl = true をコメントアウトして下さい
  • 9.4 にログの確認方法を記載していますので,確かめてみましょう!!

これでひとまずデプロイは完了です!……が,他にもすべきことがありますので, 【後半】 に進みましょう。

「無事デプロイに成功した!!」という方は是非 LGTM もお願いいたします!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

RailsアプリをHerokuでデプロイしてみた。

Railsでアプリケーションを作成してみたのでHerokuでデプロイしてみました。
下の方のを参考にしたらなんなく出来ました!アウトプットの為に投稿させてもらいます。
【初心者向け】railsアプリをherokuを使って確実にデプロイする方法【決定版】
自分はPostgreSQLを使って作っていたので最初の設定はスルーして実装しました。
gitも入っていたので省略します。

環境

Rails 5.2.1
Ruby 2.6.5
git 2.23.0
heroku/7.47.3 darwin-x64 node-v12.16.2

Herokuとは

PaaS(Platform as a Service)と呼ばれるサービス
開発したwebアプリケーションを開発、実行するための基盤(プラットフォーム)を提供するサービス
無料で使用する事が可能で簡単にデプロイ出来ちゃいます。

Herokuの登録

日本語翻訳の方がありましたのでこちらからどうぞ!
パスワードはターミナルでログインする際に使いますので忘れない様に!
https://signup.heroku.com/jp
会員登録が終わったら、PCとHerokuを紐づける為にcliをダウンロードします。OSで違いが出るのはここぐらいです!
https://devcenter.heroku.com/articles/heroku-cli

Herokuが入っているか確認

$heroku --version
>> heroku/7.47.3 darwin-x64 node-v12.16.2

PCとHerokuの紐付け

$heroku login

メールアドレスとパスワードを入力します!

Enter your Heroku credentials:
Email: ~~~~~@example.com (登録したメールアドレスを)
Password: **********  (登録したパスワードを)
Logged in as ~~~~~@example.com

URLがアプリ名になります

$heroku create アプリ名

Herokuにデプロイする

$git push heroku main or master ←自分のブランチ名によります

本番環境のマイグレーション

$heroku run rails db:migrate

これで特にエラーとかでなければ作成されたURLを叩いてみましょう!
※自分はHerokuにデプロイする時に以下のエラーが出ました

error: src refspec master does not match any
error: failed to push some refs to 'アプリURL.git'

自分のgitにはmasterというブランチはなくmainというブランチだったのでエラーが出てました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Rails]Request Specでのログインの実施

はじめに

アプリ開発において、トップページ以外ログインをしていないと見れないという設定にしていたので、postsコントローラーのshowアクションのテストコードの際に利用しました。

目次

  • 1. deviseのヘルパー機能を呼び出す設定
  • 2. FactoryBotを利用したdevise認証

1. deviseのヘルパー機能を呼び出す設定

spec/rails_helper.rb
RSpec.configure do |config|
~~
  #下記を追記
  config.include Devise::Test::IntegrationHelpers, type: :request
~~
end

2. FactoryBotを利用したdevise認証

FactoryBotを利用できるように設定します。
spec配下にsupportディレクトリを作成しfactory_bot.rbファイルを作成します。

spec/support/factory_bot.rb
RSpec.configure do |config|
  config.include FactoryBot::Syntax::Methods
end

下記の一文のコメントアウトを外して有効にします。

spec/rails_helper.rb
~~
Dir[Rails.root.join('spec', 'support', '**', '*.rb')].each { |f| require f }
~~

FactoryBotを用いてテスト用のuserを作成します。

spec/factories/users.rb
FactoryBot.define do
  factory :user do
    nickname { Faker::Name.last_name }
    email { Faker::Internet.free_email }
    password = Faker::Internet.password(min_length: 6)
    password { password }
    password_confirmation { password }
    birthday { Faker::Date.in_date_period }
    gender { 2 }
  end
end

before do ~ endでユーザーログインまでの挙動を設定しています。

spec/requests/posts_request_spec.rb
require 'rails_helper'

RSpec.describe 'Posts', type: :request do
  before do
    # 登録しているuserを使うのでcreateとします。
    @user = FactoryBot.create(:user)
    # deviseのメソッドであるsign_inでログインしています。
    sign_in @user
    @post = FactoryBot.create(:post, image: fixture_file_upload('public/images/test_image.png'))
  end

  describe "GET #show" do
    it "showアクションにリクエストすると正常にレスポンスが返ってくる" do 
      get post_path(@post)
      expect(response.status).to eq 200
    end
    it "showアクションにリクエストするとレスポンスに投稿済みのクチコミの商品名が存在する" do 
      get post_path(@post)
      expect(response.body).to include @post.name
    end
    it "showアクションにリクエストするとレスポンスに投稿済みのクチコミのカテゴリーが存在する" do 
      get post_path(@post)
      expect(response.body).to include @post.category.name
    end
    it "showアクションにリクエストするとレスポンスに投稿済みのクチコミの更新日が存在する" do 
      get post_path(@post)
      expect(response.body).to include @post.created_at.strftime("%Y.%m.%d")
    end
    it "showアクションにリクエストするとレスポンスに投稿済みのクチコミの投稿者名が存在する" do 
      get post_path(@post)
      expect(response.body).to include @post.user.nickname
    end
    it "showアクションにリクエストするとレスポンスに投稿済みのクチコミの評価が存在する" do
      get post_path(@post)
      expect(response.body).to include "#{@post.evaluation}"
    end
    it "showアクションにリクエストするとレスポンスに投稿済みのクチコミ商品購入額が存在する" do
      get post_path(@post)
      expect(response.body).to include "#{@post.price}"
    end
    it "showアクションにリクエストするとレスポンスに投稿済みのクチコミ商品購入店が存在する" do
      get post_path(@post)
      expect(response.body).to include @post.shop_name
    end
    it "showアクションにリクエストするとレスポンスに投稿済みのクチコミレビュー文が存在する" do
      get post_path(@post)
      expect(response.body).to include @post.description
    end
    it "showアクションにリクエストするとレスポンスに投稿済みのクチコミの画像が存在する" do 
      get post_path(@post)
      expect(response.body).to include 'test_image.png'
    end
    it "showアクションにリクエストするとレスポンスに投稿済みのクチコミのお気に入り数が存在する" do 
      get post_path(@post)
      expect(response.body).to include "#{@post.likes.count}"
    end
    it "showアクションにリクエストするとレスポンスに関連商品のクチコミ表示部分が存在する" do 
      get post_path(@post)
      expect(response.body).to include '関連商品のクチコミ'
    end
  end 
end

参考リンク

https://qiita.com/Hyuga-Tsukui/items/57fa77df2b2942414307

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rails+Bootstrapでtodoアプリを作ってみよう

はじめに

プログラミングを初めてまず最初に作るアプリはtodoアプリなのではないでしょうか。一度理解してしまえばなんてことはない簡単なアプリですが理解するまでが意外と大変だと思います。そんなtodoアプリをrailsとbootstrapを使って制作していきましょう!

バージョン

Rails 6.0.3.4
Ruby 2.6.3
Bootstrap 3.3.6

プロジェクトの作成

それでは早速進めていきましょう。ターミナルに

ターミナル
$rails new todo-app

と打ってプロジェクトを作成させます。途中、passwordを求められますので忘れずに入力してください。その際、入力した文字が画面に表示されませんが構わずに続けてEnterを押して進んでください。

Webpacker successfully installed ? ?

と表示されればプロジェクト作成は完了です。ケーキ食べたい...(途中Warningがたくさん出てくるかもしれませんがErrorと出てこない限りは全部無視してしまって大丈夫です。)その後

ターミナル
$cd todo-app

と入力してtodo-appに移動しましょう。(この後の作業は全てtodo-appに移動している前提で行います。)

モデルの作成

モデルを作成していきます。モデルは単数形で頭文字を大文字にするのがルールです。ここではtitleをstring型、bodyをtext型にして作っていきたいと思います。ターミナルで以下を実行してください。

ターミナル
$rails g model Task title:string body:text

できたらその後以下でマイグレートしましょう。(もし間違えて作成してしまった場合には、gをdに変えて実行すれば消すことができます。)

ターミナル
$rails db:migrate

コントローラの作成

コントローラを作ります。コントローラは複数形で全て小文字にするのがルールです。

ターミナル
rails g controller tasks

これで、大まかな構造を作ることができました。(もし間違えて作成してしまった場合には、モデルと同じくgをdに変えて実行すれば消すことができます。)次からコードを打ち込んでいきましょう!!

トップ画面 ~index~

それでは、まずはトップ画面を作成していきたいと思います。トップ画面にはやること一覧を表示させます。これにはindexアクションを用いましょう。ここではすべてのタスクをTask.allで取得します。

app/controllers/tasks_controller.rb
class TasksController < ApplicationController
  def index
    @tasks = Task.all
  end
end

次にviewを作成していきましょう。viewsフォルダのなかのtasksフォルダの中にindex.html.erbというファイルを作りましょう。(controlを押しながらクリックすれば作成できます。)ファイルを作ることができたら以下を記述しましょう。

app/views/tasks/index.html.erb
<h1>やること一覧</h1>

<table>
  <thead>
    <tr>
      <th>やること</th>
      <th>詳細</th>
    </tr>
  </thead>

  <tbody>
    <% @tasks.each do |task| %>
    <tr>
      <td><%= task.title %></td>
      <td><%= task.body %></td>
    </tr>
    <% end %>
  </tbody>
</table>

次に大事なルーティングを作成しましょう。以下のように記述してください。

config/routes.rb
resources :tasks

なんとなんとrailsだとこの1行でindex,new,create,edit,update,show,destroyの主要な7つのアクション一気に作ることができます。(今回showは使いませんが...)rails便利!

これからは、localhost:3000/tasksと打てば今作成している画面を確認できるようになります。エラーが起きていないか常に確認しながら進めていきましょう。

新規作成機能 ~new,create~

ここからは新規作成機能を作っていきましょう。まずはコントローラに記述をしていきます。

app/controllers/tasks_controller.rb
class TasksController < ApplicationController
  def index
    @tasks = Task.all
  end

#ここから追加
  def new
    @task = Task.new
  end

  def create
    @task = Task.new(task_params
    if @task.save
      redirect_to tasks_path #セーブできたらindexページに行く
    else
      render 'new' #できなかったらnewページのまま
    end
  end

  private
  def task_params
  #モデル作成時に作ったやつ
    params.require(:task).permit(:title, :body)
  end
end

ここではストロングパラメータというものを使って、Taskモデルを作成した際にできたtaskテーブルににtitle(やること)とbody(詳細)を保存します。ストロングパラメータについて詳しく知りたいかたはこちらを参照してください。そして「redirect toとrenderの違いって何??」と思ったセンスの良いかたはこちらを参照してください。

viewを作成します。

app/views/tasks/new.html.erb
<h1>やること新規作成</h1>

<%= link_to '一覧へ', tasks_path %>

<%= form_with model: @task do |f| %>
<div class="field">
  <%= f.label :やること %><br>
  <%= f.textarea_field :title %>
</div>
<div class="field">
  <%= f.label :詳細 %><br>
  <%= f.text_field :body %>
</div>
<div class="actions">
  <%= f.submit %>
</div>
<% end %>

form_withを使ってデータを保存させます。form_withについて詳しく知りたいかたはこちら
これによって作られたデータは、createアクションへ運ばれます。

ここで、フォームが空欄だった場合に保存できないようにしておきましょう。この機能をバリデーションと呼びます。

app/model/task.rb
class Task < ApplicationRecord
    validates :title, presence: true
    validates :body, presence: true
end

詳しい説明はここでは省きますが、これで空欄のままsubmitを押しても保存されないようになります。バリデーションについて詳しくしれたい方はこちら

最後にindexのページ(やること一覧のページ)に新規作成ページへのリンクを貼り付けましょう。ターミナルで

ターミナル
$rails routes

と打ってみてください。すると、

ターミナル
     tasks  GET    /tasks(.:format)  tasks#index                                                              
         POST   /tasks(.:format)  tasks#create                                                              
   new_task GET    /tasks/new(.:format)  tasks#new                                                          
    edit_task GET    /tasks/:id/edit(.:format)   tasks#edit                                                   
         task GET    /tasks/:id(.:format)    tasks#show                                                       
              PATCH  /tasks/:id(.:format)    tasks#update                                                       
              PUT    /tasks/:id(.:format)     tasks#update                                                      
              DELETE /tasks/:id(.:format)      tasks#destroy 
  ...                                                    

と出てきたと思います。このtasks#newの左側にあるnew_taskにpathをくっつけたのがnewページに行くためのリンクになります。そのため、

app/views/tasks/index.html.erb
<h1>やること一覧</h1>

<%= link_to '新規作成', new_task_path %> #追加

<table>
  <thead>
    <tr>
      <th>やること</th>
      <th>詳細</th>
    </tr>
  </thead>

  <tbody>
    <% @tasks.each do |task| %>
    <tr>
      <td><%= task.title %></td>
      <td><%= task.body %></td>
    </tr>
    <% end %>
  </tbody>
</table>


これで新規作成機能は終わりです。

編集機能 ~edit,update~

続いて、編集機能を作成していきたいと思います。編集にはeditアクションとupdateアクションを使用します。コントローラーにこの2つのアクションを記述していきましょう。

app/controllers/tasks_controller.rb
class TasksController < ApplicationController
  def index
    @tasks = Task.all
  end

  def new
    @task = Task.new
  end

  def create
    @task = Task.new(task_params)
    if @task.save
      redirect_to tasks_path
    else
      render 'new'
    end
  end

 #ここから追加
  def edit
    @task = Task.find(params[:id])
  end

  def update
    @task = Task.find(params[:id])
    if @task.update(task_params) 
      redirect_to tasks_path #updateできたらindexページに行く
    else
      render 'edit' #できなかったらeditページのまま
    end
  end
 #ここまで

  private
    def task_params
      params.require(:task).permit(:title, :body)
    end
end

ここで新しく、find(params[:id])というコードが出てきましたね。これは投稿され情報に付けられたidを探し出すコードです。例えば、「ケーキを買う」という内容を編集したいのにボタン押したら隣に記述していた「部屋の掃除をする」という内容が画面に出てきたらもう訳わかんないですよね。ここではこのfind(params[:id])を使って編集したい内容を正しく呼び出せるようにしています。(ちなみにrailsというフレームワークを使っているからこの1行だけで情報を呼び出せるのであり、使はなければSQLという言語をゴリゴリ書く必要があります。railsすげえ。)

次にviewsファイルを作成していきましょう。

app/views/tasks/edit.html.erb
<h1>やること編集</h1>

<%= link_to '一覧へ', tasks_path %>

<%= form_with model: @task do |f| %>
<div class="field">
  <%= f.label :やること %><br>
  <%= f.text_field :title %>
</div>
<div class="field">
  <%= f.label :詳細 %><br>
  <%= f.text_field :body %>
</div>
<div class="actions">
  <%= f.submit %>
</div>
<% end %>

入力フォームなのでnew.html.erbと同じコードですね。最後に一覧画面に編集画面へのリンクを付け加えて終了です。

app/views/tasks/index.html.erb
<h1>やること一覧</h1>

<%= link_to '新規作成', new_task_path %>

<table>
  <thead>
    <tr>
      <th>やること</th>
      <th>詳細</th>
    </tr>
  </thead>

  <tbody>
    <% @tasks.each do |task| %>
    <tr>
      <td><%= task.title %></td>
      <td><%= task.body %></td>
      <td><%= link_to '編集', edit_task_path(task) %></td> #追加
    </tr>
    <% end %>
  </tbody>
</table>

pathはターミナルでrails routesと打って確認してみてください。(新規作成機能作る時に一回やったやつ。)edit画面へ遷移する時にはidが必要なので今この情報を持っているtaskを最後につけることを忘れずに。

これで編集機能は終わりです。

削除機能 ~destroy~

最後は削除機能を作成していきます。コントローラーにdestroyアクションを追加しましょう。

app/controllers/tasks_controller.rb
class TasksController < ApplicationController
  def index
    @tasks = Task.all
  end

  def new
    @task = Task.new
  end

  def create
    @task = Task.new(task_params)
    if @task.save
      redirect_to tasks_path
    else
      render 'new'
    end
  end

  def edit
    @task = Task.find(params[:id])
  end

  def update
    @task = Task.find(params[:id])
    if @task.update(task_params) 
      redirect_to tasks_path
    else
      render 'edit'
    end
  end

 #ここから追加
 def destroy
    @task = Task.find(params[:id])
    @task.destroy
    redirect_to tasks_path
  end
 #ここまで

  private
    def task_params
      params.require(:task).permit(:title, :body)
    end
end

ここではfind(params[:id])で削除する情報を見つけて、destroyで削除しています。
一覧画面に削除ボタンを付けましょう。

app/views/tasks/index.html.erb
<h1>やること一覧</h1>

<%= link_to '新規作成', new_task_path %>

<table>
  <thead>
    <tr>
      <th>やること</th>
      <th>詳細</th>
    </tr>
  </thead>

  <tbody>
    <% @tasks.each do |task| %>
    <tr>
      <td><%= task.title %></td>
      <td><%= task.body %></td>
      <td><%= link_to '編集', edit_task_path(task) %></td>
    <td><%= link_to '削除', task, method: :delete, data: { confirm: '本当に削除しますか?' } %></td>
    </tr>
    </tr>
    <% end %>
  </tbody>
</table>

ここではpath名ではなく、method: :deleteと記述することでdestroyアクションを呼びます。そして、data: { confirm: '本当に削除しますか?' }と記述することで確認のモーダルウィンドウを出すことができます。

以上でtodoアプリの機能は全て作ることができました。お疲れ様でした。?

Bootstrapを使ってレイアウトを整えよう

これでtodoアプリは完成したのですが、レイアウトがちょっと味気ないですよね...そこでBootstrapというcssのフレームワークを導入してレイアウトを整えていきましょう!

下準備

Gemfile
gem 'bootstrap-sass', '~> 3.3.6'
gem 'jquery-rails'

と入力し、

ターミナル
 $bundle install

としましょう。途中passwordを求められると思うので指示通り入力してください。

次にRailsアプリの作成時に生成されるapplication.cssapplication.scssにリネームしてください。このファイルはapp/assert/stylesheet/の下にあります。

ファイル名を変更できたら、

app/assert/stylesheet/application.scss
 @import "bootstrap-sprockets"; 
 @import "bootstrap";

これらを追加してください。

そして、最後にapplication.jsを編集します。

app/javascript/application.js
//= require jquery
//= require bootstrap-sprockets

またこれらをを追加してください。これでrailsでbootstrapを使うための下準備が完了しました。

レイアウトを整える

早速やっていきましょう。

まず、画面が左に偏っていると思うので真ん中にいい感じになるようにしましょう。(語彙力なくてごめんなさい。)application.html.erbにある<%= yield %>を以下のようにdivタグで囲んで下さい。

views/layout/application.html.erb
  <div class="container">
    <%= yield %>
  </div>

この<%= yield %>には、各ページで表示されるhtmlファイルの内容が入ってきます。そのため、ここを編集するだけで全ページに編集を適用させることができます。

これで、レイアウトがいい感じに画面の真ん中にあると思います。

次に一覧ページのtableタグを以下のように編集してください。

app/views/tasks/index.html.erb
 <table class="table table-striped">

 </table>

これで投稿された内容の背景が交互に変わり見やすくなっていると思います。(これすごいですよね。初めて使った時感動しました。)

そして最後にボタンをいい感じにしておきましょう。一覧画面にある、新規作成ボタン、編集ボタン、削除ボタンを以下のようにclassを付けて編集してみてください。

app/views/tasks/index.html.erb
<%= link_to '新規作成', new_task_path, class: "btn btn-primary" %>
<%= link_to '編集', edit_task_path(task), class: "btn btn-success"  %>
<%= link_to '削除', task, method: :delete, data: { confirm: 'You sure?' }, class: "btn btn-danger" %>

ついでに新規作成画面と編集画面にある「一覧へ」ボタンも編集してみましょう。

app/views/tasks/new.html.erbとedit.html.erb
<%= link_to '一覧へ', tasks_path, class: "btn btn-info" %>

色についてはこちらのサイトに色々(?)書いてあるのでお好みの色を使ってみてください。

これで使いやすいいい感じのレイアウトになったと思います。お疲れ様でした!

最後に

これで使いやすいtodoアプリを作ることができました。たかがtodoアプリではありますが重要な要素がたくさん詰まっているので1つ1つ確認してみてください。

以上、最後まで読んでいただきありがとうございました!!!??

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[初心者向け]Railsでtodoアプリを作ってみよう

はじめに

プログラミングを初めてまず最初に作るアプリはtodoアプリだと思います。この基本の「き」とも言えるtodoアプリを一緒に作っていきましょう!

バージョン

Rails 6.0.3.4
ruby 2.6.3

プロジェクトの作成

それでは早速進めていきましょう。ターミナルに

ターミナル
$rails new todo-app

と打ってプロジェクトを作成させます。途中、passwordを求められますので忘れずに入力してください。その際、入力した文字が画面に表示されませんが構わずに続けてEnterを押して進んでください。

Webpacker successfully installed ? ?

と表示されればプロジェクト作成は完了です。ケーキ食べたい...(途中Warningがたくさん出てくるかもしれませんがErrorと出てこない限りは全部無視してしまって大丈夫です。)その後

ターミナル
$cd todo-app

と入力してtodo-appに移動しましょう。

モデルの作成

モデルを作成していきます。モデルは単数形で頭文字を大文字にするのがルールです。ここではtitleをstring型、bodyをtext型にして作っていきたいと思います。ターミナルで以下を実行してください。

ターミナル
$rails g model Task title:string body:text

できたらその後以下でマイグレートしましょう。(もし間違えて作成してしまった場合には、gをdに変えて実行すれば消すことができます。)

ターミナル
$rails db:migrate

コントローラの作成

コントローラを作ります。コントローラは複数形で全て小文字にするのがルールです。

ターミナル
rails g controller tasks

これで、大まかな構造を作ることができました。(もし間違えて作成してしまった場合には、モデルと同じくgをdに変えて実行すれば消すことができます。)次からコードを打ち込んでいきましょう!!

トップ画面 ~index~

それでは、まずはトップ画面を作成していきたいと思います。トップ画面にはやること一覧を表示させます。これにはindexアクションを用いましょう。ここではすべてのタスクをTask.allで取得します。

app/controllers/tasks_controller.rb
class TasksController < ApplicationController
  def index
    @tasks = Task.all
  end
end

次にviewを作成していきましょう。viewsフォルダのなかのtasksフォルダの中にindex.html.erbというファイルを作りましょう。(controlを押しながらクリックすれば作成できます。)ファイルを作ることができたら以下を記述しましょう。

app/views/tasks/index.html.erb
<h1>やること一覧</h1>

<table>
  <thead>
    <tr>
      <th>やること</th>
      <th>詳細</th>
    </tr>
  </thead>

  <tbody>
    <% @tasks.each do |task| %>
    <tr>
      <td><%= task.title %></td>
      <td><%= task.body %></td>
    </tr>
    <% end %>
  </tbody>
</table>

次に大事なルーティングを作成しましょう。以下のように記述してください。

config/routes.rb
resources :tasks

なんとなんとrailsだとこの1行でindex,new,create,edit,update,show,destroyの主要な7つのアクション一気に作ることができます。他の言語だと色々ごちゃごちゃと書かないといけないのですが、、、rails便利ですね。

これからは、localhost:3000/tasksと打てば今作成している画面を確認できるようになります。エラーが起きていないか常に確認しながら進めていきましょう。

新規作成機能 ~new,create~

ここからは新規作成機能を作っていきましょう。まずはコントローラに記述をしていきます。

app/controllers/tasks_controller.rb
class TasksController < ApplicationController
  def index
    @tasks = Task.all
  end

#ここから追加
  def new
    @task = Task.new
  end

  def create
    @task = Task.new(task_params
    if @task.save
      redirect_to tasks_path #セーブできたらindexページに行く
    else
      render 'new' #できなかったらnewページのまま
    end
  end

  private
  def task_params
  #モデル作成時に作ったやつ
    params.require(:task).permit(:title, :body)
  end
end

ここではストロングパラメータというものを使って、Taskモデルを作成した際にできたtaskテーブルににtitle(やること)とbody(詳細)を保存します。ストロングパラメータについて詳しく知りたいかたはこちらを参照してください。そして「redirect toとrenderの違いって何??」と思ったセンスの良いかたはこちらを参照してください。

viewを作成します。

app/views/tasks/new.html.erb
<h1>やること新規作成</h1>

<%= link_to '一覧へ', tasks_path %>

<%= form_with model: @task do |f| %>
<div class="field">
  <%= f.label :やること %><br>
  <%= f.textarea_field :title %>
</div>
<div class="field">
  <%= f.label :詳細 %><br>
  <%= f.text_field :body %>
</div>
<div class="actions">
  <%= f.submit %>
</div>
<% end %>

form_withを使ってデータを保存させます。form_withについて詳しく知りたいかたはこちら
これによって作られたデータは、createアクションへ運ばれます。

ここで、フォームが空欄だった場合に保存できないようにしておきましょう。この機能をバリデーションと呼びます。

app/model/task.rb
class Task < ApplicationRecord
    validates :title, presence: true
    validates :body, presence: true
end

詳しい説明はここでは省きますが、これで空欄のままsubmitを押しても保存されないようになります。バリデーションについて詳しくしれたい方はこちら

最後にindexのページ(やること一覧のページ)に新規作成ページへのリンクを貼り付けましょう。ターミナルで

ターミナル
$rails routes

と打ってみてください。すると、

ターミナル
     tasks  GET    /tasks(.:format)  tasks#index                                                              
         POST   /tasks(.:format)  tasks#create                                                              
   new_task GET    /tasks/new(.:format)  tasks#new                                                          
    edit_task GET    /tasks/:id/edit(.:format)   tasks#edit                                                   
         task GET    /tasks/:id(.:format)    tasks#show                                                       
              PATCH  /tasks/:id(.:format)    tasks#update                                                       
              PUT    /tasks/:id(.:format)     tasks#update                                                      
              DELETE /tasks/:id(.:format)      tasks#destroy 
  ...                                                    

と出てきたと思います。このtasks#newの左側にあるnew_taskにpathをくっつけたのがnewページに行くためのリンクになります。そのため、

app/views/tasks/index.html.erb
<h1>やること一覧</h1>

<%= link_to '新規作成', new_task_path %> #追加

<table>
  <thead>
    <tr>
      <th>やること</th>
      <th>詳細</th>
    </tr>
  </thead>

  <tbody>
    <% @tasks.each do |task| %>
    <tr>
      <td><%= task.title %></td>
      <td><%= task.body %></td>
    </tr>
    <% end %>
  </tbody>
</table>


これでtopページと新規作成機能は終わりです。

編集機能 ~edit,update~

続いて、編集機能を作成していきたいと思います。編集にはeditアクションとupdateアクションを使用します。コントローラーにこの2つのアクションを記述していきましょう。

app/controllers/tasks_controller.rb
class TasksController < ApplicationController
  def index
    @tasks = Task.all
  end

  def new
    @task = Task.new
  end

  def create
    @task = Task.new(task_params)
    if @task.save
      redirect_to tasks_path
    else
      render 'new'
    end
  end

 #ここから追加
  def edit
    @task = Task.find(params[:id])
  end

  def update
    @task = Task.find(params[:id])
    if @task.update(task_params) 
      redirect_to tasks_path #updateできたらindexページに行く
    else
      render 'edit' #できなかったらeditページのまま
    end
  end
 #ここまで

  private
    def task_params
      params.require(:task).permit(:title, :body)
    end
end

ここで新しく、find(params[:id])というコードが出てきましたね。これは投稿され情報に付けられたidを探し出すコードです。例えば、「ケーキを買う」という内容を編集したいのにボタン押したら隣に記述していた「部屋の掃除をする」という内容が画面に出てきたらもう訳わかんないですよね。ここではこのfind(params[:id])を使って編集したい内容を正しく呼び出せるようにしています。(ちなみにrailsというフレームワークを使っているからこの1行だけで情報を呼び出せるのであり、使はなければSQLという言語をゴリゴリ書く必要があります。railsすげえ。)

次にviewsファイルを作成していきましょう。

app/views/tasks/edit.html.erb
<h1>やること編集</h1>

<%= form_with model: @task do |f| %>
<div class="field">
  <%= f.label :やること %><br>
  <%= f.text_field :title %>
</div>
<div class="field">
  <%= f.label :詳細 %><br>
  <%= f.text_field :body %>
</div>
<div class="actions">
  <%= f.submit %>
</div>
<% end %>

<%= link_to '一覧へ', tasks_path %>

入力フォームなのでnew.html.erbと同じコードですね。最後に一覧画面に編集画面へのリンクを付け加えて終了です。

app/views/tasks/index.html.erb
<h1>やること一覧</h1>

<%= link_to '新規作成', new_task_path %>

<table>
  <thead>
    <tr>
      <th>やること</th>
      <th>詳細</th>
    </tr>
  </thead>

  <tbody>
    <% @tasks.each do |task| %>
    <tr>
      <td><%= task.title %></td>
      <td><%= task.body %></td>
      <td><%= link_to '編集', edit_task_path(task) %></td> #追加
    </tr>
    <% end %>
  </tbody>
</table>

pathはターミナルでrails routesと打って確認してみてください。(新規作成機能作る時に一回やったやつ。)edit画面へ遷移する時にはidが必要なので今この情報を持っているtaskを最後につけることを忘れずに。

これで編集機能は終わりです。

削除機能 ~destroy~

最後は削除機能を作成していきます。コントローラーにdestroyアクションを追加しましょう。

app/controllers/tasks_controller.rb
class TasksController < ApplicationController
  def index
    @tasks = Task.all
  end

  def new
    @task = Task.new
  end

  def create
    @task = Task.new(task_params)
    if @task.save
      redirect_to tasks_path
    else
      render 'new'
    end
  end

  def edit
    @task = Task.find(params[:id])
  end

  def update
    @task = Task.find(params[:id])
    if @task.update(task_params) 
      redirect_to tasks_path
    else
      render 'edit'
    end
  end

 #ここから追加
 def destroy
    @task = Task.find(params[:id])
    @task.destroy
    redirect_to tasks_path
  end
 #ここまで

  private
    def task_params
      params.require(:task).permit(:title, :body)
    end
end

ここではfind(params[:id])で削除する情報を見つけて、destroyで削除しています。
一覧画面に削除ボタンを付けましょう。

app/views/tasks/index.html.erb
<h1>やること一覧</h1>

<%= link_to '新規作成', new_task_path %>

<table>
  <thead>
    <tr>
      <th>やること</th>
      <th>詳細</th>
    </tr>
  </thead>

  <tbody>
    <% @tasks.each do |task| %>
    <tr>
      <td><%= task.title %></td>
      <td><%= task.body %></td>
      <td><%= link_to '編集', edit_task_path(task) %></td>
    <td><%= link_to '削除', task, method: :delete, data: { confirm: '本当に削除しますか?' } %></td>
    </tr>
    </tr>
    <% end %>
  </tbody>
</table>

ここではpath名ではなく、method: :deleteと記述することでdestroyアクションを呼びます。そして、data: { confirm: '本当に削除しますか?' }と記述することで確認のモーダルウィンドウを出すことができます。

最後に

お疲れ様です。以上でtodoアプリを作ることができました。たかがtodoアプリではありますが大事な要素が色々詰まっているので1つ1つ確認してみてください。

以上、最後まで読んでいただきありがとうございました!!!??

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

既存プロジェクトへのRidgepole導入

導入手順

Gemfile

gem 'ridgepole'

db/config.yml

development:
  adapter:  mysql2
  encoding: utf8
  database: xxxx_development
  pool:     5
  username: root
  timeout:  5000
  host:     127.0.0.1
  port:     3306

test:
  adapter:  mysql2
  encoding: utf8
  database: xxxx_test
  pool:     5
  username: root
  timeout:  5000
  host:     127.0.0.1
  port:     3306
  • スキーマファイル出力
bundle exec ridgepole -c db/config.yml --export -o db/Schemafile

マイグレーションの適用

  • diff/差分表示
bundle exec ridgepole --diff db/config.yml db/Schemafile
  • プレビュー
bundle exec ridgepole --config db/config.yml --file db/Schemafile --apply --dry-run
----------
Apply `db/Schemafile` (dry-run)
No change
  • 実適用
bundle exec ridgepole --config db/config.yml --file db/Schemafile --apply
----------
Apply `db/Schemafile`
No change
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

railsにSlimを導入する

Gemfile
gem 'slim-rails'
gem 'html2slim'
bundle

レイアウトファイルをSlimに変更する

bundle exec erb2slim app/views/layouts/ --delete
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

EC2で自動デプロイが反映されなくなったときの対処法

はじめに

現象

いつもどおり自動デプロイしたはずがまったく反映されない

やってみて

EC2インスタンスがおかしいのかなと思って再起動してみたら無事解決。
再起動に必要なコマンドを備忘します。

手順

EC2インスタンスを再起動

AWSコンソールからEC2インスタンスを再起動します。

DBを起動させる

EC2を再起動するとDBが停止状態になるので起動させます。
(私はMariaDBを使用しています)

確認コマンド

ターミナル(EC2)
$ sudo systemctl status mariadb

結果

ターミナル(EC2)
● mariadb.service - MariaDB database server
   Loaded: loaded (/usr/lib/systemd/system/mariadb.service; disabled; vendor preset: disabled)
   Active: inactive (dead)

死んじゃってるのが確認できます。


DB起動コマンド

ターミナル(EC2)
$ sudo systemctl start mariadb

これで確認するとActiveになっているはずです。

Nginxを起動させる

同じようにNginxも起動させます。

Nginx起動コマンド

ターミナル(EC2)
$ sudo systemctl start nginx

改めて自動デプロイを行う

これが本当に正解なのかわからないところがありますが(1度目の自動デプロイの内容をEC2が読み込めているなら、Unicornの起動だけでいいような気がします)

とりあえず自動デプロイを行えばEC2に反映されますし、Unicornも起動されるのでもう一度コマンドを打ちます。

ターミナル(ローカル)
% bundle exec cap production deploy

これにて一件落着です。

おわりに

スクリーンショット 2020-11-26 12.34.44.png

本当にこの画面が嫌いです。

✔︎

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

rubocopを導入してみた

始めに

rubocopはRubyで書いたコードが規則に従っているかを自動的にチェックしてくれるコーディングチェックツールです。Rubyはシンプルかつ多様な書き方が出来る言語なのでエンジニアによっては書き方にばらつきが生じます。そこで、rubocopを導入することで、複数人でコーディングをする場合にコードの可読性を保つことが出来るというメリットがあります。勿論一人で開発を進める時でも手軽にコード確認が行えますので、早速導入していきたいと思います。

rubocopのインストール

Gemfileに以下の二つを記述してからbundle installします。

Gemfile.
group :development do
  <snip>
  gem 'rubocop', require: false
  gem 'rubocop-rails'
end

これだけで導入が完了できました。

とりあえず使ってみる

このコマンドをアプリがあるディレクトリで実行するだけです。

rubocop

実行すると解析が始まり、ディレクトリ内のRubyで書かれたコードはチェックされます。

Inspecting 123 files
CCCCCCCCCCCCCCCWCCCCCCCCWCWCCCCCCCCCCCCCCCCCCCCCCCCCWCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCWCCCCCWCCCCCCCCCCCC

すごい数の警告が出てきました。

次はこの警告に対応してデバッグをしていきます。

rubocopの設定

以下のコマンドを実行します。

rubocop --auto-gen-config

するとこのように2つの設定ファイルが生成されるはずです。

Added inheritance from `.rubocop_todo.yml` in `.rubocop.yml`.

.rubocop.ymlはコーディングの規則、.rubocop_todo.ymlは修正するべきコードの部分を記述したものであると考えていいでしょう。

.rubocop.ymlの中をいじっていきます。

rubocop.yml
inherit_from: .rubocop_todo.yml

require:
  - rubocop-rails

AllCops:
  TargetRubyVersion: 2.7.1

  Exclude:
    - 'config.ru'
    - 'bin/**'
    - 'lib/**'
    - 'db/**/*'
    - 'config/**/*'
    - 'script/**/*'
    - !ruby/regexp /old_and_unused\.rb$/

AsciiComments:
  Enabled: false

Documentation:
  Enabled: false

ClassAndModuleChildren:
  Enabled: false

Style/FrozenStringLiteralComment:
  Enabled: false

Style/GuardClause:
  Enabled: false

この辺の設定は個人やチームによって異なると思います。

修正

rubocopには自動修正機能があります。以下のコマンド実行で修正が始まります。

rubocop -a
123 files inspected, 605 offenses detected, 605 offenses corrected

かなりの部分が修正されましたが、これで全部ではありません。.rubocop.todo.ymlに書かれた違反部分を消す→rubocop実行→修正のサイクルで進めていくことになると思います。

終わりに

簡単な解説でしたが、無事にrubocopを導入してコードのチェックが出来るようになりました。

参考にさせていただいた記事

Rubocop導入+手順とコマンドの備忘録

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】部分テンプレート(render)で別のコントローラーのビューを呼び出す方法

はじめに

違うファイルのビューを他のファイルのビューに表示さる際、部分的にそのビューをrenderで呼びだすことができます。
そんな、部分テンプレートの方法について紹介します。

目次

  1. 部分テンプレートファイルの名前について
  2. renderで呼び出す
  3. コントローラーへの定義

開発環境

ruby 2.6.5
rails 6.0.0

実装

それでは実装していきます〜

1. 部分テンプレートファイルの名前について

まず、部分テンプレートとしてビューファイルを作成する時は_index.html.erbのようにファイル名の前に_(アンダースコア)をつけます。

スクリーンショット 2020-11-23 5.40.44.png

今回はproductsコントローラーのビューを部分テンプレートとして使いたいので、products/_indexとなるようにファイル名を編集すます。

2. renderで呼び出す

次にテンプレートする側にrenderメッソドを記述します。
今回は以下の記述をします。

app/views/tops/index.html.erb
#省略
<%= render partial: "products/index" %>

構文はこちらです。

render partial: コントローラー名/ファイル名

partialオプションは部分テンプレートを呼び出す時に使います。ただ、強調しているだけ、つけなくても呼び出せます。

スクリーンショット 2020-11-23 5.48.40.png

これで完成!!と思ったのですが、今回@productsを定義してProductモデルからデータを取得してビューに表示してる為、topsコントローラーでも定義が必要です。

3. コントローラーへの定義

保存したデータを呼び出す場合コントローラーでインスタンス変数にデータを代入してビューで使用します。

productコントローラーで動かすのであればそちらに@productsを定義しますが、今回はtopsコントローラのアクションが動くのでそちらで定義する必要があります。

app/views/products/_index.html.erb
<h1>Products#index</h1>
<p>Find me in app/views/products/index.html.erb</p>

<div class="#">
  <% @products.each do |product|  %> #ここで@productsを定義してるので、コントローラーでも定義が必要です。
    <%= product.name %>
    <%= product.text %>
  <% end %>
</div>

このproducts/_index.html.erbが部分テンプレートとして呼び出されるので、呼び出された側のtops_contller.rbでも@products定義が必要です。

app/contollers/tops_contller.rb
class TopsController < ApplicationController
  def index
    @tops = Top.all
    @products = Product.all #部分テンプレートで_index.htmlを表示させる為の定義

  end

これで完成です!!!

まとめ

以上部分テンプレートの手順でした。
用途に応じて他にもやり方がありますが、今回はこの方法で実装してます。

最後に

私はプログラミング初学者ですが、自分と同じ様にエンジニアを目指す方々の助けになればと思い、記事を投稿しております。
それではまた次回お会いしましょう〜

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Cloud9でRuby on Railsの環境構築 ~インストールから起動まで~

はじめに

備忘録としてタイトル通り、Cloud9を使用したRuby on Railsの環境構築の手順を記載します。

独学でPHP・Laravelを学習しており、現在就活中なのでプロのエンジニアではありません。
転職希望の企業がRubyとRailsを使用した開発をメインとしているので、改めて学習が必要となりアウトプットも兼ねてカキコします。

Cloud9を選択した理由は以下の通りです。

  • 使用PCがwindows10のため、ローカルに入れるよりもLinux環境の方がエラーの際情報が多い
  • ポートフォリオのインフラにAWSを使っている為アカウントを持っていた
  • 基本無料で使用できる

なおこの記事は環境設定のハウツー的な構成ではなく、私が行った手順とエラーを時系列そのままで記載します。
「環境設定の方法を知りてえんだ!」って方はプロの記事を参考にしてください。

目標

AWSのCloud9にRuby on Railsをインストールし、アプリケーションを起動して「Yay! You’re on Rails!」を表示させる

環境

Amazon linux
ruby 2.6.3
Rails 6.0.3.4
MySQL 5.5.62(後々5.7にバージョン上げてエラーになる)
MySQL 5.5から5.7へのバージョンアップと、libmysqlclient.so.18: cannot open shared object file: No such file or directoryの解消

やってきましょう

バージョン確認

とりあえずRubyのバージョン確認。もともとインストールはされている模様。

$ ruby -v
ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-linux]

続いてRailsのバージョンも確認しておきましょう。

$ rails -v
Rails 5.0.0

Railsも入っているんですね。
でもバージョン5.0.0って、beta1が2015年12月18日にリリースされたらしくかなり古い様子。
バージョンアップしたいなあ。

$ gem install rails
$ rails -v
Rails 6.0.3.4

取り合えず最新版にアップデートできました。

そうしたらプロジェクトを作成するディレクトリを作ります。

名前は何でもいいので、ホームディレクトリに移動して"rails-training"を作成します。
そのあとcdコマンドでrails-trainingディレクトリに入り込みましょう。

$ cd
$ mkdir rails-training
$ cd rails-training

ここでMySQLのバージョン確認を思い出す。

$ mysql -v
ERROR 2002 (HY000): Can't connect to local MySQL server through socket'/var/lib/mysql/mysql.sock' (2)

エラーが出ました。
どうやらsocketファイルが無いのでMySQLに接続できていないらしい。

MySQLを再起動すると自動でsocketファイルが作成されるので、一度停止してもう一度起動してみる。
(restartでもいいけど、何となく気持ち悪いのでしっかり停止と起動を実行)

$ /etc/init.d/mysqld stop
$ /etc/init.d/mysqld start
touch: cannot touch ‘/var/log/mysqld.log’: Permission denied
chown: changing ownership of ‘/var/log/mysqld.log’: Operation not permitted
~略~
chown: changing ownership of ‘/var/lib/mysql’: Operation not permitted

パーミッションエラーですね、私には起動させる権限がないようです。

$ sudo /etc/init.d/mysqld start

ってことでsuduコマンドで強制実行。うまく再起動できました。

引き続き以下のコマンドで最低限のセキュリティ設定をします。MySQLの初期設定についてメモしておく
その後に忘れずMySQLの再起動。

$ mysql_secure_installation
~略(対話形式でパスワード等を設定)~
$ sudo /etc/init.d/mysqld restart

プロジェクト作成

ここまでやってやっとRaylsプロジェクトを作成。
カレントディレクトリが先ほど作ったrails-trainingであることを確認します。

RailsはデフォルトのデータベースがSQLiteに設定されているので、オプションで"-d mysql"を指定してあげます。

$ rails new training_app -d mysql
~略~
An error occurred while installing mysql2 (0.5.3), and Bundler cannot continue.
Make sure that `gem install mysql2 -v '0.5.3' --source 'https://rubygems.org/'` succeeds before bundling.
~略~

こんなエラーが発生しました。

「rails new」コマンドを実行すると、アプリ生成時に必要なパッケージを「RubyGems」で自動的にインストールする仕組みになっています。

さきほど、「-d mysql」オプションを指定したことにより、「gem install mysql2」コマンドが内部で実行されました。

ところが、「mysql2」というgemパッケージをインストールするには、「mysql-devel」というrpmパッケージがCentOSにインストールされていることが前提となっていたため、「mysql2」のインストールに失敗してしまったようです。
つまり、この問題を解決するには

yumで「mysql-devel」をインストール
gemで「mysql2」をインストール

という手順を踏む必要があります。
【Ruby on Rails環境構築】インストールから起動までの手順【MySQL】

という事なので、私はyumで「mysql-devel」をインストールしました。

加えて引用サイトに記載もありますが、先ほどの"rails new"コマンドは生きているので、プロジェクト自体はすでに作成されています。
ですので新しく"rails new"コマンドを実行する必要はありません。

$ sudo yum install -y mysql-devel
$ ls
app  bin  config  config.ru  db  Gemfile  lib  log  package.json  public  Rakefile  README.md storage  test  tmp  vendor

サーバー起動

プロジェクトディレクトリに移動してから実行します。

$ cd training_app
$ rails s
=> Booting Puma
=> Rails 6.0.3.4 application starting in development 
=> Run `rails server --help` for more startup options
Exiting
Traceback (most recent call last):
~略~
/home/ec2-user/.rvm/gems/ruby-2.6.3/gems/webpacker-4.3.0/lib/webpacker/configuration.rb:95:in `rescue in load': Webpacker configuration file not found /home/ec2-user/rails-training/training_app/config/webpacker.yml. Please run rails webpacker:install Error: No such file or directory @ rb_sysopen - /home/ec2-user/rails-training/training_app/config/webpacker.yml (RuntimeError)

rails webpacker:installを実行してくださいエラー:そのようなファイルまたはディレクトリはありません

ひとまず言われた通りに"rails webpacker:install"を実行。

$ rails webpacker:install
Yarn not installed. Please download and install Yarn from https://yarnpkg.com/lang/en/docs/install/

yarnがインストールされていないようなので、インストールしてから再度実行し、サーバーも起動。

$ brew install yarn
$ rails webpacker:install
$ rails s

ページにアクセスすると、以下のエラーが出て正常に表示されない。

To allow requests to **********.vfs.cloud9.ap-northeast-1.amazonaws.com, add the following to your environment configuration:

なにやらRails6からDNS再バインド攻撃対策として、自身からしかアクセスができない仕様になっているらしい。

config/application.rbのApplicationクラスにアクセスを許可する設定を記載すれば解決される様子。

<config/application.rb>

require_relative 'boot'

require 'rails/all'

# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)

module TrainingApp
  class Application < Rails::Application
    # Initialize configuration defaults for originally generated Rails version.
    config.load_defaults 6.0

    #以下一文を追加。末尾が"amazonaws.com"のドメインは許可する。
    config.hosts << ".amazonaws.com" 

    # Settings in config/environments/* take precedence over those specified here.
    # Application configuration can go into files in config/initializers
    # -- all .rb files in that directory are automatically loaded after loading
    # the framework and any gems in your application.
  end
end

これで表示されることを信じてサーバーを起動。

$ rails s

Mysql2::Error::ConnectionError
Access denied for user 'root'@'localhost' (using password: NO)

今度はMySQLのrootユーザーのパスワードが一致しないので、データベースにアクセスできないときた。
これはLaravelでも経験があるエラーなので落ち着いて対処。

Raylsではtraning_app/config/detabase.ymlにデータベース周りの設定を記載すればいいみたい。
私の環境だと17行にさっき対話形式で設定したMySQLのパスワードを書いていく。

<traning_app/config/detabase.yml>

~略~
default: &default
  adapter: mysql2
  encoding: utf8mb4
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: root
  password: ここにパスワードを記載
  socket: /var/lib/mysql/mysql.sock

development:
  <<: *default
  database: training_app_development
~略~

これでどうでしょうか、そろそろ勘弁してください。

$ rails s

ActiveRecord::NoDatabaseError
Unknown database 'training_app_development'

また出ました。
"training_app_development"なんてデータベース知りませんっておっしゃってます。

あれ?そもそもデータベース作ってないな。
ってことでデータベースを作ります。

$ mysql -u root -p
Enter password: 

> CREATE DATABASE test_table;
> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| test_table         |
+--------------------+

よしよし、データベースはできました。
後から知りましたが、Railsはdbコマンドでデータベースを作成した方がいいようです。

$ rake db:create

この作った"test_table"を使用するように設定します。

ひとつ前でパスワードを設定した"traning_app/config/detabase.yml"ファイルを眺めていると、エラー内容と同じ名前のデータベース名を発見。
多分ここに書けば反映されそう。

<traning_app/config/detabase.yml>

~略~
default: &default
  adapter: mysql2
  encoding: utf8mb4
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: root
  password: ここにパスワードを記載
  socket: /var/lib/mysql/mysql.sock

development:
  <<: *default
  database: ここにデータベース名を記載
~略~

これで完璧なはず。

$ rails s

**********.vfs.cloud9.ap-northeast-1.amazonaws.com で接続が拒否されました。

調べてみると画像の部分を押して別タブで開けば表示されるとの情報を発見。

名称未設定のデザイン (12).jpg

スクリーンショット 2020-11-26 184833.png

出来ました。
Yay! You’re on Rails!を表示できたので、とりあえず一件落着です。

MySQLのバージョンアップは以下の記事を参照。
MySQL 5.5から5.7へのバージョンアップと、libmysqlclient.so.18: cannot open shared object file: No such file or directoryの解消

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Rails]ユーザー新規登録時にメールを自動配信する機能を実装する[備忘録]

はじめに

先日、Ruby on Railsでアプリケーションを作成しました。
その中で、ユーザーに新規登録をしてもらうと同時に、ユーザーのメールアドレス宛にメールを送る機能を作りました。
ネットで調べつつ、試行錯誤しながら実装したのですが、色々な情報が入り乱れていて、少しだけ分かりにくかった印象があったので、備忘録も兼ねて、ここでまとめたいと思います。
それでは参りましょう!

メール送信機能を記述

config/environment/development.rb
#以下を追記
config.action_mailer.raise_delivery_errors = true
config.action_mailer.default_url_options = { :host => 'localhost:3000'}
config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
  :address => 'smtp.gmail.com',
  :port => 587,
  :domain => 'gmail.com',
  :user_name => "hoge@gmail.com",  # user_name, passwordはご自身のものに変更して下さい。
  :password => "hogehoge",
  :authentication => :plain,
  :enable_starttls_auto => true
}

メーラーを作成

メール送信におけるコントローラー的役割を果たすMailerを作成します。
rails g mailer <メーラー名> <メソッド名>

ターミナル
$ rails g mailer ThanxMailer complete_registration

このコマンドを打つことにより、app/mailers/application_mailer.rbapp/mailers/thanx_mailer.rbが生成されます。
次に、それらのテンプレートファイルを編集していきましょう!!

メーラーを編集

app/mailers/application_mailer.rb
class ApplicationMailer < ActionMailer::Base
  default from: '管理者'
  layout 'mailer'
end
app/mailers/thanx_mailer.rb
class ThanksMailer < ApplicationMailer
  def complete_registration(user)
    @user = user
    mail(:subject => "登録ありがとう!!!!", to: user.email)
  end
end

メール本文の作成

views/thanx_mailer/complete_registration.text.erb
<%= @user.name %> 様

会員登録ありがとう!!!!

コントローラへの記述

app/controllers/users_controller.rb
def create
  @user = User.new(user_params)
  if @user.save
    ThanxMailer.complete_registration(@user).deliver
  end
end

メーラーは、メーラー名.メソッド名と記述することで、クラスメソッドを呼び出すように実行できます。
実際の送信はdeliverメソッドが行います。

おわりに

メール送信機能自体は、記述する量も比較的少ないため、理解がしやすいと思います。
ただただコピペするだけでなく、一つ一つの意味をしっかり理解していきたいですね。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Formオブジェクトについて

Formオブジェクトとは?

 1つのフォーム送信で複数のモデルを操作したい場合や、テーブルに情報を保存しない情報に対するバリデーションを行いたい場合使うデザインパターンのこと。

実装手順

① 新たにformオブジェクト用のmedelを作成する

ターミナル
% rails g model モデル名

② form_withメソッドに対する機能とバリデーションを行う機能を持たせる。

test_form.rb
class TestForm

  include ActiveModel::Model

◯ include ActiveModel::Model
 この記述のように、ActiveModel::Modelをincludeすることで、form_withやrenderなどのヘルパーメソッドの引数として扱えたり、バリデーションの機能が使用できるようになる。
 この記述は「Formオブジェクト」を呼ばれる。

③ 保存したい複数のテーブルのカラム名全てを属性値として扱えるようにする。

test_form.rb
class TestForm

  include ActiveModel::Model
  attr_accessor :カラム名1, :カラム名2, ・・・

◯attr_accessor
 ゲッターとセッターを定義してくれるメソッド。与えられた引数をもとに属性を設定し、これを取得するメソッド(ゲッター)と更新するメソッド(セッター)を定義することができる。
 モデルに対応するテーブルのカラム名以外の属性を扱いたい場合は、attr_accessorを用いて追加する。

④ バリデーションを設定する。

test_form.rb
class TestForm

  include ActiveModel::Model
  attr_accessor :カラム名1, :カラム名2, ・・・

  validates :カラム名1, 各オプション
  validates :カラム名2, 各オプション

            or

  with_options 各オプション do
    validates :カラム名1
    valitates :カラム名2
  end

◯with_options
 同じオプションがある場合は、with_optionsを使用することで、validatesをまとめることができる。

 formオブジェクトで使用するモデルのバリデーションは、各モデルにバリデーションを記述するのではなく、formオブジェクトに記述する。
 理由としては、バリデーションが引っかかった場合に、各モデルにバリデーションを記述していると、引っかかった理由をうまく画面に表示できなくなるため。

⑤ データをテーブルに保存する処理を書く。

test_form.rb
class TestForm

  include ActiveModel::Model
  attr_accessor :カラム名1, :カラム名2, ・・・

  validates :カラム名1, 各オプション
  validates :カラム名2, 各オプション

            or

  with_options 各オプション do
    validates :カラム名1
    valitates :カラム名2
  end

 def save
    変数 = 外部のモデル名.create(カラム名1 保存したい値, カラム名2 保存したい値, ・・・)
    モデル名1.create(カラム名1 保存したい値, カラム名2 保存したい値, ・・・, カラム名: 変数.id
    モデル名2.create(カラム名1 保存したい値, カラム名2 保存したい値, ・・・, カラム名: 変数.id
)
  end

 保存したい複数のモデルに外部モデルに関するidを保存したい場合は、変数に外部モデルの情報を代入して、その変数のidを保存したいモデルに記述する。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Mysql2::Error: Table '○○_production.users' doesn't existを解消した話

表題が発生し、解消するまでの顛末をまとめました。原因は2つありました。

開発環境

ruby 2.6.5
Rails 6.0.3.2
capistrano 3.14.1
AWS EC2
webサーバーNginx
アプリケーションサーバーUnicorn

事の発端

データベース関連の修正を行ったため、本番環境(EC2)で一度テーブルを落としてから再作成しました。
そしてマイグレーションを行った際、表題のエラーが発生しました。結論、原因は2つありました。

原因その1 userを参照するreferencesメソッドが、userテーブルが作成される前に読み込まれていた

こちらの方がわかりやすくまとめられてます。
https://obel.hatenablog.jp/entry/20170719/1500456452

rails db:migrate:statusで確認すると、確かにテーブルのステータスがdownになっていました。
タイトルの通り、エラーメッセージの指摘は何ら矛盾がありませんでしたね。
ただ、開発環境ではエラーにはならず?アプリも正常に動いていたので、気づくのにかなり時間を費やしました。

こちらの記事の通り、マイグレーションファイルの日付を修正して、読み込まれる順序を変更しました。
そして、statusはupになりましたが、それでもエラー自体は解消されませんでした。

原因その2 本番環境でgit pull origin masterをしていなかった

結論、これによって上の作業がEC2上に反映され、ようやく解消に至りました。

いやもう思い違いをしていたというか、今なお十分な理解に至っていないのですが、githubへpush(作業ブランチで開発していた場合はmergeしてmasterへpullまで)したら、このコマンドは必要ないと思っていました。
実際、それで自動デプロイのコマンドを打てば、本番環境に機能を実装させることはできていました。

git pull origin masterは毎回必要なのだろうか?この辺りは宿題になっています。
git,githubは大分慣れたように思っていましたが、まだまだ知識が不足しているようです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rails 6.0~ DEPRECATION WARNINGが出力されないようにした。

rails6.0~から定数の読み込みにzeitwerkモードというものが使われるようになりました。
https://railsguides.jp/autoloading_and_reloading_constants.html
それに起因して、サーバーを起動した際に以下のようなwarningがログに出力されるようになりました。
このwarning邪魔なので消したいなあと思っていたところ、無事解決することができたので、同じことで困っている方たちのお役に立てれば良いなと思い、雑記します。

DEPRECATION WARNING: Initialization autoloaded the constants MyClass.

結論から述べると、このwarningが出る原因は、initializersディレクトリ内で自分で定義した定数(MyClass)を読み込んでいたことでした。
initializersディレクトリ内での定数の読み込みはリロード時には行われない。そして、自分で定義した定数はリロード時に変更を反映できる方が開発の便宜上良いので、initializersで読み込むべきではない、といったところでしょうか。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む