[Django] HTTPS로 동작하는 서버 구성하기 (nginx, gunicorn)

우노·2024년 7월 22일
0
post-custom-banner

목표

위와 같은 구조를 바탕으로 HTTPS로 동작하는 Django 서버를 구성하려고 합니다! nginx와 gunicorn의 세부 설명과 쓸모에 대해서는 Web Server와 Web Application Server(WAS)를 참고해주세요.

사전 체크리스트

  • Django
  • python3
  • mysql
  • requirements.txt
  • Github

서버 세팅

Ubuntu 22.04 LTS 기준

먼저 우분투 기본 세팅과 git, python, python-venv 설치를 진행합니다.

sudo apt-get update
sudo apt-get upgrade
sudo apt-get install git
sudo apt-get python3-pip
sudo apt-get install python3.10-venv

🚨 mysqlclient 패키지를 활용한다면 pip install mysqlclient 에서 발생할 에러를 위해 아래 항목들도 설치합니다.

sudo apt-get install pkg-config
sudo apt-get install libmysqlclient-dev # 또는 default-libmysqlclient-dev
sudo apt-get install python3 python3-dev build-essential

프로젝트 세팅

  1. 프로젝트 코드 세팅
git clone <레포지토리_주소>
cd <레포지토리_이름>
python3 -m venv venv
source venv/bin/activate  # venv 활성화
pip install -r requirements.txt
  1. 프로젝트 DB 세팅
python manage.py migrate

# 이미 구축된 DB를 사용한다면 상태만 조작 (원격 또는 splite 등 전부)
python manage.py migrate --fake-initial

python manage.py migrate 뒤에 옵션에 따라 프로젝트의 migration 상태를 조작할 수 있습니다!

  • -fake: 실제 SQL 실행이나 테이블 수정없이 migrations 됐다고 전달하기
    ex. python manage.py migrate --fake 프로젝트_앱 zero - 상태 초기화
  • --fake-initial: 마이그레이션이 완료되었다고 상태 조작
    ➡️ python manage.py showmigrations로 마이그레이션 상태 확인
    참고자료: django-admin/#cmdoption-migrate-fake

프로젝트 세팅 확인

python manage.py runserver 0.0.0.0:8000 로 Django 서버 켜고 < ip >:8000로 접근해서 동작 확인

만약 클라우드 가상머신을 임대해서 사용하고 있다면
테스트를 위해 인바운드 규칙에
8000(django), 80(http), 443(https)를 열어주세요!

ip ↔️ 도메인 연결하기

무료 도메인을 원한다면 내도메인.한국을 이용하세요!
다만 .kro.kr 도메인은 HTTPS 인증서를 요청할 때 🚨 too many certificates already issued for “kro.kr” 에러가 발생하므로 다른 도메인을 이용해주세요.

그리고 설정에서 A 레코드에 ip를 입력하고 저장해주세요.
조금 기다린 후에 도메인으로 접근하면 서버로 접근할 수 있습니다!

nginx + certbot

먼저 nginx와 certbot을 설치합니다.

sudo apt install nginx
sudo add-apt-repository ppa:certbot/certbot
sudo apt install python3-certbot-nginx

그리고 nginx와 도메인 연결과 간단한 확인을 위해 nginx의 sites-available에 도메인 파일을 만들어 도메인명을 작성합니다.

sudo vim /etc/nginx/sites-available/<도메인>
# /etc/nginx/sites-available/<도메인> 파일 작성
server_name <도메인>;

적용을 위해 nginx 데몬을 재시작하고 상태를 확인합니다.

sudo systemctl restart nginx
sudo nginx -t
sudo systemctl status nginx

위와 같이 ok/successful + acitve를 확인하면 위에서 작성한 파일에 에러가 없다는 의미입니다. 이후에 도메인으로 접근해서 Welcome 문구를 확인할 수 있습니다.

Certbot + Let’s Encrypt

certbot을 이용해서 Let's Encrypt에서 TLS 인증서를 발급 받습니다.

sudo certbot --nginx -d <도메인>
# 이메일 입력 후 Y Y 입력하고 기다리기

이제 도메인으로 접근하면 상단에서 https 연결을 확인할 수 있습니다.

nginx 설정

Django 서버는 8000포트에서 동작하기 때문에 HTTPS가 443 포트로 접근했을 때 해당 요청을 8000포트로 넘겨주는 설정을 해야합니다. 추가로 http 80포트로 넘겨주었을 때 https 443 포트로 redirect를 수행했습니다. http를 https로 넘겨주는 과정에서 암호화가 되지 않아 redirect 하지 말라는 이야기도 있는 점 참고해주세요..!

