Django 프로젝트 AWS로 배포하기 for Mac

Johnywhisky·2021년 10월 29일
1

aws

목록 보기
1/1

AWS-EC2와 RDS를 활용해 백엔드 서버 배포하기

mac 환경에서 aws ec2와 rds를 활용해 서버를 배포하는 방법을 정리했습니다. 처음 시작하는 분들이 스크롤 압박을 이겨내고 도움을 받으시길 바랍니다 :-)
본 포스팅은 아예 처음 하는 분들이 따라 할 수 있는 방법으로 작성되었기 때문에 보안이 취약합니다. 따라서 연습용으로 따라 해보시고 바로 EC2를 중지시키길 권장합니다.

1

가) EC2 instance 생성

인스턴스 만들기

aws 가입 후 ec2 콘솔에 접속하면 이런 창이 보일 것이다. 좌측 상단에 있는 인스턴스 시작을 눌러 ec2생성을 하자

그러면 다음과 같은 창으로 넘어가지는데 Ubuntu server 20.04 LTS를 클릭해 넘어가자. arm과 x86은 EC2 cpu의 아키텍처 차이이므로 지금 당장 우리에게 중요하진 않으니 넘어가자

다음으로 단계 2에서는 인스턴스 유형을 선택해야 한다. 여기서 프리티어 사용이 가능한 t2.micro를 선택하고 넘어간다

이후 3~5 단계에서 나오는 내용은 aws에 대해 더 공부가 필요한 내용들이니 지금은 7단계까지 쭉쭉 넘어가자. 7단게에서 우측 하단에 시작하기를 누르면 밑과 같은 화면을 볼 수 있다


키 페어를 이미 생성했다면 생성한 키페어를 쓰고, 처음 키 페어를 생성해야 하는 사람은 새 키 페어 생성을 선택하고 이름을 정해준다. 키 페어를 다운로드 해준 후 인스턴스를 시작하자. 인스턴스를 시작하자마자 바로 사용할 수 있는 것은 아니고 수초에서 수분 정도 기다리면 사용할 수 있다. 생성 된 인스턴스의 인스턴스 상태를 확인해 보면 pending/running과 같은 표시가 되어있을 것이다. running(초록 불)이면 서버가 켜져있는 상태

로컬에서 EC2 접속하기

지금부터는 터미널에서 작업 할 차례이다. 앞서 생성했던 키페어가 저장되어있는 폴더로 가자

내가 다운받은 키페어는 test.pem인데 이 파일을 안전한(?) 장소에 먼저 보관할 필요가 있다.

~ > mv ~/desktop/test.pem ~/.ssh/test.pem
mv [current directory/file_name] [target directory/file_name]

터미널에서 위 명령어를 입력해 키 페어 파일을 .ssh 폴더로 옮겨주었다.

  • 결과

다음으로 다시 aws로 돌아가 이전에 만든 EC2의 IPv4 주소를 알아야 한다

EC2 창에서 퍼블릭 DNS 또는 IPv4 퍼블릭 IP를 복사해두자. 그리고 다시 터미널로 돌아와 다음 명령어를 입력한다.

~ > chmod 400 ~/.ssh/test.pem
~ > ssh -i ~/.ssh/test.pem ubuntu@13.125.170.64
~ > ssh -i [key-pair directory/file_name] ubuntu@[IPv4 public IP address](or public DNS(IPv4) address)

이제야 우리는 로컬 컴퓨터에서 EC2 컴퓨터에 접속 한 것이다

  • 참고 1
    ubuntu@ip_address:~$ exit
    을 입력하면 EC2에서 logout 할 수 있다

  • 참고 2

    EC2 접속 시 DNS주소를 이용해 접속이 가능하다
    ~ > ssh -i ~/.ssh/key_pair_name.pem ubuntu@public_DNS(IPv4)_address

  • 참고 3

    chmod는 키파일에 대한 권한 설정을 바꿔주는 명령어인데 뒤에 숫자 세개가 나오는데 차례대로 나, 그룹, 전체에 대한 권한을 의미한다.
    권한은 세가지로 read(4), write(2), execute(1) 가 있는데 이 숫자들 합의 조합으로 권한을 나타낸다. 예를 들어 5면, 5 = 4 + 1 로 read, write 권한이 있음을 의미한다.
    즉 chmod 400의 의미는 4/0/0 이므로 나에게만 읽기 권한이 있도록 하는것이다.
    예시)
    600: 나에게만 읽기,쓰기 권한
    740: 나는 읽기, 쓰기, 실행 권한 / 그룹 읽기 권한 / 전체 권한 없음

