[배포] Nginx, Gunicorn을 통한 서버 배포 및 고찰

유승인·2025년 3월 5일
1

⛺네이버 클라우드를 통한 배포 환경 구축

동아리 최종 프로젝트를 진행할 때 로컬에서 모든 구현이 완료 된 우리의 프로젝트를 실제 사이트로 배포해볼 기회가 생겨 배포를 진행하였다.
우선 배포 플랫폼은 네이버 클라우드를 사용했고, 네이버 클라우드는 처음이라 여러 에러사항들도 있었지만 결국 배포를 성공적으로 마무리할 수 있었다.
여기서 거의 모든 사람들이 사용중인 AWS라는 배포 플랫폼이 있음에도 네이버 클라우드를 사용한 이유가 궁금할수도 있는데, 동아리에서 네이버 클라우드와 제휴를 맺어 프로젝트에서 배포를 할때는 네이버 클라우드를 필수로 사용하도록 권장하였고, AWS는 많은 정보가 있지만 그에 비해 네이버 클라우드는 그정도로 많은 정보는 존재하지 않아서 직접 에러가 생기면 하나하나 해결해볼 수 있었고, 대중적으로 사용하는 플랫폼 이외에 다른 배포 플랫폼을 사용해보는것도 좋은 경험이라 생각하여 네이버 클라우드를 통해 이번 프로젝트에서 배포를 진행하게 되었다.
(사실 배포 플랫폼을 어떤걸 사용하는가는 그렇게 중요한 사항은 아니다,, 성공적으로 배포를 완료하는게 주된 목적임)

📌내가 했던 배포 방법 (네이버 클라우드)

우선 이번 글은 Nginx, Gunicorn을 통한 배포를 다루었고, 해당 프로젝트에서는 웹소켓을 사용하여 실시간 채팅을 구현했기 때문에 웹소켓 배포가 필요하였다. 실제로 웹소켓을 포함하여 모든 배포를 성공적으로 완료했지만 이번 글에서는 추가적인 웹소켓 배포는 적지않고 내가 처음에 웹소켓 배포 전 테스트로 했던 배포 방법에 대해서 적어보겠다.
(그 이유는 웹소켓 배포까지 포함하여 적으면 글이 너무 길어질것 같고, 웹소켓 배포도 많은 에러와 해결을 반복했기에 따로 고찰이 필요하다고 생각해서 나중에 따로 글을 써볼 예정)

========================================================

  1. 새로운 VPC 생성 (public 으로 해야함)
    standard, 시간 요금제, 서버개수 1, 새로운 공인 IP 할당, IPv4 CIDR 블록: 172.16.0.0/16 (172.16.1.0/24)

  2. 새로운 ACG 생성
    ACG 이름 입력후 규칙 추가 -> TCP 22번, TCP 80번, TCP 443번, TCP 8000번 추가하기 (모든 소스 0.0.0.0/0)
    반납보호 체크 해제

  3. 실제 서버 생성하기
    OS 이미지: Ubuntu, 서버타입 Standard, Subnet 아까 생성한 서브넷 선택, 공인 IP 할당, 아까 만든 ACG
    스토리지 유형 SSD, 서버 세대 g2

  4. 인증키 생성
    각자 자기가 생성할 키 이름 넣고 생성 누르면 파일 하나 줌 (이건 잘 간직하고 있기) => 나중에 관리자 비밀번호 생성할때 필요

  5. 이제 터미널로 들어가서
    ssh root@223.130.147.76
    입력-> password치라고 나오면 아까 네이버에서 줬던 관리자 비밀번호 입력하면 SSH를 통해 네이버 클라우드 서버로 접속완료

  6. sudo apt update && sudo apt upgrade -y 입력

  7. sudo apt install python3-pip python3-venv git nginx -y 입력

  8. 프로젝트 디렉토리 생성을 위해 mkdir ~/django_project && cd ~/django_project 입력

  9. Git 레포지토리 클론해오기 git clone -b (자신의 브랜치 이름) https://github.com/pirogramming-project/Urabi.git

  10. cd Urabi를 통해 프로젝트로 들어오고

  11. python3 -m venv venv (가상환경 생성), source venv/bin/activate (가상환경 활성화)

  12. 가상환경 안만들어지면 sudo apt install python3-venv -y 진행 후 다시 생성,활성화 진행 (앞에 venv가 보이면 성공)

  13. pip install -r requirements.txt 진행 (이때 장고 버전이 5.x이면 에러발생 -> 장고 버전 낮추고 진행) -> 이때 버전 낮췄으면 로컬에도 적용시켜야함

  14. git pull origin 본인 브랜치 이름 (난 여기서 장고 버전을 낮췄기 때문에 다시 풀 해온거)

  15. pip install -r requirements.txt 다시 진행

  16. sudo apt update

  17. sudo apt install -y pkg-config libmysqlclient-dev (이건 내가 pkg-config와 MySQL 관련 개발 도구가 설치되어 있지 않아서 발생한 문제)

  18. pip install -r requirements.txt

  19. sudo apt update

  20. sudo apt install -y python3-dev build-essential (이건 내가 Python 개발 도구가 설치되어 있지 않아서 발생한 문제)

  21. pip install -r requirements.txt

  22. sudo apt install -y libmysqlclient-dev

  23. pip install -r requirements.txt

  24. cd config (config 안으로 들어오고) nano settings.py 실행해서 settings.py 열기

  25. ALLOWED_HOSTS = ['223.130.147.76'], DEBUG = False로 설정 (근데 한번 해보니깐 여기서 ALLOWED_HOSTS에 localhost도 들어가야했음)

  26. sudo systemctl status gunicorn (Gunicorn 서비스 상태 확인)

  27. Gunicorn 없으면 sudo nano /etc/systemd/system/gunicorn.service 파일 생성

