2018.11.05 - Deploy(3) - RDS설정 S3사용

5 분 소요

RDS 설정

AWS RDS 설정

RDS는 Amazon Relational Database Service(RDS)의 약자이다. 이 RDS를 사용하면 클라우드에서 관계형 데이터베이스를 더 간편하게 설정, 운영 및 확장할 수 있다. 이 Amazon RDS는 여러 데이터베이스 서비스를 제공하지만 우리는 PostgreSQL을 사용한다.

중앙에 Amazon RDS 시작하기

먼저 아마존 사이트에 접속하여 로그인을 해주고 AWS서비스에서 RDS를 검색하여 찾아 들어온다. 그리고 Amazon RDS 시작하기를 클릭하여 들어온다.

지역 설정이 꼭 아시아 태평양(서울)로 되어있는지 확인한다.

  • 엔진 선택

그리고 엔진 옵션에서 PostgreSQL을 선택하고 RDS 프리티어에 적용되는 옵션만 사용__을 __꼭 체크한다. 그리고 다음을 누른다.

RDS 프리티어에 적용되느니 옵션을 사용하지 않으면 초과 과금이 붙을 수 있다.

엔진 옵션 & RDS 프리티어 적용 옵션

  • DB 세부 정보 지정

설정 메뉴에 대한 내용들을 지정해준다. 이때 마스터 사용자 이름과 마스터 암호는 절대 잊어버리면 안되므로 평소 자주 사용하는 것으로 지정해주는 것이 좋다. 그리고 다음 버튼을 눌러서 넘어간다.

이 페이지에서 ‘다중 AZ 배포’라는 것이 있는데 동시에 여러 유저들이 접속했을 때 지연 시간을 최소화하기위해 여러곳에 복제본을 만드는 것이다. 당연히 만들때 돈이 들어가기 때문에 프리티어 적용을 해놓으면 설정할 수 없다.

DB 세부 정보 설정

  • 고급 설정 구성

여기서는 두가지 설정을 해주면 된다. 퍼블릭 액세스 가능성과 VPC 보안 그룹 항목이다. 데이터 베이스 서버는 기본적으로 퍼블릭 액세스를 막아놓는다. 데이터베이스 자체에 중요한 정보를 보관할 가능성이 높기 때문. 그래서 아마존에서는 VPC안에서만 접근 가능하도록 만들 수 있다. 그 외에는 관리자가 관리자 콘솔에서 관리만 가능하게 해놓는다. VPC안에 있는 EC2가 뚫리지 않는 이상 보안이 확보되는 것이다.

기존 VPC 그룹이 아닌 새로운 VPC 그룹으로 만들어야 하는데 보안 그룹에서 새로운 보안 그룹을 생성하고 다시 이 과정을 거쳐서 새 VPC그룹을 설정해 준다. 그리고 마지막으로 데이터베이스 옵션에 데이터베이스 이름은 지정해주지 않아도 되긴 하지만 Django Project의 이름을 써준다.(Django Project로 실습을 하고 있기 때문) 그리고 생성 버튼을 눌러서 DB인스턴스를 생성한다.

새로만든 보안그룹에서 따로 설정해 줄 부분은 없지만 우리는 Deploy를 연습하는 단계기 때문에 인바운드 설정을 해준다. 인바운드 규칙 편집에서 소스 부분을 ‘내 IP’로 바꾸어준다. 외부에서 접근하는 것을 차단시키는 것이다.

고급 설정 구성

그리고 이제 다시 PyCharm으로 으로 돌아와서 settings.dev.py에 DATABASES설정을 다시 해준다.

# Database
# https://docs.djangoproject.com/en/2.1/ref/settings/#databases
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'HOST': 'wps9th.cksojujm6f2c.ap-northeast-2.rds.amazonaws.com',
        'USER': '<본인이 설정한 ID>',
        'PASSWORD': '<본인이 설정한 password>',
        'PORT': 5432,
    }
}

그리고 이제 인스턴스가 완성된 것을 확인하고 실제 데이터베이스를 setting 한다. 방법은 다음과 같다.

ec2-deploy/app

# psycopg2-binary가 이미 설치되어있다면 넘어가도 좋다.
$ pip install psycopg2-binary

$ export DJANGO_SETIINGS_MODULE=config.settings.dev
$ ./manage.py migrate

위 명령어를 실행했을 때 정상적으로 migrate가 되면 정상 작동 된 것이다.

settings폴더의 개인정보 보호하기

먼저 dev.py에 있는 DATABASES의 값들을 .secrets의 dev.json을 만들어서 처리한다. 그리고 나서 dev.py에 dev.json의 값들을 가져다 쓴다.


"DATABASES": {
    "default": {
      "ENGINE": "django.db.backends.postgresql",
      "HOST": "<HOST주소>",
      "USER": "<본인이 설정한 ID>",
      "PASSWORD": "<본인이 설정한 PASSWORD>",
      "PORT": 5432
    }
  }
}