나) RDS 생성 및 프로젝트와 연동

RDS 생성

RDS console로 가서 데이터베이스 생성을 클릭하자

이후 다음의 화면을 볼 수 있다. MySQL을 활용해 RDS를 만들어보자. 특별히 사용중인 MySQL 버전이 있다면 밑에 버전에서 맞춰주면 된다

다음으로 가장 중요한 템플릿 설정이다. 별표 5개!⭐️⭐️⭐️⭐️⭐️

프리티어를 선택해야 과금 압박에서 벗어날 수 있다.
실수로 프로덕션 템플릿으로 생성할 경우 ⭐️한달에 100만원이 넘는 과금 폭탄⭐️을 맞을 수 있으니 잊지말고 ⭐️꼭 프리 티어를 선택⭐️하자

다음으로 설정에서 마스터 사용자 이름과 암호를 설정해주자. 두 설정 값은 장고의 settings.py에서 데이터 베이스 연동 시 필요하니 꼭 기억해두자!

연결에서는 VPC설정을 해줘야 한다. 우선 모두 Default로 설정하고 넘어가자. 중간에 퍼블릭 액세스에서 에 체크해줘야 로컬에서 연동 테스트를 하고 넘어갈 수 있으니 꼭 체크해주자. 보안 그룹도 우선은 default로 하고 넘어가자


마지막으로 추가구성이다. 여기서 초기 데이터베이스 이름을 설정해주자. 이후 장고에서 db연동 시 필요한 설정값이다.
모두 설정했다면 마지막으로 생성을 클릭해주자. 그러면 다시 RDS 콘솔로 넘어가지는데 밑에 새롭게 만든 db가 잘 생성되고 있는지 확인하자. RDS가 만들어지는데는 EC2 보다 시간이 조금 더 필요하다.

RDS가 생성되었으면 클릭해서 End-point와 port를 확인해야 한다

위 화면에서 엔드포인트와 포트도 확인했다면 이제 장고 프로젝트로 넘어가 settings.py를 수정해주자

프로젝트와 연동하기

장고 프로젝트로 넘어와 settings.py에 DATABASE를 수정해주자. RDS를 만들 때 입력했던 모든 정보를 입력해주면 된다.

DATABASES = {
	'default': {
    		'ENGINE': 'django.db.backends.mysql',
                'NAME': '초기 데이터베이스 이름',
                'USER': '마스터 사용자 이름',
                'PASSWORD': '마스터 암호',
                'HOST': '엔드포인트',
                'PORT': '포트',
                'OPTIONS': {
                    'init_command' : "SET sql_mode='STRICT_TRANS_TABLES'"
                    },
                }
            }

이제 runserver 명령어를 입력해 잘 연동되는지 확인해보자. 아마 많은 사람이 밑의 에러와 마주할 것이다.

