안녕하세요. IT 엘도라도 에 오신 것을 환영합니다.
글을 쓰는 것은 귀찮지만 다시 찾아보는 것은 더 귀찮습니다.
완전한 나만의 것으로 만들기 위해 지식을 차곡차곡 저장해 보아요.   포스팅 둘러보기 ▼

AWS (Amazon Web Service)

[AWS] Django 프로젝트 배포하기 (Elastic Beanstalk + PostgreSQL)

피그브라더 2020. 6. 12. 17:31

이전 포스팅과 연결 : [Django] 프로젝트 구조 설정 (앱, 템플릿, Static 파일 등)

 

1. 웹 서버, WSGI, 웹 어플리케이션 개념

장고 프로젝트를 배포하기에 앞서, 간단하게 웹 서버와 관련된 사전 지식들을 짚고 넘어가자.

 

  • 웹 서버 (Web Server)
    • 정적인 컨텐츠를 제공하는 서버를 의미한다.
    • 프로그램을 실행하거나 데이터베이스에 접근하는 등의 동적인 기능은 수행할 수 없다.
    • 파일 시스템에 존재하는 정적 파일들을 찾아서 제공해주는 정적 기능만 수행할 수 있다.
    • 대표적으로 Apache, Nginx 등이 있다.

 

  • 웹 어플리케이션 서버 (Web Application Server, WAS)
    • 동적인 컨텐츠를 제공할 수 있는 서버를 의미한다.
    • 프로그램을 실행하거나 데이터베이스에 접근하는 등의 동적인 기능을 수행할 수 있다.
    • CGI, WSGI 등의 인터페이스를 통해 웹 어플리케이션(Django, Flask 등)과 통신할 수 있다.
    • 이 자체로 웹 서버 역할을 수행할 수도 있고, 응답을 웹 서버에게 넘겨줄 수도 있다.
    • 대표적으로 tornado, uwsgi, mod_wsgi 등이 있다.

 

  • CGI (Commo Gateway Interface), WSGI (Web Server Gateway Interface)
    • 웹 서버가 웹 어플리케이션과 통신하기 위한 인터페이스(규칙)을 의미한다.
    • 이러한 인터페이스를 갖추고 있는 웹 서버를 웹 어플리케이션 서버(WAS)라고 부른다.

 

  • 장고의 개발 서버 (python manage.py runserver)
    • 장고가 자체적으로 제공하는 간단한 버전의 WSGI 웹 어플리케이션 서버를 의미한다.
    • 개발에 사용하는 용도로만 만들어졌기 때문에, 보안 등에 매우 취약하다.

 

▼ 출처 : https://datascienceschool.net/view-notebook/f9b09998601441f4a7026e62353cf751/

 

2. 설정 파일 분리 (로컬 vs 실서버)

로컬 개발 환경과 실서버 배포 환경은 접근 가능한 도메인 이름, 사용하는 데이터베이스 서버, Static 파일의 제공 방법 등을 다르게 설정해줘야 한다. 따라서 장고의 설정 파일(settings.py)을 로컬 개발 환경을 위한 local.py와 실서버 배포 환경을 위한 production.py로 분리하기로 한다. 단, 유지보수의 편의를 위해 두 환경 모두에서 공통으로 사용하는 설정들은 base.py라는 파일에 담고, local.py와 production.py는 base.py의 내용을 전부 import 하도록 만들어 보자.

 

참고로 필자의 경우, 직전 포스팅의 내용을 바탕으로 장고의 설정 파일(settings.py)을 프로젝트 폴더 하위의 config 폴더에 위치시켜둔 상태이다. 그러면 이제 config 폴더 하위에 settings라는 폴더 하나를 생성하고, 그 안에 base.py, local.py, production.py 파일을 생성해 보자. base.py에는 두 환경 모두에서 공통으로 사용하는 설정들을 작성해주면 되고, local.py와 production.py에는 각 환경에서만 필요로 하는 설정들을 각각 작성해주면 된다. (그리고 직전 포스팅 내용을 참고하여 설정 파일의 경로가 변경될 때 수정해줘야 하는 몇몇 부분들을 잊지 말고 챙기도록 하자.)

 

