優総研

ポートフォリオ制作なうなんだが…その2

 

 

 

 

最短で完成を目指す。

 

※雑記。現状は参考?にするのはおすすめしません。最後に整理します。

 

もくじ

内容

本の感想投稿掲示板 + Google Books APIとのマッシュアップ

他書評サービスのコミュニティ機能があまり使われておらず成功していないから、Yomuyoは本好きの間で小さくてもコミュニケーションが生まれるものにしたいな。それがこのサービスの意義。

 

  • 収益という目的性があるよって存在意義
    →Amazon APIの売り上げ条件の為採用を断念
  • おすすめの本というコミュニティ
  • Google Books APIがアプリの外見に華やかさを与えてくれる

※Amazon のAP API利用は売上実績がないとAPI利用でエラーになるので断念。Google Book APIでスタートさせて、Amazonアフィリエイトで実績を十分に作れたら、PA APIに移行するのが良いかな?

 

機能

  • Google Books API連携 + Amazonアソシエイト
  • 検索
  • 書評投稿
  • 会員機能 + 投稿
  • 書評ランキング
  • レビュアランキング
  • レコメンド(余裕があれば実装したい)

シンプルに最短で実装する為に機能を一時的にそぎ落とす。

キャッシュ戦略

  • Google Books APIのキャッシュ
    外部APIに負荷を与え過ぎて利用停止されないように、Google Books APIでの問い合わせ結果はElasticache(Redis)に1週間分をキャッシュする。
    →検索結果が高速化してUXも向上
    // まだGoogle Books APIは良いけれど、Amazon PA APIはシビアなので必須。
  • よくある参照結果もキャッシュ
    トップページなど30秒~1分程度キャッシュします。
    →DBへの負荷軽減と高速化。

 

 

技術要素

AWSのフルマネージドとSaaSでなるべくサーバレス構成にしています。

  • 開発ツール:VS Code + Git 済
  • 開発環境[ 構築|更新|テスト]自動化: Bash Shellスクリプト + docker-compose 済
  • 機密情報隠蔽化:KMS + AWS Systems Manager パラメータグループ 済
  • 自動デプロイ:Docker => GitHub => CodePipeline => CodeBuild => ECR +ECS 済
  • CI + ChatOps:GitHub => CodePipeline => CodeBuild + PHPUnit + [ CloudWatch Events => Lambda+KMS => Slack ] 済
  • フレームワーク:Laravel 済
  • データベースGUI管理ツール:phpMyAdmin on Docker 済
  • 言語:PHP7 済
  • バッチ処理:Lambda, CloudWatch(定期スナップショット) 済
  • RDBMS:RDS(MySQL) 済
  • NoSQL:Elasticache for Redis(セッションサーバ) 済
  • オブジェクトキャッシュ:Elasticache for Redis + Laravel 済
  • フロント:Vue.js + Laravel
  • CDN:Laravel + CloudFront + S3 済
  • ログ分析基盤:Cloudwatch + S3 + Athena, Elasticsearch+kibana 済
  • 日時ロギング:CloudWatch Logs(LogGroup)  => Lambda => S3
  • DNS:Route53 済
  • 証明書自動更新:Route53 + ACM + [ ALB|CloudFront ] 済
  • メール送信管理:SendGrid 済
  • メッセージキュー:Laravel + AWS SQS
  • 監視:Mackerel => [ Slack|Twilio ], CloudWatch Alert => SNS => Lambda => Slack
  • 負荷試験:Jmeter, Apache Bench, Locust
  • セキュリティ:ELB+AWS WAF(Trend Micro Managed Rules for AWS WAF) 済
  • ソーシャルログイン(Laravel Socialite+[Facebook, Twitter]) 済
  • いいねボタン:Ajax + jQuery + Laravel 済
  • マークアップ, グリッド:HTML/CSS, Bootstrap4, Laravel Blade 済
  • ER図 自動作成・更新:SchemaSpy on Docker + Nginx 済

 

 

ローカル or EC2

  • local(開発)
    EC2 + docker-compose
    ローコストで開発&確認

ECS

GitHubのブランチ名でデプロイ先を分ける

  • staging(検証)
    ECS(staging)+EC2
    本番と同じ構成でテストして表示や動作を確認します。
  • production(本番)
    ECS(production)+Fargate

Fargateにするとホスト管理の開放、スケールを気にしなくて良い。

 

Laravel