django.db.utils.OperationalError: (2003, "Can't connect to MySQL server on Endpoint:port)

에러 해결 방법

2003 에러는 MySQL 서버 접속에 실패했을 때 나타나는 에러이다. 가장 먼저 확인해야하는 것은 settings.py의 DATABASES에 정보를 제대로 입력했는지 이다.
그래도 같은 에러가 발생한다면 두 번째로 확인해봐야 하는 것은 VPC 보안그룹 inbound rules이다

다시 AWS RDS 콘솔로 넘어와 방금 만든 데이터베이스를 확인해보면 보안에 VPC 보안 그룹을 볼 수 있다. 클릭 해서 넘어가면 밑의 화면을 볼 수 있다

우측 하단에 있는 Edit inbound rules를 클릭하자

규칙 추가를 눌러 추가적인 인바운드 규칙을 더해주자. 내 IP 또는 Anywhere-IPv4를 선택해주자
이때, 동일 환경(인터넷 환경이 변하지 않는 곳)에서 지속적인 테스트를 한다면 내 IP를 선택해 최소한의 보안 환경을 만들어 줄 수 있다.

변경된 규칙을 저장한 후 다시 runserver 명령을 실행하면 정상적으로 작동하는 것을 볼 수 있다.

이로써 RDS와 내 프로젝트 간의 연동이 완성되었다!
다음으로는 RDS와 연동된 프로젝트를 EC2 배포해보자!

  • 중요!
    테스트가 끝났다면 다시 보안 그룹으로 돌아가 이전에 추가한 규칙을 삭제해주자!


2

가) Project 업로드

환경 변수 분리하기

우리는 github(gitlab 등)을 활용해 내가 만든 프로젝트를 내 레포에 push할 때 민감함 환경 변수를 분리해서 올려야 한다. 장고를 예로 들면 SECRET_KEY 등이 있다. (secret_key를 분리 하지 않은 채 푸쉬할 경우 깃헙에서 경고 메일이 날라오기도 한다.)
보통 장고의 경우 settings.py에서 SECRET_KEY와 DATABASES os에 expor를 해주거나 json 또는 모듈로 분리해 관리하는데 나는 class형태의 모듈로 만들어 분리했다.

# config_ops.py
class ConfigDEV():
    
    def __init__(self):
        self.SECRET_KEY = '장고 SECRET_KEY'
        self.DATABASES = {
            'default': {
                'ENGINE': 'django.db.backends.mysql',
                'NAME': 'RDS_NAME',
                'USER': 'RDS_USER',
                'PASSWORD': 'RDS_PASSWORD',
                'HOST': 'RDS_ENDPOINT',
                'PORT': 'RDS_PORT',
                'OPTIONS': {
                    'init_command' : "SET sql_mode='STRICT_TRANS_TABLES'"
                    },
                }
            }

또한 settings.py도 다음과 같이 변경해준다

# settings.py

from config_ops import ConfigDEV
config = ConfigDEV()
SECRET_KEY = config.SECRET_KEY
DATABASES = config.DATABASES

해당 모듈을 manage.py와 동일 디렉토리에 생성해주고 .gitignore에 추가해준다

# .gitignore

# Created by https://www.toptal.com/developers/gitignore/api/macos,python,visualstudiocode
# Edit at https://www.toptal.com/developers/gitignore?templates=macos,python,visualstudiocode

### macOS ###
... 중략

# End of https://www.toptal.com/developers/gitignore/api/macos,python,visualstudiocode

config_ops.py

환경 변수를 분리한 후 github에 푸쉬해준다


나) Ubuntu server setting

이제 가장 힘든 단계인 EC2 우분투 서버 환경을 세팅해주자.
앞서 EC2에 접속했던 것처럼 chmodssh -i 커맨드를 입력해 EC2에 접속해주자

apt-get

먼저 밑의 세 커맨드를 차례대로 입력하자

# 패키지 목록 최신화
ubuntu@ip-address:~$ sudo apt-get update
# 최신화 된 패키지 업데이트
ubuntu@ip-address:~$ sudo apt-get upgrade
# 의존성 확인 후 upgrade에서 수행되지 않은 의존성까지 설치
ubuntu@ip-address:~$ sudo apt-get dist-upgrade

pip install

ubuntu@ip-address:~$ sudo apt install python3-pip

위 커맨드를 실행 해 pip를 설치하자

Virtual Environment

가상환경은 본인이 프로젝트를 진행하며 사용한 가상환경을 설치하면 된다. 본 포스팅에서는 아나콘다(미니콘다)를 설치했다

