함께하개 프로젝트 EC2 배포 과정 정리

개발자 강세영·2022년 10월 11일
4

TIL

목록 보기
46/70
post-thumbnail

함께하개 웹사이트

처음엔 프론트엔드와 백엔드 모두 각각의 EC2 인스턴스를 만들어서 서로 연결하여 배포하려고 했으나 어쩌다 보니 둘 다 한 EC2 인스턴스에서 배포하게 됐다.

리액트 배포

  • 리액트 배포는 정적인 파일들을 생성한 build 폴더를 만들고 이걸 웹 서버 프로그램으로 배포하는 방식으로 하기로 했다.

  • 빌드 된 폴더(build)는 전체 프로젝트 폴더에 비해 용량이 매우 작아진다.

  • 처음에는 프로젝트 깃헙 저장소를 EC2에서 git clone하고 .env 파일 같이 .gitignore에 포함된 것들만 따로 복사하여 npm install → npm run build 하여 배포하려고 했다.

  • 그런데 EC2 상에서 npm run build가 잘 되지 않는 문제가 발생했다.

  • 2차 프로젝트와는 다르게 여러가지 라이브러리와 패키지를 활용했기 때문에 전체 프로젝트 폴더 용량이 커졌다.

  • EC2 프리 티어 서버의 메모리가 1GB뿐인데 프로젝트 폴더 전체 용량은 거의 2GB 정도라서 메모리 부족으로 빌드가 잘 안됐던 것이다.

  • 따라서 로컬에서 빌드하고 결과물로 나온 build 폴더만 ec2에 올려서 배포하면 된다.

  • ec2 서버에 파일 업로드하는 방법

    • 로컬 터미널에서 scp 명령어로 업로드
    • ec2 터미널로 접속해서 github 등 원격 git 저장소 git clone 받기
    • FileZilla 같은 FTP 프로그램으로 ec2에 접속하여 업로드 등 여러 가지 방법이 있다
  • 단 빌드할 때 .env 같은 필요한 파일을 반드시 포함한 상태로 빌드해야 한다.

  • npm에 있는 serve라는 간단한 웹서버 프로그램으로 첫 배포를 성공, 반드시 -s 옵션을 붙여서 실행해야 한다.

  • serve 실행 명령어, 80번 포트를 사용하려면 sudo가 필요하다.
    sudo serve -s build -l 80 &

  • serve에서 pm2로 변경, pm2는 Node.js의 프로세스를 관리해주는 프로그램이며 예기치 못한 에러로 인해 스레드가 멈춰도 자동으로 프로세스를 재실행 해주기 때문에 편리하다.

  • pm2 실행 명령어
    sudo pm2 serve build 80 --spa

  • node.js와 express를 활용하여 배포할 수도 있는데 이 방법도 빌드 파일 만들어야 하는 것은 똑같고 과정만 조금 다르다.

  • npm start는 브라우저로 localhost에 접속 시 리액트로 만든 html을 띄워주는 명령어이다.

  • pm2로 웹페이지가 잘 나오는지 확인 후 밑에서 설명한 NGINX를 통해 https로 최종 배포했다.

EC2, 도메인 연결 방법

  • 가비아에서 도메인을(withdog.me) 구입했고 가격은 8천원 정도였다.

  • EC2와 가비아 도메인 연결 방법은 구글링하면 많이 나오고 어렵지 않다, 구입 후 AWS Route 53로 연결했다.

  • 처음에는 도메인 끝에 포트번호(3000)를 붙여줘야 접속이 됐으나 (serve -s build으로 실행)

  • 웹 서버를 80번 포트(기본 http 포트)로 실행하니 포트 번호를 따로 붙이지 않아도 접속 됐다.

NGINX

  • Nginx는 가벼우면서 고성능을 목표로 하는 웹 서버 프로그램이다.

  • 클라이언트로부터 요청을 받았을 때 요청에 맞는 정적 파일을 응답해주는 HTTP Web Server로 활용되기도 하고, 리버스 프록시 서버로 활용하여 WAS 서버의 부하를 줄일 수 있는 로드 밸런서로 활용되기도 한다.

  • Nginx는 Event-Driven 구조로 동작하기 때문에 한 개 또는 고정된 프로세스만 생성하여 사용하고, 비동기 방식으로 요청들을 동시적으로 처리할 수 있다.

  • Nginx는 새로운 요청이 들어오더라도 새로운 프로세스와 쓰레드를 생성하지 않기 때문에 프로세스와 쓰레드 생성 비용이 존재하지 않고, 적은 자원으로도 효율적인 운용이 가능하다

  • 이러한 Nginx의 장점 덕분에 단일 서버에서도 동시에 많은 연결을 처리할 수 있다.