# settings/dev.py, production.json

secrets = json.load(open(os.path.join(SECRETS_DIR, 'dev.json')))
DATABASES = secrets['DATABASES']

DB만들기

서버하나에 DB는 여러개 존재할 수 있다. 우리는 progreSQL을 사용할 것이므로 progreSQL설치를 해준다.

$ brew install postgreSQL

설치가 완료되면 터미널을 새로 실행시켜준다. psql은 파이썬을 쓰듯, 인터렉티브하게 postgreSQL쪽에 데이터를 보낼 수 있다. psql을 활용해서 이를 Local에 DB를 만들고 Django에 쓸 수도 있다.

명령어는 다음과 같다.

psql --user=<실제 DB유저> --host=<만든 DB의 주소> <데이터베이스 이름=postgres>
  • 이를 치면 다음과 같이 터미널이 실행된다.
postgres=>

여기에 다음과 같이 입력하여 새로운 데이터베이스 유저를 생성해 보자.

postgres=> CREATE DATABASE ec2_deploy_dev OWNER <user name>

\l 이라고 치면 지금 있는 데이터베이스 종류가 나온다.
\dt 라고 치면 데이터베이스 테이블이 나온다.

DB를 구분하기 위해 .secrets폴더의 dev.json, production.json 파일 안에 있는 DATABASES에 NAME을 넣어준다. dev.json은 ec2_deploy_dev, production.json은 ec2_deploy를 설정한다.

예를 들면 dev.json파일에 다음과 같이 수정해 준다.

"DATABASES": {
    "default": {
      "ENGINE": "django.db.backends.postgresql",
      "HOST": "<HOST주소>",
      "NAME": "ec2_deploy_dev",
      "USER": "<본인이 설정한 ID>",
      "PASSWORD": "<본인이 설정한 PASSWORD>",
      "PORT": 5432
    }
  }
}

이렇게 NAME설정을 해주면 DB를 구분해서 사용 가능하다.

supervisor 설정

수퍼바이저는 사용자가 UNIX와 유사한 운영 체제에서 여러 프로세스를 제어 할 수있는 클라이언트 서버 시스템이다. 프로세스를 전체적으로 관리해주는 시스템이다. 먼저 .config폴더 안에 supervisor.conf파일을 만들어준다.

supervisor.conf setting

이때 PyCharm에서 supervisor.conf파일을 인식하지 못하면 환경설정에서 Editor>Code Style>File Types에 INI Config를 찾아 supervisor*.conf를 추가해준다.

이 supervisor.conf파일에 다음과 같이 적어준다.

# 어떤프로그램에 대해 적겠다.
# 아래와같이 nginx를 적어주면 nginx는 백그라운드에서 돌아가는 특징이 있기 때문에 
# 아무것도 보이지 않는다. 따라서 실행을 보기 위해서 그 아래에 다음과 같이 적는다.
[program:nginx]
command=nginx -g 'daemon off;'

[program:uwsgi]
command=uwsgi --ini /srv/project/.config/uwsgi.ini

위의 코드를 실행하기 전에 Dockerfile에 supervisor 실행 및 위의 작업들을 넣어준다. 아래의 코듸와 같이 만들어주면 된다.


# 이미지 빌드(ec2-deploy폴더에서 실행)
#  docker build -t ec2-deploy -f Dockerfile .
FROM        ubuntu:18.04
MAINTAINER  dev@lhy.kr

# 패키지 업그레이드, Python3설치
RUN         apt -y update
RUN         apt -y dist-upgrade
RUN         apt -y install python3-pip

# Nginx, uWSGI 설치 (WebServer, WSGI)
RUN         apt -y install nginx supervisor
RUN         pip3 install uwsgi

# requirements.txt파일만 복사 후, 패키지 설치
# requirements.txt파일의 내용이 바뀌지 않으면 pip3 install ...부분이 재실행되지 않음
COPY        requirements.txt /tmp/
RUN         pip3 install -r /tmp/requirements.txt

# 전체 소스코드 복사
COPY        ./   /srv/project
WORKDIR     /srv/project

# settings모듈에 대한 환경변수 설정
# export DJANGO_SETTINGS_MODULE=config.settings.production
ENV         DJANGO_SETTINGS_MODULE  config.settings.production
ENV         export LANG=C.UTF-8
# 프로세스를 실행할 명령
WORKDIR     /srv/project/app
RUN         python3 manage.py collectstatic --noinput