일반적인 배포 방식을 가정할 때, local.py와 production.py에 담겨야 하는 설정들(= 로컬 개발 환경과 실서버 배포 환경에서 다르게 설정되어야 하는 부분들)의 대표적인 예시로는 다음과 같은 것들이 있다. STATIC_ROOT와 관련한 자세한 내용은 이어지는 설명을 참고하자.

 

  local.py (로컬 개발 환경) production.py (실서버 배포 환경)
DEBUG True False
ALLOWED_HOSTS ['127.0.0.1', 'localhost'] ['{추후 등록할 도메인 이름}']
DATABASES 로컬 PostgreSQL 서버에 연결되도록 설정 AWS RDS에 연결되도록 설정
STATIC_ROOT (미설정) os.path.join(BASE_DIR, 'staticfiles')

 

3. 정적(Static) 파일 경로 설정 - 보충 설명

STATIC_ROOT를 설명하기 위해, 직전 포스팅에서 간단하게만 설명했던 정적 파일 경로 설정과 관련된 내용을 여기서 조금 자세히 다뤄보고자 한다. 이 내용을 이해하기 위해서는 맨 위의 "1. 웹 서버, WSGI, 웹 어플리케이션 개념"을 먼저 읽어보고 오기 바란다.

 

장고 패키지는 기본적으로 staticfiles라는 이름의 내장 앱(django.contrib.staticfiles)을 가지고 있다. 여기서 말하는 앱이란 우리가 startapp 명령어로 만드는 앱의 개념과 동일하다. staticfiles 앱은 장고가 프로젝트 폴더 내에서 필요한 정적 파일들을 편리하게 탐색할 수 있도록 하는 기능을 제공한다. 이 앱의 기능을 제대로 활용하려면 장고의 설정 파일에 다음과 같은 변수들의 값을 적절히 설정해줘야 한다.

 

  • STATICFILES_FINDERS
    • 프로젝트 폴더 내에서 필요한 정적 파일들을 탐색하기 위한 (staticfiles 앱에 존재하는) 함수들의 이름을 지정한다.
    • django.contrib.staticfiles.finders.FileSystemFinder 함수 : STATICFILES_DIR에 지정된 경로에서 정적 파일을 탐색한다.
    • django.contrib.staticfiles.finders.AppDirectoriesFinder 함수 : 각 앱 폴더의 static 폴더에서 정적 파일을 탐색한다. 각 앱의 위치는 INSTALLED_APPS에 명시된 정보를 바탕으로 파악한다.

 

  • STATICFILES_DIR
    • staticfiles 앱의 FileSystemFinder 함수가 정적 파일을 탐색하는 경로를 의미한다.

 

  • STATIC_URL
    • staticfiles 앱은 staticfiles(또는 static)라는 이름의 템플릿 태그 소스 파일에서 static이라는 이름의 템플릿 태그를 제공한다.
    • 템플릿 파일에서 "{% load staticfiles %}"로 해당 템플릿 태그 소스 파일을 로드하고 "{% static '{정적 파일 상대 경로}' %}"라고 작성하면, 장고는 이 부분을 STATIC_URL에 지정된 경로를 이용하여 절대 경로로 치환한다.
    • 즉 STATIC_URL은 정적 파일에게 GET 요청을 보낼 때 사용하는 URL을 의미하는 것이다.
    • 만약 정적 파일을 외부 서버(S3 등)에 저장한다면, 이 값은 해당 외부 서버의 URL로 설정이 되어야 할 것이다.

 

  • STATIC_ROOT
    • 로컬 개발 환경의 경우, STATIC_URL에 지정된 경로를 통해 정적 파일에 접근을 시도하면 장고 개발 서버가 스스로 staticfiles 앱의 함수들(STATICFILES_FINDERS에 명시)을 실행하여 필요한 정적 파일을 찾아 클라이언트에게 제공해준다.
    • 실서버 배포 환경의 경우, STATIC_URL에 지정된 경로를 통해 정적 파일에 접근을 시도하면 웹 서버가 필요한 정적 파일을 찾아서 제공해줘야 한다. 그러나 웹 서버는 장고 프로젝트 내 정적 파일들의 위치를 알지 못한다. 따라서 웹 서버가 필요한 정적 파일을 쉽게 찾아 클라이언트에게 제공할 수 있도록, 장고 프로젝트 내 정적 파일들을 한 디렉토리에 모아둘 필요가 있다. 그 경로를 지정하는 변수가 바로 STATIC_ROOT이다.
      • "python manage.py collectstatic" 명령어를 수행하면, 장고는 staticfiles의 함수들(STATICFILES_FINDERS에 명시)을 실행하여 장고 프로젝트 내 모든 정적 파일들을 찾고, 그것들을 STATIC_ROOT에 명시된 경로로 복사한다.
      • 따라서 웹 서버는 STATIC_URL에 지정된 경로로 요청이 들어오면 STATIC_ROOT에 지정된 경로로 찾아가서 정적 파일을 찾도록 설정이 되어야 한다. (곧 살펴보겠지만, Elastic Beanstalk를 사용하면 이 설정도 쉽게 할 수 있다.)
      • 만약 정적 파일을 외부 서버(S3 등)에 저장한다면, collectstatic 명령어를 통해 STATIC_ROOT에 수집된 모든 정적 파일들이 STATIC_STORAGE에 지정된 커스텀 스토리지 백엔드에 의해 해당 외부 서버로 옮겨진다. 이때 STATIC_URL은 해당 외부 서버의 URL로 설정이 되어야 할 것이다.

 

  • STATIC_STORAGE
    • 기본값 : StaticFilesStorage (웹 서버에서 직접 정적 파일을 제공하는 경우)
    • 만약 정적 파일을 외부 서버(S3 등)에 저장한다면, 이 값은 해당 외부 서버에 맞는 커스텀 스토리지 백엔드로 설정되어야 한다.

 

