[flask] gunicorn, nginx로 배포하기

somnode·2021년 12월 19일
0
post-custom-banner

웹서버 / WAS

nginx는 웹 서버이고, gunicorn은 WAS(Web Application Server)에 속한다.
웹 서버는 단순히 정적 파일을 응답하는 존재이고, WAS는 동적인 처리를 수행하는 존재이다.

웹 서버를 별도로 운영하는 이유

웹 서버를 별도로 운영하는 이유는 WAS의 부담을 줄여주기 위함이다.
이미지, html, css 등 웹 사이트 운영에 필요한 변하지 않는 파일들까지 WAS에서 처리하지 않고, 웹 서버가 처리함으로써, WAS의 부담을 줄여준다.

nginx를 사용하는 이유

  1. 빠른 속도
    동시 요청이 많아도 메모리 사용량이 현저히 적다.

  2. 리버스 프록시(Reverse proxy)로 사용 가능
    리버스 프록시란 인터넷과 백엔드 사이에 있는 서버를 가리킴.
    여러 WAS가 존재하면 클라이언트 요청을 분산시키는 역할을 수행 -> 로드 밸런싱
    캐싱 가능 (WAS까지 요청하지 않아도 클라이언트 요청에 빠르게 응답)
    민감한 WAS 정보(기기 id, MAC 주소 등)를 숨기는 보안 역할 수행

  3. SSL 지원
    HTTPS의 인증서 제공

  4. 웹 페이지 접근 인증
    로그인 정보(관리자, 사용자)를 WAS에서 하지 않고 nginx에서도 가능

  5. 압축
    gzip을 사용하여 클라이언트가 보낸 텍스트 파일을 압축

  6. 비동기 처리
    Event loop 기반으로 상당히 많은 트래픽을 동시에 처리 가능

gunicorn이란

gunicorn은 WSGI의 일종이다.
WSGI(Web Server Gateway Interface)는 CGI(Common Gateway Interface)의 일종으로 공통적인 형태(HTTP 형식)로 주고 받기 위해 만든 규약이다.
WSGI는 파이썬 앱이 웹 서버와 효율적으로 통신하기 위해 만들어진 인터페이스로써, gunicorn이나 uWSGI 등은 nginx로 들어오는 HTTP 요청을 파이썬이 이해할 수 있게 바꿔준다.

gunicorn을 사용하는 이유

기본 flask 서버는 개발용이고, 배포용으로 적합하지 않다고 한다.
flask를 단독으로 실행하면 단일 프로세스가 생성되고 한개의 프로세스가 여러 Request 요청을 처리하게 된다.
즉, Request 요청이 많아지면 서버에 병목이 생기게 된다.

gunicorn과 같은 WSGI는 여러 개의 쓰레드를 생성하여 요청을 병렬로 처리해주기 때문에 기본 flask 서버를 사용할 때보다 성능이 뛰어나다.

uWSGI 또한 python에서 사용하는 대표적인 WSGI이지만, gunicorn이 더 가볍고 성능이 좋다고 한다.

gunicorn / nginx 둘 다 꼭 써야 하는가

결론은 gunicorn 또는 nginx 둘 중 하나만 써도 상관없다.

  • gunicorn만 사용할 경우: nginx가 제공하는 추가적인 혜택을 받지 못한다.
  • nginx만 사용할 경우: session, cookie, routing, authentication 등의 기능을 수행하는 middleware가 없어서 하드 코딩해야 한다.
    -> 이 부분 잘 이해가 안되는데...

앱 배포 전 확인해야 할 점

  • SECRET_KEY가 Github public reposity 등에 공개되어 있으면, 공격자가 세션을 읽거나 조작할 수 있다.
    • SECRET_KEY는 아래와 같이 랜덤으로 생성해서 사용하자.
import os
import binascii