バージョン:5.8

  • CRUD + Eloquent ORM  済
  • リレーション
    tinker, hasMany, belogsTo, many to many,
  • Eagerローディング
    with()によるN+1対策
  • トランザクション 済
    ロック処理,ロールバック, リトライ
  • ログイン 済
  • Ajax + jQuery いいねボタン 済
  • ランキング機能 済
  • ソーシャルログイン(Facebook, Twitter) 済
  • セッション 済
    relfresh(), old()
  • バリデーション 済
    Request, 
  • アクセス管理 済
  • ストレージ + S3連携 済
  • try catch + ログ出力 済
  • 単体テスト(PHPUnit)
  • ページネーション 済
  • サービスプロバイダ(DI) 済
  • メール送信 済
  • システム管理者用ページ
    Vue.js + RestAPI(Laravel)で作る?
  • ジョブキュー・イベント + AWS SQS
  • Artisan
    auth, request, response, rule, middleware, scope, migrate, seeder, tinker,

 

おすすめ書籍

 

 

 

Slack通知

Codebuildが通ったら、slackに通知するようにする

 

 

「Incoming  Webhook」で検索して【インストール】を選択します。

 

 

 

 

 

Webhook URLを得られます、後で利用するのでメモしておきましょう。

https://は含めずに、「hooks.slack.com/ser…」をメモしてください。

 

 

 

IAM Role Lambda実行用ロールの作成

 

 

 

 

 

 

 

 

 

 

KMS 暗号化用キーの作成

Key Management Service(KMS)で環境変数を暗号化する為のキーを作成します。

【カスタマー管理型のキー】から【キーの作成】をクリックします。

 

「lambda-encrypter-key」と入力して【次へ】をクリックします。

 

 

【次へ】をクリック

 

先ほど作成した【lambda_fullaccess_role】を選択して【次へ】をクリックします。

 

 

【完了】をクリックします。

カスタマー管理型のキー(CMK)が作成された。次のLambdaの設定で利用します。

 

Slack通知用 Lambda関数の作成

 

「cloudwatch slack」で検索して「cloudwatch-alarm-to-slack-python」を見つけます。

「設定」をクリックします。

 

 

 

  1. slackChannel:#yomuyo
  2. kmsEncryptedHookUrl:WEBhookURLの内容
  3. 伝送中に暗号化するAWS KMSキー:lambda-encrypter-key

3つを入力してから、kmsEncryptedHookUrlの項目で【暗号化】をクリックします。

 

 

 

Slack通知用のLambda関数が作成されました。

 

Cloudwatch Events

 

Codebuild用ルールの作成

 

 

 

 

名前を「Codebuild-notify-Slack-Rule」にして【ルールの作成】をクリックします。

 

 

 

Lambdaに戻る

Lambda関数を差し替える

 

テスト実行用JSON

 

 

  1. テスト実行を行い、Slackに通知されるか
  2. GitHubにpushしてSlackに通知されるか

2つともクリアで設定完了

 

ログ分析基盤

 

Elasticsearch Service

 

 

例の如く、「t2.small.elasticsearch」で最小インスタンスを狙っていきます。

 

 

 

 

アクセス許可を行うIPを指定します。

 

 

 

きちんと作成されるまでには10分程度かかる。

 

待つのじゃ。

 

Kibana

ElasticsearchのKibanaのURLをクリックする

 

Kibanaにアクセスできる。

 

 

Cloudwatch Logs

Dockerのログについて、Nginx, PHP-FPMコンテナのログ標準出力になっており、awslogsドライバを通してCloudwatch Logsに出力される。

 

S3 + Athenaへ

S3にエクスポートできる。S3にエクスポートしたら「Athena」でクエリ検索が可能

ただし、この場合は手動になってしまうので、実際はLambdaで定期的にバッチ処理するか、Kinesis FirehoseからS3にエクスポートされるようにする。

 

Elasticsearch Serviceへ

 

「Elasticsearch Service」へ連携し、Kibanaで可視化できる。

 

 

 

 

 

 

 

  • AWSLambdaVPCAccessExecutionRole

ポリシーを付与します。

 

 

 

【次へ】をクリックしたら画面が遷移するので、【ストリーミングの作成】をクリックする。

 

 

 

RDS(MySQL8.0)

本当は絶対Auroraを使いたいところだけれど、お財布に厳しいのでリーンスタートとしてRDSのMySQL8.0で行きます。

 

パラメータグループの作成

 

これでパラメータグループ作成は完了。

 

 

 

 

 

 

 

 

 

 

 