[Unit]
Description=gunicorn daemon for Django project
After=network.target

[Service]
User=root
Group=www-data
WorkingDirectory=/root/django_project/Urabi
ExecStart=/root/django_project/venv/bin/gunicorn --workers 3 --bind unix:/root/django_project/Urabi/Urabi.sock config.wsgi:application

[Install]
WantedBy=multi-user.target 

서비스 설정 적용

  1. sudo systemctl daemon-reload, sudo systemctl start gunicorn, sudo systemctl enable gunicorn

  2. 이때 acitve가 나오면 성공, failed가 나오면 실패 (실패 했다면 경로 확인하자 config 안에서 진행하면 안됨)

  3. cd ~/django_project/Urabi를 통해 Urabi로 온다음에

  4. pip install --force-reinstall gunicorn==21.2.0

  5. sudo nano /etc/systemd/system/gunicorn.service 다시 열어서

[Unit]
Description=gunicorn daemon for Django project
After=network.target

[Service]
User=root
Group=www-data
WorkingDirectory=/root/django_project/Urabi  # ← 절대 경로 명시하도록 바꿔줌 
ExecStart=/root/django_project/venv/bin/gunicorn \
    --workers 3 \
    --bind unix:/run/gunicorn.sock \
    config.wsgi:application  # ← config 디렉토리 내 wsgi 모듈 지정해줌

[Install]
WantedBy=multi-user.target 

이렇게 Gunicorn 서비스 파일 수정해주고

  1. 다시 서비스 재시작 sudo systemctl daemon-reload, sudo systemctl restart gunicorn, sudo systemctl status gunicorn

  2. 이때 active(running)이 나오면 성공 (난 여기서도 안돼서 다시 파일 열어서)
    파일 여는 명령어 (sudo nano /etc/systemd/system/gunicorn.service)

[Unit]
Description=gunicorn daemon for Django project
After=network.target

[Service]
User=root
Group=www-data
WorkingDirectory=/root/django_project/Urabi
Environment="PYTHONPATH=/root/django_project/Urabi"
ExecStart=/root/django_project/Urabi/venv/bin/gunicorn \
    --workers 3 \
    --bind unix:/run/gunicorn.sock \
    config.wsgi:application
Restart=always

[Install]
WantedBy=multi-user.target

위에꺼 안되면

[Unit]
Description=gunicorn daemon for Django project
After=network.target

[Service]
User=www-data
Group=www-data
WorkingDirectory=/root/django_project/Urabi
ExecStart=/root/django_project/Urabi/venv/bin/gunicorn \
    --workers 3 \
    --bind unix:/run/gunicorn/gunicorn.sock \
    config.wsgi:application
Restart=always

[Install]
WantedBy=multi-user.target 

이걸로 수정하여 진행

  1. 소켓 디렉토리 생성 및 권한 설정도 해줬음
    sudo mkdir -p /run/gunicorn, sudo chown root:www-data /run/gunicorn, sudo chmod 775 /run/gunicorn

  2. sudo nano /etc/systemd/system/gunicorn.service, ls -l 로 구니콘 위치 확인해주고
    gunicorn --workers 3 --bind 0.0.0.0:8000 config.wsgi:application 이걸 통해 구니콘 에러 확인해주면
    이때 나는 secrets.json 파일이 없어서 에러가 발생

  3. sudo nano /root/django_project/Urabi/secrets.json 을 통해 secrets.json을 채워넣어주자

  4. 그리고 또한 confin/settings.py에서 secrets.json을 절대 경로로 바꿔줘야함