ubuntu@ip-address:~$ wget https://repo.anaconda.com/miniconda/Miniconda3-py39_4.10.3-Linux-x86_64.sh

다음으로 밑의 커맨드를 입력해 우분투에 실행권한을 주고 파일을 실행하자

# 파일명 확인
ubuntu@ip-address:~$ ls
Miniconda3-py39_4.10.3-Linux-x86_64.sh
# 실행 권한 부여
ubuntu@ip-address:~$ chmod +x Miniconda3-py39_4.10.3-Linux-x86_64.sh
# 파일 실행
ubuntu@ip-address:~$ ./Miniconda3-py39_4.10.3-Linux-x86_64.sh

열심히 엔터를 누르면 설치가 완료된다. 설치 완료 후source .bashrc 를 입력하면 (base)가 나타나는 것을 확인할 수 있다.

미니콘다 홈페이지에 가서 자신의 버전에 맞는 미니콘다 주소를 복사해 wget 뒤에 붙여서 실행하면 됩니다.

TroubleShooting

혹시라도 source .bashrc를 입력했음에도 (base)가 나타나지 않는다면 다음의 두 커맨드를 차례대로 입력해보자

source miniconda3/bin/activate

위 커맨드도 실행이 되지 않는다면

eval "$(/home/ubuntu/miniconda3/bin/conda shell.bash hook)"

우리는 지금까지 EC2에 접속해 패키지 업데이트, pip 설치, 가상환경 설치를 완료했다. 이제 github에 올린 서버를 가져오자!


  • MySQL client 참고
    우분투 서버에는 pip instsall mysqlclient 실행 시 엄청난 양의 에러가 발생한다. 이 에러를 방지하기 위해 다음 두 커맨드를 입력 해 패키지를 설치해주자
    sudo apt-get install gcc
    sudo apt-get install libmysqlclient-dev

다) Server Test

Virtual env setting

conda create -n [env_name] python=[python_version]
아나콘다 명령어를 입력 해 새로운 가상환경을 만들어주자

conda activate로 만들어준 가상환경을 실행한 후 git clone으로 본인이 repo에 올린 코드를 가져온다

  • ex)
(test) ubuntu@ip-address:~$ git clone https://github.com/my_repo/my_app.git

가져온 코드에 requirements.txt를 이용해 서버를 돌리기 위한 패키지를 모두 설치해주자

(test) ubuntu@ip-address:~$ cd my_app
(test) ubuntu@ip-address:~/my_app$ pip install -r requirements.txt

잘 설치되었는지 확인해보고 싶다면 pip freeze를 실행해 확인할 수 있다

settings.py 수정

이제 클론 받은 코드에 몇 가지 수정사항이 있다.
settings.py에서 분리해두었던 환경변수를 우분투 서버에 추가한다. 본 글과 같이 config_ops.py로 분리했다면 manage.py와 동일 디렉토리에 새롭게 만들어주면 된다. 이 때 DATABASES를 RDS와 연동해주면 EC2-RDS연동이 된다

runserver and test

이제 우분투에서 내 api 서버를 실행시킬 준비가 끝났다. 지금부터는 로컬에서 테스트할 때와 똑같이 하면 된다. 본 포스팅에서는 httpie를 활용해 테스트 했다

(base) ubuntu@ip-address:~/my_app$ conda activate test
(test) ubuntu@ip-address:~/my_app$ python manage.py runserver 0:8000
  • 결과
ubuntu@ip-address:~$ http -v get localhost:8000/api_url
  • 결과

  • 참고
    ubuntu에서 httpie 설치하는 방법
    ~$ sudo apt install httpie

  • 주의할 점
    이 단계에서 mac의 로컬 환경에서 httpie를 사용해 테스트할 시 제대로 작동하지 않습니다. ubuntu EC2에 접속한 터미널 창에서 실행하시기 바랍니다.

자! 여기까지 따라오신 분들은 우분투 서버에 나의 api 서버를 올린 것입니다!

TroubleShooting

