AWSに画像をアップロードする


Heroku上で扱う画像ファイルなどは、アプリがデプロイされるか、
アプリが再起動(24時間に1回自動で行われる)される度に消えてしまう仕様です。

なので、アップロードされた画像を「AWS」というサービスに保存されるようにする事で
この問題を解決していきたいと思います。

目次

①.AWSアカウントの作成
②.AWS上のセキュリティ対策
③.S3でバケット(保存先)を作成
④.画像がS3のバケットに保存されるように設定


それでは早速始めていきます!

①AWSアカウントの作成

https://aws.amazon.com/jp/
にアクセスしてサインアップします。
注意点は、アカウントの種類は「パーソナル」を選択すること。
サポートプランは無料のベーシックプランでOK
ここまできたら、実際にサインインして確認します。

②AWS上のセキュリティ対策

3つの設定を行います。

1.二段階認証の設定
2.IAMユーザーの作成
3.git-secretsの利用

1.二段階認証の設定

Authyというアプリケーションを利用するので、
ますは「Authy」を自分のスマホにインストールします。
初期設定が終われば、次はAWSマネジメントコンソールにアクセスします。

ヘッダーからアカウント名をクリックし、メニューの中から 「マイセキュリティ資格情報」 を選択

モーダル画面が表示されたら「Continue to Security Credentials」 をクリック。

メニューの中の 「多要素認証(MFA)」 をクリックすると内容が展開されるので、「MFAの有効化」 をクリック

モーダル画面の中から「仮想MFAデバイス」が選択されているのを確認して、続行をクリック
→次の画面で「QRコードの表示」をクリックすると、QRコードが表示されます。
→この状態で先ほどの「Authy」アプリを起動し、「Accounts」にある「+」ボタンをタップ
→設定画面を終了すると、二段階認証用のパスワードが表示されるようになります。

ワンタイムパスワードなので、30秒経つと自動的にそのパスワードは無効になります。
このパスワードをAWSで入力します。

1つ目のパスワードを上の欄に、その次に表示されたパスワードを下の欄に入力
最後に「MFAの割り当て」をクリックして設定は完了


2.IAMユーザーの作成

IAMユーザーとは、AWSのサービスの1つで、機能を制限したユーザーを作成できる機能のこと
※最初にAWSで作ったアカウントはルートユーザー

検索のフォームに「IAM」と打ち込み、出てきた検索結果からIAMのページに飛ぶ

遷移先のページで個々の「IAMユーザーの作成」をクリックし、「ユーザーの管理」をクリック

遷移先のページで「ユーザーを追加」をクリックすると下記の画面になりますので、
作成するユーザー名の入力と、「プログラムによるアクセス」にチェック
そのあとは右下の「次のステップ:アクセス権限」をクリックして進みます。

「既存のポリシーを直接アタッチ」から検索窓に「amazons3」と入力し、
「AmazonS3FullAccess」に設定後、右下の「次のステップ:タグ」をクリック

遷移後は何もせず「次のステップ: 確認」をクリック
最後に設定の確認をして、「ユーザーの作成」をクリック

これで「IAMユーザー」の作成が完了です。
最後に、忘れずに認証情報(.csv)をダウンロードしておきます。


「IAMユーザーのパスワード」を設定

IAMのメニューから作成したIAMユーザーをクリック
→「認証情報」のタブをクリックすると以下のページに遷移するのでコンソールのパスワード欄の「管理」 をクリック

「有効化」「自動生成パスワード」をクリック

ルートユーザーの時と同じく、認証情報はダウンロードしておきます。


作成したIAMユーザーでログイン

今作成したIAMユーザーでログインできることを確認してみます。
まずルートアカウントのサインアウトを行います。

続いて、先ほどダウンロードしたcredential.csvを開くと下記の情報が記載されている

User name
Password
Console login link

User name と Password を入力して、Console login link のURLにアクセス

その後の画面で正常にログインできれば成功です。


ルートユーザーで再度サインイン

サインイン画面で「ルートユーザーのEメールを使用したサインイン」を選択。
二段階認証を経てログインできます。


IAMユーザーも二段階認証に

IAMのユーザー選択画面から先ほど作成したユーザー名を選択し、
遷移したページ先で以下の「MFAデバイスの割当」をクリック

あとは ルートユーザーの時と同じ手順で二段階認証の設定を行う。


3.git-secretsの利用

「git-secrets」は、誤操作でパスワードをGitHubにpushしてしまう防いでくれるツールです。

まずはターミナルから、Homebrewを経由してgit-secretsを導入。

% cd ~/
% brew install git-secrets

git-secretsが導入できたら、設定を適用したいリポジトリに移動し、git-secretsを有効化

% cd アプリケーション名 #開発中のアプリケーションディレクトリに移動
% git secrets --install