server {
    listen 80;
    server_name <도메인> www.<도메인>;

    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    server_name <도메인> www.<도메인>;

    ssl_certificate /etc/letsencrypt/live/<도메인>/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/<도메인>/privkey.pem;

	# 애플리케이션 내부에서 cors 설정을 했다면
    add_header 'Access-Control-Allow-Origin' '<프론트엔드_도메인>';
    add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, <기타_HTTP_메소드>';
    add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization';
    add_header 'Access-Control-Allow-Credentials' 'true';
    
    location / {
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_pass http://127.0.0.1:8080;
        proxy_redirect off;
    }

    location /static/ {
        alias /path/to/static/files/;  # 정적 파일 경로
    }

    location /media/ {
        alias /path/to/media/files/;  # 미디어 파일 경로
    }
}

이 글에서는 nginx를 역프록시 서버로 활용하고 있지만 location을 통해 정적 파일을 넘겨주는 Web Server의 역할도 수행할 수 있습니다.

sites-enabled 연결하기

다른 도메인 파일과 헷갈리지 않도록 sites-enabled/*sites-enabled/<도메인>처럼 연결할 도메인으로 수정합니다.

sudo vim /etc/nginx/nginx.conf

# http 블럭 마지막줄 수정
http {
	...
	include /etc/nginx/sites-enabled/<도메인>;
}

그리고 sites-enabled에 sites-available에 있는 파일의 심볼릭 링크를 생성하여 활용합니다.

sudo ln -s /etc/nginx/sites-available/<도메인> /etc/nginx/sites-enabled/

위에서 재시작했던 것과 동일하게 nginx 데몬을 재시작하고 ok/successful과 active를 확인하여 파일 수정에 에러가 없는지 확인하고 적용합니다.

sudo systemctl restart nginx
sudo nginx -t
sudo systemctl status nginx

Django 서버 켜기

python manage.py runserver 0.0.0.0:8000

# 백그라운드에서 계속 돌도록 함
nohup python manage.py runserver 0.0.0.0:8000 &

🚨 Django는 디버깅용으로 python manage.py runserver를 제공하므로 배포가 되는지 테스트하는 용도로만 사용해주세요.
세부 설명: Web-Server와-WAS#gunicorn

gunicorn 설정

# 프로젝트 폴더에서 가상환경 켜고
pip install gunicorn

# gunicorn 동작 1차 테스트하고 종료
gunicorn --bind 0.0.0.0:8000 config.wsgi:application

# gunicorn 데몬 등록
sudo vim /etc/systemd/system/gunicorn.service 

데몬을 아래와 같이 작성하여 선언합니다.

# /etc/systemd/system/gunicorn.service 작성
[Unit]
Description=gunicorn daemon
After=network.target

[Service]
User=ubuntu
Group=www-data
WorkingDirectory=/home/ubuntu/<프로젝트_폴더(gunicorn 명령어 실행 위치)>
ExecStart=/home/ubuntu/<프로젝트_폴더(gunicorn 있는 venv 위치)>/venv/bin/gunicorn --workers 3 --bind 0.0.0.0:8000 config.wsgi:application

[Install]
WantedBy=multi-user.target

gunicorn 데몬을 동작시켜 파일에 에러가 없는지 확인하고 gunicorn을 실행합니다.

sudo systemctl start gunicorn
sudo systemctl enable gunicorn
sudo systemctl status gunicorn

# nginx 재시동
sudo systemctl restart nginx
sudo nginx -t
sudo systemctl status nginx

nginx와 gunicorn 모두 active로 동작을 확인합니다.

서버 동작을 확인하고 상단의 주소창에서 https:로 시작하는 주소와 TLS인증서를 확인하면 끝 !!

아키텍처

다음과 같이 서버를 구성하여

다음과 같은 아키텍처를 구현하였습니다!

To Do

HTTPS 설정과 각 서비스끼리 통신, 그리고 서버 비용 부담 감소 등의 편의를 위해서 nginx를 django 애플리케이션과 같은 서버 인스턴스에서 동작시켰지만 단일 책임 원칙 준수와 서버의 부하 대비를 위해서는 서로 통신이 가능한 두개의 인스턴스로 나누어 nginx가 "진짜 웹서버"로써 역할을 할 수 있도록 해야합니다!

하나의 VPC 내에서 서로 통신이 가능하다는 전제 하에 proxy_pass http://<application_private_ip>:8080; 로 수정해서 요청을 전달할 수 있습니다!

profile
기록하는 감자
post-custom-banner

0개의 댓글