정적 파일 S3로 이동

django-storages, Amazon S3를 이용한다.

django-storages, Amazon S3

boto3를 사용하는 것을 강력히 추천한다.

$ pip install django-storages
# settings.py
DEBUG = os.environ.get('MODE') == 'DEBUG'
STORAGE_S3 = os.environ.get('STORAGE') == 'S3' or DEBUG is False
print('DEBUG: {}'.format(DEBUG))
print('STORAGE_S3 : {}'.format(STORAGE_S3))


# AWS
AWS_ACCESSS_KEY_ID = config['aws']['access_key_id']
AWS_SECRET_ACCESS_KEY = config['aws']['secret_access_key']
AWS_STORAGE_BUCKET_NAME = config['aws']['s3_storage_bucket_name']
AWS_S3_CUSTOM_DOMAIN = '{}.s3.amazonaws.com'.format(AWS_STORAGE_BUCKET_NAME)

# Static Setting
STATIC_DIR = os.path.join(BASE_DIR, 'static')
STATICFILES_DIRS = (
    STATIC_DIR,
)
if STORAGE_S3:
    # To use boto3 set
    DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
    # collectstatic to automatically put your static files
    # in your s3 bucket
    STATICFILES_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
    STATIC_URL = 's3.{region}.amazonaws.com/{bucket_name}/'.format(
        region=config['aws']['s3_region'],
        bucket_name=config['aws']['s3_storage_bucket_name']
    )
else:
    STATIC_ROOT = os.path.join(ROOT_DIR, 'static_root')
    STATIC_URL = '/static/'

# Application definition
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    'storages',

    'member',
]

settings_common.json 파일에 설정 값을 추가한다.

  "aws": {
    "access_key_id": "YOUR_ID",
    "secret_access_key": "YOUR_KEY",
    "s3_storage_bucket_name": "YOUR_BUCKET_NAME"
  }

키 값은 아래의 명령 혹은 AWS에 접속해 확인 할 수 있다.

$ cd ~/.aws
$ cat credentials

shell 스크립트 작성

$ vi ~/.scripts/manages3

#!/bin/zsh
MODE='DEBUG' STORAGE='S3' ./manage.py $*

# 작성 후 권한 변경
$ chmod 755 ~/.scripts/manages3

# 실행은 아래의 명령으로 한다.
$ manages3 collectstatic

# AWS S3에 접속해 정상적으로 업로드 된 것을 확인

# 아래와 같이 서버를 실행하면,
# local의 이미지 노출
$ manage runserver
DEBUG: True
STORAGE_S3 : False

# aws s3 이미지 노출
$ manages3 runserver
DEBUG: True
STORAGE_S3 : True

배포

$ scp
$ pip install -r requirements.txt
$ ./manage.py collectstatic
$ reset

서버 페이지에 들어가 이미지가 정상노출을 확인한다.

하지만 admin page에서 프로필 사진을 등록하면 에러가 발생한다.

에러 확인 하기

아래와 같이 등록해준다.

$ sudo vi /etc/uwsgi/sites/app.ini
logger = file:/tmp/uwsgi.log
logger = internalservererror file:/tmp/uwsgi500.log

또한, 아래와 같이 elseMedia path를 추가한다.

# settings.py
if STORAGE_S3:
    DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
    STATICFILES_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
    STATIC_URL = 's3.{region}.amazonaws.com/{bucket_name}/'.format(
        region=config['aws']['s3_region'],
        bucket_name=config['aws']['s3_storage_bucket_name']
    )
else:
    STATIC_ROOT = os.path.join(ROOT_DIR, 'static_root')
    STATIC_URL = '/static/'
    # Media path
    MEDIA_URL = '/media/'
    MEDIA_ROOT = os.path.join(ROOT_DIR, 'media')
# urls.py
if settings.DEBUG and settings.STORAGE_S3 is False:
    urlpatterns += static(
        settings.MEDIA_URL,
        document_root=settings.MEDIA_ROOT
    )

에러를 수정하기 위해 AWS_S3_SIGNATURE_VERSION을 추가한다.

# settings.py
AWS_S3_HOST = 's3.{}.amazonaws.com'.format(config['aws']['s3_region'])
AWS_S3_SIGNATURE_VERSION = config['aws']['s3_signature_version']
AWS_S3_CUSTOM_DOMAIN = '{}.s3.amazonaws.com'.format(AWS_STORAGE_BUCKET_NAME)

서버에 다시 배포하여 확인한다.

$ scp
$ pip install -r requirements.txt
$ reset

admin page에서 프로필 사진이 정상적으로 등록된다.

static 경로로 올리기

# settings.py
if STORAGE_S3:
    DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
    # Static files
    # collectstatic 요청은 STATICFILES_STORAGE 처리
    STATICFILES_STORAGE = 'deploy_ec2.storages.StaticStorage'
    STATICFILES_LOCATION = 'static'
    STATIC_URL = 'https://{custom_domain}/{staticfiles_location}/'.format(
        custom_domain=AWS_S3_CUSTOM_DOMAIN,
        staticfiles_location=STATICFILES_LOCATION,
    )

