- 投稿日:2021-01-12T23:39:24+09:00
RailsでAjaxで「いいね!」機能を実装する。
何をしたか
Railsの課題を実施しています。その中で「いいね!」機能をAjaxで作成しましょうというタスクがありました。
実は以前、非同期通信ではない「いいね!」機能は作成したことがありました。▼その時の記録はこちら
Railsで「いいね!」機能を作る - ①アソシエーションに別名をつける
Railsで「いいね!」機能を作る - ②「いいね!」のcreateアクション
Railsで「いいね!」機能を作る - ③「いいね!」を解除できるようにするまた、別の記事で「いつかAjaxで”いいね!”機能を作ってみたい」と言いつつ、作っていなかったので、作成の手順をノートにまとめたいと思います。
なお、ここではメモ程度に実装手順を紹介しています。実際に詳しい実装の手順は↑上記の記事をご覧ください。
また、実装環境は以下の通りです。
Rails 5.2.3
Ruby 2.6.0
仕様確認・定義する
「いいね!」機能はアソシエーションの定義に若干ひねりが必要です。今回もまずはDB構造と仕様を確認します。その結果、DB構造と仕様は下記の通りでした。
※
images
テーブルは画面外にあります。
※他にもあるテーブルの中の一部を表示しています。このうち、「自分の投稿にいいね!できない」というのは、下記のように、自分の投稿の時にはビューに「いいね!」ボタンを表示しない、という形で実現しています。
また、
like_posts
と赤文字で書いてあるのは、この後記載するアソシエーションの別名です。
「アソシエーションの別名って???」という方は、こちらの記事に詳しく説明してあります。Railsで「いいね!」機能を作る - ①アソシエーションに別名をつける
マイグレーションの作成
では、ここから実装していきます。まずは、マイグレーションファイルを作成します。
users
テーブルとposts
テーブルはそれぞれ作成済という前提です。
likes
テーブルは下記のように作成します。db/migrate/XXXXXXXXXX_create_likes.rbclass CreateLikes < ActiveRecord::Migration[5.2] def change create_table :likes do |t| t.references :user t.references :post t.timestamps t.index [:user_id, :post_id], unique: true end end end「いいね!」は
likes
テーブルにデータをきちんと入力できれば実現されます。
(どうしてそうなるのかは、こちらの記事をご覧くださいませ^^)
Railsで「いいね!」機能を作る - ②「いいね!」のcreateアクションまた、
t.index [:user_id, :post_id], unique: trueの部分で、同じユーザー・同じ投稿への「いいね!」が投稿できないように、DB側で制御をしています。
アソシエーションの定義
posts
、users
、likes
のモデルファイルに、それぞれ下記のようにアソシエーションを記載しました。class User < ApplicationRecord # ★1 has_many :posts, dependent: :destroy # ★2 has_many :likes, dependent: :destroy has_many :like_posts, through: :likes, source: :post end class Post < ApplicationRecord # ★1 belongs_to :user # ★2 has_many :likes, dependent: :destroy has_many :users, through: :likes end class Like < ApplicationRecord belongs_to :post belongs_to :user end
User
とPost
の★1と★2の下の2行は、それぞれ
- ★1 ... ユーザーが投稿したpostに関する定義
- ★2 ... ユーザーが「いいね!」したpostに関する定義
です。
User
=>Like
=>Post
の流れに、:like_posts
とアソシエーションの別名をつけているのがポイントです。has_many :like_posts, through: :likes, source: :postこれで、
user = User.first user.like_posts↑この形で、ユーザーが「いいね!」した投稿一覧が取得できます。
コントローラーの定義
コントローラーの内容は以下の通りです。「いいね!」(
create
)と「いいね!解除」(destroy
)をそれぞれ以下のように定義します。likes_controller.rbclass LikesController < ApplicationController def create @post = Post.find(params[:post]) current_user.like(@post) end def destroy @post = Like.find(params[:id]).post current_user.unlike(@post) end endコード中にある
like
とunlike
はそれぞれ、「いいね!」と「いいね解除」を行う、User
のモデルメソッドです。Userのモデルメソッドの定義(
like
とunlike
)
User.rb
には、以下のモデルメソッドを定義します。models/user.rbclass User < ApplicationRecord has_many :posts, dependent: :destroy has_many :likes, dependent: :destroy has_many :like_posts, through: :likes, source: :post def own?(object) id == object.user_id end def like(post) likes.find_or_create_by(post: post) end def like?(post) like_posts.include?(post) end def unlike(post) like_posts.delete(post) end end
like?
はユーザーがすでにその投稿に「いいね!」しているかを判別するメソッドです。この後ビューで使うので載せています。また、
owm?
も今回の実装には直接関係がないのですが、この後ビューで使用しているので載せています。対象のオブジェクトの作成者を判別するメソッドです。like_posts.delete(post)何気に、
like_posts
でユーザーが「いいね!」したポスト一覧を取得してdestroyしているのも、ミソかなあと思っています。ビューの定義(非同期「ではない」実装の場合)
その後、ビューを書いていきます。まずは非同期ではない実装でビューを作成していきます。なお、読みやすさのため、装飾のための要素や機能は省いています。
- @posts.each do |post| - if logged_in? # ログイン確認 - if current_user.own?(post) # 所有を確認 = link_to post_path(post), method: :delete do = icon 'far', 'trash-alt' # ゴミ箱アイコン = link_to edit_post_path(post) do = icon 'far', 'edit' # 編集アイコン - else - if current_user&.like?(post) # すでにいいね!してるか確認 = link_to like_path(current_user.likes.find_by(post: post)), method: :delete do = icon 'fa', 'heart' # ハート(黒) - else = link_to likes_path(post: post), method: :post do = icon 'far', 'heart' # ハート(白)
logged_in?
はGemで生成されている、ログインしているかどうかを判別するメソッドです。実は、ここまでの実装で、「いいね!」機能自体はできています。「いいね!」ボタンを押して、画面をリロードすると「いいね!」と「いいね解除」がそれぞれ切り替えられているのがわかります。
▼「いいね!」を押してから、リロード(画面外のボタンを押下)すると、アイコンが切り替わっている
「いいね!」を非同期で実現する
さて、ここからが本題です。これら「いいね!」機能を非同期で実現していきます。
リンクを
remote: true
にするまずはリンクを
remote: true
にして、通信を非同期通信にします。- @posts.each do |post| - if logged_in? - # 省略 - else # それぞれ、link_toの後ろにremote: trueを追記 - if current_user&.like?(post) = link_to like_path(current_user.likes.find_by(post: post)), method: :delete, remote: ture do = icon 'fa', 'heart' - else = link_to likes_path(post: post), method: :post, remote: ture do = icon 'far', 'heart'Ajax用のビューファイルを作る - (1)ボタンの移動
上記のビューファイルのうち、さらに
like
とunlike
のボタンについては、それぞれ後に続くAjaxの処理用に、別のパーシャルに分ます。また、Ajaxの処理の目印となるように、id
属性も付与しています。- @posts.each do |post| - if logged_in? - # 省略 - else id="like-button-#{post.id}" # id属性を追記 - if current_user&.like?(post) = render 'likes/unlike_button', post: post # パーシャルへ移動 - else = render 'likes/like_button', post: post # 同上パーシャルの中身はこちらです。
views/likes/_unlike_button.html.slim= link_to like_path(current_user.likes.find_by(post: post)), method: :delete, remote: true do span.c-icon-button= icon 'fa', 'heart', class: 'fa-lg'views/likes/_like_button.html.slim= link_to likes_path(post: post), method: :post, remote: true do span.c-icon-button= icon 'far', 'heart', class: 'fa-lg'Ajax用のビューファイルを作る - (2)
.js.erb
ファイルを作る
likes
のcreate
、destroy
アクションに対応した.js.erb
ファイルをそれぞれ作成します。/views/likes/create.js.erb$("#like-button-<%= @post.id %>").html("<%= j(render 'unlike_button', post: @post) %>")/views/likes/destroy.js.erb$("#like-button-<%= @post.id %>").html("<%= j(render 'like_button', post: @post) %>")
.js.erb
ファイルについては、こちらの記事で解説していますので、よろしければご覧ください。remote: trueでajaxの投稿をPOSTをするよ。
完成!
非常に簡単なステップでしたが、上記の実装で、いいね!機能が非同期で実装できています
意外にあっさり、簡単ですね!感想...「いいね!」機能もRailsの機能でAjaxを作るのも実は3回目だったので、どちらもスルスルできてよかったです
- 投稿日:2021-01-12T23:16:31+09:00
ターミナルのコンソール内においてテストコードのエラーメッセージが表示されない時の対処法
きっかけ
items_controllers.rbit "郵便番号が空では登録できないこと" do @user_item.postal_code = nil binding.pry @user_item.valid? expect(@user_item.errors.full_messages).to include("Postal code can't be blank") endコントローラー内において、上記のような記述をして、ターミナルにてエラーメッセージを取得しようと思ったら、下のような結果が出た。
コンソール[1] pry(#<RSpec::ExampleGroups::UserItem::Nested::Nested_2>)> @user_item.errors.full_messages => []なぜ??
エラーメッセージが出ないということはバリデーションに問題があるのかと思ったが、原因はそこではなかった。結論
binding.pryの記述する場所が間違っていた。
or
binding.pryをかけてからエラーメッセージにたどり着くまでの順番を間違えていた。binding.pryをかけるとその箇所でPCの動きが止まるが、そこからコンソールで動かす場合は、順番が大切なのだった。むやみやたらにエラーメッセージをくれぇぇぇ!と言っても、パソコン様は動いてはくれない。
まずは、上記の場所にbinding.pryをかけた場合は、コンソール@item.valid?という記述をコンソール上でしなくてはいけなかった。
そして、返り値がfalseだった場合に、初めてコンソール@item.errors.full_messagesを記述することでエラーメッセージが返ってくるようになる。
まとめ
binding.pryをかけた場合は、むやみやたらにパラムスの値やエラーメッセージを求めるのではなく、通常のコントーラーでの動きと同じように順番を確認しながら進めるとコンソールは望みを叶えてくれる。
- 投稿日:2021-01-12T22:27:31+09:00
ActiveStorageを使う!
アウトプットに時間を使わなきゃと思いつつ中々やらずにいたので、重い腰をあげようかと…
今回は、毎度使っていてあれ?と振り返っていることなので自分が使っていることこちらに書き出してみようと思います。概要
- ActiveStorageとは
- 導入方法
- 使用方法
- 保存した画像を表示
- 画像加工のツールについて
- 感想
ActiveStorageとは
Active StorageとはAmazon S3、Google Cloud Storage、Microsoft Azure Storageなどの クラウドストレージサービスへのファイルのアップロードや、ファイルをActive Recordオブジェクトにアタッチする機能を提供します。development環境とtest環境向けのローカルディスクベースのサービスを利用できるようになっており、ファイルを下位のサービスにミラーリングしてバックアップや移行に用いることもできます。
アプリケーションでActive Storageを用いることで、ImageMagickで画像のアップロードを変換したり、 PDFやビデオなどの非画像アップロードの画像表現を生成したり、任意のファイルからメタデータを抽出したりできます。
画像アップロード機能が簡単に実装できるGemです。
ActiveStorageはRails5.2から標準で搭載されるようになりました。導入方法
- Active Record モデルを用意する まず、導入したいアプリケーションのディレクトリで
rails active_storage:installrails active_storage:installコマンドを実行すると、Active Storageに関連したマイグレーションが作成されます。
続けてマイグレートします。rails db:migrate生成されるテーブルは以下の二つになります。
画像用のカラムを用意する必要がない点もActive Storageの特徴の一つです。active_storage_blobs
カラム名 保存内容 id ID key ファイルを一意に識別するkey filename アップロードしたファイルの名前 content_type ファイルの種類 metadata メタデータ(画像なら縦横の大きさなどが格納される) byte_size ファイルサイズ(byte単位) checksum チェックサム created_at 作成日時 active_storage_attachments
カラム名 保存内容 id ID name モデルの属性名(Userモデルのavatarとか) record_type モデル名(Userとか) record_id record_typeのモデルのID blob_id active_storage_blogsのID created_at 作成日時 使用方法
例としてpostテーブルをつかいます。
やることは
- Active Storageのテーブルとpostテーブルのアソシエーションを定義
- post_controller.rbにて、imageカラムの保存を許可
モデルに1つの画像を添付するには、has_one_attachedを使います。
class モデル < ApplicationRecord has_one_attached :ファイル名 endhas_one_attachedメソッド
各レコードとファイルを1対1の関係で紐づけるメソッドです。
has_one_attachedメソッドを記述したモデルの各レコードは、それぞれ1つのファイルを添付できます。
has_one_attached:ファイル名は、
:photo、:avatar、:hogeなど、ファイルの用途に合わせて好きなものを指定してください。画像の保存を許可するストロングパラメーター
private def post_params params.require(:post).permit(:image).merge(user_id: current_user.id) endストロングパラメーターについて割愛
これでイメージ画像の保存を許可できました。保存した画像を表示
- image_tagメソッド
img要素を生成するRailsのヘルパーメソッドです。
image_tagメソッドでは、複雑なRailsのディレクトリパスを指定しなくても、モデルから画像ファイルを呼び出して引数に記述するだけで、画像を表示するimg要素を生成します。つまり簡単。
# ファイルをモデルから指定する場合 <%= image_tag モデル.画像ファイル %> <%= image_tag user.avatar %> # app/assets/ディレクトリ下の画像ファイルパスでも指定できる <%= image_tag 画像ファイルのパス %> <%= image_tag "avatar.png" %>自談(自分体験談そして余談)
image_tagの存在忘れて一生懸命画像表示しようとしていたw
この場合のpostモデルから引っ張ってもimage自体は他テーブルにあるため、下記のような表示にしかならなかった。#<ActiveStorage::Attached::One:0x00007f8ba7b765f8>どうようにしてオプションもある。
<%= image_tag post.image, class: 'post-image' if post.image.attached? %>attached?メソッド
レコードにファイルが添付されているかどうかで、trueかfalseを返すメソッドです。
モデル.ファイル名.attached?用途としては必ず画像を表示する記述が読み込まれるため、画像が存在しない場合にはエラーが起きてしまうので、こちらのメソッドを使い
if文を使って工夫することで画像が存在する場合にのみ画像を読み込む記述を読み込まれるようにすることができる。
**クラス属性もつけられます。画像加工のツールについて
- ImageMagick
- MiniMagick
- ImageProcessing
ImageMagick
コマンドラインから画像に処理を加えることができるツールです。
処理としては、画像の作成やサイズ変更、保存形式の変更などがあります。ImageMagickはGemではなく、ソフトウェアです。
そのため、Homebrewからインストールします。GemではないImageMagickをRubyやRailsで扱うには、MiniMagickというGemが必要となります。
brew install imagemagickこちらは一度入れれば問題ないのですでに入っている場合は、とばしてOK。
MiniMagick
ImageMagickの機能をRubyで扱えるようにしてくれるGemです。
RailsでImageMagickを扱うために必要となります。ImageProcessing
MiniMagickでは提供できない、画像サイズを調整する機能を提供するGemです。
# Gemfileの一番下に記述する gem 'mini_magick' gem 'image_processing', '~> 1.2'bundle installgemの更新をしたので、サーバーの立ち上げを忘れずに。
こちらを実装するとvariantメソッドを使用することで、ファイルの表示サイズを指定できます。
モデル.ファイル名.variant(resize: '幅x高さ')感想
記録として残すつもりで書きました。
最後グダッてしまいましたが、文章力や伝え方について改善点があればコメントお願いします。
他、書いてあることに間違いがあれば申し訳ないです。ご指摘お願いします。
このほかにもS3を使う方法なんかをよく見かけますがこちらは実際に試したあと、追記で残そうかなと思っています。
以上、ありがとうございました。参考URL
https://qiita.com/hmmrjn/items/7cc5e5348755c517458a
https://qiita.com/kimuray/items/3335a87d3488be340374
https://qiita.com/sibakenY/items/e550166970e84d96e16e
https://railsguides.jp/active_storage_overview.html#active-storage%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6
- 投稿日:2021-01-12T22:22:44+09:00
【Rails】created_atを使って「〇〇分前」と表示したい!
概要
本記事ではRailsのcreated_atで作成日時を表示する際に、従来の時間表記ではなく「〇〇分前」のように表示する方法を記載しています。
完成イメージは以下の通りです。
開発環境
- Ruby 2.6.5p114
- Rails 6.0.3.4
手順
① 日本語設定をする
まずは、日本語設定をします。
configのapplication.rbファイルに以下を記述してくださいconfig/application.rbconfig.i18n.default_locale = :ja② 日本語のyamlファイルを作成する
次に、どのような日本語表記にするかyamlファイルに設定を書いていきます。
日本語に関するja.ymlはRailsにデフォルトで入っていないため、以下を参考に作成してください。
ja.ymlファイルはconfig/locales配下に作成します。config/locales/ja.ymlja: datetime: distance_in_words: about_x_hours: one: 約1時間 other: 約%{count}時間 about_x_months: one: 約1ヶ月 other: 約%{count}ヶ月 about_x_years: one: 約1年 other: 約%{count}年 almost_x_years: one: 1年弱 other: "%{count}年弱" half_a_minute: 30秒前後 less_than_x_seconds: one: 1秒以内 other: "%{count}秒未満" less_than_x_minutes: one: 1分以内 other: "%{count}分未満" over_x_years: one: 1年以上 other: "%{count}年以上" x_seconds: one: 1秒 other: "%{count}秒" x_minutes: one: 1分 other: "%{count}分" x_days: one: 1日 other: "%{count}日" x_months: one: 1ヶ月 other: "%{count}ヶ月" x_years: one: 1年 other: "%{count}年"③ ビューで表示する
最後にビューで表示します。
表示する際は、Railsが用意してくれているtime_ago_in_words
メソッドを使用します。
引数には作成日時を渡します。index.html.erb<p>#{time_ago_in_words(post.created_at)}前にアウトプット</p> ※postの部分は適宜書きかえてください
以上で実装が完了しました!
表示方法を自分好みに修正したい時はja.yml
にて修正してください。
- 投稿日:2021-01-12T22:22:44+09:00
【Rails】created_atを使って「〇〇分前」という表記にしたい!
概要
本記事ではRailsのcreated_atで作成日時を表示する際に、従来の時間表記ではなく「〇〇分前」のように表示する方法を記載しています。
完成イメージは以下の通りです。
開発環境
- Ruby 2.6.5p114
- Rails 6.0.3.4
方法
① 日本語設定をする
まずは、日本語設定をします。
configのapplication.rbファイルに以下を記述してくださいconfig/application.rbconfig.i18n.default_locale = :ja② 日本語のyamlファイルを作成する
次に、どのような日本語表記にするかyamlファイルに設定を書いていきます。
日本語に関するja.ymlはRailsにデフォルトで入っていないため、以下を参考に作成してください。
ja.ymlファイルはconfig/locales配下に作成します。config/locales/ja.ymlja: datetime: distance_in_words: about_x_hours: one: 約1時間 other: 約%{count}時間 about_x_months: one: 約1ヶ月 other: 約%{count}ヶ月 about_x_years: one: 約1年 other: 約%{count}年 almost_x_years: one: 1年弱 other: "%{count}年弱" half_a_minute: 30秒前後 less_than_x_seconds: one: 1秒以内 other: "%{count}秒未満" less_than_x_minutes: one: 1分以内 other: "%{count}分未満" over_x_years: one: 1年以上 other: "%{count}年以上" x_seconds: one: 1秒 other: "%{count}秒" x_minutes: one: 1分 other: "%{count}分" x_days: one: 1日 other: "%{count}日" x_months: one: 1ヶ月 other: "%{count}ヶ月" x_years: one: 1年 other: "%{count}年"③ ビューで表示する
最後にビューで表示します。
表示する際は、Railsが用意してくれているtime_ago_in_words
を使用します。
引数には作成日時を渡します。index.html.erb<p>#{time_ago_in_words(post.created_at)}前にアウトプット</p> ※postの部分は適宜書きかえてください
以上で実装が完了しました!
表示方法を自分好みに修正したい時はja.yml
にて修正してください。
- 投稿日:2021-01-12T22:22:44+09:00
【Rails】created_atを使って「〇〇分前」と表示させたい!
概要
本記事ではRailsのcreated_atで作成日時を表示する際に、従来の時間表記ではなく「〇〇分前」のように表示する方法を記載しています。
完成イメージは以下の通りです。
開発環境
- Ruby 2.6.5p114
- Rails 6.0.3.4
手順
① 日本語設定をする
まずは、日本語設定をします。
configのapplication.rbファイルに以下を記述してくださいconfig/application.rbconfig.i18n.default_locale = :ja② 日本語のyamlファイルを作成する
次に、どのような日本語表記にするかyamlファイルに設定を書いていきます。
日本語に関するja.ymlはRailsにデフォルトで入っていないため、以下を参考に作成してください。
ja.ymlファイルはconfig/locales配下に作成します。config/locales/ja.ymlja: datetime: distance_in_words: about_x_hours: one: 約1時間 other: 約%{count}時間 about_x_months: one: 約1ヶ月 other: 約%{count}ヶ月 about_x_years: one: 約1年 other: 約%{count}年 almost_x_years: one: 1年弱 other: "%{count}年弱" half_a_minute: 30秒前後 less_than_x_seconds: one: 1秒以内 other: "%{count}秒未満" less_than_x_minutes: one: 1分以内 other: "%{count}分未満" over_x_years: one: 1年以上 other: "%{count}年以上" x_seconds: one: 1秒 other: "%{count}秒" x_minutes: one: 1分 other: "%{count}分" x_days: one: 1日 other: "%{count}日" x_months: one: 1ヶ月 other: "%{count}ヶ月" x_years: one: 1年 other: "%{count}年"③ ビューで表示する
最後にビューで表示します。
表示する際は、Railsが用意してくれているtime_ago_in_words
メソッドを使用します。
引数には作成日時を渡します。index.html.erb<p>#{time_ago_in_words(post.created_at)}前にアウトプット</p> ※postの部分は適宜書きかえてください
以上で実装が完了しました!
表示方法を自分好みに修正したい時はja.yml
にて修正してください。
- 投稿日:2021-01-12T19:05:12+09:00
[備忘録]本番環境への更新(AWS EC2)
前提
- Railsでアプリケーションを作成している
- AWSの初期設定が完了しており、EC2のインスタンスなども起動し、既にアプリケーションをデプロイしている
- Capistranoによる自動デプロイ設定が完了している
本記事の目的
- 作成したアプリケーションにローカルで変更を加えたので、AWSで本番環境にも変更を反映させたい
- herokuでのプッシュ方法をいつも忘れてしまうので、備忘録代わりに投稿しておきたい
手順
1.ローカル環境での変更をgithub上のmasterブランチへプッシュ
2.EC2へログイン
terminal~ % cd .ssh .ssh % ssh -i example.pem ec2-user@更新したいアプリケーションのElastic IP3.EC2内のアプリケーションのリポジトリへ移動
terminal[ec2-user@ip-○○○-○○-○○-○○ ~]$ cd /var/www/アプリケーション名4.現在動いているサーバーを落とす
terminal[ec2-user@ip-○○○-○○-○○-○○ リポジトリ名]$ ps aux | grep unicorn ↓ 「unicorn master -c」の文字があるプロセスIDを探す ↓ kill プロセスID ↓ exit を実行し、EC2インスタンスからログアウト5.ローカルの更新したいアプリケーションのディレクトリにて自動デプロイを実行
terminalアプリケーション名 % bundle exec cap production deploy6.Elastic IPよりアクセスし。変更が反映されているか確認
以上。
- 投稿日:2021-01-12T18:48:55+09:00
Rails APIモード + devise_token_auth + Vue.js 3 で認証機能付きのSPAを作る(Rails編)
はじめに
本記事はAPIをRailsのAPIモードで開発し、フロント側をVue.js 3で開発して、認証基盤にdevise_token_authを用いてトークンベースの認証機能付きのSPAを作るチュートリアルになります。
RailsやVue.jsの環境構築には触れませんのであしからず。
Rails newでAPI作成
今回はsample-apiというプロジェクト名とします。
$ rails new sample-api -d postgresql -T --api $ cd sample-api $ rails db:createDBはPostgreSQLを使用し、testディレクトリの生成をスキップし、APIモードでRailsアプリの雛形を作成するように実行します。
--api を指定することで、以下の設定がなされます。
・利用するミドルウェアを通常よりも絞り込んでアプリケーションを起動するよう設定します。特に、ブラウザ向けアプリケーションで有用なミドルウェア(cookiesのサポートなど)を一切利用しなくなります。
・ApplicationControllerを、通常のActionController::Baseの代わりにActionController::APIから継承します。ミドルウェアと同様、Action Controllerモジュールのうち、ブラウザ向けアプリケーションでしか使われないモジュールをすべて除外します。
・ビュー、ヘルパー、アセットを生成しないようジェネレーターを設定します。
deviseおよびdevise_token_authのインストール
Gemfileを編集します。
gem 'jbuilder' gem 'rack-cors' gem 'devise' gem 'devise_token_auth' $ bundlejbuilderおよびrack-corsはデフォルトでコメントアウトされているので、それを外せばOKです。
簡単に使用意図を説明すると、jbuilderはRailsでJSONをシンプルに扱うため、rack-corsはCORS(Cross Origin Resource Sharing)の設定を簡単に行うために導入しています。
CORSとはなんぞや?については徳丸浩さんのYou Tubeが詳しかったです。
CORSの原理を知って正しく使おうbundle installが終了したら、以下のコマンドを実行します。
sample-api $ rails g devise:install sample-api $ rails g devise_token_auth:install User auth上記のコマンドを実行すると、deviseのinitializerファイルやlocaleファイルが作成されます。
deviseの詳しい使い方については以下の記事が非常に詳しいです。次に作成されたmigrationファイルを修正します。
## Rememberable t.datetime :remember_created_at # 追加 ## Trackable t.integer :sign_in_count, default: 0, null: false t.datetime :current_sign_in_at t.datetime :last_sign_in_at t.string :current_sign_in_ip t.string :last_sign_in_ip ## Confirmable t.string :confirmation_token t.datetime :confirmed_at t.datetime :confirmation_sent_at t.string :unconfirmed_email # Only if using reconfirmable修正したら以下のコマンドを実行します。
$ rails db:migraterack-corsの設定
config/initializers/cors.rbを以下のように編集します。
Rails.application.config.middleware.insert_before 0, Rack::Cors do allow do origins '*' resource '*', headers: :any, expose: ['access-token', 'expiry', 'token-type', 'uid', 'client'], methods: [:get, :post, :options, :delete, :put, :patch, :head] end end上記の設定を行うことで、異なるオリジン(localhost:4200 から localhost:3000への通信等)間での通信を行うことができるようになります。
APIの疎通確認を行う
デフォルトの設定だと、リクエスト毎にtokenが更新されてしまうので、configをいじります。
# config/initializers/devise_token_auth.rb # By default the authorization headers will change after each request. The # client is responsible for keeping track of the changing tokens. Change # this to false to prevent the Authorization header from changing after # each request. config.change_headers_on_each_request = false # <= コメントアウトを外して、trueからfalseに修正する # By default, users will need to re-authenticate after 2 weeks. This setting # determines how long tokens will remain valid after they are issued. config.token_lifespan = 2.weeks # <= コメントアウトを外すここまでできたら、認証機能を試してみましょう。
まずは以下のコマンドを実行してください。
$ rails c > User.create!(name: 'テストユーザー', email: 'test-user+1@example.com', password: 'password')次に、rails s コマンドでローカルサーバーを起動し以下のcurlコマンドを実行します。
$ curl -D - localhost:3000/auth/sign_in -X POST -d '{"email":"test-user+1@example.com", "password":"password"}' -H "content-type:application/json"ログインに成功すると、以下のような値が返ると思います。
HTTP/1.1 200 OKn" X-Frame-Options: SAMEORIGIN X-XSS-Protection: 1; mode=block X-Content-Type-Options: nosniff X-Download-Options: noopen X-Permitted-Cross-Domain-Policies: none Referrer-Policy: strict-origin-when-cross-origin Content-Type: application/json; charset=utf-8 access-token: q1wL0eAS3IwKGcs5-8vEyA token-type: Bearer client: sd74van0pd3Sxs4O-fowvQ expiry: 1641540499 uid: book-clip-test1@gmail.com ETag: W/"12ac3053b26f91ca234280ac13a0790c" Cache-Control: max-age=0, private, must-revalidate X-Request-Id: 707fe01b-d25a-4167-b0f2-95e009c9271a X-Runtime: 0.403161 Vary: Origin Transfer-Encoding: chunked {"data":{"id":1,"email":"test-user+1@example.com","provider":"email","uid":"test-user+1@example.com","allow_password_change":false,"name":"テストユーザー"}}access-tokenやclientの値はログインしているかどうか、の判定に用いることになります。
curlで指定しているオプションについて説明しておくと、
-D - # レスポンスヘッダーを表示する。 -X # HTTPメソッドの指定に用いる -d # POSTリクエストでRequest bodyにdataを入れて送信 -H # ヘッダ情報の付与を行っています。
tokenの検証を行う
devise_token_authは、token検証のための機能も提供しています。
先ほど取得したtokenを活用して、以下のcurlリクエストを送ってみます。
$ curl -D - -H "access-token:取得したtoken" -H "client:取得したclient" -H "expiry:取得したexpiry" -H "uid:取得したuid" -H "content-type:application/json" localhost:3000/auth/validate_token成功すると以下のレスポンスが返る
HTTP/1.1 200 OK X-Frame-Options: SAMEORIGIN X-XSS-Protection: 1; mode=block X-Content-Type-Options: nosniff X-Download-Options: noopen X-Permitted-Cross-Domain-Policies: none Referrer-Policy: strict-origin-when-cross-origin Content-Type: application/json; charset=utf-8 access-token: q1wL0eAS3IwKGcs5-8vEyA token-type: Bearer client: sd74van0pd3Sxs4O-fowvQ expiry: 1641540499 uid: book-clip-test1@gmail.com ETag: W/"f3e45c8f2942619bd67981aead0bc740" Cache-Control: max-age=0, private, must-revalidate X-Request-Id: 0b9e57df-1f3b-4597-9c0f-01a6b3f904be X-Runtime: 0.086486 Vary: Origin Transfer-Encoding: chunked {"success":true,"data":{"id":1,"provider":"email","uid":"test-user+1@example.com","allow_password_change":false,"name":"テストユーザー"}}scaffoldで簡単なCRUDを行う
deviseを用いているので、authenticate_user!メソッドが使えるようになります。
なので、scaffoldを用いて作成したcontrollerにauthenticate_user!メソッドを記述して、
きちんと認証判定が行われるかをテストしてみましょう。$ rails g scaffold Post title:string body:text user:references Running via Spring preloader in process 21 invoke active_record create db/migrate/20210112131218_create_posts.rb create app/models/post.rb error rspec [not found] # Rspecを導入していないため invoke resource_route route resources :posts invoke scaffold_controller create app/controllers/posts_controller.rb error rspec [not found] # Rspecを導入していないため invoke jbuilder create app/views/posts create app/views/posts/index.json.jbuilder create app/views/posts/show.json.jbuilder create app/views/posts/_post.json.jbuilderいくつか編集を加えます。
# app/controllers/posts_controller.rb class PostsController < ApplicationController before_action :set_post, only: [:show, :update, :destroy] before_action :authenticate_user! # authenticate_user!を各アクション実行前に呼ばれるようにする def index @posts = Post.all end def show end def create @post = Post.new(post_params) if @post.save render :show, status: :created # locationを削除 else render json: @post.errors, status: :unprocessable_entity end end def update if @post.update(post_params) render :show, status: :ok # locationを削除 else render json: @post.errors, status: :unprocessable_entity end end def destroy @post.destroy end private def set_post @post = Post.find(params[:id]) end def post_params params.permit(:title, :body).merge(user: current_user) # require を削除し、current_userをmerge end end# config/routes.rb scope format: 'json' do # json形式のリクエストに対応 resources :users do resources :posts end end# app/models/user.rb has_many :posts, dependent: :destroy# app/views/_post.json.jbuilder json.extract! post, :id, :title, :body, :user_id, :created_at, :updated_at # json.url post_url(post, format: :json)を削除するこの状態でマイグレーションを実行してください。
$ rails db:migrate先ほどと同様、curlコマンドで動作確認をしてみます。
# createアクション $ curl localhost:3000/users/1/posts -X POST -d '{"title":"レビュー", "body":"面白い"}' \ -H "content-type:application/json" \ -H "access-token:取得したaccess-token" \ -H "client:取得したclient" \ -H "expiry:取得したexpiry" \ -H "uid:取得したuid" {"id":1,"title":"レビュー","body":"面白い","user_id":1,"created_at":"2021-01-12T22:27:28.290+09:00","updated_at":"2021-01-12T22:27:28.290+09:00"} # indexアクション $ curl localhost:3000/users/1/posts -H "content-type:application/json" \ -H "content-type:application/json" \ -H "access-token:取得したaccess-token" \ -H "client:取得したclient" \ -H "expiry:取得したexpiry" \ -H "uid:取得したuid" [{"id":1,"title":"レビュー","body":"面白い","user_id":1,"created_at":"2021-01-12T22:26:53.244+09:00","updated_at":"2021-01-12T22:26:53.244+09:00"}]きちんと認証をパスしていて、正常にjsonを取得することができました。
次回はVue.js側を実装していきます。
- 投稿日:2021-01-12T17:27:52+09:00
RailsアプリをHerokuにデプロイする
個人の忘備録になります。基本操作はすべて、macOSのターミナルで行います。
1.Heroku CLIをインストール
ターミナルを開き、下記のコマンドを入力します。
% brew tap heroku/brew && brew install herokuインストールが終了したら、完了を確認するために下記のコマンドを入力します。
ここでは、heroku/x.y.z出力に表示されるはずです。% heroku --version heroku/7.0.0 (darwin-x64) node-v8.0.02.ログイン
CLIをインストールした後、heroku loginコマンドを実行します。メールアドレスやパスワードをもとめられるので、入力していきます。
% heroku login => Enter your Heroku credentials. # メールアドレスを入力し、エンターキーを押す => Email: # パスワードを入力して、エンターキーを押す => Password:Logged in as メールアドレス と出力されるとログイン成功です。
3.Heroku上にアプリケーションを作成
% cd デプロイしたいアプリの場所アプリ作成の際にアプリ名は一意でなければいけないので、他の人がすでに使っている名前だとエラーが表示されます。
またアプリ名がドメインの一部となるので、ドメインに使えない文字列は利用できません。
以上2点に気をつけて、以下のコマンドを入力します。% heroku create アプリケーション名正しく設定できたことを確認する場合は、以下を入力します。
% git config --list | grep heroku入力後、 fatal: not in a git directory 以外が表示されていれば成功です。
4.MySQLを使用する場合
Herokuでは、使用するデータベースの設定が、デフォルトでPostgreSQLになっています。MySQLを使うためには、ClearDBというデータベースサービスが提供しているアドオンを追加することによって、HerokuでMySQLを使用できるようになります。
下記のコマンドを入力して、ClearDBアドオンを追加します。
% heroku addons:add cleardbRuby on Railsを使う場合は、MySQLに対応するGemについて考慮する必要があり、そちらの設定を変更します。
まず、下記のコマンドを入力してください。% heroku_cleardb=`heroku config:get CLEARDB_DATABASE_URL`これでClearDBデータベースのURLを変数heroku_cleardbに格納できました。
続いて、下記のコマンドを入力します。% heroku config:set DATABASE_URL=mysql2${heroku_cleardb:5}5.Heroku上にmaster.keyを設置
Heroku上には、環境変数(OSが提供するデータ共有機能の1つで、「どのディレクトリ・ファイルからでも参照できる変数」)としてmaster.keyの値を設置します。
まず、下記のコマンドを入力してください。% heroku config:set RAILS_MASTER_KEY=`cat config/master.key`設定が正しくできているか、Herokuの環境変数一覧を表示するには、以下のコマンドを入力します。
% heroku config入力後、RAILS_MASTER_KEY: という項目がある場合、成功です。
6.アプリケーションをHerokuへ追加
Herokuへコミットをプッシュすることで、Herokuにアプリケーションの情報が追加できます。以下のコマンドを入力します。
% git push heroku master7.Heroku上でマイグレーションファイルを実行
データベースにはマイグレーションの情報が反映されていませんので、heroku runを頭につけて、マイグレーションを実行してください。
% heroku run rails db:migrate8.アプリケーションの情報を確認
これまでの作業で、Herokuに反映したアプリケーションの情報を確認するには、以下のコマンドを入力します。
% heroku apps:info9.アプリケーションを開く
% heroku open問題なく、アプリケーションが開けましたらデプロイ完了です。
おまけ1:ログのチェック
アプリが適切に機能しないような問題が発生した場合は、ログをチェックします。--tailをつけることでログの最終10行のみ表示にすることができます。
% heroku logs --tail --app アプリケーション名おまけ2:環境変数が更新されない場合の対処法
ファイルの変更履歴が存在する場合
Herokuにプッシュしたときから少しでもファイルに差分が存在する場合は、通常通りコミットして、そのコミットをHerokuにプッシュすれば問題ありません。以下のコードを上から順に実行していきます。
% git add . % git commit -m "後から見てわかりやすいコミット名" % git push heroku masterファイルの変更履歴が存在しない場合
特に変更するファイルもないがHerokuに設定した環境変数だけ本番環境に反映させたい場合は、上記の方法ですと、「Everything up-to-date(すでに最新の状態に更新されています)」と表示されます。したがって、意図的に空のコミットを作成してHerokuにプッシュする方法を行います。以下のコードを上から順に実行していきます。
% git commit --allow-empty -m "空のcommit" % git push heroku master
- 投稿日:2021-01-12T17:23:55+09:00
【Rails6】Rubocop警告一覧
はじめに
転職活動用ポートフォリオ作成中です。
今回、CircleCiを導入したことにより、push時にRubocopでコードのスタイルチェックが行われるようになり、そこで指摘されるエラーをまとめていきたいと思いました。環境
Ruby on Rails '6.0.0'
Ruby '2.6.5'①Layout/ExtraSpacing: Unnecessary spacing detected.
不必要な、スペースが確認されたためです
app/models/company.rbvalidates_format_of :password, on: :create, with: PASSWORD_REGEX, message: 'には英字と数字の両方を含めて設定してください'今回は少しわかりにくいですが、「on: :create」と「with: ~」の間に1文字分余計なスペースがあったため、警告が出た模様です。
②Layout/EmptyLineBetweenDefs: Use empty lines between method definitions.
メソッドの定義文が1つの空の行で区切られているかどうかをチェックしています。
def a end def b end「def a」と「def b」の間に空白を開けていなかったため発生した模様です。
③Layout/TrailingWhitespace: Trailing whitespace detected.
該当部分の末尾に余計な空白が存在しているため、警告が出ました。
終わりに
bundle exec rubocop -a上記コマンドを使用すれば、勝手に修正してくれるみたいですが、
なぜか意味を調べたくなったので、投稿しました。気が向けば新たな警告文が出た場合は、投稿していきます。。。。
- 投稿日:2021-01-12T16:43:20+09:00
[備忘録]本番環境への更新(heroku)
前提
- Railsでアプリケーションを作成している
- heroku CLIのインストールが済んでいる
本記事の目的
- 作成したアプリケーションにローカルで変更を加えたので、herokuで本番環境にも変更を反映させたい
- herokuでのプッシュ方法をいつも忘れてしまうので、備忘録代わりに投稿しておきたい
手順
1.ローカル環境での変更をgithub上のmasterブランチへプッシュ
2.ターミナル上からherokuへログインterminal% heroku login --interactive ↓ heroku: Enter your login credentials Email [example@example.com]: example@example.com Password: ******** Logged in as example@example.com どのディレクトリで実行しても問題ないが、更新したいアプリのディレクトリ上で実行するのが無難だと思う。
3.アプリケーションをherokuにプッシュするterminal% git push heroku master 更新したいアプリのディレクトリ上で実行
4.もしデータベース関連でも変更がある場合は以下も実行するterminal% heroku run rails db:migrate 更新したいアプリのディレクトリ上で実行以上。
- 投稿日:2021-01-12T15:49:16+09:00
DBのロールバックが出来ない時の対処方法〜UnknownMigrationVersionError〜
はじめに
rails db:rollbackでマイグレーションファイルをロールバックしようとしたところ、下記のようにUnknownMigrationVersionErrorというエラーに悩まされたので、この解決方法について書こうと思います。
ターミナルrails aborted! ActiveRecord::UnknownMigrationVersionError: No migration with version number 20201230115014.UnknownMigrationVersionErrorの詳細
rails db:migrateでマイグレーションファイルの状態を確認したところ、NO FILEと書かれた身に覚えのないデータが残っていました。これは、statusがupのままVSコード上でマイグレーションファイルを削除してしまったため、DB上には対象ファイルが残っている状態でした。(おそらく、ActiveHashでモデルを作成した時に、skip-migrationを忘れて謝って作成したファイルだと思います...)
つまり、対応するファイルが存在しないマイグレーションデータが残ったままになっていたのが、UnknownMigrationVersionErrorの原因でした。解決方法
1. VSコード上にマイグレーションファイルを作成
まず、NO FILEとなっていたIDが20201230115014のデータに対応するファイルを、VSコード上で作成します。(今回は、「20201230115014.dummy.rb」というファイル名で作成しています。)
2. ステータスをdownにする
マイグレーションファイルを作成しただけの状態で、ロールバックしようとすると下記のようにエラーが出ます。これは、先ほど作成したマイグレーションファイルの中身が何も記述されていないことが原因です。
ターミナルrails aborted! NameError: uninitialized constant Dummyこれを解消するために、マイグレーションファイルの中身を書いてあげます。その後、一度マイグレートしてあげましょう。
20201230115014.dummy.rbclass Dummy < ActiveRecord::Migration[6.0] def change end endターミナルrails db:migrateこの状態で、ロールバックしてあげると、無事ステータスがダウンになりました。
3. 対象ファイルの削除
ここまで来れば、後は対象ファイルを削除するだけです。VSコードで「20201230115014.dummy.rb」のファイルを削除した後、rails db:migrate:statusで確認すると、無事対象のデータが削除できてることが確認できます。
終わりに
何気なく、ステータスを確認せずにマイグレーションファイルを削除すると後々面倒だなと思いました。何か見てくださる方のヒントになれば幸いです。
参考
- 投稿日:2021-01-12T15:49:16+09:00
マイグレーションのロールバックが出来ない時の対処方法 〜UnknownMigrationVersionError〜
はじめに
rails db:rollbackでマイグレーションファイルをロールバックしようとしたところ、下記のようにUnknownMigrationVersionErrorというエラーに悩まされたので、この解決方法について書こうと思います。
ターミナルrails aborted! ActiveRecord::UnknownMigrationVersionError: No migration with version number 20201230115014.UnknownMigrationVersionErrorの詳細
rails db:migrateでマイグレーションファイルの状態を確認したところ、NO FILEと書かれた身に覚えのないデータが残っていました。これは、statusがupのままVSコード上でマイグレーションファイルを削除してしまったため、DB上には対象ファイルが残っている状態でした。(おそらく、ActiveHashでモデルを作成した時に、skip-migrationを忘れて謝って作成したファイルだと思います...)
つまり、対応するファイルが存在しないマイグレーションデータが残ったままになっていたのが、UnknownMigrationVersionErrorの原因でした。解決方法
1. VSコード上にマイグレーションファイルを作成
まず、NO FILEとなっていたIDが20201230115014のデータに対応するファイルを、VSコード上で作成します。(今回は、「20201230115014.dummy.rb」というファイル名で作成しています。)
2. ステータスをdownにする
マイグレーションファイルを作成しただけの状態で、ロールバックしようとすると下記のようにエラーが出ます。これは、先ほど作成したマイグレーションファイルの中身が何も記述されていないことが原因です。
ターミナルrails aborted! NameError: uninitialized constant Dummyこれを解消するために、マイグレーションファイルの中身を書いてあげます。その後、一度マイグレートしてあげましょう。
20201230115014.dummy.rbclass Dummy < ActiveRecord::Migration[6.0] def change end endターミナルrails db:migrateこの状態で、ロールバックしてあげると、無事ステータスがダウンになりました。
3. 対象ファイルの削除
ここまで来れば、後は対象ファイルを削除するだけです。VSコードで「20201230115014.dummy.rb」のファイルを削除した後、rails db:migrate:statusで確認すると、無事対象のデータが削除できてることが確認できます。
終わりに
何気なく、ステータスを確認せずにマイグレーションファイルを削除すると後々面倒だなと思いました。何か見てくださる方のヒントになれば幸いです。
参考
- 投稿日:2021-01-12T15:49:16+09:00
マイグレーションのロールバックが出来ない時の対処方法 UnknownMigrationVersionError
はじめに
rails db:rollbackでマイグレーションファイルをロールバックしようとしたところ、下記のようにUnknownMigrationVersionErrorというエラーに悩まされたので、この解決方法について書こうと思います。
ターミナルrails aborted! ActiveRecord::UnknownMigrationVersionError: No migration with version number 20201230115014.UnknownMigrationVersionErrorの詳細
rails db:migrateでマイグレーションファイルの状態を確認したところ、NO FILEと書かれた身に覚えのないデータが残っていました。これは、statusがupのままVSコード上でマイグレーションファイルを削除してしまったため、DB上には対象ファイルが残っている状態でした。(おそらく、ActiveHashでモデルを作成した時に、skip-migrationを忘れて謝って作成したファイルだと思います...)
つまり、対応するファイルが存在しないマイグレーションデータが残ったままになっていたのが、UnknownMigrationVersionErrorの原因でした。解決方法
1. VSコード上にマイグレーションファイルを作成
まず、NO FILEとなっていたIDが20201230115014のデータに対応するファイルを、VSコード上で作成します。(今回は、「20201230115014.dummy.rb」というファイル名で作成しています。)
2. ステータスをdownにする
マイグレーションファイルを作成しただけの状態で、ロールバックしようとすると下記のようにエラーが出ます。これは、先ほど作成したマイグレーションファイルの中身が何も記述されていないことが原因です。
ターミナルrails aborted! NameError: uninitialized constant Dummyこれを解消するために、マイグレーションファイルの中身を書いてあげます。その後、一度マイグレートしてあげましょう。
20201230115014.dummy.rbclass Dummy < ActiveRecord::Migration[6.0] def change end endターミナルrails db:migrateこの状態で、ロールバックしてあげると、無事ステータスがダウンになりました。
3. 対象ファイルの削除
ここまで来れば、後は対象ファイルを削除するだけです。VSコードで「20201230115014.dummy.rb」のファイルを削除した後、rails db:migrate:statusで確認すると、無事対象のデータが削除できてることが確認できます。
終わりに
何気なく、ステータスを確認せずにマイグレーションファイルを削除すると後々面倒だなと思いました。何か見てくださる方のヒントになれば幸いです。
参考
- 投稿日:2021-01-12T15:49:16+09:00
マイグレーションのロールバックが出来ない時の対処方法(UnknownMigrationVersionError)
はじめに
rails db:rollbackでマイグレーションファイルをロールバックしようとしたところ、下記のようにUnknownMigrationVersionErrorというエラーに悩まされたので、この解決方法について書こうと思います。
ターミナルrails aborted! ActiveRecord::UnknownMigrationVersionError: No migration with version number 20201230115014.UnknownMigrationVersionErrorの詳細
rails db:migrateでマイグレーションファイルの状態を確認したところ、NO FILEと書かれた身に覚えのないデータが残っていました。
これは、ステータスがupのままVSコード上でマイグレーションファイルを削除してしまったため、DB上にはデータが残っている状態でした。(おそらく、ActiveHashでモデルを作成した時に、skip-migrationを忘れて謝って作成したファイルだと思います...)
まとめると、対応するファイルが存在しないマイグレーションデータが残ったままになっていたのが、UnknownMigrationVersionErrorの原因でした。解決方法
1. VSコード上にマイグレーションファイルを作成
まず、NO FILEとなっていたIDが20201230115014のデータに対応するファイルを、VSコード上で作成します。(今回は、「20201230115014.dummy.rb」というファイル名で作成しています。)
2. ステータスをdownにする
マイグレーションファイルを作成しただけの状態で、ロールバックしようとすると下記のようにエラーが出ます。これは、先ほど作成したマイグレーションファイルの中身が何も記述されていないことが原因です。
ターミナルrails aborted! NameError: uninitialized constant Dummyこれを解消するために、マイグレーションファイル内にクラスとchangeアクションを記述します。その後、一度マイグレートを行います。
20201230115014.dummy.rbclass Dummy < ActiveRecord::Migration[6.0] def change end endターミナルrails db:migrateこの状態で、ロールバックしてあげると、無事ステータスがダウンになりました。
3. 対象ファイルの削除
ここまで来れば、後は対象ファイルを削除するだけです。VSコードで「20201230115014.dummy.rb」のファイルを削除した後、rails db:migrate:statusで確認すると、無事対象のデータが削除できてることが確認できます。
終わりに
何気なく、ステータスを確認せずにマイグレーションファイルを削除すると後々面倒だなと思いました。何か見てくださる方のヒントになれば幸いです。
参考
- 投稿日:2021-01-12T11:50:05+09:00
Ruby on Rails <2021年> かんたんログイン機能の実装(form_with)
前提条件
- gem 'devise' でログイン機能を実装していること
- 新規登録で mail: guest@guest, password: 111111で登録していること
<%= form_with(model: User.new, url:new_user_session_path) do |f| %> <%= f.hidden_field :email, value: "guest@guest" %> <%= f.hidden_field :password, value: "111111" %> <%= f.submit "ゲストログイン", class:"btn btn-success btn-sm mb-3 btn-block guest"%> <% end %>ゲストログインがクリックされるとログイン画面へ行き、
valueの中のemailとpasswordが入り
ログインした画面へリダイレクトされます。
- 投稿日:2021-01-12T11:31:43+09:00
【Rails6】SNS認証による新規登録機能実装(Facebook, Google)
はじめに
現在Raisを使用しアプリケーションの作成を行なっています。今回は既に実装しているログイン機能にSNSアカウントでログインできるよう追加実装していきます。備忘録及び復習のため記述していきます。
環境
Ruby on Rails '6.0.0'
Ruby '2.6.5'前提
- devise(gem)を使用し、ユーザーのログイン機能実装済み。
- facebook for developersと Google Cloud Platformから外部APIの設定済み。
①Gemの導入と環境変数の設定
omniauth
・Google,Facebook,Twitter等のSNSアカウントを用いてユーザー登録やログインなどを実装できるgemです。今回はFacebookとGoogleのomniauthをインストールします。Gemfilegem 'omniauth-google-oauth2' gem 'omniauth-facebook'ターミナル% bundle installまた、外部API設定時に取得したIDやシークレットキーを環境変数に設定します。環境変数の設定方法はいくつかあると思いますが、私の場合は、「gem 'dotenv-rails'」により「.envファイル」を設定済みのため、そこに記述しました。
.envFACEBOOK_CLIENT_ID='アプリID' FACEBOOK_CLIENT_SECRET='app secret' GOOGLE_CLIENT_ID='クライアントID' GOOGLE_CLIENT_SECRET='クライアントシークレット'次にアプリケーション側で環境変数を読み込む記述をします。
config/initializers/devise.rb(省略) config.omniauth :facebook,ENV['FACEBOOK_CLIENT_ID'],ENV['FACEBOOK_CLIENT_SECRET'] config.omniauth :google_oauth2,ENV['GOOGLE_CLIENT_ID'],ENV['GOOGLE_CLIENT_SECRET']②モデルの設定
SNSのWebAPIにリクエストを送ると、API上で認証を行います。その後、SNS上に登録されている情報がアプリケーション側に返される仕組みです。
APIからのレスポンスの中の、「uid」と「provider」をアプリケーションのデータベースに、ユーザー情報(名前など)と共に保存します。
しかし、「SNS認証とユーザー登録のタイミングが異なる」仕様であるため、SNS認証時にはusersテーブルのレコードを作成することはできません。そこでSNS認証時の情報を保存する別テーブル(SnsCredentialモデル)を作成しました。ターミナル% rails g model sns_credentialdb/migrate/XXXXXXXXXXXXXXXX_create_sns_credentials.rbclass CreateSnsCredentials < ActiveRecord::Migration[6.0] def change create_table :sns_credentials do |t| t.string :provider t.string :uid t.references :user, foreign_key: true t.timestamps end end enduserモデルとのアソシエーションのため、外部キーとしてuser_idを持たせています。編集がおわり次第、「rails db:migrate」にてデータベースに反映します。
アソシエーションの設定をします。
app/models/user.rb(省略) has_many :sns_credentials, dependent: :destroy devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable, :omniauthable, omniauth_providers: [:facebook, :google_oauth2]omniauth_providers: [:facebook, :google_oauth2]と記述することで、FacebookとGoogleのOumniAuthを使用できます。
app/models/sns_credential.rbclass SnsCredential < ApplicationRecord belongs_to :user end③deviseの再設定
deviseのコントローラーを再設定するため、コントローラーを作成しています。
ターミナル% rails g devise:controllers users生成したコントローラーを使用させるために、deviseのルーティングを変更します。
config/routesRails.application.routes.draw do devise_for :users, controllers: { omniauth_callbacks: 'users/omniauth_callbacks', registrations: 'users/registrations', sessions: 'users/sessions', } ・・・(省略)・・・ end④コントローラーの設定
app/controllers/users/omniauth_callbacks_controller.rbclass Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController def facebook authorization end def google_oauth2 authorization end private def authorization @user = User.from_omniauth(request.env["omniauth.auth"] end endプライベートメソッド内に記述している「authorization」を呼び出す「facebook」と「google_oauth2」というアクションを定義します。
⑤Viewファイルの編集
app/views/users/registrations/new.html.erb(省略) <div class="d-flex justify-content-between mt-4"> <%= link_to user_facebook_omniauth_authorize_path, class:"btn btn-outline-primary sns-btn", method: :post do %> <i class="fab fa-facebook fa-2x"></i> <% end %> <%= link_to user_google_oauth2_omniauth_authorize_path, class:"btn btn-outline-danger sns-btn", method: :post do %> <i class="fab fa-google fa-2x"></i> <% end %> </div>⑥modelの編集
Userモデルに入るデータのため、Userモデルにクラスメソッドを作成しました。
クラスメソッドとは、クラスで共通の情報を使った処理に使用するようです。記述方法は、メソッド名の前にselfを.(ドット)で繋いで定義します。app/models/user.rb(省略) def self.from_omniauth(auth) sns = SnsCredential.where(provider: auth.provider, uid: auth.uid).first_or_create end上記でomniauth_callbacks_controller.rbに記述した、User.from_omniauthも呼び出せるようになりました。
また、取得した情報内の「provider」と「uid」をsnsに代入して、first_or_createメソッドを用いています。
(first_or_createメソッド:保存するレコードがデータベースに存在するか検索を行い、検索した条件のレコードがあればそのレコードのインスタンスを返し、なければ新しくインスタンスを保存するメソッドです。)次に、SNS認証を行なったかどうか確認した後のコードを記述します。
app/models/user.rb(省略) def self.from_omniauth(auth) sns = SnsCredential.where(provider: auth.provider, uid: auth.uid).first_or_create user = User.where(email: auth.info.email).first_or_initialize( first_name: auth.info.last_name, last_name: auth.info.first_name, email: auth.info.email ) end上記で追加したコードは、SNS認証を行なっていなかった場合、メールアドレスで検索をします。first_or_initializeを用いて、名前とメールアドレスを返します。
(first_or_initialize:whereメソッドとともに使うことで、whereで検索した条件のレコードがあればそのレコードのインスタンスを返し、なければ新しくインスタンスを作るメソッドです。)first_or_create:新規レコードをデータベースに保存する
first_or_initialize:新規レコードをデータベースに保存しない⑦controllerの編集
Userモデルから返ってきた後の処理を記述します。
app/controllers/users/omniauth_callbacks_controller.rbclass Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController def facebook authorization end def google_oauth2 authorization end private def authorization @user = User.from_omniauth(request.env['omniauth.auth']) if @user.persisted? #ユーザー情報が登録済みのため、新規登録ではなくログイン処理を行う sign_in_and_redirect @user, event: :authentication else #ユーザー情報が未登録のため、新規登録画面へ遷移する render template: 'devise/registrations/new' end end endUserモデルから返ってきた値を@userに代入します。これは、viewで取得した「名前」と「メールアドレス」を表示させるためです。
次に、ログイン時の記述を行います。
⑧Userモデルの編集
app/models/user.rb(省略) def self.from_omniauth(auth) sns = SnsCredential.where(provider: auth.provider, uid: auth.uid).first_or_create user = User.where(email: auth.info.email).first_or_initialize( first_name: auth.info.last_name, last_name: auth.info.first_name, email: auth.info.email ) # userが登録済みであるか判断 if user.persisted? sns.user = user sns.save end user endpersisted?で登録済みのユーザーと判断を行い、「if文」の中が呼ばれます。新規登録時は、SnsCredentialモデルが保存されるタイミングで、user_idが確定していなかったので、SnsCredentialモデルとUserモデルは紐づいていません。ログインの際に、sns.userを更新して紐付けを行います。
最後にログイン画面のviewファイルも編集して完了です!!
終わりに
もしこの記事を参考に、実装してみて、うまくできない場合は教えてください。。初めての実装のためお許しください。。
- 投稿日:2021-01-12T10:56:52+09:00
GitHub Actions/Cache : Dockerコンテナ上でbundle installしたgemをキャッシュする
GitHub AcitonsでRailsプロジェクトのrspec/rubocopを実行するような場合に、
bundle install
したgemのキャッシュを行いたいのだが、Permission denied
エラーが出てうまくキャッシュが作れず、キャッシュキーが汚染1されることがあった。error例Post job cleanup. /bin/tar --posix --use-compress-program zstd -T0 -cf cache.tzst -P -C /home/runner/work/project/project --files-from manifest.txt /bin/tar: ../../../../../tmp/cache/bundle/gems/puma-5.1.1/ext/puma_http11/.gem.20201227-1-wtkvl5: Cannot open: Permission denied /bin/tar: ../../../../../tmp/cache/bundle/gems/mini_racer-0.3.1/ext/mini_racer_extension/.gem. [中略] /bin/tar: ../../../../../tmp/cache/bundle/gems/sassc-2.4.0/ext/.gem.20201227-1-1ompol5: Cannot open: Permission denied /bin/tar: Exiting with failure status due to previous errors Warning: Tar failed with error: The process '/bin/tar' failed with exit code 2上記のエラーはローカルの
/var/cache/bundle
ディレクトリを、コンテナの/bundle
にマウントした状態でBUNDLE_PATH=/bundle bundle install
を実行し、/var/cache/bundle
をActions/Cache
でキャッシュさせる場合に発生したdocker-compose と workflow のyamlは下記のような感じ
docker-compose.yamlversion: '3.7' services: app: ... volumes: - ./:/app:cached - /var/cache/bundle:/bundle.github/workflows/ci.ymljobs: ... rspec: ... env: GEMS_CACHE_DIR: /tmp/cache/bundle steps: ... - name: Cache bundle gems id: cache-bundle-gems uses: actions/cache@v2 with: path: ${{env.GEMS_CACHE_DIR}} key: ${{ runner.os }}-${{ matrix.ruby }}-${{ env.GEM_CACHE_TAG }}-${{ hashFiles('Gemfile.lock') }} restore-keys: | ${{ runner.os }}-${{ matrix.ruby }}-${{ env.GEM_CACHE_TAG }}-解決策 1 アクセス権限を書き換える
アクセス権限の問題なので、雑にアクセス権限を書き換えてしまう方法。
cache作成時のtarコマンドでsudoがつけられたらいいのだが、そういうオプションはないので、下記のようなアクション
steps
の最後に差し込む.github/workflows/ci.yml- name: Fix permissions of bundle id: fix-permissions-of-bundle run: sudo chmod -R a+rwx ${{env.GEMS_CACHE_DIR}}この方法で多分大丈夫。
ですが、アクセス権限書き換えることになるので、その辺りの動作確認は取れません(そこが問題になることなんてほぼないと思いますが…
解決策 2 コンテナ上でtarを作成してcacheさせる
アクセス権限の問題が発生するのは、コンテナ上で
bundler install
しているのにローカルでtarを作成しているため。コンテナ上で
/bundle
のtarを作成し、そのtarをActions/Cache
でキャッシュして貰えば問題ありません。下記の設定では必要最低限の軽量コンテナのみを立ち上げて(今回は
standalone
という名前のコンテナを使用している)、tarを作っています。.github/workflows/ci.ymlenv: GEMS_ARCHIVE_DIR: /tmp/cache/bundle-gem-archive ... # $GEMS_ARCHIVE_DIR ディレクトリをキャッシュ - name: Cache bundle gems id: cache-bundle-gems uses: actions/cache@v2 with: path: ${{ env.GEMS_ARCHIVE_DIR }} key: ${{ runner.os }}-${{ matrix.ruby }}-${{ env.GEM_CACHE_TAG }}-${{ hashFiles('Gemfile.lock') }} restore-keys: | ${{ runner.os }}-${{ matrix.ruby }}-${{ env.GEM_CACHE_TAG }}- # $GEMS_ARCHIVE_DIR をマウントした軽量コンテナ上で /archive/bundle.tar からファイルを抽出 - name: Unarchive bundle gems cache id: unarchive-bundle-gems-cache run: test -f ${{env.GEMS_ARCHIVE_DIR}}/bundle.tar && docker-compose run --rm -v ${{ env.GEMS_ARCHIVE_DIR }}:/archive:cached standalone tar -xf /archive/bundle.tar -C / || echo "bundle gem cache does not exist." ... # 軽量コンテナ上で $GEM_HOME の内容をtarで固め、/archive/bundle.tar の名前で保存 - name: Create bundle gems archive id: create-bundle-gems-archive if: steps.cache-bundle-gems.outputs.cache-hit != 'true' run: docker-compose run --rm -v ${{ env.GEMS_ARCHIVE_DIR }}:/archive:cached standalone tar -cf /archive/bundle.tar -C / bundleこの方法の場合、コンテナ上でtarを実行するので多少遅くなるようだが自分の環境の場合はほぼ誤差(数秒程度)しか変わらなかった(gemの量などにもよると思う)。
- 投稿日:2021-01-12T09:01:31+09:00
[Rails]date_selectにBootstrapのform-controlが適用されない
課題
Rails + Boostrap でdate_selectにform-controlが適用されない
_form.html.erb<%= form.date_select :date, class: "form-control" %>結論
以下のような書き方で適用されます。
_form.html.erb<%= form.date_select :date, {}, {class: 'form-control', style: 'display: inline-block;width: auto;'} %>ちなみにこの style: 'display: inline-block;width: auto; をサボると想定外にダッさい表示になります。
地味に初学者が躓くので再度メモしました。
技術背景も含めて以下記事が完全回答版です。
過去の先人たちに感謝いたします。参考情報
Rails date_selectをBootstrapを使っていい感じにする
https://qiita.com/t_oginogin/items/519fb52e1708e26a8b73
- 投稿日:2021-01-12T08:01:10+09:00
Rails + apipieでDartのクライアントコードを生成する
背景
こちらの記事の続きです。
Docker+Rails6+apipieを使ってAPI定義をSwagger UIに表示 - Qiita
Dart/Flutterでアプリ開発をするメンバーに上記の定義ファイルを送ったところ、「Dartのクライアントコードを自動生成できない」と言われてしまいました。
今回はDartのクライアントコードを自動生成することをゴールに、apipieの設定を書き換えます。
環境
Ruby: 2.7.1 Rails: 6.0.3Flutter/Dartとは?
Flutterについてざっくりまとめるとこんな感じでしょうか。
- モバイルアプリフレームワークの1つ
- Googleが開発
- Dartという言語を使う
ReactNativeなどはWeb系経験者がアプリに手を出しやすくするものだが、DartはWeb系の経験がなくとも手を出しやすい、ようです。
また、DartはTypeScriptと似たaltJSです。
基本的にFlutter用の言語で、altJSとして使うんだったらTypeScriptを使うほうが良さげ、なようです。cf. Flutterとは何か? 使うメリットや特徴を理解する (1/4):CodeZine(コードジン)
apipieの調整
さて実装面。
アプリ担当からは以下のエラーを受け取りました。E/flutter (17797): [ERROR:flutter/lib/ui/ui_dart_state.cc(166)] Unhandled Exception: ApiException 500: Exception during deserialization. (Inner exception: ApiException 500: Could not find a suitable class for deserialization) E/flutter (17797): E/flutter (17797): #0 ApiClient._deserialize (package:openapi/api_client.dart:57:5) E/flutter (17797): #1 ApiClient._deserialize.<anonymous closure> (package:openapi/api_client.dart:50:43) E/flutter (17797): #2 MappedIterator.moveNext (dart:_internal/iterable.dart:392:20) E/flutter (17797): #3 MapBase._fillMapWithIterables (dart:collection/maps.dart:83:39) E/flutter (17797): #4 new LinkedHashMap.fromIterables (dart:collection/linked_hash_map.dart:127:13) E/flutter (17797): #5 ApiClient._deserialize (package:openapi/api_client.dart:49:26) E/flutter (17797): #6 ApiClient.deserialize (package:openapi/api_client.dart:67:12) E/flutter (17797): #7 UsersApi.postApiV1UsersToken (package:openapi/api/users_api.dart:77:49)当方Dartは未経験でして、エラーを見てもなんのこっちゃだったので、アプリ担当に定義ファイルをいじってもらい動くようにしてもらいました。
その結果、以下3つが主な原因とわかりました。
- basePathの指定がない
- "additionalProperties": falseに指定している
- レスポンスにtitleがない
basePathを指定する
api_base_url
が空欄だと警告がでるそうなので追加します。apipie.rb- config.api_base_url = "" + config.api_base_url = "/api"※ 前回の記事 にて、わざわざ
api_base_url
を消していたようです。。"additionalProperties": falseに指定している
additionalProperties
とは、「propertiesで指定したプロパティ以外も受け付けるかどうか」を指定するオプションのようです。trueならpropertiesに記載がないプロパティも受け付けるが、falseだとダメなようです。
cf. OpenAPIのadditional_propertyオプションってなに?
クライアントコード生成時にエラーが起きた原因は、これがfalseになっていたから、のようです。
apipieでは初期値がfalseのため、trueに設定しましょう。
apipie.rb+ config.swagger_allow_additional_properties_in_response = truecf. Swagger-Specific Configuration Parameters
なお、レスポンスする全てのプロパティ(
status, message
など意図して返すもの)を含んでいる状態で生成を試みてもエラーは起きるようです。
(ここは未解決...)レスポンスにtitleがない
API定義(swagger.json)に
title
がない場合、クラス名がInlineResponse200
となってしまうようです。swagger.json"schema": { "type": "object", + "title": "Token", "properties": {apipieのリファレンスを見ても該当箇所に
title
を追加する記述は見つかりませんでした...。が、 Response Description に似た実装があり、試したところうまくいきました!
api/v1/users_controller.rb+ def_param_group :user_login_token do + property :status, Integer, desc: "status code" + property :message, String + property :detail, String, desc: "user token" + end api :POST, "/v1/users/token", "get access token" description "ログイン認証をしてトークンを返す" formats ["json"] param :email, String, desc: "メールアドレス", required: true param :password, String, desc: "パスワード", required: true - returns code: 200, desc: "return user token" + returns :user_login_token, desc: "return user token" error code: 401, desc: "Unauthorized."以上の修正をすることで、Dartのクライアントコードを生成できるようになりました。
余談:ブラウザからDartのクライアントコードを作る
- Swagger Editor にAPI定義ファイルをペーストする
- ヘッダーの
Generate Client
からdart
を選択するという方法で、クライアントコードを生成できます。
今回程度の動作確認であれば十分できますね。
- 投稿日:2021-01-12T07:19:16+09:00
ローカルに保存されている画像データをZIPダウンロードしたいとき
解決したいこと
ローカルに保存されている、main.jpg、top_map_off.jpgをzipダウンロードしたい。
解決策
画像はバイナリモードでしか書き出せないので、openするときにバイナリモードを指定する。
Zip::OutputStream.open('example.zip') do |zip| default_images = ["main", "top_map_off"] default_images.each do |default_image| img_path = Rails.root.join( "app", "lib", "output", "download_tpl", @kikaku_date.kikaku_cd, "#{target}", "images", "#{default_image}.jpg ) # ローカルに保存されている画像ファイルのフルパス zip.put_next_entry "#{@zip_file_basename}/#{target}/images/#{default_image}.jpg" zip.print open(img_path, "rb").read # 画像をバイナリモードで開く end end
- 投稿日:2021-01-12T02:30:37+09:00
No such file or directory @ rb_sysopen - public/user_images/1.jpgのようなエラー
画像を保存できない
Railsでアプリケーションを作っている時に、遭遇しました。
解決策は、publicディレクトリの中にuser_imagesというフォルダがない、ということなので、その名前のフォルダを作ることでエラーはなくなりました。
- 投稿日:2021-01-12T01:47:40+09:00
Webpacker::Manifest::MissingEntryError
rails generate controller home topなどのコマンドで適当に作成したページを開くと、以下のエラーが発生。
ターミナル上は以下の表示。
Webpacker can't find application in アプリ名/public/packs/manifest.json. Possible causes: 1. You want to set webpacker.yml value of compile to true for your environment unless you are using the `webpack -w` or the webpack-dev-server. 2. webpack has not yet re-run to reflect updates. 3. You have misconfigured Webpacker's config/webpacker.yml file. 4. Your webpack configuration is not creating a manifest.
・manifest.jsonが見つからない。
・views/layouts内のapplication.html.erbの9行目がおかしい。
この2点を解決するために色々調べて試したが、改善せず。
調べた記事の中に、nodeのverが関係しているんじゃないかな〜的な物があったので、nodeのverを変更するついでに、webpacker installのために導入したnode,yarnやらを再installしてみた。
node verは最新版から、stableでinstallしたv14.15.4に変更。
下記の手順で実行すると、解決できました。1.nodeのver変更するため、homebrewで管理しているnodeをuninstall。
(homebrewでnodeのver変更する方法がわからなかったので。)
nodebrewでstable版を再インストール。
2.yarn/icu4cも一旦uninstall。再度homebrewでインストール
※nodeはnodebrewでinstall済みのため、--ignore-dependencies オプションをつけること。brew install yarn --ignore-dependenciesicu4cは--ignore-dependencies不要。
brew install icu4c3.rails newで新しいプロジェクトを作成。
このときすでにnode,yarn,icu4cがinstallされていれば、勝手にwebpackerもinstallされる。
4.rails sして、適当に作成したページを確認するとエラー発生せず。これのせいで全く作業が進まなかったので改善してほんと良かった。
rails6以降は標準でwebpackerが導入されるようで、私のrailsもv6.0.3.4です。
今回の原因は、nodeのverの相性が悪かったのかな〜と思っていますが、そういうことにしときます。
- 投稿日:2021-01-12T01:02:43+09:00
enumの使い方(日本語表記の導入)
今回はECサイトの制作をしていた際の注文ステータスのカラムにenumを使用し、プルダウン表記を活用しました。enumを使用することが初めてであったので、忘れない様に自身のメモとして記事を残したいと思います。
以下の記事は実装の際に参考にさせていただきました。(参考記事)
・【初心者向け】i18nを利用して、enumのf.selectオプションを日本語化する[Rails]
https://qiita.com/tanutanu/items/d44a92425188a4489ec6
・[初学者]Railsのi18nによる日本語化対応
https://qiita.com/shimadama/items/7e5c3d75c9a9f51abdd5Gemfile
Gemfile.gem 'enum_help'terminal.bundle installまずは、enumを使用するのに必要なgemをGemfileに記述します。
上記のように記述ができたら、ターミナルでbundle installのコマンドを実行します。model.rb
order_item.rbenum production_status: {cannot_be_started: 0, waiting_for_production: 1, in_production: 2, production_completed: 3}次にモデルのファイルに記述を加えます。
enum カラム名: {名前(今回はステータス名): 0, 名前(今回はステータス名):1 ... n }
enumの後には、enumを使用するカラム名と波カッコ内にはその名目(英語表記)と各値に0から順に数字を振っていきます。
このように各名目に数字を対応させるため、テーブルを作成する際のenumのデータ型はinteger(整数型)となります。日本語の設定に変更する
config/application.rbmodule NaganoApp class Application < Rails::Application # Initialize configuration defaults for originally generated Rails version. # config.load_defaults 5.2 config.i18n.default_locale = :ja #この1行のみ追加 # 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この1行の記載がないと日本語表記に変換されません。
ja.ymlja: enums: order_item: production_status: cannot_be_started: "着手不可" waiting_for_production: "制作待ち" in_production: "制作中" production_completed: "制作完了"先ほどのmodelファイルの内容を日本語訳をして記述します。
断層やカラム名の記述に誤りがあると、表示されなくなってしまいます。viewに表示する
<%= form_with model: @order_item, url: admin_order_item_path(order_item), method: :patch, local: true do |f| %> <%= f.select :production_status, OrderItem.production_statuses.keys.map {|k| [I18n.t("enums.order_item.production_status.#{k}"), k]} %> <%= f.submit "変更" %> <% end %>フォーム内にプルダウン表記でステータスを変更できるようにしました。
f.select以降は以下のような構成になっており、こちらもカラム名の単数、複数表記を誤ると表示がされなくなります。
:カラム名(単数形), モデル名.カラム名(複数形).keys.map {|k| [I18n.t("enums.モデル名.カラム名(単数形).#{k}"), k]} %
keys.map以降の表記により、設定されているenumの値の0から順に選択肢を入れてくれているようなイメージになります。enumのメリット
今回のようにステータス管理など頻繁に更新が必要な際には特に有効な機能になります。
数値で管理を行うため、何か変更する必要がある時でも修正が容易に行えることが大きなメリットです。
model側とviewに必要な記述を少し書き加えるだけで、アプリケーション全体に変更を行うことが可能です。終わり
今回は以上になります。
私自身もプログラミング初心者ですが、同じ様な立場の方に少しでも参考になれば幸いです。
また、もし内容に誤りなどがございましたら、ご指摘いただけますと幸いです。
- 投稿日:2021-01-12T00:12:17+09:00
【Rails AWS Docker】既存Ruby on Rails + MySQLアプリをDockerで構築し、AWSにデプロイする(6)
ポートフォリをとして作ったRuby on RailsアプリをDockerコンテナ化し本番環境をAWSで構築するまでの道のりです。
ポートフォリオ自体はこちらとなります。
【ポートフォリオ】転職活動時に作成したポートフォリオの概要(テッ◯キャンプ)かなり苦しめられたので、どなたかのお役に立てれば。
タイトル 1 ローカル環境のRailsアプリをDockerコンテナ化 2 AWSにVPCを作成する。パブリックサブネットを作成する 3 プライベートサブネットを作成する 4 EC2インスタンスを作成する 5 RDSを作成する 6 DockerコンテナをAWSにアップロードする DockerコンテナをAWSにアップロードします。
この項目では、かなり苦しめられました。ngixやpuma、RDSなど接続しなくてはいけないものが多々あり、また、AWS内にコンテナを構築することにより今、自分がどこにいるのか、よくわからなくなる状態に陥ることがありました。
この記事通りに、行ってもうまくいかないかもしれませんが、少しでもどなたかのお力になれればと思います。
自分はできれば、再びあのような思いをしたく無いので忘れないうちに記事として残しておきました。作成したAWSインスタンスにローカルdockerコンテナをアップロードしていきます。
コンテナ内での環境変数を設定します。
現状、database.ymlを確認すると
database.ymlusername: root password: password host: dbこのように、セキュアな情報がハードコーディングされています。
このままではgithubのコードから漏洩する危険があるので、本番環境のコンテナ内で環境変数を用いてセキュアな情報を設定します。環境変数については、参考までに下記をご覧下さい。
環境変数について環境変数を利用するために、dotenv-railsというgemをインストールします。
Gemfileに以下を追記します。Gemfile.gem 'dotenv-rails'dotenv-railsをインストールすることにより、「.env」ファイルに記載された環境変数をdockerコンテナ内で、下記のような記述で取り出せる様になります。
ENV['DATABASE_PASSWORD']また、.envファイルをgithubに上げてしまうと元も子も無いので、gitignoreに追記します。
.env.envを下記の様に記載してアプリ直下に配置します。
DB_USERNAME=root DB_PASSWORD=gakjadfmoaeur DB_HOST=fito2-db-instance.〇〇〇〇〇〇.ap-northeast-1.rds.amazonaws.com DB_DATABASE=fitO2_dbDB_HOSTはRDSのエンドポイントです。
AWSのRDSダッシュボードのデーターベースをクリックし、一覧から該当のRDSを選択すると確認できます。database.ymlの修正
database.ymlに下記を追加して、本番環境は環境変数から値を読み込むようにします。
database.ymlproduction: <<: *default database: <%= ENV['DB_DATABASE'] %> adapter: mysql2 encoding: utf8mb4 charset: utf8mb4 collation: utf8mb4_general_ci host: <%= ENV['DB_HOST'] %> username: <%= ENV['DB_USERNAME'] %> password: <%= ENV['DB_PASSWORD'] %>nginx.confの修正
以下の部分を本番用に修正します。
nginx.confserver { listen 80; # =========ローカルと本番切り替え=========== server_name 固定IP; # server_name localhost; # ======================================docker-compose.ymlの修正
docker-compose.ymlversion: '3' services: app: build: context: . # =========ローカルと本番切り替え=========== command: bundle exec puma -C config/puma.rb -e production # command: bundle exec puma -C config/puma.rb # ====================================== volumes: - .:/fitO2 - public-data:/fitO2/public - tmp-data:/fitO2/tmp - log-data:/fitO2/log networks: - fitO2-network # =========ローカルと本番切り替え=========== # depends_on: # - db # db: # image: mysql:5.7 # environment: # MYSQL_ROOT_PASSWORD: password # MYSQL_USER: user # MYSQL_PASSWORD: password # MYSQL_DATABASE: fitO2_development # volumes: # - db-data:/var/lib/mysql # networks: # - fitO2-network # ====================================== web: build: context: ./nginx_docker volumes: - public-data:/fitO2/public - tmp-data:/fitO2/tmp ports: - 80:80 depends_on: - app networks: - fitO2-network volumes: public-data: tmp-data: log-data: db-data: networks: fitO2-network: external: true変更点
・dbコンテナは本番環境ではRDSを使用するので不要のため、コメントアウト
(appコンテナのdepend onもまとめてコメントアウトします)
・command のrails s に -e production を追記して、本番環境でサーバー立ち上げるようにした。AWSインスタンス上にdockerコンテナを構築する。
今一度、AWSインスタンス上にdockerコンテナを構築するまでの流れを整理します。
今、現状ローカルでは、下記のようにfitO2というファイルを元に、下記の様なdockerコンテナをbuildできる状態でした。
そして、fitO2というファイルを書き換えて、以下の様なコンテナが作成されるように修正しました。(DBコンテナ削除)
その、fitO2をgithubにpushします。そして、AWS側からclone(2回目以降はpull)します。
cloneしたfitO2を元に、AWS内でbuildしてコンテナを構築し、AWS内に作成しているRDSと接続します。
通信の流れはオレンジの矢印となります。
githubへプッシュ
まずは、修正したファイルをコミットしてgithubへpushします。
AWSにdockerをインストール
ssh接続でAWSにログインし、dockerをインストールします。
sudo yum install -y docker //インストール sudo service docker start //docker起動 sudo usermod -G docker ec2-user //ec2-userに権限付与 exit //ログアウト //再度sshでログイン docker info下記の様な表示がされたらオッケーです。
Client: Debug Mode: false Server: Containers: 0 Running: 0 Paused: 0 Stopped: 0 Images: 0 Server Version: 19.03.13-cesudo chkconfig docker on //EC2起動時にDockerを自動で立ち上げるdocker-composeをインストールします。
sudo curl -L "https://github.com/docker/compose/releases/download/1.24.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose docker-compose -v docker-compose version 1.24.0, build 0aa59064 //上記の様にバージョンが表示されたら成功gitをインストールします。
sudo yum install -y git秘密鍵を作成します。(質問は全てエンター)
ssh-keygen -t rsa -b 4096作成した秘密鍵を表示し、コピーします。
cat ~/.ssh/id_rsa.pubhttps://github.com/settings/keys
こちらにアクセスします。titleを適宜入力し、keyのところに先ほどコピーした文字列を貼り付けます。
(ssh-rsa~から含めます)ssh -T git@github.com上記を打ち込んで、途中yesと入力し、下記の様に表示されればAWSとgithubの認証が成立しました。
Hi yourname! You've successfully authenticated, but GitHub does not provide shell access.以後、AWSインスタンス側から自身のgithubに対し、pull等ができる様になります。
githubからcloneします。(URLはリポジトリを表示したときのURL)
cd / //ルート直下に移動します。homeディレクトリでコンテナを展開すると、pumaとnginxの連携でエラーが出る場合がありますl。puma.sockの読み取りエラーsudo git clone https://github.com/〇〇〇〇〇〇/〇〇〇〇〇〇ls //lsと打って、ディレクトリがあるか確認する。今、gitignoreに記載したファイル(.env master.key)は含まれていません。そこで、ssh通信を用いてローカルからAWSに直接ファイルを転送します。
exit //一旦ログアウトして、ローカルのfitO2ファイルに移動 sudo scp -i ~/.ssh/fitO2_key.pem .env ec2-user@固定IP:/home/ec2-user/ sudo scp -i ~/.ssh/fitO2_key.pem config/master.key ec2user@固定IP:/home/ec2-user //scpコマンドを用いて、AWSに.envを転送します。 .env のように転送したファイルが表示されたらオッケー。 //権限の関係で一旦、ホームディレクトリに転送します。再度、ログイン。
cd ls -aホームディレクトリに移動して.envとmaster.keyが存在していたら問題ない。
.envとmaster.keyを移動させる。sudo mv .env /fitO2 sudo mv master.key /fitO2/config念のため移動してるか確認
ls -a /fitO2/ ls -a /fitO2/configコンテナの起動
cd /fitO2 docker-compose build //コンテナを作成します。 (Permition deniedになった場合は、sudo chmod 777 /usr/local/bin/docker-compose を実行します。) docker network create fitO2-network //ネットワークを作成します。 docker-compose run app rails assets:precompile RAILS_ENV=production //プリコンパイルを実施 docker-compose up //コンテナを起動します。下記のように表示されていれば問題なく起動しています。
Creating fito2_app_1 ... done Creating fito2_web_1 ... done Attaching to fito2_app_1, fito2_web_1 app_1 | Puma starting in single mode... app_1 | * Version 3.12.6 (ruby 2.5.1-p57), codename: Llamas in Pajamas app_1 | * Min threads: 5, max threads: 5 app_1 | * Environment: production app_1 | * Listening on tcp://0.0.0.0:3000 app_1 | * Listening on unix:///fitO2/tmp/sockets/puma.sock app_1 | Use Ctrl-C to stop別タブを開いて、AWSにログインします。
データベースの作成
docker-compose exec app rails db:create db:migrate RAILS_ENV=production docker-compose exec app rails db:seed RAILS_ENV=production //(必要に応じて)以上で、http://固定IPにアクセスした場合、正しく表示されるはずです。
RDSのデーターベース確認方法
dockerコンテナにログインし、mysqlを起動させます。
docker ps //コンテナIDを調べます。NAMESがfito2_app_1の方のCONTAINER IDをコピーします。
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES c5ec64f0ea76 fito2_web "/bin/sh -c '/usr/sb…" 5 minutes ago Up 5 minutes 0.0.0.0:80->80/tcp fito2_web_1 442b9ddb3a20 fito2_app "bundle exec puma -C…" 5 minutes ago Up 5 minutes fito2_app_1コンテナにログインします
docker exec -it 442b9ddb3a20 bashmysqlを起動させます。
service mysql startmysql -u root -h RDSのエンドポイント -p //パスワードを入力してログインセキュリティーグループで制限んしているので、作成したEC2コンテナからしかアクセスできません。
ローカルから試してみてください。
以上となります。
参考
EC2上でRailsアプリケーションにDockerを導入する(Rails、Nginx、RDS)
開発環境において既存のRailsアプリにDockerを導入する方法(Rails、nginx、mysql)
無料!かつ最短?で Ruby on Rails on Docker on AWS のアプリを公開するぞ。
【画像付きで丁寧に解説】AWS(EC2)にRailsアプリをイチから上げる方法【その1〜ネットワーク,RDS環境設定編〜】
Nginx + Rails (Puma) on Docker のいくつかの実用パターン
Docker + Rails + Puma + Nginx + MySQL
- 投稿日:2021-01-12T00:12:17+09:00
【Rails AWS Docker 】既存Ruby on Rails + MySQLアプリをDockerで構築し、AWSにデプロイする(6)
ポートフォリをとして作ったRuby on RailsアプリをDockerコンテナ化し本番環境をAWSで構築するまでの道のりです。
ポートフォリオ自体はこちらとなります。
【ポートフォリオ】転職活動時に作成したポートフォリオの概要(テッ◯キャンプ)かなり苦しめられたので、どなたかのお役に立てれば。
タイトル 1 ローカル環境のRailsアプリをDockerコンテナ化 2 AWSにVPCを作成する。パブリックサブネットを作成する 3 プライベートサブネットを作成する 4 EC2インスタンスを作成する 5 RDSを作成する 6 DockerコンテナをAWSにアップロードする DockerコンテナをAWSにアップロードします。
この項目では、かなり苦しめられました。ngixやpuma、RDSなど接続しなくてはいけないものが多々あり、また、AWS内にコンテナを構築することにより今、自分がどこにいるのか、よくわからなくなる状態に陥ることがありました。
この記事通りに、行ってもうまくいかないかもしれませんが、少しでもどなたかのお力になれればと思います。
自分はできれば、再びあのような思いをしたく無いので忘れないうちに記事として残しておきました。作成したAWSインスタンスにローカルdockerコンテナをアップロードしていきます。
コンテナ内での環境変数を設定します。
現状、database.ymlを確認すると
database.ymlusername: root password: password host: dbこのように、セキュアな情報がハードコーディングされています。
このままではgithubのコードから漏洩する危険があるので、本番環境のコンテナ内で環境変数を用いてセキュアな情報を設定します。環境変数については、参考までに下記をご覧下さい。
環境変数について環境変数を利用するために、dotenv-railsというgemをインストールします。
Gemfileに以下を追記します。Gemfile.gem 'dotenv-rails'dotenv-railsをインストールすることにより、「.env」ファイルに記載された環境変数をdockerコンテナ内で、下記のような記述で取り出せる様になります。
ENV['DATABASE_PASSWORD']また、.envファイルをgithubに上げてしまうと元も子も無いので、gitignoreに追記します。
.env.envを下記の様に記載してアプリ直下に配置します。
DB_USERNAME=root DB_PASSWORD=gakjadfmoaeur DB_HOST=fito2-db-instance.〇〇〇〇〇〇.ap-northeast-1.rds.amazonaws.com DB_DATABASE=fitO2_dbDB_HOSTはRDSのエンドポイントです。
AWSのRDSダッシュボードのデーターベースをクリックし、一覧から該当のRDSを選択すると確認できます。database.ymlの修正
database.ymlに下記を追加して、本番環境は環境変数から値を読み込むようにします。
database.ymlproduction: <<: *default database: <%= ENV['DB_DATABASE'] %> adapter: mysql2 encoding: utf8mb4 charset: utf8mb4 collation: utf8mb4_general_ci host: <%= ENV['DB_HOST'] %> username: <%= ENV['DB_USERNAME'] %> password: <%= ENV['DB_PASSWORD'] %>nginx.confの修正
以下の部分を本番用に修正します。
nginx.confserver { listen 80; # =========ローカルと本番切り替え=========== server_name 固定IP; # server_name localhost; # ======================================docker-compose.ymlの修正
docker-compose.ymlversion: '3' services: app: build: context: . # =========ローカルと本番切り替え=========== command: bundle exec puma -C config/puma.rb -e production # command: bundle exec puma -C config/puma.rb # ====================================== volumes: - .:/fitO2 - public-data:/fitO2/public - tmp-data:/fitO2/tmp - log-data:/fitO2/log networks: - fitO2-network # =========ローカルと本番切り替え=========== # depends_on: # - db # db: # image: mysql:5.7 # environment: # MYSQL_ROOT_PASSWORD: password # MYSQL_USER: user # MYSQL_PASSWORD: password # MYSQL_DATABASE: fitO2_development # volumes: # - db-data:/var/lib/mysql # networks: # - fitO2-network # ====================================== web: build: context: ./nginx_docker volumes: - public-data:/fitO2/public - tmp-data:/fitO2/tmp ports: - 80:80 depends_on: - app networks: - fitO2-network volumes: public-data: tmp-data: log-data: db-data: networks: fitO2-network: external: true変更点
・dbコンテナは本番環境ではRDSを使用するので不要のため、コメントアウト
(appコンテナのdepend onもまとめてコメントアウトします)
・command のrails s に -e production を追記して、本番環境でサーバー立ち上げるようにした。AWSインスタンス上にdockerコンテナを構築する。
今一度、AWSインスタンス上にdockerコンテナを構築するまでの流れを整理します。
今、現状ローカルでは、下記のようにfitO2というファイルを元に、下記の様なdockerコンテナをbuildできる状態でした。
そして、fitO2というファイルを書き換えて、以下の様なコンテナが作成されるように修正しました。(DBコンテナ削除)
その、fitO2をgithubにpushします。そして、AWS側からclone(2回目以降はpull)します。
cloneしたfitO2を元に、AWS内でbuildしてコンテナを構築し、AWS内に作成しているRDSと接続します。
通信の流れはオレンジの矢印となります。
githubへプッシュ
まずは、修正したファイルをコミットしてgithubへpushします。
AWSにdockerをインストール
ssh接続でAWSにログインし、dockerをインストールします。
sudo yum install -y docker //インストール sudo service docker start //docker起動 sudo usermod -G docker ec2-user //ec2-userに権限付与 exit //ログアウト //再度sshでログイン docker info下記の様な表示がされたらオッケーです。
Client: Debug Mode: false Server: Containers: 0 Running: 0 Paused: 0 Stopped: 0 Images: 0 Server Version: 19.03.13-cesudo chkconfig docker on //EC2起動時にDockerを自動で立ち上げるdocker-composeをインストールします。
sudo curl -L "https://github.com/docker/compose/releases/download/1.24.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose docker-compose -v docker-compose version 1.24.0, build 0aa59064 //上記の様にバージョンが表示されたら成功gitをインストールします。
sudo yum install -y git秘密鍵を作成します。(質問は全てエンター)
ssh-keygen -t rsa -b 4096作成した秘密鍵を表示し、コピーします。
cat ~/.ssh/id_rsa.pubhttps://github.com/settings/keys
こちらにアクセスします。titleを適宜入力し、keyのところに先ほどコピーした文字列を貼り付けます。
(ssh-rsa~から含めます)ssh -T git@github.com上記を打ち込んで、途中yesと入力し、下記の様に表示されればAWSとgithubの認証が成立しました。
Hi yourname! You've successfully authenticated, but GitHub does not provide shell access.以後、AWSインスタンス側から自身のgithubに対し、pull等ができる様になります。
githubからcloneします。(URLはリポジトリを表示したときのURL)
cd / //ルート直下に移動します。homeディレクトリでコンテナを展開すると、pumaとnginxの連携でエラーが出る場合がありますl。puma.sockの読み取りエラーsudo git clone https://github.com/〇〇〇〇〇〇/〇〇〇〇〇〇ls //lsと打って、ディレクトリがあるか確認する。今、gitignoreに記載したファイル(.env master.key)は含まれていません。そこで、ssh通信を用いてローカルからAWSに直接ファイルを転送します。
exit //一旦ログアウトして、ローカルのfitO2ファイルに移動 sudo scp -i ~/.ssh/fitO2_key.pem .env ec2-user@固定IP:/home/ec2-user/ sudo scp -i ~/.ssh/fitO2_key.pem config/master.key ec2user@固定IP:/home/ec2-user //scpコマンドを用いて、AWSに.envを転送します。 .env のように転送したファイルが表示されたらオッケー。 //権限の関係で一旦、ホームディレクトリに転送します。再度、ログイン。
cd ls -aホームディレクトリに移動して.envとmaster.keyが存在していたら問題ない。
.envとmaster.keyを移動させる。sudo mv .env /fitO2 sudo mv master.key /fitO2/config念のため移動してるか確認
ls -a /fitO2/ ls -a /fitO2/configコンテナの起動
cd /fitO2 docker-compose build //コンテナを作成します。 (Permition deniedになった場合は、sudo chmod 777 /usr/local/bin/docker-compose を実行します。) docker network create fitO2-network //ネットワークを作成します。 docker-compose run app rails assets:precompile RAILS_ENV=production //プリコンパイルを実施 docker-compose up //コンテナを起動します。下記のように表示されていれば問題なく起動しています。
Creating fito2_app_1 ... done Creating fito2_web_1 ... done Attaching to fito2_app_1, fito2_web_1 app_1 | Puma starting in single mode... app_1 | * Version 3.12.6 (ruby 2.5.1-p57), codename: Llamas in Pajamas app_1 | * Min threads: 5, max threads: 5 app_1 | * Environment: production app_1 | * Listening on tcp://0.0.0.0:3000 app_1 | * Listening on unix:///fitO2/tmp/sockets/puma.sock app_1 | Use Ctrl-C to stop別タブを開いて、AWSにログインします。
データベースの作成
docker-compose exec app rails db:create db:migrate RAILS_ENV=production docker-compose exec app rails db:seed RAILS_ENV=production //(必要に応じて)以上で、http://固定IPにアクセスした場合、正しく表示されるはずです。
RDSのデーターベース確認方法
dockerコンテナにログインし、mysqlを起動させます。
docker ps //コンテナIDを調べます。NAMESがfito2_app_1の方のCONTAINER IDをコピーします。
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES c5ec64f0ea76 fito2_web "/bin/sh -c '/usr/sb…" 5 minutes ago Up 5 minutes 0.0.0.0:80->80/tcp fito2_web_1 442b9ddb3a20 fito2_app "bundle exec puma -C…" 5 minutes ago Up 5 minutes fito2_app_1コンテナにログインします
docker exec -it 442b9ddb3a20 bashmysqlを起動させます。
service mysql startmysql -u root -h RDSのエンドポイント -p //パスワードを入力してログインセキュリティーグループで制限んしているので、作成したEC2コンテナからしかアクセスできません。
ローカルから試してみてください。
以上となります。
参考
EC2上でRailsアプリケーションにDockerを導入する(Rails、Nginx、RDS)
開発環境において既存のRailsアプリにDockerを導入する方法(Rails、nginx、mysql)
無料!かつ最短?で Ruby on Rails on Docker on AWS のアプリを公開するぞ。
【画像付きで丁寧に解説】AWS(EC2)にRailsアプリをイチから上げる方法【その1〜ネットワーク,RDS環境設定編〜】
Nginx + Rails (Puma) on Docker のいくつかの実用パターン
Docker + Rails + Puma + Nginx + MySQL
- 投稿日:2021-01-12T00:11:46+09:00
【Rails AWS Docker】既存Ruby on Rails + MySQLアプリをDockerで構築し、AWSにデプロイする(5)
ポートフォリをとして作ったRuby on RailsアプリをDockerコンテナ化し本番環境をAWSで構築するまでの道のりです。
ポートフォリオ自体はこちらとなります。
【ポートフォリオ】転職活動時に作成したポートフォリオの概要(テッ◯キャンプ)かなり苦しめられたので、どなたかのお役に立てれば。
タイトル 1 ローカル環境のRailsアプリをDockerコンテナ化 2 AWSにVPCを作成する。パブリックサブネットを作成する 3 プライベートサブネットを作成する 4 EC2インスタンスを作成する 5 RDSを作成する 6 DockerコンテナをAWSにアップロードする RDSを作成します。
既存Ruby on Rails + MySQLアプリをDockerで構築し、AWSにデプロイする(1)においてデーターベースコンテナを作成しました。
実はプライベートサブネット内にEC2インスタンスを配置して、その中にデーターベースコンテナを配置してもいいのですが、AWSにはRDSというサービスがあります。
ざっくりいうと、RDSとはデーターベース用のインスタンスで、自動的に可用性や耐障害性を高めた構成にしてくれます。また、異なるアベイラビリティーゾーンにバックアップ用のデーターベースを配置することにより、災害等により一箇所のデーターベースで障害が生じた際にも、問題なく稼働できる様になっています。プライベートサブネットを二つ作った理由はこれです。
今回は、このRDSを用いてデーターベースインスタンスを作成します。
サブネットグループの作成
まずは、プライベートサブネット2つをまとめた、サブネットグループを作成します。
RDSダッシュボードに入り、サブネットグループを選択し、「DBサブネットグループを作成」をクリックします。
サブネットに先ほど、作成した二つのプライベートサブネットを追加します。
今、下記のような異なるアベイラビリティーゾーンを持つサブネットグループが作成されました。
パラメーターグループの設定
RDSでは直接データベースの設定を触れないので、パラメーターグループで設定します。
パラメーターグループファミリーは、開発環境のバージョンに合わせます。
オプショングループの設定
RDSダッシュボードに入り、オブショングループを選択し、「グループを作成」をクリックします。
開発環境に合わせてエンジンとメジャーエンジンバージョンを設定します。
RDSを作成する。
RDSダッシュボードに入り、データベースを選択し、「データーベースの作成」をクリックします。
下記は設定例です。ご自身の運用に合わせて設定してください。「データーベースの作成」クリック
少し分かりずらいですが、下記の様にRDSがサブネットグループとdb用セキュリティーグループと結びつけることにより、
結果として下記の構成となります。
これでRDSインスタンスが作成できました。
次回(6)へ続く
DockerコンテナをAWSにアップロードする
- 投稿日:2021-01-12T00:11:46+09:00
【Rails】既存Ruby on Rails + MySQLアプリをDockerで構築し、AWSにデプロイする(5)
ポートフォリをとして作ったRuby on RailsアプリをDockerコンテナ化し本番環境をAWSで構築するまでの道のりです。
ポートフォリオ自体はこちらとなります。
【ポートフォリオ】転職活動時に作成したポートフォリオの概要(テッ◯キャンプ)かなり苦しめられたので、どなたかのお役に立てれば。
タイトル 1 ローカル環境のRailsアプリをDockerコンテナ化 2 AWSにVPCを作成する。パブリックサブネットを作成する 3 プライベートサブネットを作成する 4 EC2インスタンスを作成する 5 RDSを作成する 6 DockerコンテナをAWSにアップロードする RDSを作成します。
既存Ruby on Rails + MySQLアプリをDockerで構築し、AWSにデプロイする(1)においてデーターベースコンテナを作成しました。
実はプライベートサブネット内にEC2インスタンスを配置して、その中にデーターベースコンテナを配置してもいいのですが、AWSにはRDSというサービスがあります。
ざっくりいうと、RDSとはデーターベース用のインスタンスで、自動的に可用性や耐障害性を高めた構成にしてくれます。また、異なるアベイラビリティーゾーンにバックアップ用のデーターベースを配置することにより、災害等により一箇所のデーターベースで障害が生じた際にも、問題なく稼働できる様になっています。プライベートサブネットを二つ作った理由はこれです。
今回は、このRDSを用いてデーターベースインスタンスを作成します。
サブネットグループの作成
まずは、プライベートサブネット2つをまとめた、サブネットグループを作成します。
RDSダッシュボードに入り、サブネットグループを選択し、「DBサブネットグループを作成」をクリックします。
サブネットに先ほど、作成した二つのプライベートサブネットを追加します。
今、下記のような異なるアベイラビリティーゾーンを持つサブネットグループが作成されました。
パラメーターグループの設定
RDSでは直接データベースの設定を触れないので、パラメーターグループで設定します。
パラメーターグループファミリーは、開発環境のバージョンに合わせます。
オプショングループの設定
RDSダッシュボードに入り、オブショングループを選択し、「グループを作成」をクリックします。
開発環境に合わせてエンジンとメジャーエンジンバージョンを設定します。
RDSを作成する。
RDSダッシュボードに入り、データベースを選択し、「データーベースの作成」をクリックします。
下記は設定例です。ご自身の運用に合わせて設定してください。「データーベースの作成」クリック
少し分かりずらいですが、下記の様にRDSがサブネットグループとdb用セキュリティーグループと結びつけることにより、
結果として下記の構成となります。
これでRDSインスタンスが作成できました。
次回(6)へ続く
DockerコンテナをAWSにアップロードする
- 投稿日:2021-01-12T00:11:19+09:00
【Rails AWS Docker】既存Ruby on Rails + MySQLアプリをDockerで構築し、AWSにデプロイする(4)
ポートフォリをとして作ったRuby on RailsアプリをDockerコンテナ化し本番環境をAWSで構築するまでの道のりです。
ポートフォリオ自体はこちらとなります。
【ポートフォリオ】転職活動時に作成したポートフォリオの概要(テッ◯キャンプ)かなり苦しめられたので、どなたかのお役に立てれば。
タイトル 1 ローカル環境のRailsアプリをDockerコンテナ化 2 AWSにVPCを作成する。パブリックサブネットを作成する 3 プライベートサブネットを作成する 4 EC2インスタンスを作成する 5 RDSを作成する 6 DockerコンテナをAWSにアップロードする EC2インスタンスを作成する
EC2とは
Elastic Compute Cloudと呼ばれるもので、ざっくりというと仮想的なパソコンです。
それを、AWS上に置くことによって、クラウド上にシステムを構築します。作成したEC2インスタンスは、パブリックサブネットに配置して、アプリ本体を置きます。
EC2インスタンスの作成
今回はインスタンスタイプにクイックスタートの「AMAZON Linux2」を利用します。
(ざっくり)どのタイプのコンピューターにするかということです。今回はOSがlinuxのものを選択します。タイプは無料利用枠のt2.microを選択します。
(ざっくり)どの程度のスペックにするかとうことです。→「次のステッップ:インスタンスの詳細設定」をクリックします。
項目 内容 説明(ざっくり) インスタンス数 1 起動するインスタンスの数 購入のオプション チェックなし スポットインスタンスを選択すると安価に利用できる(常時起動しない場合) ネットワーク fitO2_vpc 先ほど作成したVPCに配置する。 サブネット fitO2_public_subnet_1a 先ほど作成したパブリックサブネットに配置する。 自動割り当てパブリック IP サブネット設定を使用(無効) 後ほど固定IPを割り振るため 配置グループ チェックなし 複数のインスタンス間の通信を高速化する設定 キャパシティーの予約 なし リソースの上限を超えた時にインスタンスが起動できなくなることを回避(有料) IAM ロール なし AWSのリソースに紐付けることができる権限設定を行うサービス CPU オプション なし CPUの性能に対するオプション シャットダウン動作 停止 シャットダウン時の動作 停止 - 休止動作 なし 停止動作に休止動作を追加する 終了保護の有効化 なし 誤った終了を防止します モニタリング なし 5分間隔の監視を1分感覚にする。 テナンシー 共有 ハードディスクを占有するか否か Elastic Inference なし 機械学習に効率化 クレジット仕様 なし 規定量の通信量が来た際に制限がなくなる ネットワークインターフェイス
プライマリIP 10.0.10.10 サブネットにプライベートIPアドレスを設定する。 「次のステップ:ストレージの追加」をクリック
項目 内容 説明(ざっくり) サイズ 8 ボリュームタイプ 汎用SSD 合わせて削除 チェック 暗号化 暗号化なし 「タグの追加」をクリック
項目 内容 説明(ざっくり) name fitO2_web ボリュームタイプ 汎用SSD インスタンス チェック ボリューム チェック 「セキュリティーグループの設定」をクリック
既存のセキュリティーグループを選択する。
先ほど作成した(fitO2_SG)を適用する。「確認と作成」をクリック
「起動」をクリック
キーペアの作成
新しいキーペアの作成(すでにキーペアを作っている場合は既存のものを作っても良い)
キーペア名を設定し、ダウンロード。
(ざっくり)キーペアとは、ssh接続する際に、使用する鍵。ssh接続する際にダウンロードしたキーペアを用いて接続し、AWS側にで生成した鍵と合致した場合接続が許可される。
「インスタンスの作成」
作成されたら、分かりやすいようにインスタンスに名前を設定しておきます。
nameのところをクリックしたら名前が編集できます。ダウンロードしたキーペアを適当な位置に移動させます。(今回は ~/.ssh)
Elastic IP(固定)アドレスを作成する。
現状インスタンスを停止したら、IPアドレスが変わってしまうので、固定IPを割り割り振ります。
EC2ダッシュボードから、Elastic IPを選択します。
「新しいアドレスの割り当て」をクリックします。
ネットワークボーダーグループ : ap-northease-1
パブリック IPv4 アドレスプール : Amazon の IPv4 アドレスプール「割り当て」をクリック。
作成したElastic IPアドレスに分かりやすい様に名前を付けておく。
Elastic IP(固定)アドレスをインスタンスに紐づける。
作成したIPアドレスにチェックを入れてアクションから「アドレスの関連付け」を選択する。
リソース : インスタンス
インスタンス : 作成したインスタンスを選択
プライベートIPアドレス : 10.0.10.10「関連付け」をクリック
ssh接続を試みて確認する。
インスタンスの状態が、runningになっていることを確認後
sudo ssh -i ~/.ssh/fitO2_key.pem ec2-user@固定IPアドレス~/.ssh/fitO2_key.pemは秘密鍵の保管している場所/秘密鍵の名前
固定IPは、インスタンス一覧から選択して、説明タグをクリックし、Elastic IPと表示されているものです。Are you sure you want to continue connecting (yes/no/[fingerprint])? yesと打ってenter__| __|_ ) _| ( / Amazon Linux 2 AMI ___|\___|___| https://aws.amazon.com/amazon-linux-2/上記のように表示されれば接続成功。
図にすると下記の様なイメージで接続しました。
EC2には先ほど作成したセキュリティーグループ(fitO2_SG)が適用されています。
インバウンド(入ってくる方の通信)に対し、ssh接続の場合、22番ポートを使用し、全てのアクセス元(0.0.0.0/0)を許可しているので、ssh接続することが可能だったわけです。
EC2インスタンスを作成し、ssh接続を行うことができました。
80番ポートもhttp通信に対し、開放されているので、今作成したインスタンスは下記のような通信を許可する状態となっています。
これでパブリックサブネットEC2インスタンスに対し、上記のようなセキュリティーグループが設定されました。
次回(5)へ続く
RDSを作成する
- 投稿日:2021-01-12T00:10:53+09:00
【Rails AWS Docker】既存Ruby on Rails + MySQLアプリをDockerで構築し、AWSにデプロイする(3)
ポートフォリをとして作ったRuby on RailsアプリをDockerコンテナ化し本番環境をAWSで構築するまでの道のりです。
ポートフォリオ自体はこちらとなります。
【ポートフォリオ】転職活動時に作成したポートフォリオの概要(テッ◯キャンプ)かなり苦しめられたので、どなたかのお役に立てれば。
タイトル 1 ローカル環境のRailsアプリをDockerコンテナ化 2 AWSにVPCを作成する。パブリックサブネットを作成する 3 プライベートサブネットを作成する 4 EC2インスタンスを作成する 5 RDSを作成する 6 DockerコンテナをAWSにアップロードする プライベートサブネットを作成します。
実は後ほど、理由は説明するのですがプライベートサブネットは異なるアベイラビリティーゾーンで2つ作る必要がありあります。
アベイラビリティーゾーンとは、ざっくりいうとクラウドが保管されているサーバーが実際に置かれている地域です。よって下記のような構成を作ります。
一つ目のプライベートサブネット作成します。
AWSコンソールからVPCダッシュボードに入り、サブネットを選択してサブネットを作成する。
VPC IDに先ほど作成したVPCを選択する。
アベイラビリティーゾーン:今回はap-northease-1a
名前 : fitO2_private_subnet_1a
IPv4 CIDR ブロック : 10.0.20.0/24
「サブネットを作成」をクリック
二つ目のプライベートサブネット作成します。
AWSコンソールからVPCダッシュボードに入り、サブネットを選択してサブネットを作成する。
VPC IDに先ほど作成したVPCを選択する。
アベイラビリティーゾーン:今回はap-northease-1c
名前 : fitO2_private_subnet_1c
IPv4 CIDR ブロック : 10.0.21.0/24
「サブネットを作成」をクリック
プライベートサブネット用のセキュリティーグループを作成する。
EC2ダッシュボードのセキュリティーグループをクリックして、「セキュリティーグループの作成」をクリックする。
「ルールの追加」をクリックします。
セキュリティーグループ名 : fitO2_db_SG
VPC : 先ほど作成したVPC
インバウンド :
タイプ : MYSQL/Aurora
ポート : 3306
ソース :
カスタム
パブリックサブネットに置かれたEC2インスタンスに適用する予定の(先ほど作った)セキュリティーグループを指定。(名前を打ち込んだら出てくる。出てこなければグループIDをコピペ)これで、プライベートサブネット2つと、それに適用させる予定の下記の様なセキュリティーグループの作成が完了しました。
次回(4)へ続く
EC2インスタンスを作成する |