データベースの作成

開発用EC2, ECSのインスタンスと同じVPCを選択しよう。

 

 

先ほど作成したパラメータグループを指定します。

 

 

 

 

 

 

 

RDSの書き込みエンドポイントをCNAMEで登録する

Route53に

  • 名前:db-master.yomuyo.net
  • タイプ:CNAME
  • 値:<RDSエンドポイント>

こうしておくとわかりやすいです。

 

セキュリティグループ

 

MySQL/Auroraに対して、開発用EC2, ECSのインスタンスのセキュリティグループIDをソースに指定する。

これでEC2からRDSにセキュアに接続できるようになる。

 

 

.gitignoreについて

 

 

 

GitHubにアップロード

ECSまで自動デプロイされる。

 

本番のALBにアクセスして問題ないことを確認する
http://yomuyo-alb-xxx.ap-northeast-1.elb.amazonaws.com/

 

 

Route53 + ACM + ALBで自動証明書更新環境

 

Route53

「yomuyo.net」のホストゾーンを作成します。

 

 

【レコードセットの作成】をクリックします。

 

 

www.yomuyo.netとALBをエイリアスで紐づけます。CNAMEよりエイリアスのほうが通信コスパが良いです。

 

 

yomuyo.netとALBをエイリアスで紐づけます。

 

 

これでRoute53の設定はおしまい。

 

 

バリュードメインのDNSサーバの設定にAWSのネームサーバを設定します。

 

http://yomuyo.net/

これでアクセスが出来ました。

 

ACM(Certificate Manager)

AWSの証明書発行フルマネージドサービス

 

「yomuyo.net」, 「www.yomuyo.net」を取得します。

 

 

 

 

 

 

 

 

10分程度待ってからリロードさせてください。

 

証明書が発行されていますね!

 

ALBにACM証明書を適用する

 

【保存】をクリックします。

 

https://yomuyo.net/

アクセス出来ました。
※アクセスできない場合はセキュリティグループのインバウンドに443で接続できる設定になっているか見直してください。

 

HTTP(80)のアクセスをHTTPSにリダイレクトする

一昔前はNginx側でリダイレクトしていたけれど、今はALBのルールから設定が行えます。

 

 

 

 

 

 

http://yomuyo.net/にアクセスした時に、

https://yomuyo.net/にリダイレクトがかかれば設定は大丈夫。

 

ネイキッドドメインをhttps://www.<ドメイン名>にリダイレクト

443のルールをこのようにすると良い。

https://example.net/
→https://www.example.net

 

MackerelでECSの監視

https://mackerel.io/ja/

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

MACKEREL_APIKEYをメモに控えます。この値は後でECSで利用します。

 

 

ECS側設定

タスク定義から、mackerel-container-agentのコンテナを追加する形でMackerelと紐づけることが出来ます。

 

 

 

 

 

  • コンテナ名
    mackerel-container-agent
  • イメージ
    mackerel/mackerel-container-agent:latest
  • メモリ制限 ハード制限
    128

 

環境変数

  • MACKEREL_CONTAINER_PLATFORM
    ecs_v3
  • MACKEREL_APIKEY: Mackerel API
    ※さっき控えたAPIキーの値

 

 

 

 

【サービスの更新】からウィザードに沿って次へと進めて、反映を行ってください。

 

Mackerelにホストが追加されました。

 

コンテナ毎にグラフも見れるわけです。

 

MackerelからSlackへの通知

メトリクスを監視します。

 

CPUの使用率監視

 

 

 

 

SWAP監視

 

 

 

 

以前Slackで作成していたいWebhookのURLを利用することでMackerelとSlackの連携を行います。

 

これで連携が出来ました。

 

 

MackerelからTwillioに通知する

(姓と名が逆だけれど、これは後で直せるYO!)

https://twilio.kddi-web.com/signup/

サインアップで進めていきます。

 

 

 

 

SendGrid メール送信管理

【Setting】=> 【Sender Authentication】=>【Domain Authentication】を選択します。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

逆引き設定について

  • Proプランかつ固定IPアドレス追加で予算オーバーになるんだが!
    →導入せず
  • 実際のエンタープライズな利用では逆引き設定は必ず設定しましょう

 

Web API Keyの取得

【Setting】=> 【API Keys】=>  【Create API Key】

 

 

※SendGridのAPIキーは漏らさないようにしましょう、APIキーだけで第三者にSendGridが使われます。

 

curlコマンドでAPIキーで送信できるかをテストします。

