Skip to content
VDS · 웹사이트 및 웹 서버

Python 애플리케이션: gunicorn과 systemd

VDS에서 Django, Flask, FastAPI를 실행하는 방법을 알아봐요. 가상 환경, 소켓을 사용하는 gunicorn, systemd 서비스, 등급에 맞는 워커 수 선택을 다뤄요.

업데이트 2026년 8월 23일

내장 개발 서버(manage.py runserver, flask run)는 프로덕션 부하를 견디도록 설계되지 않았어요. VPS에서는 gunicorn이나 uvicorn을 설치하고, 웹 서버가 이들을 외부에 서비스하도록 구성해요.

환경

프로젝트 패키지를 시스템 Python에 절대 설치하지 마세요 — 시스템 업데이트가 애플리케이션을 망가뜨려요.

apt install -y python3 python3-venv python3-pip
useradd -r -m -d /srv/app -s /usr/sbin/nologin appuser

sudo -u appuser python3 -m venv /srv/app/venv
sudo -u appuser /srv/app/venv/bin/pip install -r /srv/app/requirements.txt
sudo -u appuser /srv/app/venv/bin/pip install gunicorn

FastAPI의 경우 uvicorn[standard]를 추가로 설치하세요 — gunicorn이 이를 워커 클래스로 실행해요.

서비스

/etc/systemd/system/myapp.service:

[Unit]
Description=Python app
After=network-online.target postgresql.service

[Service]
User=appuser
WorkingDirectory=/srv/app
EnvironmentFile=/srv/app/.env
ExecStart=/srv/app/venv/bin/gunicorn \
  --workers 3 \
  --bind unix:/run/myapp/gunicorn.sock \
  --access-logfile - \
  myproject.wsgi:application
RuntimeDirectory=myapp
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target

FastAPI의 경우 실행 줄을 --worker-class uvicorn.workers.UvicornWorker app.main:app로 바꾸세요.

systemctl daemon-reload
systemctl enable --now myapp
journalctl -u myapp -f

RuntimeDirectory=myapp는 시작할 때마다 필요한 권한과 함께 /run/myapp을 생성해요 — 그렇지 않으면 소켓을 둘 곳이 없어요.

워커 수

일반적인 공식은 2 × cores + 1이지만, 메모리 한계에 부딪혀요. 각 Django 워커는 80–200 MB를 사용해요.

티어코어워커
VDS-112
VDS-223
VDS-345
VDS-469

애플리케이션이 네트워크와 데이터베이스 대기에 많은 시간을 쓴다면, 프로세스 수를 늘리는 대신 --threads 2를 추가하세요.

웹 서버

location / {
    proxy_pass http://unix:/run/myapp/gunicorn.sock;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
}

location /static/ { alias /srv/app/static/; }

Django 정적 파일은 collectstatic으로 수집한 뒤 Nginx로 직접 서비스하세요. Python을 거쳐 전달하면 불필요한 부하가 생겨요.

자주 발생하는 오류

증상원인
502 및 connect() to unix:... failed (13)Nginx에 소켓 권한이 없음 — --umask 007과 공유 그룹을 설정하세요
ModuleNotFoundErrorvenv가 아닌 시스템 Python으로 실행함
Django의 DisallowedHost도메인이 ALLOWED_HOSTS에 추가되지 않음
워커가 타임아웃으로 종료됨긴 요청 — --timeout을 늘리고 무거운 작업을 큐로 옮기세요
릴리스 후 모든 것이 멈춤systemctl restart myapp을 잊음 — 메모리 속 코드는 스스로 갱신되지 않아요

시크릿은 settings.py가 아니라 600 권한의 EnvironmentFile에 보관하세요. 설정 파일은 결국 저장소에 들어가게 돼요.