기존 코드:

with open('secrets.json') as f:

수정된 코드:

SECRETS_PATH = os.path.join(BASE_DIR, 'secrets.json')

바꾸고 나서 sudo chmod 600 /root/django_project/Urabi/secrets.json, sudo chown root:www-data /root/django_project/Urabi/secrets.json 으로 secrets.json 읽을수 있는지 확인하고

  1. Gunicorn 재실행
    sudo systemctl daemon-reload
    sudo systemctl restart gunicorn
    sudo systemctl status gunicorn
    -> 이때 나는 active 성공

  2. Gunicorn active 했으면 이제 Nginx 설정하고 Gunicorn과 연결해야함

  3. nginx -v 로 nginx 설치 확인

  4. 없으면 sudo apt update, sudo apt install nginx -y 으로 nginx 설치

  5. sudo nano /etc/nginx/sites-available/urabi 열어서

server {
    listen 80;
    server_name YOUR_SERVER_IP;

    location /static/ {
        root /root/django_project/Urabi;
        expires 365d;
        add_header Cache-Control "public, no-transform";
    }

    location / {
        include proxy_params;
        proxy_pass http://unix:/run/gunicorn.sock;
        proxy_set_header Host $http_host;
        proxy_redirect off;
    }
}

미리 말하지만 이거 어차피 에러뜸

  1. sudo ln -s /etc/nginx/sites-available/urabi /etc/nginx/sites-enabled/ 랑 sudo nginx -t로 설정 적용 및 오류 확인
    syntax is ok**와 test is successful 메시지 뜨면 일단 성공

  2. sudo systemctl restart nginx
    sudo systemctl enable nginx
    sudo systemctl status nginx
    을 통해 nginx 재시작 해주기

  3. 이제 방화벽을 설정해주자
    sudo ufw status 으로 방화벽 설정 확인
    inactive이면 sudo ufw enable, sudo ufw allow OpenSSH, sudo ufw allow 'Nginx Full', sudo ufw status verbose
    이제 Status: active 확인하자

  4. 근데 sudo ufw status verbose 했는데
    Status: active
    Logging: on (low)
    Default: deny (incoming), allow (outgoing), disabled (routed)
    New profiles: skip

To                         Action      From
--                         ------      ----
22/tcp (OpenSSH)           ALLOW IN    Anywhere
80,443/tcp (Nginx Full)    ALLOW IN    Anywhere
22/tcp (OpenSSH (v6))      ALLOW IN    Anywhere (v6)
80,443/tcp (Nginx Full (v6)) ALLOW IN    Anywhere (v6)

이런식으로 8000 포트가 존재하지않으면 8000 포트도 추가해줘야함
sudo ufw allow 8000하고 sudo ufw status verbose 으로 8000 포트 들어왔는지 확인

  1. 그러고 배포 한줄 알고 들어갔는데 Nginx 기본 페이지만 표시됐음 (/etc/nginx/sites-enabled/default 파일이 활성화되어 있어서)

  2. 파일 비활성화 해주기
    sudo rm /etc/nginx/sites-enabled/default, ln -s /etc/nginx/sites-available/django_project /etc/nginx/sites-enabled/

  3. sudo nginx -t로 nginx 재시작 -> 실패 (/etc/nginx/sites-available/django_project 파일이 존재하지 않아서 nginx 설정에서 문제 발생했음)

  4. ls -l /etc/nginx/sites-available/로 /etc/nginx/sites-available/ 디렉토리에 django_project 파일이 실제로 있는지 확인

  5. 있는데 나는 걍 기존 심볼릭 링크 제거하고 다시 실행
    sudo rm -f /etc/nginx/sites-enabled/django_project, sudo rm -f /etc/nginx/sites-enabled/urabi

  6. 심볼릭 링크 다시 생성
    sudo ln -s /etc/nginx/sites-available/urabi /etc/nginx/sites-enabled/

  7. 제대로 생성 되었는지 확인하자 sudo nginx -t
    yntax is ok와 test is successful 메시지가 출력되면 성공

  8. nginx 다시 시작 및 활성화 상태 확인
    sudo systemctl restart nginx, sudo systemctl status nginx

  9. 이때 nginx에 active 뜨길래 성공한줄 알고 브라우저 열었더니 이제 Cross-Origin-Opener-Policy 문제랑 400 에러 떴음
    Cross-Origin-Opener-Policy는 HTTP 사용해서 수신 무시당한거고 400은 ALLOWED_HOSTS 설정 문제이거나 정적 파일 또는 미디어 파일 경로가 제대로 설정되지 않았다는거

  10. 이제 다시 settings.py 들어가서 (sudo nano /root/django_project/Urabi/config/settings.py) 아까 안해줬던 localhost를 추가해주자

