DBサーバーを構築(AWS、RDS)
内容
AWSでDBサーバーを構築する
基礎知識
- RDS
→フルマネージドなリレーショナルデータベースのサービス(AWSが運用管理までしてくれる)
→オンプレミス(自分でDBサーバーを構築)やOn EC2(EC2上にMySQLをインストールしDBサーバーとして運用)よりも、構築や運用の手間が軽減され、コア機能の開発に注力できるというメリットがある
→高い可用性(複数のアベイラビリティゾーンにマスターとスレイブを設計し、マスターの接続ができなくなった時でもスレイブが動く)
DBサーバーを構築
1. プライベートサブネットを作成
2. RDSを設置(作成準備)
- DB用のセキュリティグループを作成
- EC2ダッシュボードでセキュリティグループタブを開く
- セキュリティグループの作成→セキュリティグループ名と説明を入力、VPCは作成したもの→インバウンドルールで追加→タイプ「MySQL/Aurora」、ソース「作成した〜-webのセキュリティグループ」(Webサーバーのインスタンス)→作成
- RDSのダッシュボードでサブネットグループタブを開く
- DBサブネットグループの作成→名前と説明を入力、VPCを関連付け→アベイラビリティゾーンで1aと1c、サブネットもプライベートの番号の方を2つ選択→作成
- パラメーターグループタブを開く
- パラメーターグループを作成→パラメーターグループファミリー「mysql8.0」、名前と説明を入力→作成
- オプショングループタブを開く
- グループを作成→名前と説明はパラメーターグループと同じにし、エンジンは「mysql」、メジャーエンジンバージョンは「8.0」→作成
3. RDSを設置(作成)
- RDSダッシュボードでデータベースタブを開く
- データベースの作成→エンジンのオプションは「MySQL」の最新バージョンを選択→テンプレートは今回は「無料利用枠」→DBインスタンス識別子は「〜-web」→マスターユーザー名とパスワードを設定→ストレージの自動スケーリングは無効にしておく→接続は自身で設定したものを選択、パブリックアクセスはなし、セキュリティグループは「既存のdbの方」、アベイラビリティゾーンは「1aの方」→データベースポートは「3306」→追加設定でDB パラメータグループとオプショングループを設定、バックアップは有効にし長めに設定しておく→あとはマイナーバージョン自動アップグレードの自動化だけチェック→作成
※自動スナップショットは設定期間が過ぎると消える(RDSインスタンスを削除しても一緒に削除される)が、手動で取得したスナップショットは削除されない(RDSインスタンスを削除しても)→存在していると料金がかかるのでRDSインスタンスを削除した際は、スナップショットも削除する
※RDSインスタンスの停止は、本番環境ではしない方が望ましい
4. WebサーバーからRDSに接続
ターミナル
[ec2-user@ip-〜]$ sudo yum -y install mysql
- RDSダッシュボードでデータベースを開く
- 作成したDBを開き、エンドポイントをコピー
ターミナル
[ec2-user@ip-〜]$ mysql -h エンドポイント -u root(マスターユーザー名) -p Enter password: exit; # 終了
次回
通信について
ドメインを登録(AWS、Route53)
内容
基礎知識
- ドメイン
→インターネット上に存在するコンピューターやネットワークを識別するための名前(インターネット上の住所)
→IPアドレスは数字の列なので非常に覚えにくい、そこでドメイン名を用いてWebサイトにアクセスできるようにする
→ICANNがドメイン全体を管理し、レジストリがトップレベルドメインを管理、さらにその下のレジストラ、もしくはレスラからドメインは購入する - DNS(Domain Name System)
→ドメイン名の管理システムで、ドメイン名をIPアドレスに変換する
→Webサイトを見るには、ドメインとサーバー(IPアドレス)をDNSで紐付ける必要がある
→ドメイン名とそれに紐づくIPアドレスを登録しているネームサーバーと、「ドメインに紐づくIPアドレスを教えて」を問い合わせると色々なネームサーバーに聞いてIPアドレスを調べて教えてくれるフルリゾルバ、の2種類がある - Route 53
→AWSのDNSサービスで、ネームサーバーの役割を果たす
→SLA100%のため、稼働率100%でRoute53が落ちることはないと保証している
ドメインの登録
1. ドメインの購入
- 迷ったら「お名前.com」で購入しておけば無難
- お名前.comから指定したドメイン名で購入する
- 1年目はキャンペーン等で安くなっていることが多いが、2年目からの料金もチェックしておくことが大事
- 購入後はドメイン設定から、自動更新設定を解除しておく
2. Route53でDNSを設定
- Route53のダッシュボードで、ホストゾーンタブを開く
- ホストゾーンの作成→ドメイン名とタイプ「パブリックホストゾーン」を選択し、作成
- EC2ダッシュボードで、インスタンスタブを開く
- インスタンスを起動する
- ssh -i 〜でログイン
ターミナル
# ネームサーバーを調べる [ec2-user@ip-〜]$ dig ドメイン名 NS +short
- ネームサーバーをお名前.comからRoute53に変更
- お名前.comのHPにログインし、ドメイン一覧→ネームサーバー→他のネームサーバーを利用をクリック
- ネームサーバーをRoute53のNSで表示されていた値に変更(最後の.は入力しなくてよい)→設定
- 設定完了まで数日かかるので、後でネームサーバーが変更できているかターミナルでコマンド入力して確認しておく
- Route53のダッシュボードに戻る
- レコードセットの作成→値にパブリックIPアドレスを入力→作成
次回
DBサーバーを構築
Webサーバーを構築(AWS、EC2)
内容
EC2でWebサーバーを構築する
基礎知識
- EC2(Elastic Compute Cloud)
→AWSクラウド上の仮想サーバー
→インスタンスとはEC2から立てられたサーバーのこと - AMI(Amazon Machine Image)
→インスタンス起動に必要な情報が入ったOSのイメージ、サーバーのテンプレートのようなもの - インスタンスタイプ
→サーバーのスペックを定義したもの
→アクセス数等に応じて必要なスペックのタイプを選択し、スペックにより料金が異なる - ストレージ
→サーバーにくっつけるデータの保存場所
→EC2のストレージには、EBS(Elastic Block Store)とインスタンスストアの2種類ある - Elastic IP
→AWSから割り振られた固定の「パブリックIPアドレス」のこと
→このパブリックIPアドレスをEC2インスタンスに紐付けることで、インスタンスの起動・停止にかかわらず常に同じIPアドレスで通信をすることが可能となる - SSH
→サーバーと自分のパソコンをセキュアに繋ぐサービスのこと(EC2にログインする時はSSHを使用する)
→通信内容が暗号化された遠隔ログインシステム - ポート番号
→プログラムのアドレスで、同一コンピューター内で通信を行うプログラムを識別する時に利用される(住所でいう部屋番号みたいなもの) - ウェルノウンポート番号
→代表的なプログラムが使うポート番号で、0〜1023までの整数値をとる
例)SSHは22番、SMTPは25番、HTTPは80番、HTTPSは443番 - 動的に決まる番号
→49142〜65535までの整数値をとる - ファイアウォール
→ネットワークを不正アクセスから守るために、「通してよい通信だけを通して、それ以外は通さない」機能の総称
→AWSでは、セキュリティグループがファイアウォールの役割を担っている - yumコマンド
→Linuxにおけるソフトウェア管理の仕組みで、MacOSにとってのhomebrewと同じ役割を果たす
→yumコマンドを利用することで、yumの管理下にあるプログラムのバージョンを管理したり、一括でアップデートしたりできる - パッケージ
→LinuxOS下におけるある役割/機能をもったプログラムの集合のことで、「ソフトウェア」や「ライブラリ」とも呼べる - -yワイオプション
→yum install などのコマンドでは、本当にインストールして良いのか [y/n](「Yes or No」の意味)が問われるので、あらかじめオプションで「-y」を設定する事で「すべての問いにYesで自動的に答える」という設定してコマンドを実行している
Webサーバーの構築
1. EC2インスタンスを設置
- EC2を開き、インスタンスを選択
- インスタンスを起動
- AMIを選択→クイックスタートからAmazon Linux 2を選択
- インスタンスタイプの選択→無料利用枠のt2.microを選択
- インスタンスの詳細の設定→ネットワークは設定したpvc、サブネットはpublic、自動割り当てパブリックIPは有効、キャパシティの予約はなし、ネットワークインターフェイスのプライマリIPを設定したIPアドレスの範囲内で設定
- ストレージの追加
- タグの追加→キーを「Name」、値を任意で設定
- セキュリティグループの設定→セキュリティグループ名を先ほど設定した値とする→確認と作成
- インスタンス作成の確認→起動
- キーペアを作成→新しいキーペアの作成からキーペア名を任意でを設定→キーペアのダウンロード→インスタンスの作成
2. Elastic IPを作成
さきほど作成した「EC2インスタンス」には作成時にIPアドレスが自動で割り振られている、これを「パブリックIP」と言う
しかし、サーバーを再起動させるたびにこのパブリックIPが変わってしまうという欠点を持っていて、IPが変わってしまうと、設定ファイル等をその都度書き換えなければならない
これを解決してくれるのが「Elastic IP」
※インスタンスが停止していても、Elastic IPアドレスが複数存在する場合は料金が発生するため、インスタンスを停止するときには、そのインスタンスと紐づいているElastic IPアドレスも解放する必要がある
- Elastic IPタブを開き、Elastic IP アドレスの割り当てをクリック
- Amazon の IPv4 アドレスプールにチェックが入ってることを確認後、割り当てをクリック
- 取得したElastic IPアドレスとEC2インスタンスを紐付け→右上にあるアクションをクリックし、Elastic IP アドレスの関連付けを選択
- インスタンスにチェックが入っていることを確認→インスタンスの入力欄をクリックし、表示されたインスタンスIDを選択→関連付けるをクリック
- Elastic IPが設定されているか確認
3. ファイアウォールを設定
- 現状SSH以外ではWebサーバーにアクセスできない
- EC2ダッシュボードのインスタンス→セキュリティ→セキュリティグループ→インバウンドルールの編集
- ルールの追加→タイプ「HTTP」、ソース「0.0.0.0/0」と「::/0」→保存
4. Apacheをインストール
- SSHでサーバーにログイン
ターミナル
% cd ~ % mkdir ~/.ssh # .sshというディレクトリを作成 # File existsとエラーが表示されたとしても、.sshディレクトリは存在しているのということなので、そのまま進む % mv Downloads/ダウンロードした鍵の名前.pem .ssh/ # mvコマンドで、ダウンロードしたpemファイルを、ダウンロードディレクトリから、.sshディレクトリに移動する # 「ダウンロードした鍵の名前」の部分は、Finderでダウンロードフォルダから「〜.pem」というファイルを探し、「〜」の部分の名前に置き換える % cd .ssh/ % ls # pemファイルが存在するか確認 % chmod 600 ダウンロードした鍵の名前.pem # 「ダウンロードした鍵の名前」はFinderのダウンロードフォルダから探す % ssh -i ダウンロードした鍵の名前.pem ec2-user@作成したEC2インスタンスに紐付けたElastic IP # 「作成したEC2インスタンスに紐付けたElastic IP」は自身のものに編集
- Elastic IPを設定していない場合は以下の通りパブリック IPv4 アドレスをコピーして記述する
- マネジメントコンソールでEC2ダッシュボードのインスタンスを選択
- 作成したインスタンスのパブリック IPv4 アドレスをコピー
- 初めてログインするサーバーの場合信頼できるかyes/noで聞かれるため、yesとうつ
※ssh接続は、一定時間操作をせずにいるとタイムアウトしてしまう、その場合は「ssh -i 〜」のコマンドを再度実行することでサーバーに接続できる - 何も作業がない時は「exit」でログアウトしておく
- 再度ログイン(ssh -i 〜)
sudo lsof -i -n -P
コマンドで右端のNAME欄で、LISTENを書かれているものが受け入れているポート番号- Apacheをインストール
ターミナル
# パッケージをアップデート [ec2-user@ip-10-0-10-10 ~]$ sudo yum update -y # Apacheをインストール [ec2-user@ip-10-0-10-10 ~]$ sudo yum -y install httpd # Apacheを起動 [ec2-user@ip-10-0-10-10 ~]$ sudo systemctl start httpd.service # 起動しているか確認 [ec2-user@ip-10-0-10-10 ~]$ sudo systemctl status httpd.service # その他の確認方法 [ec2-user@ip-10-0-10-10 ~]$ ps -axu # サーバー起動時にApacheも起動されるように設定 [ec2-user@ip-10-0-10-10 ~]$ sudo systemctl enable httpd.service # 自動起動できているか確認 [ec2-user@ip-10-0-10-10 ~]$ sudo systemctl is-enabled httpd.service
以上でWebページを表示するところまで実装
5. 使用しない時は無料枠を超えないように設定変更
- Elastic IPアドレスの解放
→EC2ダッシュボード→Elastic IP→アクション→アドレスの関連付けの解除→アドレスの解放 - EC2インスタンスの停止
→EC2ダッシュボード→インスタンス→インスタンスの状態→停止
※インスタンスを停止するとEIastic IPアドレスは関連付けられなくなるので、あらかじめEIastic IPアドレスを解放し課金されないように気をつける
次回
ドメインを登録
ネットワークを構築(AWS)
内容
AWSのネットワークについて
基礎知識
- リージョン
→AWSの各サービスが提供されている地域のこと
基本アメリカが最先端のサービスを提供するが、日本にいるなら東京を選択するのが良い) - アベイラビリティゾーン
→独立したデータセンターのこと - VPC(Virtual Private Cloud)
→AWS上に仮想ネットワークを作成できるサービス - サブネット
→VPCを細かく区切ったネットワーク
→パブリックサブネットはインターネットから接続できて、プライベートサブネットはインターネットから接続できない - IPアドレス
→ネットワーク上の機器を識別するためのインターネット上の住所 - パブリックIPアドレス
→インターネットに接続する際にしようするIPアドレス
→重複すると正しく通信できなくなるのでICANNという団体が管理しており、プロバイダーやサーバー事業者から貸し出される - プライベートIPアドレス
→インターネットで使用されないIPアドレス
→決められた範囲内のアドレスを自由にしようすることができる
ネットワーク構築
1. VPC作成
2. サブネットを作成
- IAMユーザーでサブネットを開く
- 新規作成
- 先ほど作成したVPCを選択し、name、アベイラビリティゾーン、IPv4 CIDR ブロックを設定
- 同じ手順でパブリックサブネットとプライベートサブネットを作成
3. ルーティングを設定
- IAMユーザーでインターネットゲートウェイを開く
- 新規作成
- nameを設定
- VPCにアタッチ
- IAMユーザーでルートテーブルを開く
- 新規作成
- nameとVPCを設定し、作成
- 作成したルートテーブルで、「サブネットの関連付け」タブを開き、編集をクリック
- public-subnetを選択し、保存
- 作成したルートテーブルで、「ルート」タブを開き、編集をクリック
- ルートの追加で、デフォルトルートをインターネットゲートウェイに設定し、保存
以上により、パブリックサブネットからインターネットに接続できるようになる
考慮するポイント
- プライベートIPアドレス範囲から指定する
→VPCでは仮想のプライベートネットワーク空間を作成するので、プライベートIPアドレスの使用が推奨 - 作成後は変更できないので、大きめに設定
→大きさは/28〜/16だが、/16が推奨 - オンプレミスや他VPCのレンジと重複しないように注意
→相互接続する可能性がある場合は、重複しないように設計 - 異なるシステムの場合はアカウントを分ける
→異なるシステムを同一アカウントで管理すると煩雑になる - サブネットの設計は、将来に必要なIPアドレス数を見積もって設定
→/24が標準的
次回
Webサーバーを構築
画像複数投稿機能(swiperの導入)
内容
画像複数投稿機能の実装
画像複数投稿機能
画像単一投稿から複数枚投稿に編集する
1. アソシエーションを変更
has_many_attachedメソッドを用いることで、投稿と画像の間に1対多の関係を設定
app/model/post.rb
class Post < ApplicationRecord has_many_attached :images end
2. フォームを編集
複数の画像を送信できるように、フォームを編集
単一画像の投稿では:imageとなっていたが、複数の画像に対応できるように編集し、複数投稿できるようにするため、multiple: trueを使用
app/views/posts/new.html.erb
<%= form_with model: @post_address, url: posts_path, local: true do |f| %> <%= f.file_field :images, multiple: true, class:"map-image", id:"map-image" %> <% end %>
3. ストロングパラメーターを編集
送信されてきたparamsをコントローラで受け取り、こちらも画像の配列を受け取れるように変更
app/controllers/posts_controller
private def post_params params.require(:post_address).permit(:map_link, {images: []}, :distance, :course, :slope, :traffic, :crowd, :view, :comment, :postal_code, :prefecture_code, :city, :street).merge(user_id: current_user.id) end
4. 保存した画像を表示できるようにする
app/views/posts/index.html.erb
<% post.images.each do |image| %> <%= image_tag image, class: 'post-image' if post.image.attached? %> <%= image_tag 'noimage.png', class: 'post-image' unless post.image.attached? %> <% end %>
5. スライダー機能「swiper」を導入
公式サイトを参考にswiperを導入する
swiper
やり方はいくつかあるが、今回はCDNでswiperを読み込む(単体で動くのでjQuery本体を読み込む必要はない)
application.html.erb
<head> <link rel="stylesheet" href="https://unpkg.com/swiper@7/swiper-bundle.min.css"/> <script src="https://unpkg.com/swiper@7/swiper-bundle.min.js"></script> </head>
6. HTMLを編集
公式サイトを引用し、必要なものを追加(今回スクロールバーは使わず)
一覧ページと詳細ページでスワイパーを使用するため、部分テンプレートに記述する
_swiper.html.erb
<% if post.images.length > 1 %> <div class='swiper'> <div class="swiper-wrapper"> <% post.images.each do |image| %> <div class="swiper-slide"> <%= image_tag image, class: 'swiper-image' if post.images.attached? %> </div> <% end %> </div> <div class="swiper-pagination"></div> <div class="swiper-button-prev"></div> <div class="swiper-button-next"></div> </div> <% elsif post.images.length == 1 %> <div class='back-image'> <% post.images.each do |image| %> <%= image_tag image, class: 'post-image' if post.images.attached? %> <% end %> </div> <% else %> <div class='back-image'> <%= image_tag 'noimage.png', class: 'post-image' unless post.images.attached? %> </div> <% end %>
上記の条件分岐により、投稿画像が2枚以上あればスワイパー機能を使用し、1枚以下であれば画像を貼り付けるだけとした
7. CSSを編集
swiper.cssを作成し、画像をスワイプできるように実装
swiper.css
/* どんな画像でも高さが一定になるように指定し、枠内に収まるように記述 */ .swiper-slide { height: 0; overflow: hidden; padding-bottom: 75%; background-color: #f2f2f2; position: relative; } .swiper-image { position: absolute; top: 0; left: 0; bottom: 0; right: 0; margin: auto; height: 100%; width: auto; } /* 矢印ボタンをわかりやすいものに変更(ネットで調べたものを引用) */ .swiper-button-prev { background: url(https://haniwaman.com/wp/wp-content/uploads/2018/05/swiper3.png) no-repeat center center / contain; } .swiper-button-next { background: url(https://haniwaman.com/wp/wp-content/uploads/2018/05/swiper4.png) no-repeat center center / contain; } /* デフォルトの矢印ボタンを消去(!importantは、cssのどこに記述しても優先順位が先になるという記述) */ .swiper-button-prev::after, .swiper-button-next::after { content: none !important; } /* 画像が1枚以下の時は以下の記述で、背景画像とその枠内に画像を収めるようにする */ .back-image { height: 0; overflow: hidden; padding-bottom: 75%; background-color: #f2f2f2; position: relative; } .post-image { position: absolute; top: 0; left: 0; bottom: 0; right: 0; margin: auto; height: 100%; width: auto; }
画像が2枚以上の時と1枚以下の時でclass名を同じにしたらビューが崩れてしまったため、別々にclassを指定し同じcssを記述した
親要素(div)が影響している?
8. jsを編集
swiper.jsを作成し、application.jsで読み込む
swiper.jsには公式サイトで記述されていたコードを引用し、使用する動作により修正を加える
application.js
require("../swiper")
swiper.js
// 公式サイトの記述だけでは矢印ボタンを押してもスワイプできなかったので1行目を追記 window.addEventListener('DOMContentLoaded', function(){ const swiper = new Swiper('.swiper', { // 画像をループさせる loop: true, // 何枚目の画像かわかるようにし、クリックによるページ遷移を有効にする pagination: { el: '.swiper-pagination', type: 'bullets', clickable: true, }, // ページ遷移させる navigation: { nextEl: '.swiper-button-next', prevEl: '.swiper-button-prev', }, }); });
所感
swiperもしくはslickを用いてスライダー機能の実装を試みた
ネットにはかなり情報があったが、バージョンが古かったり、自分のコードとは少し異なったりで、導入するのにかなり苦戦した
動かない時はなぜ動かないのかを理解できるようにならないと、今後も同じように時間がかかってしまうと思った
日々勉強
住所検索(ransack)
内容
投稿を住所で絞り込めるように実装する
住所検索機能
1. ransackで関連するモデルのカラムを検索する
フォームタグの要素名に、関連するモデル名関連するモデルのカラム名を指定する
postモデルに紐づくaddressモデルのカラム名(postal_code)を検索条件にしたい時、f.フォームヘルパー :要素名、の要素名をaddress_postal_code_eqとする
search.html.erb
<%= search_form_for @q, url: posts_path do |f| %> <%= f.text_field :address_postal_code_eq %>
2. ビューを編集
投稿一覧/詳細ページに住所を追加する
ページネーションのデザイン変更
ページネーション機能はkaminariというgemを用いて実装しているが、デザインがいまいちなので自分でカスタマイズしてみる
1. kaminariのviewファイルを作成
viewフォルダの中にkaminariのview一覧を作成
ターミナル
rails g kaminari:views default
2. 日本語化
kaminariのgemのデフォが英語になっているので、これを日本語環境に変える
config/application.rb
config.i18n.default_locale = :ja config.i18n.load_path += Dir[Rails.root.join('config', 'locales', '**', '*.{rb,yml}').to_s]
一行目は、日本語環境を適用、二行目は複数のlacaleファイル(後述)が適用されるコード
次にconfig/localesフォルダーにkaminari.ja.ymlを作成し、コード記述
config/locales/kaminari.ja.yml
ja: views: pagination: first: "« 最初" last: "最後 »" previous: "‹ 前へ" next: "次へ ›" truncate: "..."
3. 該当のcssファイルを編集
index.css
.pagination{ display: flex; justify-content: flex-start; } .pagination span{ width: 3em; background-color: #fff; text-align: center; border: solid 1px #ddd; margin-right: 0.3vw; transition: .3s; -webkit-transform: scale(1); transform: scale(1); } .pagination span.current{ background-color: limegreen; color: #fff; } .pagination span:hover{ background-color: #ddd; transition: .3s; -webkit-transform: scale(1.1); transform: scale(1.1); }
4. 投稿件数を表示したい場合
投稿何件中何件表示、という表示をさせたい時は以下のように記述する
index.html.erb
<%= @posts.total_count %>件中<%= @posts.offset_value + 1 %>〜<%= @posts.offset_value + @posts.length %>件表示
次回
画像複数投稿機能を実装
Formオブジェクトのテストコード
内容
Formオブジェクトパターンのテストコードを実装する
テストコード
1. テストのファイルを作成
ターミナル
% rails g rspec:model post_address
2. FactoryBotを生成
3. テストコードを記述
4. テストコードを実行
ターミナル
% bundle exec rspec spec/models/post_address_spec.rb
Formオブジェクトの日本語化
独自に定義したFormオブジェクトを日本語化したい時
ja.ymlにてactiverecordアトリビュート以下に書いても反映されない
ja.yml
activerecord: attributes: post_address: postal_code: 郵便番号 prefecture_code: 都道府県 city: 市区町村 street: 番地
正しくは、activemodelアトリビュート以下に書く
ja.yml
activemodel: attributes: post_address: postal_code: 郵便番号 prefecture_code: 都道府県 city: 市区町村 street: 番地
次回
投稿を住所で絞り込めるように実装する