NGINX, GUNICORN, DJANGO 통합


Gunicorn, WSGI란?

  • Gunicorn은 WSGI(Web Server Gateway Interface)의 일종이며, Django 서버 배포를 하기 활용할 수 있다.

  • WSGI는 CGI(Common Gateway Interface)의 일종으로 파이썬 애플리케이션이(파이썬 스크립트) 웹 서버와 통신하기 위한 인터페이스이다. 웹 서버에서의 요청을 해석하여 파이썬 애플리케이션 쪽으로 전달하는 역할을 수행한다.

  • 정리하자면 server/gateway side(Nginx 쪽)와 application/framework side(Django 쪽)를 둘 다 구현하고 있는 하나의 프로그램이다. 서버에 대해선 어플리케이션 역할을 수행하고, 어플리케이션에 대해선 서버의 역할을 수행한다.

  • Django에선 개발 목적으로 python manage.py runserver 명령어를 통해 웹 사이트를 띄울 수 있다. 하지만 보안이나 성능적으로 적절하지 않기 때문에 배포 환경에선 Gunicorn을 사용하는 것이다. 즉, production 환경에 적합하다.

  • 참고로 Django에는 이미 WSGI 파일이 포함되어 있다. wsgi.py 는 프로젝트 디렉토리에 위치해 있고, django.core.wsgi.py, django.core.handlers.wsgi.py 역시 내부적으로 구현이 되어 있는 상태이다.

  • Nginx와 Gunicorn 둘 중 하나만 써도 될까? 라는 질문에 대한 답은 모두 'Yes' 이다.

  • Gunicorn이 WSGI middleware로서 웹서버 역할을 수행하므로 Gunicorn만 써도 되지만, Nginx가 제공하는 추가적 혜택을 받지 못한다.

  • Django는 WSGI interface를 이미 어느 정도 구현했기 때문에 Nginx만 써도 되지만, 그럴 경우 seesion / cookie / routing / authentication 등의 기능을 수행하는 middleware가 없어서 이 기능들을 구현하려면 따로 개발 해야 한다. (Do not reinvent the wheel.)

reverse proxy란?

  • 프록시/리버스프록시 기본 개념:
    https://losskatsu.github.io/it-infra/reverse-proxy/

  • 기본 nginx 설정 파일을 보면 URL 경로가 /로 시작하여 들어오는 경우, root에 지정된 경로에 따라 일치하는 파일을 웹서버로 뿌려주는 식으로 작동한다는 것을 알 수 있다.

  • 하지만 리버스 프록시를 서버 블록에 적용하면  URL 경로와 적합한 서버 블록을 찾은 후 해당 서버 블록의 정보의 proxy_pass에 따르는 내용을 보여준다.

  • 리버스 프록시를 통해 로컬호스트의 8000번 포트에 접속해야 할 수 있는 것을 퍼블릭IP의 443번 포트(https)에 접속했을 때도 가능하게 만들 수 있다.

  • 리버스 프록시의 장점: 1. 보안 2. 성능 3. 트래픽 분산, 로드 밸런싱