ALLOWED_HOSTS = ['223.130.147.76', 'localhost'] 

로 바꾸기 (여기서 223.130.147.76은 서버의 공인 IP 주소 -> 각자 바꿔야함)

  1. 이제 다시 Gunicorn과 Nginx 재시작
    sudo systemctl restart gunicorn
    sudo systemctl restart nginx
    curl ifconfig.me

  2. 이제 HTTPS를 임시로 설정해주자 (현재 도메인을 발급받지않아서 그냥 임시 방편으로만 설정하는거)
    sudo apt install certbot python3-certbot-nginx -y
    sudo certbot --nginx -d 223.130.147.76 (원래는 도메인이 있다면 223.130.147.76 대신 도메인 이름 넣기)
    sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
    -keyout /etc/ssl/private/nginx-selfsigned.key \
    -out /etc/ssl/certs/nginx-selfsigned.crt (자체 서명된 SSL 인증서 생성)

  3. Nginx 설정 파일 수정 (sudo nano /etc/nginx/sites-available/urabi)

server {
    listen 443 ssl;
    server_name 223.130.147.76;

    ssl_certificate /etc/ssl/certs/nginx-selfsigned.crt;
    ssl_certificate_key /etc/ssl/private/nginx-selfsigned.key;

    location / {
        include proxy_params;
        proxy_pass http://unix:/run/gunicorn.sock;
    }

    location /static/ {
        root /root/django_project/Urabi;
    }
}

server {
    listen 80;
    server_name 223.130.147.76;

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

  1. sudo nginx -t으로 설정 파일 테스트하고 재시작 sudo systemctl restart nginx

  2. 이제 HTTPS로 접속하자 -> 이때 배포 성공 (근데 css는 안보였음)

  3. python manage.py collectstatic => 에러 발생

  4. sudo nano /root/django_project/Urabi/config/settings.py를 통해서

STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'static/') 

이렇게 바꿔주고 nginx 재시작 했었음

sudo nginx -t
sudo systemctl restart nginx

근데 이때도 css 안뜸

  1. sudo nano /etc/nginx/sites-available/django_project 확인
    비어있으면 채워넣어주자
server {
    listen 80;
    server_name 223.130.147.76;  # 서버의 공인 IP 주소

    location /static/ {
        root /root/django_project/Urabi;  # STATIC_ROOT 경로
        expires 365d;
        add_header Cache-Control "public, no-transform";
    }

    location / {
        include proxy_params;
        proxy_pass http://unix:/run/gunicorn.sock;
    }
}

참고: Ctrl + O → Enter → Ctrl + X (저장 후 나오기)

  1. 방금 작성한거 nginx에 적용하기 위해 심볼릭 링크 생성
    sudo ln -s /etc/nginx/sites-available/django_project /etc/nginx/sites-enabled/

  2. nginx 재시작 sudo systemctl restart nginx

  3. 근데 이때 nginx에서 서버 충돌 났었음
    해결하기 위해서
    grep -r "server_name" /etc/nginx/sites-enabled/ 으로 확인
    grep -r "server_name" /etc/nginx/
    grep -r "server_name" /etc/nginx/sites-enabled/

해보니깐 django_project와 urabi 두 설정 파일에서 충돌이 난거였음
urabi를 사용하고 django_project를 비활성화하자

  1. sudo rm /etc/nginx/sites-enabled/django_project

  2. nginx 활성화 확인하고 다시 재시작
    sudo nginx -t
    sudo systemctl restart nginx

  3. default 파일도 비활성화 해주자
    sudo rm /etc/nginx/sites-enabled/default

  4. ls -l /etc/nginx/sites-enabled/ 로 한번 확인

  5. python manage.py collectstatic --noinput
    0 static files copied to '/root/django_project/Urabi/static', 160 unmodified.
    => static 파일을 다 가져오기는 하는데 여전히 css가 안뜸

  6. sudo nano /etc/nginx/sites-available/urabi
    다시 확인
    이때