# Nginx
#  기존에 존재하던 Nginx설정파일들 삭제
RUN         rm -rf  /etc/nginx/sites-available/*
RUN         rm -rf  /etc/nginx/sites-enabled/*

# 프로젝트 Nginx설정파일 복사 및 enabled로 링크 설정
RUN         cp -f   /srv/project/.config/app.nginx \
                    /etc/nginx/sites-available/
RUN         ln -sf  /etc/nginx/sites-available/app.nginx \
                    /etc/nginx/sites-enabled/app.nginx

# supervisor 설정파일 복사
RUN         cp -f   /srv/project/.config/supervisord.conf \
                    /etc/supervisor/conf.d/

# Command로 supervisor 실행
CMD         supervisord -n

위와 같이 Dockerfile를 입력한 상태에서 쉘 없이 docker run을 시켜준다.

docker run --rm -it -p 7000:80 --name ec2 ec2-deploy

위의 run을 실행하고 나면 success메세지가 뜬다.

success 메세지

위와 같이 설정을 한 후에 local서버에는 환경변수에 dev를 주고, docker server에는 환경변수에 prodcution을 넣어준다. 그리고 각자 실행시킨다. 이렇게 넣고 각자 실행시키면 데이터베이스를 따로 쓰는 것을 확인 할 수 있다. 하지만 여기에 이미지 파일을 올리면 나오지 않는다.

docker와 local환경의 localhost를 실행할 때 둘다 127.0.0.1을 안쓰고 localhost방식을 사용하면 충돌이 일어나서 csrf오류가 나는 것 처럼 보인다. 따라서 하나는 127.0.0.1을 사용하여 충돌을 피해주는 것이 좋다.

Default Storage에 S3사용

도커 이미지는 상태 값은 가지지 않는다. 이미지를 가지고 만든 컨테이너 안에 데이터를 가지고있다가 컨테이너가 사라지면 이미지 파일도 사라진다. 따라서 .media파일안에 있는 이미지 파일들은 컨테이너가 사라질때 같이 없어지기 때문에 도커를 새로 실행하면 이미지 값을 찾을수 없다고 나온다.(404 Not Found) 그리고 데이터베이스는 남아있기 때문에 파일이 있는 것처럼 나온다. 이를 해결하기위해 django-storages를 사용하여 S3와 연결해준다.

settings폴더의 dev.py와 production.py에 다음 코드를 입력해준다.

DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'

이 상태에서 runserver를 해주면 ValueError가 난다. 이 에러는 해당 스토리지에서 파일을 기록하거나 가져올때는 권한에 대한 내용이 설정에 들어가 있어야한다. 이 설정을 적어준다.

# .secrets/dev.json
AWS_ACCESS_KEY_ID = '<~/.aws/credentials의 해당 키를 써준다.>'
AWS_SECRET_ACCESS_KEY = '<~/.aws/credentials의 해당 키를 써준다.>'
AWS_STORAGE_BUCKET_NAME = '<지정해준 AWS bucket name을 써준다.>'

# dev.py
AWS_ACCESS_KEY_ID = secrets['AWS_ACCESS_KEY_ID']
AWS_SECRET_ACCESS_KEY = secrets['AWS_SECRET_ACCESS_KEY']
AWS_STORAGE_BUCKET_NAME = secrets['AWS_STORAGE_BUCKET_NAME']

키 값들을 넣어줄때는 외부에 공개되면 나중에 해킹을 당하거나 불이익을 받을 수 있으므로 .secrets폴더에 넣고 키 값을 불러와서 사용하는게 좋다. 좋은게 아니라 그렇게 해줘야 한다. 그리고 다시한번 .secrets폴더가 .gitignore에 추강되어있는지 확인한다! (production.py에도 동일하게 작성)

# production.py
AWS_S3_SIGNATURE_VERSION = 's3v4'
AWS_S3_REGION_NAME = 'ap-northeast-2'

위의 내용은 S3버전 및 지역을 설정해주는 것이다. dev.py에는 필요가 없으나 production.py에는 추가해주는 것이 좋다.

  • 위의 코드를 완성하고 파일을 다시 업로드하고 확인하면 정상적으로 올라가는 것을 볼 수 있다.

bucket에 폴더 구분해서 올리기

s3boto3.py에 있는 location설정을 사용한다. 이 location은 어떤 경로를 기준으로 저장하고 싶을때 사용한다. 예를 들면 media파일은 media폴더에, static파일은 static폴더에 저장할 때 사용한다. 근데 그냥 settings.py에 넣으면 한군대에 몰아서 저장된다. 따라서 설정을 따로따로 지정해주어야한다. 이를 위해 config폴더 안쪽에 storages.py파일을 만들어준다.

config
├── __init__.py
├── __pycache__
├── settings
├── storages.py	*
├── urls.py
├── views.py
└── wsgi

그리고 그 storages.py파일 안에 아래의 내용들을 추가해주고 settings/dev.py에 약간의 수정을 해준다..

from storages.backends.s3boto3 import S3Boto3Storage


class MediaStorage(S3Boto3Storage):
	 # 이 스토리지를 사용해서 저장되는 파일들이 
	 # <location값>/<추가경로> 부분에 저장된다.
    location = 'media'


# settings/dev.py
DEFAULT_FILE_STORAGE = 'config.storages.MediaStorage'

이렇게 설정을 해주고 다시 runserver이후에 파일을 업로드 후 S3의 bucket을 확인해보면 media파일 안에 구분되어 올라가는 것을 확인할 수 있다.