【個人開発】みた映画を管理/共有するアプリを作りました。

# はじめに
これまでに観た映画を点数をつけて登録するアプリMyMovieをつくりました。
サービスURL : [mymovie.jp](https://mymovie.jp/)
![映画一覧.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/374911/8d8ac46a-8305-937f-5225-fb1a834309ad.png)

自身はインフラエンジニアなので普段プログラムは書かないですが、AWSの勉強のために資格の勉強やハンズオンをやっていたところ、

これ自分のサービスがあったらもっと実践的な学習ができるのに。。

と思ったのがきっかけで作りました。

なので、この記事は(ポエムに近いものですが) **AWSについてより実践的な勉強がしたい！** と思っている方におすすめです。
もちろん、それ以外の方の参考にもなれば幸いです。

# サービスについて
本サービスは **最近観た映画や今まで観たことある映画を登録し一覧表示** できるアプリです。

TMDBという外部APIから映画の情報を取得しているので、登録したい映画を検索すると、ポスター画像、映画公開日の情報も同時に取得できます。

そのため、登録した映画一覧にはポスター画像が並び、また登録日、点数の順番だけでなく、映画公開日順に並べ替えることができます。

映画公開日順に並ぶのは他の類似サービスにはなく、映画ファンにとっては地味に嬉しい機能かと思います(少なくとも筆者は嬉しいです)

また、ログインなしでも他ユーザの登録映画一覧が閲覧可能なので、自分の趣味趣向に会うユーザや、全く知らない映画ばかり観ているユーザの存在を知ることができます(あくまでユーザが増えればの話ですが…)

# 使い方
使用方法はシンプルで、映画を**登録、編集、一覧表示、ユーザのフォロー機能** くらいしかありません。

ユーザ情報としてURLを一つだけ登録できるので、自身のSNSとか公開できます。

*  映画登録
   <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/374911/05eca434-6987-2365-c946-54619cf11e3f.gif" width="500">

* スコア編集(削除もできます)
   <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/374911/a0f012ba-5d48-45d6-88a2-51b0793fff70.gif" width="500">

* 並べ替え
   <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/374911/06c37200-1367-9afa-614c-cf3d9ef10958.gif" width="500">

* フォロー
   <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/374911/271c8248-05ca-7edc-26ed-8b7e0d746f44.gif" width="500">

# アプリケーション アーキテクチャ
アプリ部分のアーキテクチャ構成は以下となります。
![基本.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/374911/9fd0d150-40f5-8ea6-5b3b-e4fefe7f0f28.png)

サーバレスアーキテクチャのオーソドックススタイルですが、一からサービスを作るとなるとそれぞれのサービスの理解が求められるので、結構大変でした。

バックエンドの処理を全てLambdaで書いているので、個人的には　**Lambda＋AWS SDK** の力がついたのが収穫でした。

SDKは以下Pythonの公式リファレンスが充実しているので、言語で迷った方はPython一択かと思います。(pythonは他にもWEB上に記事が多いです)
この公式リファレンスがあれば、大体のAWSサービスをコードで扱うことができます。
[boto3 公式リファレンス](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/index.html)

この**アーキテクチャの悩み**についてですが、
 DynamoDBを使用しているので**データでソート**したり、**データ同士をくっつけたり** がしづらく、今後サービスを発展させることが難しいことです。

また、Lambdaをコールド待機させているので、**初回API実行時にLambdaの起動から始まり、レスポンスがめっちゃ悪い** です。

これは今後改善していきたいですが、RDSを使ったり、Lambdaを常時起動させておくとそれなりの費用がかかるので、やるかやらないかは筆者のモチベーション次第です。


ちなみにこのアーキテクチャは以下のハンズオンで学びました。
このハンズオンのシリーズは個人的にかなり勉強になると思うので、AWSの勉強方法で迷ったり、資格の勉強では身になっている手応えがなかったりする方は、とりあえず全部やるでいいと思います。
[AWS Hands-on for Beginners - Serverless](https://pages.awscloud.com/JAPAN-event-OE-Hands-on-for-Beginners-Serverless-2-2022-reg-event.html?trk=aws_introduction_page)

# インフラアーキテクチャ
ここに記載する内容を勉強するためにサービスを作ったので、気合が入ってます。
ちなみに、インフラ作るのに、サービス作るのと同じくらいの時間がかかりました。。

最初にシステム全体のアーキテクチャが以下になります。
![完成版_全体像.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/374911/5b278143-d4f9-a1ee-e6ce-701c0dedacf9.png)

今回構築したかったシステムは以下です。
* サーバレス 
* CI/CD
* マルチアカウント
* データレイク

流行りを追いかけた構成ですが、この構成には以下のメリットがあります。

#### サーバレスによる運用負荷の低減
　このシステムにおけるインフラは、すべてAWSによるマネージドサービスを利用しており、OS以下の管理を必要としません。また、どのサービスもグルーバルまたはリージョン単位で冗長化されていて、単一障害点が存在しない、かつ柔軟にスケール可能となります。  つまり、 **セキュリティパッチの適用や、拡張、冗長化構成を考える必要がなく、運用負荷だけでなく、可用性、拡張性、セキュリティ設計で考慮すべきところが大幅に削減** されます。

#### GItHub Actionsによる高速アップデート
　ビジネスの成長のためには、サービスを常に変化させ、顧客に必要とされるサービスの最適解を常に模索する必要があります。しかし、サービスの変更は障害の元。安定稼働を求められるインフラにとって、サービスの変更をデプロイするのはドキドキです。そこで必要なのがCI/CDの構築。できるだけサービスに影響を与えないサービスの変更は、ここが鍵になってきます。デプロイまでにテストや承認フローを実行し、デプロイも少しずつ行うや、問題があったらすぐ戻る仕組みをつくるなど、慎重に行います。CI/CDはこの複雑になりやすいサービス更新フローをコードの力で自動化します。

ちなみに本システムのCI/CD設計は以下です。
* CI/CD設計
  * GitHubへPush時に、GitHub Actionsが自動で実行されます。
  * (アプリケーションファイル)GitHubActionsが実行されると、AWSから取得した値をJSファイルに書き込むShellScriptが実行され、GItHubリポジトリのアプリケーションファイルがS3へ差分アップロードされます。
  * (IaCファイル)GitHubActionsが実行されると、実行環境であるUbuntuにcfn-lintがインストールされCloudFormationファイルの構文チェックが実行されます。<br>その後、sam deployコマンドによって、CloudFormation定義ファイルに記載されたサービスがデプロイされます。
  * GitHubActionsのEnviromentsにstgとprodが設定されており、一度のPushで二つの環境にアプリケーションファイルがデプロイされます。<br>なお、prod環境へのデプロイは事前に登録されたGItHubユーザによる承認が必要です。  

今回GitHubActionsかAWS codeシリーズを使うかで迷いましたが、 **フローをファイル一枚に記述するシンプルな実装、無料枠の多さ** からGitHubActionsにしました。
デプロイフローの中で複数のAWSサービスと連携するなど複雑なことを行うのであれば、codeシリーズを使うのがいいと思います。


#### マルチアカウント戦略でセキュリティ向上とガバナンス強化
　マルチアカウントのメリットは、 **ユーザの権限管理を容易になることと、サービスを拡張、新規サービスの立ち上げがしやすくなること** だと考えています。
　例えば、システムに関わるサービスには触れないがログは欲しいみたいなユーザが出てくると思いますが、この場合ログをひとつのアカウントにまとめ、ユーザにそのアカウントのみアクセスできるようにすると、IAM権限周りを設定しなくとも実現できます。また、アカウントにSCPを適用し操作できる範囲を絞ることも可能です。
　また、新規サービスの立ち上げの場合は、他のサービスと共通になるシステム(監査ログ集約や名前解決など)をひとつのアカウントに集約することで、サービスのアカウント内をよりシンプルな内容にできます。

#### データレイク
　データレイクとして全てのログをS3で管理するようにすると、 **分析が容易** になります。Athenaを使用すればログをダウンロードせず、コンソール上でSQL操作ができ、容易に障害の原因調査やPV数などの情報をクライアントへ提供できます。
　特にAIを使わないとか、データ量が少ないといった場合でも、障害発生時、ログをダウンロードしてgrepとかsortとかuniqコマンドとかでログ分析するより、データ集約＋Athenaの方が圧倒的にはやいはずです。
　ちなみに、AthenaでS3のログを分析する要件がある場合、 **データをパーティションで分ないととんでもないスキャン量になる** と今回学びました。以下の記事が参考になります。
[Amazon Athenaのパーティションを理解する](https://dev.classmethod.jp/articles/athena-partition-reinvent/)
[CloudFrontのログをAthenaで読むためにリネームするLambda用Pythonスクリプト書いてみた](https://dev.classmethod.jp/articles/arrange-cflogs-for-athena-by-lambda/)
[Kinesis Data Firehoseの動的パーティショニングを試してみる](https://qiita.com/itsuki3/items/84778d7e436dac618f02)

<br>
<br>

#### デメリット
上記で一通りメリットを挙げましたが、デメリットもあるかと思います。

それは **AWS依存度** の高さです。


AWS特有のサービス、AWS特有の複雑な設定をしてしまっているので、**仮にAWSに大幅値上げをされたり、人手不足の状態でチームにいいAWS人材がこない** と詰みそうです。

人手不足はドキュメントをしっかり作成するなどして、新規入場者の負担を緩和するのがよいと思います。

クラウド依存は、理想を言うと、他クラウドに移行しやすい状態を作るために、Azureなど他のクラウドの勉強も行い、マルチクラウドで運用するのがベストかと思います。

個人的にはAzureがAIに強いサービスを提供しているみたいなので、今後Azureのサービスを利用したくなったときにすぐにサービスに取り込めるようにするため、サブのクラウドにはAzureがいいと思っています。
(ちなみに、AWSができるとAzure使うのは簡単みたいな話もありますが、AWSに慣れた筆者としてはAzureはユーザ管理や、監視、ストレージの設定をよく飲み込めずとっつきづらいです)

# 非機能設計

本システムの非機能設計を以下に記載します。
### 可用性  
* 本システムはAWSマネージドサービスの中でもグローバルサービス、VPC外に存在するリージョンサービスのみで構成されており、単一障害点は存在しないシステムとなっている。	
* AWSより提供されているマネージドサービスそれぞれの可用性目標値は以下を参照。いずれも99.900%を超える高い可用性目標値が設定されている。  
https://docs.aws.amazon.com/ja_jp/wellarchitected/latest/reliability-pillar/appendix-a-designed-for-availability-for-select-aws-services.html	
	
### 性能・拡張性	
AWSマネージドサービスの拡張機能を以下に記載する。	
* CloudFront  
	CloudFrontはデフォルトで150Gbpsのデータ転送速度、ディストリビューションあたり250,000リクエスト数の処理性能が提供されている。これらの値は申請をすることで上限緩和が可能である。
* API Gateway  
	毎秒10,000リクエストの処理性能に加え最大5,000リクエストのバースト容量が提供されている。バースト容量を除く処理性能は申請をすることで上限緩和が可能である。
* Lambda  
	Lambda関数に割り当てられるメモリの量は128MB〜10,240MBの範囲で設定でき、この値によって仮想CPUの量も決まる。同時実行数はでリージョンあたりデフォルトで1,000回の呼び出しが提供されており、この値は申請をすることで上限緩和が可能である。
* DynamoDB  
	オンデマンドキャパシティモードを設定すると、無制限の読み取り/書き込み性能が提供されるようトラフィック量によりスケールアウトされる。初回のスケールアウトは毎秒4,000書き込みユニット、12,000読み取りユニットが即座に維持されるよう設定し、以降は前回のピークトラフィックの最大2倍までの性能が即座に維持される。テーブルの項目数、バイト数は無制限である。
* S3  
	パーティション化されたプレフィックス毎に毎秒3,500以上のPUT、毎秒5,500回以上のGETリクエストが可能である。バケット内のプレフィックスの量は無制限のため、プレフィックスを作成し処理を並列化することで拡張可能である。
* KinesisDataFirehose  
	配信ストリーム毎にデフォルトで毎秒最大2,000トランザクション、5,000レコード、5MB書き込みが可能である。これらの値は申請をすることで上限緩和が可能である。
	
### 運用・保守性	
* IAM  
  * IAMユーザは管理アカウントのみに作成する。各AWSアカウントにAdministrator権限ポリシーを含んだロールを作成し、AWSマネジメントコンソール操作は管理アカウントのIAMユーザがスイッチロールすることで行う。  
* 監視  
  * 各システムアカウントのCloudWatchメトリクスを運用監視アカウントで閲覧できるよう設定し、メトリクス監視は運用監視基盤で行う。  
  * AWS利用料監視は管理アカウントで行い、設定した料金を超えた場合メール通知するよう設定する。また、設定した月の予算に達した場合は、Route53で設定されたサービスURLが書き変わるLambdaが実行され、サービスが停止する。  
  * 各システムのCloudFrontアクセスログ、APIGatewayアクセスログ、Lambdaエラーログはログ集約アカウントのS3に保存し、Athenaを利用し閲覧する。  
* バックアップ  
  * サービスの資産であるユーザの投稿データが保存されたDynamoDBテーブルのみ、AWS Backupを利用し日毎にバックアップを取得し、7日分保存する。  
* 保守  
  * 本システムはすべてAWSマネージドサービスを利用しているため、保守作業はない。また、AWS側のメンテナンスによるサービス停止も発生しない。  
	
### セキュリティ	
* AWSアカウント管理  
  * AWSマネジメントコンソールにログインするIAMユーザは、すべて管理アカウントに集約する。各AWSアカウントの操作は管理アカウントのIAMユーザが、他アカウントのロールにスイッチすることで実現する。  
  * 管理者が一人のため、管理アカウントのIAMユーザ、各アカウントのロールにはAdministrator権限ポリシーを付与する。  
* ネットワークセキュリティ  
  * 各AWSサービスへのインバウンド通信は意図した通信元からの通信のみ受け付けるよう、IAMロール、ポリシー、S3バケットポリシーは最小権限で設計する。  
  * CloudFrontのOAC、APIGatewayのAPIキーを利用することで、S3、APIGatewayへの外部アクセスをCloudFrontを経由した通信のみに限定する。  
  * 本システムはAWSから標準で提供されている、DDoS攻撃を検知緩和するAWS Shield Standardによる保護を受けている。  
* 暗号化  
  * 本システムにより発生する通信は、すべてTLSによる通信暗号化を行う。外部からの通信に使用するTLS証明書はAWS Certificate Managerで発行、管理し、CloudFrontに適用する。  
  * AWS環境に保存されるデータはすべてAWS管理のキーを使用し暗号化する。  

# 使用技術まとめ
* フロントエンド
  * HTML  
  * CSS  
  * JavaScript  
  * Bootstrap 5  
* バックエンド  
  * Python 3.9  
  * Node.js 14  
* インフラ(AWS)  
  * CloudFront  
  * S3  
  * API Gateway  
  * Lambda  
  * DynamoDB  
  * Cognito  
  * AWS Organizations  
  * AWS Backup  
  * CloudWatch  
  * CloudWatchLogs  
  * KinesisDataFirehose  
  * EventBridge  
  * SNS  
  * Athena  
  * Route53  
  * AWSCertificateManager  
  * CloudFormation  
  * AWS SAM  
* コード管理  
  * GitHub  
* CI/CD  
  * GItHub Actions  
  * Ubuntu  
* API  
  * TMDB  

# まとめ
AWSの勉強のためにサービスを作りましたが、期待通りかなり勉強になりました。

AWSの各サービスについてより理解できたのはもちろんですが、通信やデータ移動の経路をすべて設計することで、権限や暗号化といったセキュリティに強くなった気がします。実務においても、IAM権限においては怖いと思うことが全くなくなりました。

また、LambdaとGitHubActionsをしっかり触ってみることで、運用自動化についても検討できるようになりました。基本的にすべてのサービスをコードで動かせることがわかったので、最適なインフラを目指して検討する知識の幅が広がったと思います。

#### 今後の活動
最後に今後行いたいことについて列挙します。

* サービス運用。パフォーマンスチューニング

* 同じサービスを、コンテナ、RDBを使って再構築
　今回使用しなかったAWSサービスの学習のためです。ですが、性能、コストの観点からこの構成が適切と判断できた場合は、リプレイスしたいと思っています。

* 同じサービスをterraformで作成
　AWS依存を避けるため、CloudFormationよりterraformを使うのがよいかもと思っています。

* ブログ記事の作成
　アウトプットのため、細かい実装などの記事を作成していきたいです。

# 最後に
この記事を最後までご覧いただきありがとうございます。
紹介したサービスですが、是非ご利用くださると嬉しいです。

#### 観た映画を登録、管理できるサービス　MyMovie
![映画一覧.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/374911/8d8ac46a-8305-937f-5225-fb1a834309ad.png)

サービスURL : [mymovie.jp](https://mymovie.jp/)
GitHub(アプリ) : [github.com/hamasakiharuya/mymovie](https://github.com/hamasakiharuya/mymovie)
GitHub(CloudFormation/SAM) : [github.com/hamasakiharuya/sam-mymovie](https://github.com/hamasakiharuya/sam-mymovie)