우분투 환경에서 httpie 테스트 시 time out이 발생하거나 runserver 명령어를 입력해도 작동하지 않는 경우 RDS연동에 문제가 있을 가능성이 있다.
다음의 테스트를 따라 settings.py에 연동 되어있는 DATABASES를 RDS가 아닌 우분투의 로컬 sql과 연동해보자

  • step 1
    우분투에 mysql 설치
# terminal
ubuntu@ip-address:~$ sudo apt install mysql-server
ubuntu@ip-address:~$ sudo mysql

# mysql
mysql> create user 'USER_NAME'@'localhost' identified by 'PASSWORD'
FLUSH PRIVILEGES;
mysql> quit

# terminal
ubuntu@ip-address:~$ mysql -u USER_NAME -p
Enter password: password 입력

# mysql
mysql> create database db_name character set utf8mb4 collate utf8mb4_general_ci;

위 명령어를 따라오면 우분투 서버에 mysql을 설치해 새로운 유저를 생성하고 테스트용 데이터 베이스를 만들 수 있다

  • step 2
    settings.py의 DATABASES를 우분투 로컬 설정으로 변경
# config_ops.py
class ConfigTest():
    
    def __init__(self):
        self.SECRET_KEY = 'SECRET_KEY'
        self.DATABASES = {
            'default': {
                'ENGINE': 'django.db.backends.mysql',
                'NAME': 'db_name',
                'USER': 'USER_NAME',
                'PASSWORD': 'PASSWORD',
                'HOST': 'localhost',
                'PORT': '3306',
                'OPTIONS': {
                    'init_command' : "SET sql_mode='STRICT_TRANS_TABLES'"
                    },
                }
            }

# settings.py
from config_ops.py import ConfigTest
config = ConfigTest()
# ... (중략)
DATABASES = config.DATABASES

이후 다시 runserver 명령을 입력하면 제대로 작동하는 것을 확인할 수 있다


3

가) 배포

지금까지 따라왔다면 우리는 EC2에 RDS와 연동 된 api 서버를 올렸다. 이제 EC2 외부에서 EC2에 접근할 수 있게 열어주어야 한다

AWS 설정 변경

다시 AWS로 돌아가 EC2 인스턴스 정보 위에서 우클릭하고 네트워킹-보안 그룹 변경을 누르면 다음 사진과 같은 모달창을 볼 수 있다

우리가 최초 EC2 생성 시 자동으로 배정되어있던 보안 그룹(launch-wizard-1)에 추가적으로 RDS의 보안 그룹(본 글의 경우 default)을 클릭 해 추가해주자. 추가해주지 않을 경우 RDS 접속이 제한되니 꼭 해주자
그리고 보안 그룹에서 launch-wizard-1를 눌러 해당 페이지로 이동해 RDS에서 보안 그룹 인바운드 규칙을 변경해 준 것과 같이 모든 트레픽 / Anywhere-IPv4를 추가해주면 된다

settings.py 수정

우리는 로컬에서 EC2에 접속할 때 EC2 IPv4 주소를 사용해 접속했었다. 그리고 EC2 내에서 테스트를 진행했는데 이제 누구든 어디서나 EC2 서버에 접속할 수 있게 열어주기 위해서 몇가지 수정할 코드가 있다. 먼저 EC2에서 settings.py를 열어 ALLOWED_HOST에 EC2 접속 시 사용했던 ip주소를 추가해주자. 그리고 runserver 명령을 실행하면 정상적으로 실행되는 것을 알 수 있다

# settings.py on EC2
# ... (중략)
ALLOWED_HOST = ["IPv4_address"]

그리고 내 로컬에서 httpie를 활용해 테스트를 진행하면 정상적으로 작동하는 걸 볼 수 있다. 만약 누군가가 IP주소를 알고있다면 내가 배포한 api를 사용할 수 있게 된 것이다!

그런데 내 컴퓨터를 끄면?

내 컴퓨터를 끄면 EC2 인스턴스는 살아있지만, 서버는 죽어 있단 걸 알 수 있을 것이다. 배포까지 끝났는데 무언가 이상하다. 내가 컴퓨터를 꺼도 서버는 계속 살아있어야 하지 않을까? 이제 마지막 단계이다.