storages.py를 만들고 아래의 내용을 추가한다.

# storages.py
from django.conf import settings
from storages.backends.s3boto3 import S3Boto3Storage


class StaticStorage(S3Boto3Storage):
    location = settings.STATICFILES_LOCATION

배포

# Local
$ MODE='DEBUG' STORAGE='S3' ./manage.py collectstatic
$ scp

# Server
$ ./manage.py collectstatic
$ reset

media 경로로 올리기

# settings.py
if STORAGE_S3:
    # Static files
    # collectstatic 요청은 STATICFILES_STORAGE 처리

    # ...

    # Media files
    DEFAULT_FILE_STORAGE = 'deploy_ec2.storages.MediaStorage'
    MEDIAFILES_LOCATION = 'media'
    MEDIA_URL = 'https://{custom_domain}/{mediafiles_location}/'.format(
        custom_domain=AWS_S3_CUSTOM_DOMAIN,
        mediafiles_location=MEDIAFILES_LOCATION,
    )
# storages.py
class MediaStorage(S3Boto3Storage):
    location = settings.MEDIAFILES_LOCATION

같은 이름의 파일 오버라이트 문제 해결

# storages.py
from django.conf import settings
from storages.backends.s3boto3 import S3Boto3Storage


class StaticStorage(S3Boto3Storage):
    location = settings.STATICFILES_LOCATION
    file_overwrite = True


class MediaStorage(S3Boto3Storage):
    location = settings.MEDIAFILES_LOCATION
    file_overwrite = False

AWS RDS

데이터베이스 서버를 하나 만들어준다고 생각하자.

DB 생성

launch Instance > Postgresql 선택 > DEV/TEST > Next

  • Only show options that are eligible for RDS Free Tier 체크
  • Settings (입력)
    • DB Instance Identifier
    • Master Username
    • Master Password
    • Confirm Password

Next

  • Database Options
    • Database Name : deploy_ec2

Security Group 관리

  • 이름과 설명만 적고 새로운 Security Group 추가한다.

DB생성이 되면, Instance Actions > Modify 로 이동한다.

  • Network & Security > Security Group을 새로 생성한 것으로 변경한다.
  • 그 후 기본으로 생생된 Security Group을 삭제한다.

생성한 Security Group에 Inbound 추가

  • Type : PostgreSQL
  • Protocol : TCP
  • Port Range : 5432
  • Source : MyIP

PgAdimin 연결

Servers > Create > Servers 클릭

  • General
    • Name : RDS - DeployEc2
  • Connection
    • Host : wps-deploy.cgzidgqkn5zw.ap-northeast-2.rds.amazonaws.com
    • Maintanace database : deploy_ec2
    • User name : hm
    • Password : Your_Password

장고와 연결

settings_local.json 파일에 추가한다.

  "db-rds": {
    "engine": "django.db.backends.postgresql_psycopg2",
    "name": "deploy_ec2",
    "user": "hm",
    "password": "YOUR_Password",
    "host": "wps-deploy.cgzidgqkn5zw.ap-northeast-2.rds.amazonaws.com",
    "port": "5432"
  }
# settings.py
DB_RDS = os.environ.get('DB') == 'RDS'
print('DB_RDS : {}'.format(DB_RDS))

if DEBUG and DB_RDS:
    # RDS DB로 접속해 테스트
    config_db = config['db_rds']
else:
    config_db = config['db']
DATABASES = {
    'default': {
        'ENGINE': config_db['engine'],
        'NAME': config_db['name'],
        'USER': config_db['user'],
        'PASSWORD': config_db['password'],
        'HOST': config_db['host'],
        'PORT': config_db['port'],
    }
}
$ MODE='DEBUG' DB='RDS' STORAGE='S3' ./manage.py migrate

$ MODE='DEBUG' DB='RDS' STORAGE='S3' ./manage.py runserver

배포

배포전에 데이터베이스 삭제

# Server
sudo apt-get remove postgresql

# Local
$ scp

admin 사이트에 접속하면 안된다. Security Group에 inbound를 추가해야하 한다.

wps-deploy-rds에 다음과 같이 추가한다.

  • Type : PostgreSQL
  • Protocol : TCP
  • Port Range : 5432
  • Source : sg라고 치면 Security Group 모두 나온다. 여기서 EC2의 Security Group을 추가한다.

Admin 페이지에 로그인하기 위해 슈퍼유저를 만든다.

# Local
$ MODE='DEBUG' DB='RDS' STORAGE='S3' ./manage.py createsuperuser

Elastic Beanstalk

Elastic Beanstalk가 용량 프로비저닝, 로드 밸런싱, Auto Scaling, 애플리케이션 상태 모니터링에 대한 배포 정보를 자동으로 처리합니다.

Elastic Beanstalk가 자동으로 해주는 것을 나중에 개별적으로 다 다뤄보는 것이 좋다. 로드 밸런싱은 쉬워서 금방할 수 있고, 모니터링은 어렵지만 공부하는게 좋다.

과제