続いて、「どのようなコードのコミットを防ぐのか」を設定していきます。
この後の作業は開発中アプリケーションのディレクトリで実行します。


git-secretsの条件を設定

下記のコマンドを実行することで、secret_key, access_keyなど、
アップロードしたくないAWS関連の秘密情報を一括で設定することができる

% git secrets --register-aws --global

現在のgit-secretsの設定は、下記のコマンドで確認することができる

% git secrets --list

secrets.providers git secrets --aws-provider
secrets.patterns [A-Z0-9]{20}
secrets.patterns ("|')?(AWS|aws|Aws)?_?(SECRET|secret|Secret)?_?(ACCESS|access|Access)?_?(KEY|key|Key)("|')?\s*(:|=>|=)\s*("|')?[A-Za-z0-9/\+=]{40}("|')?
secrets.patterns ("|')?(AWS|aws|Aws)?_?(ACCOUNT|account|Account)_?(ID|id|Id)?("|')?\s*(:|=>|=)\s*("|')?[0-9]{4}\-?[0-9]{4}\-?[0-9]{4}("|')?
secrets.allowed AKIAIOSFODNN7EXAMPLE
secrets.allowed wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY

これで、「git secrets --install」を行なったリポジトリでは、
コミット時にAWSの秘密情報を含んだコードがないかチェックされるようになりました。
「うっかりパスワードをGitHubに載せてしまった」という事態を防ぐことができるので、必ず設定を行う。


今後作成する全てのリポジトリにgit-secretsが適用されるように設定

特段の理由がなければ、以下のコマンドを実行して自動で適用されるようにしておく。

% git secrets --install ~/.git-templates/git-secrets
% git config --global init.templatedir '~/.git-templates/git-secrets'

GitHub Desktopからgit-secretsを利用できるように設定

GitHub Desktop経由でgit secretsを利用する場合は、追加の設定をします。
この時、Github Desktopがapplicationフォルダに存在している必要があ流ので適宜移動しておく。

以下を実行

% sudo cp /usr/local/bin/git-secrets /Applications/GitHub\ Desktop.app/Contents/Resources/app/git/bin/git-secrets

上記コマンドで「No such file or directory」のエラーがでる場合は以下のコマンドを実行

% sudo cp /usr/local/bin/git-secrets /Applications/GitHub\ Desktop.app/Contents/Resources/git/bin/git-secrets

③S3を利用してバケットを作成

S3で実際にデータが格納される場所のことを「バケット」と呼びます。
バケットの名前はアクセスするときのURLに使用されるため、
英数字で、まだ誰も付けたことがない名前を使う必要がある。

「ストレージ」のS3をクリック

遷移後の画面で「バケットを作成」をクリックし、バケット名の入力とリージョンを設定。
リージョンは「アジアパシフィック(東京)ap-northeast-1」 を選択し「次へ」

次の画面ではそのまま「次へ」

遷移後の画面で、「パブリックアクセスをすべてブロック」のチェックを外し、
新規の〜、任意の〜 にチェックを入れ、最後に上段のオレンジ色の注意喚起箇所のチェックを入れる。
「次へ」を選択し「バケットを作成」で完了です。


バケットポリシー

どのようなアクセスに対してS3への保存やデータの読み取りを許可するか決められる仕組み。
今回は、作成したIAMユーザーからのアクセスのみを許可するように設定します。

AWSのトップページから「IAM」と検索し、表示されたメニューをクリック

「ユーザー」を選択し、「ユーザー名」のところの先ほど作成したユーザー名をクリック

IAMユーザーの情報が表示されるので、必要な情報を取得。
後ほど「ユーザーのARN」が必要にるので、一度メモアプリなどに保存しておきます。

次に、バケットポリシーの設定を行います。
IAMの時と同じようにサービス一覧からS3を選びます。先ほど作成したバケットをクリック。

「アクセス権限」をクリックし、「バケットポリシー」をクリック
表示されるコード記述欄にポリシーの入力をします。
先ほどメモしておいた 「ユーザーのARN」を①に、「作成したバケット名」を②に記述します。

{
   "Version": "2012-10-17",
   "Id": "Policy1544152951996",
   "Statement": [
       {
           "Sid": "Stmt1544152948221",
           "Effect": "Allow",
           "Principal": {
               "AWS": "①"
           },
           "Action": "s3:*",
           "Resource": "arn:aws:s3:::②"
       }
   ]
}

書けたら右上の「保存」をクリック
※保存ボタンを押した直後に「このバケットに対してパブリックアクセスの〜」と出ていれば保存成功。


④画像がS3のバケットに保存されるように設定

1.投稿機能の挙動確認
2.ローカル環境の画像保存先をS3に変更
3.本番環境の画像保存先をS3に変更

1.投稿機能の挙動確認

下記2点ができていることを確認します。
・ローカル環境で画像投稿が出来る
・画像がデータベースに保存されている

2.ローカル環境の画像保存先をS3に変更

以下の要領で進めていきます。

a.必要なGemをインストール
b.保存先を指定
c.環境変数を設定
d.正しく保存できるか確認

a.必要なGemをインストール

下記Gemを追記し、bundle install

gem "aws-sdk-s3", require: false

b.保存先を指定

development.rbを編集します。
画像の保存先を「:local」→「:amazon」に変更

config/environments/development.rb

config.active_storage.service = :local ←修正前
config.active_storage.service = :amazon ←修正後

storage.ymlを編集します。

config/storage.yml
test:
 service: Disk
 root: <%= Rails.root.join("tmp/storage") %>

local:
 service: Disk
 root: <%= Rails.root.join("storage") %>

amazon:
 service: S3
 access_key_id: <%= ENV['AWS_ACCESS_KEY_ID'] %>
 secret_access_key: <%= ENV['AWS_SECRET_ACCESS_KEY'] %>
 region: ap-northeast-1
 bucket: 自身のバケット名

(省略)

今設定した

「 access_key_id: <%= ENV['AWS_ACCESS_KEY_ID'] %>」「secret_access_key: <%= ENV['AWS_SECRET_ACCESS_KEY'] %>」 

の部分は環境変数を読み込むための記述です。

c.環境変数を設定

【MacOSがCatalina以降の場合】
ターミナルで以下のコマンド実行

 vim ~/.zshrc

「i」を打ち込みインサートモードへ。下記のコードを貼り付けます。

export AWS_ACCESS_KEY_ID="ここにCSVファイルのAccess key IDの値をコピー"
export AWS_SECRET_ACCESS_KEY="ここにCSVファイルのSecret access keyの値をコピー"

完了したら、「escキー」→「:wq」の順で実行し、保存。
編集後は「.zshrc」を読み込み直す。

% source ~/.zshrc

【MacOSがMojave以前の場合】
ターミナルで以下のコマンド実行

 vim ~/.bash_profile

「i」を打ち込みインサートモードへ。下記のコードを貼り付けます。

export AWS_ACCESS_KEY_ID="ここにCSVファイルのAccess key IDの値をコピー"
export AWS_SECRET_ACCESS_KEY="ここにCSVファイルのSecret access keyの値をコピー"

完了したら、「escキー」→「:wq」の順で実行し、保存。
編集後は「.zshrc」を読み込み直す。

$ source ~/.bash_profile

d.正しく保存出来るか確認

ローカル環境で開発中のアプリから画像投稿を行い、S3に保存されているか確認してください。
バケット名をクリックすると投稿した画像があるはずなので、投稿日時を確かめ、今投稿した画像であることを確認します。

3.本番環境の画像保存先をS3に変更

以下の要領で進めます。

a.保存先を指定
b.Heroku上で環境変数を設定
c.正しく保存できるか確認

a.保存先を指定

本番環境の設定ファイルを開き、画像がS3に保存されるように設定を変更します。

production.rbを編集

config/environments/production.rb
config.active_storage.service = :local  ←修正前
config.active_storage.service = :amazon ←修正後

b.Heroku上で環境変数を設定

Herokuで環境変数を扱うにはマスターキーの時と同様、「heroku config:setコマンド」を打つ必要がある。

ターミナル(開発中のアプリ)で実行

heroku config:set AWS_ACCESS_KEY_ID="ここにCSVファイルの「Access key ID」の値をコピー"
heroku config:set AWS_SECRET_ACCESS_KEY="ここにCSVファイルの「Secret access key」の値をコピー"

環境変数が正しく設定できているかを確認するためにターミナル(開発中のアプリ)で下記のコマンドを入力

% heroku config

AWS_ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY

がそれぞれ設定されていたら成功。
編集内容をコミット。(ブランチを切っている場合は必ずmasterブランチにマージする)
ターミナル(開発中のアプリ)で下記のコマンドを実行し、編集した内容をHerokuに反映。

% git push heroku master

このコマンドにより、herokuがリモートリポジトリのmasterブランチを参照することが出来る。

c.正しく保存できるか確認

本番環境で、開発中のアプリから画像投稿を行います。
そのためには以下の方法で「Web URL」を確認する必要がある。

以下のコマンドをターミナル(開発中のアプリ)で実行することで、
Herokuにデプロイされたアプリケーションの情報を見ることができる。

% heroku apps:info

「Web URL: 〜」の部分を確認できたら公開されたページにアクセスし、画像投稿をします。
その後、AWSトップページからS3にアクセスし、バケットにファイルが保存されているか確認します。

バケット名をクリックすると投稿した画像があるはずなので、投稿日時を確かめ、今投稿した画像であることを確認します。


以上です。お疲れ様でした。