나) Gunicorn을 이용한 배포

gunicorn 설치

(test) ubuntu@ip-address:~/my_app$ pip install gunicorn
  • 결과

그리고 밑의 명령어를 입력해 서버를 실행해준다

(test) ubuntu@ip-address:~/my_app$ gunicorn --bind=0.0.0.0:8000 app_name.wsgi
  • 결과

위 결과창과 똑같은 화면을 봤다면 이제 EC2에 접속한 터미널 창을 종료해도 로컬에서 마음껏 테스트할 수 있다! 물론 내가 아닌 다른사람도 ip주소만 알고 있다면 당신의 EC2와 RDS를 사용할 수 있으니 프로젝트 팀원에게만 알려주도록 하자

만약 EC2가 꺼진다면?

뭐... 그럴 일은 없지만 AWS가 불의의 사고로 서울 리전의 서버 컴퓨터가 셧다운되 EC2가 중지(종료 X)되더라도 계속해서 우리의 api서버를 구동시키는 명령어가 있다. 사실 뇌피셜 100%라 이 가정이 맞는지 잘 모르겠다... 그냥 AWS에서 EC2를 수동으로 중지시키더라도... 정도로 타협해야겠다. 아무튼 정확히는 우분투 서버 세션이 끊어지더라도 지속적으로 동작할 수 있게 해주는 명령어가 있다. 바로 nohup이다.
우리가 로컬 터미널에서 EC2 로그아웃을 하는 상황이 아닌, EC2에서 세션 로그아웃이 발생하면 gunicorn으로 서버를 구동했더라도 서버는 자동으로 닫히게 된다. 예를 들어 밑에 사진과 같이 앞서 얘기한것처럼 AWS에서 EC2를 수동 중지 시켜보았다

그리고 로컬 터미널에서 EC2 서버로 http request를 보냈는데 다음과 같이 time out이 발생했다.

이렇게 내가 EC2에서 로그아웃하는 상황이 아닌, EC2 세션 자체가 로그아웃 하는 상황이 발생하더라고 지속적으로 서버는 다운되지 않게 하는 명령어가 nohup이다.

  • 참고
    리눅스는 해당 터미널에서 실행한 프로세스들에게 HUP signal을 전달해 종료시키게 되는데, 이 HUP signal을 프로세스가 무시하게 하는 명령어라서 nohup이다.
(test) ubuntu@ip-address:~/my_app$ nohup gunicorn --bind=0.0.0.0:8000 app_name.wsgi &

밑의 명령어를 입력해 제대로 구동 중인지 확인해보자

(test) ubuntu@ip-address:~/my_app$ ps -ef | grep python


세번째 줄을 확인해보면 내 서버가 잘 구동중인걸 알 수 있다. 마지막으로 nohup으로 구동시킨 서버를 종료하기 위해서는 다음 명령어를 입력하면 된다

(test) ubuntu@ip-address:~$ kill [PID]

PID는 ps -ef | grep python명령어를 입력했을 때 내 서버 정보의 앞부분의 숫자 중 첫번째 숫자이다. 위 사진의 경우에는 935번

위 사진과 같이 서버가 잘 종료된 것을 확인할 수 있다.
여기까지 따라왔다면 이제 맥을 종료해도 (프론트 엔드에게 빨리 다음 진도 나가자고 떼쓸 수 있게 된 것이다) 서버는 종료되지 않는다



여기까지 내 api 서버를 RDS와 연동시키고, EC2에 배포하는 방법을 알아보았습니다. 앞서 첫 서론에서 얘기했듯 이 방법은 가장 처음 aws를 접하고, 서버를 배포할 때 사용하는 방법입니다.



아직 우리에겐 nginx와 도커 그리고 k8s가 남아있습니다!!(아참! 자동화도 해야죠!)

profile
안녕하세요 :) 1년 차 Pythonist 백엔드 개발자 윤서준입니다.

0개의 댓글