HTTPS 적용

  • 처음에는 AWS에서 제공하는 인증서와 Application Load Balancer(ALB)를 이용하여 적용하려 했으나 이 방법으로는 루트 도메인(https://withdog.me)을 사용할 수 없는 것 같아 Nginx를 통해 https를 적용하는 방법으로 바꿨다.

  • 무료 SSL 인증서(Let's Encrypt)라고 구글링하면 Nginx에 적용하는 방법이 많이 나온다.

  • 인증서 발급은 ec2 인스턴스에 접속한 상태로 진행하였다.

  • https 적용이 정상적으로 된 것을 확인하면 리액트의 config과 .env 환경변수 파일에 있는 경로들을 모두 https가 들어간 경로로 변경하고 다시 빌드한다.

NGINX 설정 코드 설명

  • location 경로 설정을 잘못하면 리버스 프록시 기능이 제대로 작동하지 않으므로 적절한 설정 방법을 찾아야 한다.

  • 밑에 설정 파일을 적용하기 전에 Nginx 기본 설정 파일을 초기화 하는 작업이 필요하다.

  • options-ssl-nginx.conf 파일과 ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem을 설정에 추가해주면 보안을 강화할 수 있다.

  • root 권한으로 밑의 curl 명령어 실행해서 파일이 정상적으로 생성된 것을 확인하고 nginx 설정에 추가해주면 된다.

    • curl -s https://raw.githubusercontent.com/certbot/certbot/master/certbot-nginx/certbot_nginx/_internal/tls_configs/options-ssl-nginx.conf > /etc/letsencrypt/options-ssl-nginx.conf

    • curl -s https://raw.githubusercontent.com/certbot/certbot/master/certbot/certbot/ssl-dhparams.pem > /etc/letsencrypt/ssl-dhparams.pem

  • Nginx 설정 코드의 앞에 #를 붙이면 주석이 된다.

# 설정파일 경로 /etc/nginx/sites-available/config.conf
server {
        # 기본 http 포트(80)으로 접속하면 https로 리다이렉트 시킴
		# 도메인 끝에 추가로 입력한 부분까지 포함하여 리다이렉트 됨
        listen 80;
        server_name withdog.me;
        root html;

        location / {
                return 301 https://withdog.me$request_uri;
        }
}

server {
        # SSL/TLS 설정
		# https 기본포트(443)로 접속하면 빌드 된 index.html을 제공함
        listen 443 ssl default_server;
        listen [::]:443 ssl default_server;
        ssl on;
				
		# SSL/TLS 인증서 파일 경로
        ssl_certificate /etc/letsencrypt/live/withdog.me/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/withdog.me/privkey.pem;
        include /etc/letsencrypt/options-ssl-nginx.conf; # 보안 강화 옵션
		ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # 보안 강화 옵션
        
        server_name withdog.me;

        location / {
                root /home/ubuntu/build7; # 빌드파일 경로
                index index.html index.htm; # index.html->index.htm 순서대로 파일 탐색
                try_files $uri /index.html;
        }

        # 백엔드 서버(Django)를 위한 리버스 프록시 설정 
		# 클라이언트의 모든 백엔드 요청을 /api로 시작하는 경로로 지정했다
        # Django의 엔드포인트 경로에도 모두 앞에 /api를 붙여야 한다
        # gunicorn은 localhost 8000번 포트로 실행한다
        # 이렇게 할 경우 gunicorn을 -b 0:8000(퍼블릭IP, 외부 접속 가능)으로 실행하면 안되고 
        # localhost로 실행되게 해야하므로 -b 0:8000 옵션은 쓰지 않는다
        location ^~ /api {
                proxy_pass http://127.0.0.1:8000;
                proxy_http_version 1.1;
        }

        # socket.io를 위한 리버스 프록시 설정
		# 출처: flask-socketio 공식문서, MDN Protocol upgrade mechanism
        location ^~ /socket.io {
                include proxy_params;
                proxy_http_version 1.1;
                proxy_buffering off;
                proxy_set_header Upgrade $http_upgrade;
                proxy_set_header Connection "Upgrade";
                proxy_pass http://127.0.0.1:8000/socket.io;
        }
}
  • Nginx 서비스 실행 명령어
    sudo service nginx start

  • Nginx 서비스 실행 상태 확인
    sudo service nginx status 입력 후 터미널에 Active: active (running)라고 뜨면 정상

  • 배포가 정상적으로 됐다면, Django 설정 파일에서 개발 편의를 위해 사용했던 모든 host와 origin을 허용하는 환경변수를 수정해야한다.

  • settings.py에서 SECURE / CORS / ALLOWED HOSTS에 관련된 환경변수들을 적절하게 수정해주고, socketio 서버에도 적용한다.

SECURE_HSTS_INCLUDE_SUBDOMAINS = True
ALLOWED_HOSTS = ["localhost", "127.0.0.1", "withdog.me"]
CORS_ALLOWED_ORIGINS = ['https://withdog.me']

배포 흐름 정리

참고자료

0개의 댓글