メールが受信できたので作業を進めます。

 

 

LaravelでSendGrid APIによるメール送信

 

AWS System ManagerのパラメータストアにAPIキーを設定する

コンテナでの環境変数の値を確認

ECS配下のEC2にSSHログインする。

 

PHP-FPMのコンテナにログインする

 

環境変数を確認する

 

SendGridのAPIキーやDBの接続情報が入っていればOK

 

 

SendGrid-PHPライブラリのインストール

 

アップデートと同時にインストールも行われる

 

※リードレプリカする予算はないので、Slave(Read)もマスターのドメインを利用します。

貧乏が悪いのだ。

 

 

 

 

アクセスすると送信できる
https://<ドメイン名>/contact/

 

セッション管理 ElastiCache Redis

  • ALBのスティッキーセッションで良いんじゃない?って実装してトラブルになっている事例を良く聞く。
  • セキュリティエンジニアではないけれど、Cookieの値をクライアント側にそのまま持たせるのはセキュリティ上良くないことは知っている。

  • クラスタ構成の場合は素直にセッションサーバを建てるのが安定。
  • セッションサーバが単一障害点になる!
    だからフルマネージドのElastiCacheを利用する

パラメータグループの作成

 

 

Redisの作成

 

パラメータグループは先ほど作成したものをきちんと選択します。

レプリケーション数は予算がないので「0」にした。実際はちゃんとレプリケーションさせましょ~!

 

 

ElastiCache(Redis)が作成されました。

 

Route53

  • プライマリエンドポイントをCNAMEで「elasticache-redis-primary.yomuyo.net」に設定する

 

セキュリティグループ

  • Redisのセキュリティグループに、EC2からのみアクセスできるように設定
    ※エンドポイントの正引きがローカルIPなので大丈夫だとは思いますが、念の為「0.0.0.0/0」のように全開放するのは避けて下さい。

 

接続テスト

redis-cliコマンドを利用したいのでredisをインストール

 

EC2からElastiCacheに接続する

接続できた。

 

これでLaravelの設定を行えば、ElastiCacheにセッションを保存することが可能になります。

 

Laravelとの連携

 

設定状況「確認」

 

predisライブラリのインストール

 

 

 

値を確認出来たらおっけ

 

環境変数設定

AWS System Managerのパラメータストア, ECSのPHP-FPMコンテナの環境変数にRedis_HOSTを設定する必要がある。

 

 

 

Google Books API取得

https://console.developers.google.com/

 

Google Books APIで取得できるデータはキーが豊富!

…なんだけど、[“id”]を起点として責めていった方が良い。[“thumnail”]などのキーが本によってあったりなかったりして安定しない。ISBNコードが抜けていたり、独自もありで配列処理を頑張らなくちゃで厄介…。

だから[“id”]を利用すると良い、IDを制すものがGoogle Books APIを制すのだ。

 

APIキーがなくても使える!

 

 

配列の視覚化

var_dump()の前後に<pre>タグを行うと見やすくなる。

 

ヘッダーに検索フォームを付けた

/views/layouts/partials/header.blade.php

 

 

/app/Http/Controllers/BookController.php

 

Google APIで本を検索します。

?q=intitle:{$post_data[“name”]}&q=inauthor:{$post_data[“name”]}&country=JP”;

  • intitle
    タイトル
  • inauthor
    著者名

何でもヒットするとUI上問題があるので、この2つを指定します。

 

表示先

 

検索結果の取得

ちゃんとデータを引っ張れている。

 

小ネタ

値を入れずにGoogle Books APIに投げると、ハッキング関係の本がレコメンドされる
https://www.googleapis.com/books/v1/volumes?q=intitle:&country=JP

初見はひやっとした;

 

Bootstrap4 グリッド

cosの高さをそろえて横並びにする

 

  • .containerのdivでrowを囲む
  • rowのカラムにCSSでflex属性を付与する為のrow-eq-heightクラスを付与する
    ※row-eq-heightの名前は何でも良いです。
  • CSSでパディングを設定する為にinnerboxクラスを付与する

 

 

/public/css/style.css

  • .row-eq-heightにdisplay:flex; flex-wrap:wrap;
  • .innerboxにパディングを設定する

 

 

 

 

 

 

フォーカスした際にplaceholderを非表示にする

 

 

Laravel MVC周りのひな形

MVC

 

レイアウトの作成

 

 

 

 

 

 

booksテーブルの作成

テーブルは複数系で指定します