server {
    listen 443 ssl;
    server_name 223.130.147.76;

    ssl_certificate /etc/ssl/certs/nginx-selfsigned.crt;
    ssl_certificate_key /etc/ssl/private/nginx-selfsigned.key;

    location / {
        include proxy_params;
        proxy_pass http://unix:/run/gunicorn.sock;
    }

    location /static/ {
        root /root/django_project/Urabi;
        expires 1y;
        add_header Cache-Control "public";
    }
}

server {
    listen 80;
    server_name 223.130.147.76;

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

이걸로 수정했었음 하지만 여전히 에러 발생

  1. 현재 읽기 권한이 없는데 읽기 권한을 부여시켜 주자
    sudo chmod -R 775 /root/django_project/Urabi/static/
    sudo chown -R www-data:www-data /root/django_project/Urabi/static/

  2. 다시
    sudo nginx -t
    sudo systemctl restart nginx

  3. sudo chmod -R 755 /root/django_project/Urabi/static/
    sudo chown -R www-data:www-data /root/django_project/Urabi/static/

  4. 권한이 변경 되었는지 확인
    ls -ld /root/django_project/Urabi/static/
    ls -l /root/django_project/Urabi/static/img/

  5. /root/ 디렉토리 접근 허용
    sudo chmod +x /root
    sudo chmod +x /root/django_project
    sudo chmod +x /root/django_project/Urabi

  6. sudo nano /etc/nginx/sites-available/urabi으로 nginx 설정 확인

  7. 그리고 다시 nginx 재시작

  8. 계속 안돼서 curl -kI https://223.130.147.76/static/main.css 으로 직접 확인
    근데 이때 403 에러는 없어졌는데 404 에러가 뜸

  9. 404 니깐 경로문제일거라고 생각하고 경로 설정을 다시함

  10. python manage.py collectstatic 실행하고

  11. ls -l /root/django_project/Urabi/static/ 으로 static에 있는 파일들 경로 확인 => 여기서 main.css가 없는거 보고 경로문제가 맞았다는걸 알고 문제 해결함

  12. Nginx 설정 변경하자
    sudo nano /etc/nginx/sites-available/urabi

  13. 아까 설정한 root를 다시 alias로 변경해주자
    변경할부분:

location /static/ {
    alias /root/django_project/Urabi/static/;
    expires 1y;
    add_header Cache-Control "public";
    autoindex on; 
}
  1. 다시 Nginx 재시작
    sudo nginx -t
    sudo systemctl restart nginx

  2. curl -kI https://223.130.147.76/static/main/main.css 으로 잘 됐는지 확인
    200 으로 나와서 해결된걸 알고 다시 브라우저로 접속하니깐 css 까지 잘 나오도록 배포 완료.

  3. 이제 MySQL 연결해보자
    sudo systemctl status mysql
    => 현재 당연히 설치도 안했으니 not found

  4. MySQL 설치하기
    sudo apt update
    sudo apt install mysql-server -y

  5. sudo systemctl status mysql 명령어로 MySQL active 상태 확인

  6. sudo mysql -u root -p 으로 mysql 접속

  7. SHOW DATABASES; 로 urabi_db 있는지 확인 => 안만들어줬기때문에 당연히 없음

  8. CREATE DATABASE urabi_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; => urabi_db 생성

  9. SHOW DATABASES; 로 urabi_db 있는지 확인 => 생김

  10. 이제 Django가 MySQL에 접근할 수 있도록 사용자 계정을 생성하고 권한을 부여

CREATE USER 'urabi_user'@'localhost' IDENTIFIED BY '본인 mysql password';
GRANT ALL PRIVILEGES ON urabi_db.* TO 'urabi_user'@'localhost';
FLUSH PRIVILEGES;
  1. SELECT User, Host FROM mysql.user; 으로 urabi_user 있는지 확인

  2. 이제 settings.py 바꿔주자
    sudo nano /root/django_project/Urabi/config/settings.py 들어가서

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'urabi_db',           # 현재 우리 데이터베이스 이름
        'USER': 'urabi_user',         # 아까 설정해준 사용자 이름
        'PASSWORD': get_secret("DB_PASSWORD"),  # mysql 비밀번호
        'HOST': 'localhost',          # MySQL 서버
        'PORT': '3306',               # MySQL 기본 포트
    }
}

으로 바꾸기

  1. 가상 환경 활성화 해주고 마이그레이션 실행
    cd /root/django_project/Urabi
    source venv/bin/activate
    python manage.py migrate

마이그레이션 적용되고 배포한 사이트 접속하면 500에러 해결완료.

🔦배포 중 발생한 에러와 해결과정

