EBSで保管しているログとファイルをCloudWatchとS3に移行する方法

# 概要

今、弊社で運営しているAWSサーバーには少し困った問題があります。AWSにサーバーを配布する前に行われたアプリケーションのロジック上、アプリケーションで生成するログと添付ファイルをEBSで保存しています。単純にインスタンス一つでサーバーを運用する構造では大きな問題はないのですが、会社内で拡張性と可用性の問題を検討するようになり、変更する必要が生じました。

新しいインスタンスを作成しても、インスタンスごとに同じファイルとログを保有していないため、この点はどのような形式でもインスタンス複数数を運用しようとしたときに問題が発生します。実際、今までインスタンス1つに中断配布形式で運用することに方針を決めたし、実際にトラフィックもそれほど大きくないので実質的な問題はありませんでした。

しかし、コード管理とリリース管理において、SVNでGitHubを利用してdev、test、masterの3つの環境に分けたことと、本番サーバーと同じ仕様のテストサーバーを運用したいという意見があり、状況が大きく変わりました。テストサーバーを運用するには、本番サーバーと同じ仕様でサーバーを展開できる環境が構築されなければならず、単一インスタンスで解決させるアーキテクチャでは対応しにくい点が多かったです。

まずはテストサーバーの構築が最優先だったので、とりあえずスナップショットとCloudFormation、SSMでライブサーバーとテストサーバーのCI/CDパイプライン構築を完了した状態です。

:::note info
あとは、アプリケーションで生成されるログをCloudWatchに、添付ファイルをS3に保存するように変更する作業です。 実行方法については、参考リンクから大きく変わることはないので、参考リンクを見ていただければ幸いです。
:::

# 本文

## ログ

まず、作業しやすいログを先に分離することにしました。

 方法としてはcloudwatch-agentをSSMを使ってインストールすることにしました。

まず、cloudwatch-agentをインスタンスにインストールします。

[Download and configure the CloudWatch agent - Amazon CloudWatch](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/download-CloudWatch-Agent-on-EC2-Instance-SSM-first.html)

今度はログを取得する方法で、Springログバックで取得するアプリケーションのログ、Apacheアクセスログとエラーログ、そしてバッチを回しているcrontopログをそれぞれ取得するように設定しましたが、

 400エラーが出て because no identity-based policy allows the logs:PutRetentionPolicy actionというメッセージが出ました。

それでブログを探した結果、私がウィザードで180日間ログを維持するように設定してたのですが、ログ維持期間を設定するには別途にPutRetentionPolicyというポリシーを作る必要があることが分かりました。

[EC2でCloudWatchエージェント設定時、ロググループの保持期間が反映されない - サーバーワークスエンジニアブログ](https://blog.serverworks.co.jp/cloudwatch-agent-putretentionpolicy)

[Amazon CloudWatch Logs】ApacheのアクセスログをCloudWatch Logsへ収集する - サーバーワークスエンジニアブログ](https://blog.serverworks.co.jp/cloud-watch-logs-apache-access-log-setting)

とりあえず、既存にログを収集してたので、この部分をクラウドウォッチエージェントで追跡してawsでログ化することなので大きく難しくはありませんでした。

## ファイル

次はファイルのアップロードと読み込みをEBSからS3に移行する作業です。基本的なアプリケーションのロジック修正は社内の開発チームメンバーが主に行いましたが、AWSとの権限問題などの問題を解決するためにサポートをしました。

まず、オブジェクトに対する権限を正しく設定しなければならない部分があり、400エラーが出る部分はACL設定で解決しました。

基本的にファイルを保管するS3はユーザがアクセスできるようにする必要があったので、パブリックでアクセスができるように変更するのが正解で、修正やアップロードはACLで制限して読み取り権限だけパブリックでできるようにすることにしました。

その後、ファイルのアップロード自体は解決しましたが、アプリケーションで実際にファイルを見たり、ダウンロードする部分のロジック修正が必要になりました。

そこで、バックエンドとフロントエンドの修正を行います。

### バックエンド 

1.まず、バックエンドでファイル名がURLにそのまま公開されるのを避けるため、ファイル名を変更して暗号化する作業をします。

2.ファイルも同じように適用しましたが、名前を読み込む部分で拡張子も一緒に名前が暗号化されてしまうため、dbに保存するファイル名はそのままにしてs3に保存する名前だけ暗号化して渡すように変更しました。 

3. s3に上げるファイル名が重複することがあるので、タイムスタンプを付ける作業を追加しました。

[[Spring]ファイルアップロード時のファイル名変更](https://devhooney.tistory.com/2?category=1052331)

### フロントエンド

1.フロントでの画像の読み込みは基本的にaxios通信でbaseurlを使ってs3 urlを指定する方法を使います。

2.corsエラーが出るので、エラーが発生した部分s3でcors許可編集をして変更しました。

3.アプリケーション上に表示されるファイル名が暗号化された名前で表示される部分を修正しました。

[ウェブクライアントでファイルダウンロード時ファイル名変更](https://velog.io/@lgj9172/ウェブクライアントでファイルダウンロード時ファイル名変更)

[React S3 画像/映像の読み込み](https://tomakesure.tistory.com/23)

4.ハードコーディングでイメージを表示したり、ファイルをダウンロードしてるところがあって修正は大きな手間はかかりませんでしたが、ボタンでダウンロードをするのは下のlinkで処理する部分を参考して作成しました。

[Javascript rename file on download](https://stackoverflow.com/questions/7428831/javascript-rename-file-on-download/56195387#56195387)

これでEBSからのログはCloudWatchに分離、ファイルはS3に分離する作業が終わりました。

## 参考リンク

[AWS S3 オブジェクトのアップロードと権限設定 - JAVA](https://goateedev.tistory.com/298)

[[aws s3] AmazonS3Exception 400解決](https://velog.io/@vector13/aws-s3-AmazonS3Exception-400-解決)

[#011) AWS S3生成する/パブリックACL,バケットポリシー](https://cloudbim.tistory.com/15)

[Access control list (ACL) overview - Amazon Simple Storage Service](https://docs.aws.amazon.com/AmazonS3/latest/userguide/acl-overview.html#canned-acl)

[Access control list (ACL) overview - Amazon Simple Storage Service](https://docs.aws.amazon.com/AmazonS3/latest/userguide/acl-overview.html)

[Resolve HTTP 403 "Access Denied" AmazonS3Exception in Amazon EMR](https://repost.aws/knowledge-center/emr-s3-403-access-denied)

[S3のアクセスコントロールが多すぎて訳がわからないので整理してみる｜DevelopersIO](https://dev.classmethod.jp/articles/s3-acl-wakewakame/)

[Java : MultiPartFileの名前を一意な名前に変更してフォルダに保存し、URLをデータベースに保存する](https://stackoverflow.com/questions/52956968/java-rename-multipartfile-with-unique-name-and-store-it-to-a-folder-and-save-t)

[MultipartFile の originalFilename を変更する方法](https://stackoverflow.com/questions/35914212/how-to-change-originalfilename-of-multipartfile)