4. AWS Elastic Beanstalk 개념

사이트를 제작하여 직접 배포를 해본 사람은 알겠지만, 어쩌면 개발보다 배포가 더 힘들다고 느낀 적도 많을 것이다. 그만큼 배포를 하기 위해선 고려해야 할 게 조금 많은 게 아니다. 인스턴스 관련 설정, 데이터베이스 관련 설정, 보안 그룹 관련 설정, 웹 서버 관련 설정 등등 셀 수 없을 정도이다. 이러한 불편함을 조금이나마 개선해줄 수 있는 AWS의 서비스가 바로 Elastic Beanstalk(이하 EB)이다.

 

EB는 개발자가 애플리케이션을 쉽게 배포하고 관리할 수 있도록 제공되는 서비스이다. 배포하고자 하는 애플리케이션에 대하여 1개 이상의 플랫폼 환경을 편리하게 구축할 수 있고, 개발이 진척될 때마다 원하는 환경을 선택하여 쉽게 배포할 수 있다. 예를 들어 나의 애플리케이션을 위한 실서버와 테스트 서버를 두 개 구축하고 싶다면, 해당 애플리케이션에 대하여 2개의 환경을 구축하면 된다. 실서버에 배포하고 싶을 때는 실서버 환경을 선택하여 배포하면 되고, 테스트 서버에 배포하고 싶을 때는 테스트 서버 환경을 선택하여 배포하면 된다.

 

각각의 환경은 여러 리소스들(인스턴스, 데이터베이스 서버, 로드 밸런서 등)로 구성이 되며, 배포하고자 하는 애플리케이션의 유형에 맞춰 특정 플랫폼(Java, Python, PHP 등)으로 구축된다. EB 자체는 과금 대상이 아니지만, EB의 각 환경을 구성하는 리소스들 각각에 대하여 사용하는 만큼 과금이 이뤄진다. 즉 EB는 이미 존재하는 AWS의 각 리소스들을 쉽게 조합하여 배포 환경을 구축하고 관리할 수 있도록 도와주는 도구일 뿐인 것이다. 그리고 각 환경에 대한 배포는 프로젝트 폴더 자체를 압축(= 번들링)하여 업로드하는 식으로 진행된다. 그렇게 압축되어 업로드되는 파일들 각각을 애플리케이션 버전이라고 부른다. 즉 배포란 특정 애플리케이션 버전을 특정 환경에 업로드하는 것을 말한다.

 

EB를 사용하는 경우 각 리소스에 대한 설정, 웹 서버에 대한 설정 등을 편리하게 수행할 수 있다. 더불어, 상태 모니터링이나 로그를 살펴보는 기능들도 쉽게 사용이 가능하기 때문에 서버의 관리도 용이하게 할 수 있다는 장점이 있다.

 

5. AWS Elastic Beanstalk 배포 방법

5-1. AWS 계정 및 자격 증명 발급

