Django、静的ファイル、メディアファイルをAWS S3で管理
概要
CentOS7でDjangoを動かすシリーズシリーズの一つです。 今回はAmazon S3を使って静的ファイルを配信していきます。
Amazon S3側の操作
まずですが、IAMの画面に移動し、「ユーザーを追加」ボタンを押します。
ユーザー名を入れ、「プログラムによるアクセス」にチェックを入れます。そして、「次のステップ:アクセス権限」を押します。
「グループの作成」ボタンを押します。
グループ名を入れて、真ん中の入力欄にS3Full のように入力して絞り込みします。出てきた「AmazonS3FullAccess」にチェックを入れて、「グループの作成」
今作ったグループが表示されるので、チェックを入れて「次のステップ:確認」
確認画面になるので、気が済んだら「ユーザーの作成」
アクセスキーと、シークレットアクセスキーをメモしておきます。終わったら、右下の「閉じる」ボタンです。
S3のページに行き、「バケットを作成」ボタンを押します。
バケットの設定です。バケット名には、ドットを含めないようにしましょう。それ以外は、通常のURLに使うような文字列で作成します。
次へを押していき、この画面で「このバケットにパブリック読み取りアクセス権限を付与する」を選択し、次へ、そしてバケットを作成します。
Django側の操作
ここまで来たら、次は使用しているDjangoプロジェクトに移動します。
sudo pip3.7 install django-storages
sudo pip3/7 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つの理由により、静的ファイルとメディアファイルは、それぞれ別のストレージクラスを使うことにしました。
- /staticと/media、別々のURLとして配信したい
- 静的ファイルとメディアファイルで、同名ファイルがあった際の挙動を変えたい
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ライブラリをインストールします。
sudo pip3.7 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の料金体系が分かりにくいと聞かれたので纏めた