naritoブログ

このブログはPython/DjangoとBootstrap4で作成されました
Githubのソースコード

Django、静的ファイル、メディアファイルをAWS S3で管理

約144日前 2018年3月25日22:23
プログラミング関連
Django Python AWS

概要


開発環境であれば、Djangoプロジェクト内に静的ファイル、メディアファイルを保存します。
本番環境であれば、例えば/var/www/staticや/var/www/media にファイルを置き、そこにあるファイルをNginxやApache等で配信する、ということをよく行います。

今回はそれらではなく、Amazon S3を使うように設定していきます。堅牢なクラウドにファイルを保存しておきと、色々と安心できます。


Amazon S3側の操作


まずですが、IAMの画面に移動し、「ユーザーを追加」ボタンを押します。
IAMの画面



ユーザー名を入れ、「プログラムによるアクセス」にチェックを入れます。そして、「次のステップ:アクセス権限」を押します。
ユーザーの設定



「グループの作成」ボタンを押します。
グループの作成



グループ名を入れて、真ん中の入力欄にS3Full のように入力して絞り込みします。出てきた「AmazonS3FullAccess」にチェックを入れて、「グループの作成」
グループの設定



今作ったグループが表示されるので、チェックを入れて「次のステップ:確認」
グループの割当



確認画面になるので、気が済んだら「ユーザーの作成」
ユーザーの確認



アクセスキーと、シークレットアクセスキーをメモしておきます。終わったら、右下の「閉じる」ボタンです。
ユーザー情報



S3のページに行き、「バケットを作成」ボタンを押します。
S3トップページ



バケットの設定です。バケット名には、ドットを含めないようにしましょう。それ以外は、通常のURLに使うような文字列で作成します。
S3トップページ



次へを押していき、この画面で「このバケットにパブリック読み取りアクセス権限を付与する」を選択し、次へ、そしてバケットを作成します。
公開設定



Django側の操作


ここまで来たら、次は使用しているDjangoプロジェクトに移動します。
pip install django-storages
pip install boto3


settings.pyのINSTALLED_APPSに追加
INSTALLED_APPS = [
...
...
    'storages',
]




settings.pyの一番下に、以下を追加します。
# 共通の設定
AWS_ACCESS_KEY_ID = 'メモしたアクセスキー'
AWS_SECRET_ACCESS_KEY = 'メモしたシークレットアクセスキー'
AWS_STORAGE_BUCKET_NAME = 'narito-blog'
AWS_S3_CUSTOM_DOMAIN = '%s.s3.amazonaws.com' % AWS_STORAGE_BUCKET_NAME
AWS_S3_OBJECT_PARAMETERS = {
    'CacheControl': 'max-age=86400',  # 1日はそのキャッシュを使う
}


# 静的ファイルの設定
AWS_LOCATION = 'static'
STATICFILES_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
STATIC_URL = "https://%s/%s/" % (AWS_S3_CUSTOM_DOMAIN, AWS_LOCATION)


# メディアファイルの設定。今回は「project」というプロジェクト名の例
DEFAULT_FILE_STORAGE = 'project.backends.MediaStorage'



以下の2つの理由により、静的ファイルとメディアファイルは、それぞれ別のストレージクラスを使うことにしました。
1./staticと/media、別々のURLとして配信したい
2.静的ファイルとメディアファイルで、同名ファイルがあった際の挙動を変えたい


2番目ですが、Djangoデフォルトのファイルアップロードの仕組みとして、同名ファイルが既にあれば違う名前を適当に作成してくれます。1.pngを何回もアップロードすると、1_EWFWEF.pngみたいにリネームしてくれますね。しかしS3Boto3Storageのデフォルトは、同名ファイルを上書きしてしまいます。cssやjsファイルであればこの挙動は問題ありませんが、メディアファイルだとちょっと怖いですね。


静的ファイルのストレージクラスは、STATICFILES_STORAGEで指定します。django-storagesに用意されているS3Boto3Storageを使います。S3Boto3Storageは、settings.pyに書いた各種変数を内部で参照します。AWS_LOCATION = 'static'としているので、S3Boto3Storageクラスは/static というURLで管理することになります。「この定数いらなくね?」と思っても消さないほうが最初は無難です。
# 静的ファイルの設定
AWS_LOCATION = 'static'
STATICFILES_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
STATIC_URL = "https://%s/%s/" % (AWS_S3_CUSTOM_DOMAIN, AWS_LOCATION)



メディアファイルはDEFAULT_FILE_STORAGEで指定しますが、静的ファイルと別のストレージクラスを使いたいので、'project.backends.MediaStorage'としています。カスタムなストレージクラスを使わないなら、ここを静的ファイルと同じ'storages.backends.s3boto3.S3Boto3Storage'としましょう。
DEFAULT_FILE_STORAGE = 'project.backends.MediaStorage'



では、今定義したストレージクラスを作りましょう。settings.pyと同じ階層に、backends.pyを作成します。
from storages.backends.s3boto3 import S3Boto3Storage


class MediaStorage(S3Boto3Storage):
    location = 'media'  # /media というURLで配信
    file_overwrite = False  # 同名ファイルは上書きせずに似た名前のファイルに
                       



これで、静的なファイルであればpython manage.py collectstatic でS3にアップロードされるようになり、メディアファイルも自動でアップロードされるようになります。collectstaticは、ローカル環境でも動作するのが魅力ですね。


既にプロジェクト内などにあるメディアファイルは、手作業でS3の画面からアップロードできます。
メディアファイルのアップロード


AWS CLI


AWS CLIを使うことで、フォルダ毎でのダウンロード・アップロードが簡単に行なえます。まず、Pythonライブラリをインストールします。
pip install awscli


その後、初期設定を行います。以下コマンドです。
aws configure


色々と聞かれるので、答えましょう。リージョンですが、アジアパシフィック (東京)は「ap-northeast-1」です。
AWS Access Key ID [None]:アクセスキー
AWS Secret Access Key []:アクセスシークレットキー
Default region name []:ap-northeast-1
Default output format []:JSON


その後は、コマンドで好きに操作できます。
# バケットの全てを、ローカルの「all」フォルダへ
aws s3 cp s3://narito-blog/ all --recursive

# バケットの「media」フォルダ内全てを、ローカルの「local_media」フォルダへ
aws s3 cp s3://narito-blog/media/ local_media --recursive

# ローカルの「local_media」フォルダを、バケットの「media」へアップロード
aws s3 cp local_media s3://narito-blog/media/ --recursive


参考サイト


英語ですが、こちらのサイトの解説がオススメです。
S3とDjangoの解説

料金については、良いQiitaのまとめ記事があります。
S3の料金体系が分かりにくいと聞かれたので纏めた

aws s3へフォルダ毎でダウンロードとアップロードする方法
【AWS S3コマンド】S3から複数ファイルを同時にダウンロード/アップロードする方法。