「–create=テーブル名」オプションをつけた方が出来上がるひな形が良い。

 

 

テーブル作成

 

シーダファイル BooksTableSeederの作成

シーダを利用してbooksテーブルにサンプルデータを入れます。

 

シーダファイルを編集してサンプルデータを入れる

 

DatabaseSeeder.phpファイルを編集して、「BooksTableSeeder」をシーダ対象とする。

 

 

シーダの実行

これでサンプルデータが入る

 

データの確認

データが入っていることが確認できた。

 

BookController, Bookモデルの作成

 

Model格納ディレクトリの作成

 

Bookモデルの作成 ※モデルは単数系で指定する

 

 

Bookコントローラの作成

 

コントローラのひな形作成

 

 

 

 

画像フォルダ作成

ここに画像を格納する

 

 

 

 

 

 

 

ログイン

Laravelのmake:authを利用すると、ログインがあっという間に作れます。CSRF対策もされており、パスワードリセット機能もあります。

これだとポートフォリオ的にいまいちなので、一通り機能を作ったらソーシャルログイン機能をつけよう。

 

 

下記のルーティングが出来る

  • https://<ドメイン>/login
  • https://<ドメイン>/register
  • https://<ドメイン>/password/reset

 

ログインユーザ用のテーブルの作成

 

シーディング

 

 

シーダー定義ファイルに追加

 

ダミーデータの挿入

phpMyAdminでデータを確認して完了

 

 

日本語化対応

make:authによって作成されたbladeのヘルパー関数を日本語に置き換える

 

ロケール設定

 

 

Facebookログインの申請

API利用のキーとシークレットが必要

  1. Facebookの開発者登録
    https://developers.facebook.com/apps
    【設定】→ 【ベーシック】からキーとIDを確認できる。
  2. OauthリダイレクトIDを取得, コールバックURLの設定
    https://<ドメイン名>/auth/callback/facebook
    Facebookログイン → 【設定】から設定。
    ※httpsしか対応していないので注意。

 

Twitterでのソーシャルログイン申請

API利用のキーとシークレットが必要

申請を行います。
https://developer.twitter.com/

ソーシャルログインしか利用しませんと強調したら、6時間程度で審査完

 

 

 

socialiteのインストール

 

config/app.php

 

 

.env.local

Twitterの場合はアクセストークンが必要。

 

/config/services.php

 

 

 

 

/app/Http/Controllers/SocialController.php

 

ポイント

TwitterとFacebookでユーザ情報の取得方法を変えています。

  • Twitter
    $getInfo = Socialite::driver($provider)->user();
  • Facebook
    ->stateless()が必要
    $getInfo = Socialite::driver($provider)->stateless()->user();

プロバイダ先の仕様によって取得方法を変えていく必要がありそうですね!

 

/routes/web.php

 

 

代入値のホワイトリスト設定

外部から入力される値を$fillableでホワイトリスト形式で指定します。

※$guardedはブラックリスト形式。$fillableと$guardedの併用はできない。

 

migrate:refreshで実行

一度テーブル作っちゃっているのでこのオプションが必要

こうなる。

 

再度シーディング

 

 

 

 

Facebookログイン
https://<ドメイン名>/auth/login/facebook

 

dd($user)して値を見る

https://<ドメイン名>/auth/facebook/callback?code=※略

callbackからユーザデータを取得できました。

 

Facebookでログインボタンを押して認証すれば、こんな風にログイン出来る。

 

 

 

ユーザ名が取れました。

 

Laravel \ Socialite \ Two \ InvalidStateException
No message

statelessにつけない場合は不安定になるんだが…。

 

Twitterでもログインを確認。これでソーシャルログインはおしまい。

余裕ができたらLINEでのログインは行いたい。

 

ログイン後のURLを変更する

/homeではなく、/mypageに変更

 

 

 

アクセス管理

ログイン状態でないとアクセスできない会員ページを作ろう

 

 

投稿機能

 

レビュー、いいね、コメントの3テーブルを作ります。

 

 

 

 

 

バリデーション

フォーム投稿でのバリデーション

  • 未入力の場合
  • 文字数が50を超えた場合

 

バリデーション用のクラスを作成する

 

 

今回のヘッダーの検索のように処理の後に同じURIにリダイレクトするPOSTの場合は、そのままだとリダイレクトループになりエラーになります。だからリダイレクトページを指定しました。

 

 

xxxx.header.blade.php

 

 

ページネーション

/app/Http/Controllers/BookController.php