우선 가장 먼저 발생한 에러는 pip install -r requirements.txt을 실행했을때였다.
로컬에서 모두 정상적으로 발생한 의존성 파일들을 배포 환경의 서버로 설치를 해줬는데, 이때 로컬 환경과 배포 환경에서 패키지들의 버전차이로 인해서 requirements.txt가 제대로 설치되지 않는 패키지들이 정말 많았다. 대표적인 예시로 Django를 설치하던 도중 그때 당시 실제 에러로그를 살펴보면

(venv) root@seungin-server:~/django_project/Urabi# pip install -r requirements.txt
Collecting asgiref==3.8.1
  Downloading asgiref-3.8.1-py3-none-any.whl (23 kB)
ERROR: Could not find a version that satisfies the requirement Django==5.1.5 (from -r requirements.txt (line 2)) (from versions: 1.1.3, 1.1.4, 1.2, 1.2.1, 1.2.2, 1.2.3, 1.2.4, 1.2.5, 1.2.6, 1.2.7, 1.3, 1.3.1, 1.3.2, 1.3.3, 1.3.4, 1.3.5, 1.3.6, 1.3.7, 1.4, 1.4.1, 1.4.2, 1.4.3, 1.4.4, 1.4.5, 1.4.6, 1.4.7, 1.4.8, 1.4.9, 1.4.10, 1.4.11, 1.4.12, 1.4.13, 1.4.14, 1.4.15, 1.4.16, 1.4.17, 1.4.18, 1.4.19, 1.4.20, 1.4.21, 1.4.22, 1.5, 1.5.1, 1.5.2, 1.5.3, 1.5.4, 1.5.5, 1.5.6, 1.5.7, 1.5.8, 1.5.9, 1.5.10, 1.5.11, 1.5.12, 1.6, 1.6.1, 1.6.2, 1.6.3, 1.6.4, 1.6.5, 1.6.6, 1.6.7, 1.6.8, 1.6.9, 1.6.10, 1.6.11, 1.7, 1.7.1, 1.7.2, 1.7.3, 1.7.4, 1.7.5, 1.7.6, 1.7.7, 1.7.8, 1.7.9, 1.7.10, 1.7.11, 1.8a1, 1.8b1, 1.8b2, 1.8rc1, 1.8, 1.8.1, 1.8.2, 1.8.3, 1.8.4, 1.8.5, 1.8.6, 1.8.7, 1.8.8, 1.8.9, 1.8.10, 1.8.11, 1.8.12, 1.8.13, 1.8.14, 1.8.15, 1.8.16, 1.8.17, 1.8.18, 1.8.19, 1.9a1, 1.9b1, 1.9rc1, 1.9rc2, 1.9, 1.9.1, 1.9.2, 1.9.3, 1.9.4, 1.9.5, 1.9.6, 1.9.7, 1.9.8, 1.9.9, 1.9.10, 1.9.11, 1.9.12, 1.9.13, 1.10a1, 1.10b1, 1.10rc1, 1.10, 1.10.1, 1.10.2, 1.10.3, 1.10.4, 1.10.5, 1.10.6, 1.10.7, 1.10.8, 1.11a1, 1.11b1, 1.11rc1, 1.11, 1.11.1, 1.11.2, 1.11.3, 1.11.4, 1.11.5, 1.11.6, 1.11.7, 1.11.8, 1.11.9, 1.11.10, 1.11.11, 1.11.12, 1.11.13, 1.11.14, 1.11.15, 1.11.16, 1.11.17, 1.11.18, 1.11.20, 1.11.21, 1.11.22, 1.11.23, 1.11.24, 1.11.25, 1.11.26, 1.11.27, 1.11.28, 1.11.29, 2.0a1, 2.0b1, 2.0rc1, 2.0, 2.0.1, 2.0.2, 2.0.3, 2.0.4, 2.0.5, 2.0.6, 2.0.7, 2.0.8, 2.0.9, 2.0.10, 2.0.12, 2.0.13, 2.1a1, 2.1b1, 2.1rc1, 2.1, 2.1.1, 2.1.2, 2.1.3, 2.1.4, 2.1.5, 2.1.7, 2.1.8, 2.1.9, 2.1.10, 2.1.11, 2.1.12, 2.1.13, 2.1.14, 2.1.15, 2.2a1, 2.2b1, 2.2rc1, 2.2, 2.2.1, 2.2.2, 2.2.3, 2.2.4, 2.2.5, 2.2.6, 2.2.7, 2.2.8, 2.2.9, 2.2.10, 2.2.11, 2.2.12, 2.2.13, 2.2.14, 2.2.15, 2.2.16, 2.2.17, 2.2.18, 2.2.19, 2.2.20, 2.2.21, 2.2.22, 2.2.23, 2.2.24, 2.2.25, 2.2.26, 2.2.27, 2.2.28, 3.0a1, 3.0b1, 3.0rc1, 3.0, 3.0.1, 3.0.2, 3.0.3, 3.0.4, 3.0.5, 3.0.6, 3.0.7, 3.0.8, 3.0.9, 3.0.10, 3.0.11, 3.0.12, 3.0.13, 3.0.14, 3.1a1, 3.1b1, 3.1rc1, 3.1, 3.1.1, 3.1.2, 3.1.3, 3.1.4, 3.1.5, 3.1.6, 3.1.7, 3.1.8, 3.1.9, 3.1.10, 3.1.11, 3.1.12, 3.1.13, 3.1.14, 3.2a1, 3.2b1, 3.2rc1, 3.2, 3.2.1, 3.2.2, 3.2.3, 3.2.4, 3.2.5, 3.2.6, 3.2.7, 3.2.8, 3.2.9, 3.2.10, 3.2.11, 3.2.12, 3.2.13, 3.2.14, 3.2.15, 3.2.16, 3.2.17, 3.2.18, 3.2.19, 3.2.20, 3.2.21, 3.2.22, 3.2.23, 3.2.24, 3.2.25, 4.0a1, 4.0b1, 4.0rc1, 4.0, 4.0.1, 4.0.2, 4.0.3, 4.0.4, 4.0.5, 4.0.6, 4.0.7, 4.0.8, 4.0.9, 4.0.10, 4.1a1, 4.1b1, 4.1rc1, 4.1, 4.1.1, 4.1.2, 4.1.3, 4.1.4, 4.1.5, 4.1.6, 4.1.7, 4.1.8, 4.1.9, 4.1.10, 4.1.11, 4.1.12, 4.1.13, 4.2a1, 4.2b1, 4.2rc1, 4.2, 4.2.1, 4.2.2, 4.2.3, 4.2.4, 4.2.5, 4.2.6, 4.2.7, 4.2.8, 4.2.9, 4.2.10, 4.2.11, 4.2.12, 4.2.13, 4.2.14, 4.2.15, 4.2.16, 4.2.17, 4.2.18, 4.2.19)
ERROR: No matching distribution found for Django==5.1.5 (from -r requirements.txt (line 2))