random_number = os.urandom(24)
random_hex = binascii.hexlify(random_number)
print(random_hex)
  • flask의 debug 모드가 켜져 있으면 안 된다.
    • debug 모드가 켜져 있는 상태에서 에러가 발생할 경우, 임의의 코드를 실행할 수 있는 shell이 열리게 된다. (누군가 shell을 통해 명령어를 실행시킬 수 있다...!)

필요한 패키지 설치

sudo apt-get install nginx gunicorn

테스트 flask app 코드

# app.py

from flask import Flask

app = Flask(__name__)

@app.route("/")
def index():
    return "hello world"

if __name__ == "__main__":
    app.run()

gunicorn 연결 테스트

방법1) 포트 연결

  • app:app(MODULE_NAME):(VARIABLE_NAME)을 의미한다.
    • 앞쪽 app은 app.py 파일명을 의미하고, 뒤쪽 app은 Flask app의 변수명이다.
gunicorn3 --bind 0.0.0.0:8000 app:app &

방법2) 소켓 연결

gunicorn3 --bind unix:/home/somnode/flaskapp/gunicorn.sock app:app &
  • 정상적으로 동작한다면, 실행 중지하고, 아래 리눅스 서비스로 등록한다.

리눅스 서비스 생성

  • 경로 등은 본인의 환경에 알맞게 설정한다.
# /etc/systemd/system/flaskapp.service

[Unit]
Description=gunicorn daemon
After=network.target

[Service]
User=root
Group=root
WorkingDirectory=/home/somnode/flaskapp
ExecStart=/usr/bin/gunicorn3 \
        --workers 5 \
        --bind unix:/home/somnode/flaskapp/gunicorn.sock \
        --access-logfile /home/somnode/flaskapp/access.log \
        --error-logfile /home/somnode/flaskapp/error.log \
        app:app &

[Install]
WantedBy=multi-user.target

서비스 등록

flaskapp은 아까 리눅스 서비스 생성 시 파일명이다.

sudo systemctl daemon-reload
sudo systemctl enable flaskapp
sudo systemctl start flaskapp

서비스 구동 확인

  • status로 서비스가 정상적으로 실행되고 있는지 확인한다.
sudo systemctl status flaskapp

nginx 사이트 설정

nginx conf 파일 생성

  • 도메인 연결이 되어 있을 경우 (도메인 예시: flaskapp.com, www.flaskapp.com)
# /etc/nginx/conf.d/flaskapp.conf

server {
    listen 80;
    listen [::]:80;
    server_name flaskapp.com www.flaskapp.com;

    location / {
        proxy_pass http://unix:/home/somnode/flaskapp/gunicorn.sock;
    }
}
  • 도메인 연결이 안 되어 있을 경우
    • 80 포트 말고 다른 포트 사용
# /etc/nginx/conf.d/flaskapp.conf

server {
    listen 5000;
    listen [::]:5000;
    server_name _;

    location / {
        proxy_pass http://unix:/home/somnode/flaskapp/gunicorn.sock;
    }
}

nginx, flaskapp 서비스 재시작

sudo systemctl restart nginx flaskapp

nginx 기본 포트 번호 변경

  • nginx 기본 포트인 80 포트에 apache 등 다른 웹 서버가 떠 있을 경우,
    /etc/nginx/sites-enabled/default에서 listen 부분의 80을 8080 등 다른 번호로 수정하면 된다.

502 Bad Gateway 이슈 발생 시

  • gunicorn으로 실행 후 socket 파일이 정상적으로 생겼는지 확인한다.
  • socket 파일이 존재하는 경로의 상위 디렉터리(/ 디렉터리)부터 권한이 맞는지 확인한다.
    • 아래 명령어로 확인 가능
namei -m /home/somnode/flaskapp/gunicorn.sock
  • 만약 drwx------ 이렇게 group과 other에서 rx 권한이 없다면, 해당 경로를 아래와 같이 권한을 부여한다.
chmod 755 /home/somnode/

참고 사이트

post-custom-banner

0개의 댓글