AWS上でTerraformを使用してDjangoアプリケーションをデプロイする。Amazon S3への接続
これは「AWS上でTerraformを使用してDjangoアプリケーションをデプロイする」ガイドの第6部です。以前のステップはこちらで確認できます:
このステップでは、以下を行います:
- メディアファイルの保存用にS3バケットを作成する。
- DjangoプロジェクトにS3ストレージを追加する。
- Django管理画面に画像アップロードを追加する。
- S3 Terraformバックエンドを追加する。
S3について
なぜ私たちは実際にS3ストレージが必要なのでしょうか?
ユーザーがウェブサーバーにファイルをアップロードすると、Djangoはデフォルトでこのファイルをファイルシステムに保存します。複数のコンテナーをウェブECSサービスで持つことができるため、web_1
コンテナーがユーザーファイルを作成し、web_2
コンテナーがそれらを読み取ろうとする状況が発生する可能性があります。さらに、ECSがweb_1
コンテナーを再作成すると、ユーザーファイルが失われます。
このような状況では、ステートフル(状態を持つ)な振る舞いがあります。ステートレス(状態を持たない)な振る舞いを実現するには、ユーザーファイルを共通のグローバルストレージに保存する必要があります。私たちが探しているストレージがS3です。
この場合、web_1
コンテナーはS3バケットに新しいファイルを作成し、web_2
コンテナーはこのファイルを読み取ることができます。
AWSからのS3の説明は以下の通りです:
Amazon Simple Storage Service(Amazon S3)は、業界をリードするスケーラビリティ、データの可用性、セキュリティ、パフォーマンスを提供するオブジェクトストレージサービスです。あらゆる規模の企業や業界の顧客は、データレイク、クラウドネイティブアプリケーション、モバイルアプリのようなあらゆる使用事例に対して、任意の量のデータを保存して保護することができます。コスト効果の高いストレージクラスと使いやすい管理機能を備えることで、コストを最適化し、データを整理し、特定のビジネス、組織、コンプライアンス要件に応じた細かいアクセスコントロールを設定できます。
ローカルストレージへのファイルアップロード
しかしまず、Djangoアプリケーションでファイルのアップロードを追加しましょう。
S3で作業するためには、django-storagesとboto3パッケージをインストールする必要があります。そしてDjangoで画像を扱うためには、Pillowライブラリが必要です。これらをrequirements.txt
に追加してpip install -r requirements.txt
を実行しましょう。
boto3==1.24.45
django_storages==1.13.1
pillow==9.1.0
しかし、今のところは、DjangoデフォルトのFileSystemStorageで作業します。
新しいDjangoアプリケーションphotos
を作成し、image
ImageFieldを持つPhoto
モデルを作りましょう。
django-aws-backend
プロジェクトに移動して、python manage.py startapp photos
として新しいDjangoアプリケーションを作成します。
その後、settings.py
のINSTALLED_APPS
の最後にphotos
アプリケーションを追加し、MEDIA_URLとMEDIA_ROOTの設定を指定します:
INSTALLED_APPS = [
...
'photos.apps.PhotosConfig',
]
MEDIA_URL = "/media/"
MEDIA_ROOT = BASE_DIR / "media"
さて、Photo
モデルをphotos/models.py
で作成できる準備ができました:
from django.db import models
class Photo(models.Model):
title = models.CharField("Title", max_length=255)
image = models.ImageField("Image", upload_to="photos/")
管理パネルでPhoto
オブジェクトを管理できるようにするために、PhotoAdmin
クラスをphotos/admin.py
に追加します。
from django.contrib import admin
from photos.models import Photo
@admin.register(Photo)
class PhotoAdmin(admin.ModelAdmin):
list_display = ["title"]
最後に、Django開発サーバーがメディアファイルを提供できるようにするために、django_aws/urls.py
に以下のコードを追加します:
from django_aws.settings import DEBUG, MEDIA_URL, MEDIA_ROOT
from django.conf.urls.static import static
...
if DEBUG:
urlpatterns += static(MEDIA_URL, document_root=MEDIA_ROOT)
必要なコードはすべてそろったので、マイグレーションを作成して適用できます。
$ python manage.py makemigrations photos
Migrations for 'photos':
photos/migrations/0001_initial.py
- Create model Photo
$ python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, photos, sessions
Running migrations:
Applying photos.0001_initial... OK
では、Django管理画面で新しくPhoto
を作成してみましょう。python manage.py runserver
を実行し、管理パネルhttp://127.0.0.1:8000/admin/photos/photo/にアクセスします。「Add Photo」をクリックし、タイトルを入力してあなたのファイルシステムから任意の画像を選んで、「Save and continue editing」をクリックします。
Djangoはイメージをローカルファイルシステムに保存します。"current image"をクリックしてブラウザで表示できることを確認し、django-aws-backend/media/photos
フォルダで確認することもできます。
Django S3設定
ローカルファイルシステムに画像をアップロードすることに成功しました。私はローカル開発用にFileSystemStorage
を使用するのを好みます。これにより、インターネット接続なしでも作業やテストを実行できます。
しかし、本番環境のためには、S3設定を提供する必要があります。settings.py
に以下の設定を追加しましょう:
DEFAULT_FILE_STORAGE = env(
"DEFAULT_FILE_STORAGE", default="django.core.files.storage.FileSystemStorage"
)
AWS_ACCESS_KEY_ID = env("AWS_ACCESS_KEY_ID", default="")
AWS_SECRET_ACCESS_KEY = env("AWS_SECRET_ACCESS_KEY", default="")
AWS_STORAGE_BUCKET_NAME = env("AWS_STORAGE_BUCKET_NAME", default="")
AWS_S3_REGION_NAME = env("AWS_S3_REGION_NAME", default="")
AWS_S3_ENDPOINT_URL = env("AWS_S3_ENDPOINT_URL", default="")
AWS_S3_FILE_OVERWRITE = False
ここで定義しました:
- DEFAULT_FILE_STORAGE。ローカル開発用に
FileSystemStorage
を保持し、本番用にS3Boto3Storageを設定します。 AWS_ACCESS_KEY_ID
,AWS_SECRET_ACCESS_KEY
- AWSアクセスキーを文字列として。AWS_STORAGE_BUCKET_NAME
- S3バケットの名前。AWS_S3_REGION_NAME
- AWSリージョン。私は同じus-east-2
リージョンを使用しますが、あなたは自分のリージョンを指定できます。AWS_S3_ENDPOINT_URL
- S3に接続するためのURL。AWS_S3_FILE_OVERWRITE
- 指定された名前のファイルがすでに存在する場合、django-storages
は余分な文字を追加します。
Djangoの部分はこれで終わりです。アプリケーションはS3で作業する用意ができました。変更をコミットしてプッシュし、CI/CDが正常に通過したことを確認してください。
S3バケットの作成
Terraformプロジェクトに移動しましょう。S3バケットを作成しましょう。まず、variables.tf
にバケット名を指定する必要があります。名前はAWSリージョン内で一意である必要があります。
# S3
variable "prod_media_bucket" {
description = "S3 Bucket for production media files"
default = "prod-media-427861343"
}
次に、django-aws-infrastructure
プロジェクトに新しいs3.tf
ファイルを以下の内容で作成し、terraform apply
を実行します:
resource "aws_s3_bucket" "prod_media" {
bucket = var.prod_media_bucket
acl = "public-read"
cors_rule {
allowed_headers = ["*"]
allowed_methods = ["GET", "HEAD"]
allowed_origins = ["*"]
expose_headers = ["ETag"]
max_age_seconds = 3000
}
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "PublicReadGetObject"
Principal = "*"
Action = [
"s3:GetObject",
]
Effect = "Allow"
Resource = [
"arn:aws:s3:::${var.prod_media_bucket}",
"arn:aws:s3:::${var.prod_media_bucket}/*"
]
},
]
})
}
resource "aws_iam_user" "prod_media_bucket" {
name = "prod-media-bucket"
}
resource "aws_iam_user_policy" "prod_media_bucket" {
user = aws_iam_user.prod_media_bucket.name
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = [
"s3:*",
]
Effect = "Allow"
Resource = [
"arn:aws:s3:::${var.prod_media_bucket}",
"arn:aws:s3:::${var.prod_media_bucket}/*"
]
},
]
})
}
resource "aws_iam_access_key" "prod_media_bucket" {
user = aws_iam_user.prod_media_bucket.name
}
ここでは作成しました:
- 新しいS3バケット。このバケットのすべてのファイルにパブリックアクセスを許可し、CORSルールを定義してブラウザが任意の元のファイル(または私たちのドメインのためのもの)を読み込むことを許可します。
- S3バケットへの接続に使用する新しいIAMユーザーとIAMポリシー。
- ユーザーがDjangoアプリケーションで使用するIAMアクセスキー。
AWS S3コンソールでバケットをチェックしましょう:
ECSサービスにS3の認証情報を渡す準備ができました。ecs.tf
の変数を追加します:
...
locals {
container_vars = {
...
s3_media_bucket = var.prod_media_bucket
s3_access_key = aws_iam_access_key.prod_media_bucket.id
s3_secret_key = aws_iam_access_key.prod_media_bucket.secret
}
}
...
これらの変数を使ってtemplates/backend_container.json.tpl
で環境変数を定義し、terraform apply
で変更を適用します:
[
{
...
"environment": [
...
{
"name": "DEFAULT_FILE_STORAGE",
"value": "storages.backends.s3boto3.S3Boto3Storage"
},
{
"name": "AWS_ACCESS_KEY_ID",
"value": "${s3_access_key}"
},
{
"name": "AWS_SECRET_ACCESS_KEY",
"value": "${s3_secret_key}"
},
{
"name": "AWS_STORAGE_BUCKET_NAME",
"value": "${s3_media_bucket}"
},
{
"name": "AWS_S3_REGION_NAME",
"value": "${region}"
},
{
"name": "AWS_S3_ENDPOINT_URL",
"value": "https://${s3_media_bucket}.s3.${region}.amazonaws.com/"
}
],
...
}
]
ECSサービスの新しいバージョンのデプロイを待ちます。その後、デプロイされたアプリケーションの管理パネルに行って新しい写真を追加してみましょう。フィールドを埋めて「Save and continue editing」をクリックし、画像のURLをクリックしてS3に正しくアップロードされたことを確認してください。
また、S3コンソールで
こちらの記事はdev.toの良い記事を日本人向けに翻訳しています。
https://dev.to/daiquiri_team/deploying-django-application-on-aws-with-terraform-connecting-to-amazon-s3-2a23