이렇게 Django 5.1.5 버전을 찾지 못해서 에러가 발생했다. 왜 그런가 알아보니 Django 5.1.5는 아직 릴리즈되지 않은 버전이였고, 이것을 알고난뒤 Django 버전을 4.2.19로 수정하여 다시 설치를 진행하였다.
Django 버전뿐만 아니라 현재 프로젝트에 설치된 패키지의 개수는 75개였고, 이것들의 버전이 충돌하여 의존성 파일이 설치가 안될때는 에러로그를 보고 설치가 가능한 버전 중 가장 최신의 버전으로 다운그레이드 및 업그레이드하여 각 패키지의 버전을 모두 맞춰줘서 해당 에러를 해결했다.

또한 Gunicorn 설치 과정 및 서비스 파일 생성 부분에서도 에러가 발생하였는데 우선 Gunicorn을 설치하고 초기 설정을 진행할때 문제점이 발생했다.

계속 Gunicorn이 설치가 되지않아 그때 당시의 나는 pip install --force-reinstall gunicorn==21.2.0 명령어를 통해 Gunicorn 21.2.0 버전을 설치하려고 하였고, 이게 문제를 일으켰는데 이 또한 배포 환경에서는 gunicorn 21.2.0이 존재하지 않는 버전으로, Gunicorn의 최신 릴리스는 20.x 대 버전인걸 알아냈다. 그래서 pip가 해당 버전을 찾지 못하고 설치 오류를 발생시켰을거라고 생각한다.
이때 나는 계속 Gunicorn이 제대로 설치되지 않아 --force-reinstall 사용하여 Gunicorn을 설치하려고 했는데, 지금 다시 생각해보면 이 명령어는 강제로 재설치를 하는것이기 때문에 의존성 충돌이 발생하거나 기존 환경을 불안정하게 만들 수 있다고 생각한다. 때문에 잘 설치가 되지않는다고 무작정 --force-reinstall을 사용하는것이 아닌 먼저 설치가 안된 원인을 찾고 패키지를 설치하거나 업그레이드하는 명령어를 사용하는 것이 더 좋다는것을 깨달았다.