EB를 포함한 AWS의 서비스를 이용하기 위해서는 AWS 계정이 있어야 하며, 더불어 해당 계정에 IAM 사용자를 추가하여 자격 증명(Credential)이라는 것을 발급받아야 한다. 먼저 AWS 사이트에서 회원가입을 하여 계정에 로그인을 하고, 다음 과정을 통해 IAM 사용자를 추가하도록 한다.

 

  • 상단 탭의 [서비스] 드롭다운을 선택하여 검색 창을 켠 뒤, IAM을 검색해서 들어간다.
  • [사용자] 탭을 클릭하여 사용자 목록 페이지로 이동한 뒤, [사용자 추가] 버튼을 클릭한다.
  • 사용자 이름을 입력하고, 액세스 유형으로 [프로그래밍 방식 액세스]를 선택한다.
  • [그룹 생성] 버튼을 클릭하여 그룹 생성을 위한 창을 띄운다.
  • 그룹 이름을 입력하고 정책으로 AWSElasticBeanstalkFullAccess를 체크하여 그룹을 생성한다.
  • 태그는 굳이 추가하지 않아도 되니 [다음]을 눌러 넘어가고 [사용자 만들기]를 클릭한다.
  • 자격 증명(Credential) 정보가 담긴 CSV 파일을 다운로드한다. (잘 관리해야 함!)

 

5-2. requirements.txt 파일 준비 (+ AWS EB CLI 설치)

우리는 이미 앞선 과정에서 장고 애플리케이션에 필요한 파이썬 패키지들을 프로젝트 폴더에 위치한 requirements.txt라는 파일에 작성해 두었다. EB는 애플리케이션 버전을 배포할 때 프로젝트 폴더에 위치하는 requirements.txt 파일을 읽어서 필요한 파이썬 패키지들을 설치하게 된다. 따라서 현재 위치 그대로 두면 된다. 단, 이후의 과정에서 EB를 커맨드에서 원격 제어하기 위해서는 AWS EB CLI라는 도구가 필요할 것이기 때문에, 여기서 미리 requirements.txt에 "awsebcli==3.15.0"을 추가하고 "pip install -r requirements.txt" 명령어를 수행하여 AWS EB CLI를 설치하도록 하자.

 

5-3. EB 설정 파일 생성 (.ebextensions 폴더)

EB가 애플리케이션 버전을 배포할 때마다 참조하게 될 EB 설정 파일이 필요하다. 프로젝트 폴더에 .ebextensions라는 이름의 폴더를 생성하고, 그 안에 django.config 파일과 packages.config 파일을 생성하자. 먼저, django.config 파일 안에는 다음과 같이 작성한다.

 

# EB 배포 시 자동 실행할 명령어
container_commands:
  # 데이터베이스 마이그레이션
  01_migrate: command:
    "source /opt/python/run/venv/bin/activate && python manage.py migrate --noinput"
    leader_only: true

  # 정적(Static) 파일들을 STATIC_ROOT에 수집
  02_collectstatic: command:
    "source /opt/python/run/venv/bin/activate && python manage.py collectstatic --noinput"
    leader_only: true

# EB 설정
option_settings:
  # 마이그레이션은 애플리케이션 시작 전(배포 중)에 이뤄짐
  # 따라서 이를 위해 장고 설정 파일의 경로를 명시적으로 지정
  "aws:elasticbeanstalk:application:environment":
    DJANGO_SETTINGS_MODULE: "config.settings.production"

  # 웹 서버가 장고 웹 어플리케이션과 통신할 때 사용할 WSGI 인터페이스 관련 설정
  "aws:elasticbeanstalk:container:python":
    WSGIPath: config/wsgi.py

  # STATIC_URL로 요청되는 정적 파일들을 STATIC_ROOT에서 찾도록 설정
  "aws:elasticbeanstalk:container:python:staticfiles":
    "/static/": "staticfiles/"

 

다음으로, packages.config 파일 안에는 다음과 같이 작성한다.

 

packages:
  yum:
    # PostgreSQL 데이터베이스를 사용하기 위함
    postgresql95-devel: []

 

5-4. Git 커밋

Git을 사용하여 개발을 하고 있는 경우, 커밋까지 완료된 다음에 다음 과정을 진행하도록 하자.

 

5-5. EB CLI 리포지토리 초기화