이 이후에는 Gunicorn의 서비스 파일을 생성하였는데 서비스 파일을 생성한 후, Gunicorn 서비스를 활성화하려 했지만 에러가 발생했다. 이 또한 그때 당시의 에러 로그를 살펴보면

Active: failed (Result: exit-code) since Sat 2025-02-08 11:50:59 KST; 10s ago
Main PID: 49980 (code=exited, status=203/EXEC)

이라고 나왔고, Gunicorn이 활성화 되지않았고 실행 파일을 찾지 못하는 중이였다.
그래서 나는 이 문제를 해결하기 위해 가상 환경 경로 확인, Gunicorn 실행 파일 확인, 소켓 디렉토리 생성 및 권한 설정 등을 시도하고 수정했지만 여전히 Gunicorn 서비스가 계속 실패했었다.
마지막으로 모든게 잘 되지않아서

gunicorn --workers 3 --bind 0.0.0.0:8000 config.wsgi:application

을 통해 Gunicorn을 수동으로 실행하였고, 이때 에러를 해결하고 성공적으로 실행할 수 있었다.

왜 Gunicorn을 수동으로 실행했어야지만 성공적으로 실행되었을까를 고민해봤는데 내가 생각한 원인으로는

  1. 경로 문제: 초기 오류는 Gunicorn 실행 파일을 찾지 못하는 것이였고, 이것은 서비스 파일에서 지정한 경로와 실제 가상 환경의 경로가 일치하지 않았기 때문이라고 예상한다.

  2. 권한 문제: 현재 root 사용자로 실행하고 있었는데 웹 서버는 보안상의 이유로 root가 아닌 사용자로 실행하는 것이 더 좋은 배포 환경을 구축하는 방법이였다.

  3. 설정 파일 문제: config.wsgi:application을 지정했는데, 해당 파일의 올바른 위치와 WSGI 애플리케이션을 올바르게 정의하고 있는지 확인해야 했었다.

  4. 소켓 vs 포트: 서비스 파일에서는 Unix 소켓을 사용하도록 설정되어 있지만, 수동 실행 시에는 포트를 사용했다. 이 부분은 아직 완벽하게 이해를 하진 못했지만 이 과정에서도 에러가 발생했을수도 있다고 생각했다.

  5. 환경 변수: 가상 환경의 활성화 상태와 필요한 환경 변수가 제대로 설정되어 있는지 확인해야 했었다.

🚩마무리

프로젝트를 진행하고 우리가 만든 서비스를 사용자들이 이용하려면 배포는 중요한 작업이고, 필수적인 작업이다. 이러한 배포를 처음부터 끝까지 직접 경험하면서 많은 에러를 보고 해결하여 결국 성공하면서 느낀점은 기한이 정해져있는 프로젝트 특성상 배포 작업은 생각보다 빨리 진행해야한다는것을 느꼈다. 나 역시도 배포 작업을 최종 배포 전에 혼자서 3번을 해봐서 익숙해졌다고 생각했지만, 프로젝트 최종 제출 이틀전에 다시 배포를 할 때에는 예전에 발생한 에러들을 빠르게 잡지 못해서 나의 예상 시간보다 많은 시간을 소요하였다. 또한 배포를 하고 난 뒤에는 로컬에서 구현한 모든 기능이 배포 환경에서 생각보다 달라져있는 경우도 많았기에 이 경우를 대비해 배포 환경에서의 리팩토링 기한도 필수적으로 잡아놓고 진행했어야했다.
그리고 Nginx, Gunicorn (웹소켓을 더한다면 Daphne와 Redis까지) 서비스 파일의 코드들이 조금만 달라져도 active: running 으로 활성화 되어있는 서버들이 다시 fail이 되는 경우는 흔하게 발생하였다. 또한 모든 서버를 정상적으로 작동시키는 와중에 서버가 내려가기도 하고, 서버가 모두 잘 작동되는 와중에도 원하는 기능의 구현이 실패하는 등 여러가지 문제도 발생했었다.
하지만 이런 과정도 개발을 하면서 충분히 일어날수 있는 과정이고 오히려 시간이 부족하여 집중적으로 문제들을 해결해나가는 과정에서 배포 관련의 실력뿐 아니라 전반적인 개발의 대한 지식도 함께 늘었다고 생각하여 개인적으로 이번 배포 작업은 매우 뿌듯하게 임했던것 같다!

profile
한입 개발자

0개의 댓글

관련 채용 정보