프로젝트 폴더에서 "eb init" 명령어를 수행하여 EB CLI 리포지토리를 초기화한다.

 

  1. 리전 선택 : Asia Pacific (Seoul)
  2. 자격 증명 정보 입력 : 다운로드한 CSV 파일 참고
  3. 애플리케이션 이름 입력
  4. 플랫폼 선택 : 물어보는 플랫폼이 맞다면 Y 입력
  5. 플랫폼 버전 선택 (가상 환경에서 무엇을 쓰는지 알고 있어야 함)
  6. CodeCommit 사용 여부 설정 (Y/N) : N 입력 (무슨 기능인지 모르겠다)
  7. 인스턴스 SSH 접속 설정 (Y/N) : Y 입력
  8. 키 페어 선택 혹은 생성 : 새로운 키 페어 생성 (잘 관리해야 함!)

 

5-6. EB 환경 생성

프로젝트 폴더에서 "eb create {환경 이름}" 명령어를 수행하여 방금 생성한 애플리케이션에 대한 환경을 생성한다. EB는 해당 애플리케이션 실행을 위한 각종 리소스들을 생성하고 진행 상황 정보를 터미널에 실시간으로 출력해준다.

 

5-7. EB 데이터베이스 추가 및 연결 (PostgreSQL)

AWS 사이트에 들어가서 Elastic Beanstalk 페이지에 들어가 보면, 애플리케이션 하나가 생성되어 있고 그 안에 환경이 하나 구축되어 있음을 볼 수 있다. 해당 환경을 선택한 뒤, 좌측 탭에서 [구성]을 클릭하면 해당 환경을 구성하는 리소스들의 목록을 볼 수 있다. 이 중에서 [데이터베이스] 부분은 아직 설정을 해주지 않았기 때문에 내용이 비어있을 것이다. [편집]을 클릭하여 PostgreSQL을 위한 RDS를 할당해 보자. DB 엔진으로는 PostgreSQL을 선택하고, 사용자 이름 및 비밀번호 등의 정보를 입력하고 [적용]을 클릭하면 RDS 할당이 진행된다. 할당이 완료되면 실서버 배포 환경 설정 파일에 해당하는 production.py의 내용을 다음과 같이 수정해주자.

 

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': os.environ.get('RDS_DB_NAME'),
        'USER': os.environ.get('RDS_USERNAME'),
        'PASSWORD': os.environ.get('RDS_PASSWORD'),
        'HOST': os.environ.get('RDS_HOSTNAME'),
        'PORT': os.environ.get('RDS_PORT')
    }
}

 

5-8. 실서버 배포 환경 설정 파일 ALLOWED_HOSTS 수정

프로젝트 폴더에서 "eb status" 명령어를 수행하면 방금 생성한 환경의 도메인 이름을 알아낼 수 있다. 이 도메인 이름을 실서버 배포 환경 설정 파일에 해당하는 production.py의 ALLOWED_HOSTS 변수에 추가해준다. 그렇지 않으면 장고 웹 어플리케이션이 해당 도메인으로부터의 요청을 거부할 것이다.

 

5-9. EB 환경 변수 설정

마찬가지로 [구성]을 클릭한 뒤, [소프트웨어] 부분의 [편집]을 클릭해 보자. 여기서 해당 EC2 인스턴스의 환경 변수를 설정할 수 있다. 실서버 배포 환경에서는 로컬 개발 환경과는 다른 설정 파일을 사용해야 하기 때문에, 이를 위해 이름은 "DJANGO_SETTINGS_MODULE"이고 값은 "config.settings.production"인 환경 변수를 하나 추가해 주자. 참고로, 5-7의 코드에서 보이는 RDS 관련 환경 변수들은 굳이 직접 추가해줄 필요 없다. EB에서 RDS를 할당하면 알아서 해당 환경 변수들이 설정되기 때문이다.

 

5-10. EB 환경에 배포하기

드디어 프로젝트를 배포할 때이다. 프로젝트 폴더에서 "eb deploy {환경 이름}" 명령어를 수행하자. 그러면 EB CLI가 현재 프로젝트 폴더의 내용을 압축(= 번들링)하여 이를 해당 환경에 업로드(배포)하게 된다. 참고로, Git을 사용하는 경우 커밋을 먼저 한 다음에 이 명령어를 수행해야 한다. 이제 개발을 진행하면서 변경 사항이 생길 때마다 Git 커밋을 수행하고 "eb deploy {환경 이름}" 명령어만 수행해주면 자동으로 배포가 진행될 것이다.