دیپلوی FastAPI روی سرور مجازی و اختصاصی: Gunicorn، Uvicorn Workers و Nginx برای Production
اگر با uvicorn main:app --reload اپلیکیشن را تست کردهاید و حالا میخواهید آن را روی یک سرور واقعی بالا بیاورید، این راهنما همان مسیر را قدمبهقدم نشان میدهد. از انتخاب نوع سرور تا پیکربندی Gunicorn، سرویس systemd و ریورس پروکسی Nginx با HTTPS.

چرا فقط Uvicorn کافی نیست
سرور توسعهای که با دستور uvicorn main:app --reload اجرا میشود، تکپردازه است و با هر تغییر کد ریاستارت میشود؛ برای کار واقعی روی اینترنت ساخته نشده. اگر همین حالت را روی production رها کنید، یک درخواست سنگین کل اپلیکیشن را کند میکند و اگر پردازه کرش کند، چیزی آن را دوباره بالا نمیآورد.
برای دیپلوی FastAPI روی سرور به سه چیز نیاز دارید: چند پردازه worker که درخواستها را موازی پاسخ بدهند، یک process manager که در صورت کرش پردازه را دوباره اجرا کند، و یک وبسرور جلویی که SSL، فایلهای استاتیک و چند دامنه را مدیریت کند. Gunicorn با Uvicorn Workers نقش اول و دوم را میگیرد، Nginx نقش سوم را.
انتخاب سرور: مجازی یا اختصاصی
برای شروع یک API با ترافیک معمولی، یک سرور مجازی (VPS) کافی است. اگر اپلیکیشن دیتابیس سنگین دارد یا چند سرویس همزمان روی یک ماشین اجرا میشود، سرور اختصاصی منابع ثابتتری میدهد. تفاوت اصلی را در این جدول میبینید:
| معیار | سرور مجازی (VPS) | سرور اختصاصی |
|---|---|---|
| مناسب برای | API در حال توسعه، MVP، ترافیک متوسط | چند سرویس همزمان، دیتابیس سنگین، ترافیک بالا |
| مقیاسپذیری | تغییر پلن در چند دقیقه | نیاز به سفارش سرور جدید |
| هزینه شروع | پایینتر، بیلینگ ساعتی یا ماهانه | ثابت و بالاتر، معمولا ماهانه |
| مدیریت | همان دسترسی root، بدون تفاوت در نصب | همان دسترسی root، بدون تفاوت در نصب |
مراحل نصب Gunicorn، systemd و Nginx در هر دو حالت یکسان است؛ تفاوت فقط در منابعی است که به اپلیکیشن میدهید. برای اکثر پروژهها شروع با یک سرور مجازی و ارتقا در صورت نیاز منطقیتر از خرید سرور اختصاصی از روز اول است. اگر هنوز سرور نخریدهاید، مراحل خرید و راهاندازی سرور مجازی از انتخاب پلن تا اولین اتصال SSH را پوشش میدهد.
آمادهسازی محیط روی سرور
بعد از اتصال با SSH، یک محیط مجزا برای پروژه بسازید تا پکیجهای پایتون با بقیه سرور قاطی نشوند:
sudo apt update && sudo apt install -y python3-venv python3-pip nginx
mkdir -p /srv/myapp && cd /srv/myapp
python3 -m venv venv
source venv/bin/activate
pip install fastapi uvicorn gunicorn
کد اپلیکیشن را همینجا قرار بدهید یا با git clone بیاورید. مطمئن شوید فایل اصلی (مثلا main.py) شیء app را export میکند، چون Gunicorn دقیقا به همین اسم نیاز دارد.
پیکربندی Gunicorn با Uvicorn Workers
Gunicorn خودش پروتکل ASGI که FastAPI روی آن کار میکند را نمیفهمد، برای همین هر worker را با کلاس UvicornWorker اجرا میکنید. یک فایل gunicorn.conf.py کنار main.py بسازید:
bind = "127.0.0.1:8000"
workers = 4
worker_class = "uvicorn.workers.UvicornWorker"
timeout = 60
accesslog = "/srv/myapp/logs/access.log"
errorlog = "/srv/myapp/logs/error.log"
تعداد worker را با فرمول (2 × تعداد هسته CPU) + 1 حساب کنید، نه یک عدد دلخواه. روی یک سرور مجازی ۲ هستهای این عدد ۵ است؛ بیشتر از این، worker ها برای یک منبع محدود رقابت میکنند و کندتر میشوند. اگر پروژه Django یا Go است نه FastAPI، همین الگوی Gunicorn یا systemd با جزئیات متفاوت در دیپلوی پروژه Django روی سرور اختصاصی و VPS و دیپلوی اپلیکیشن Go روی سرور اختصاصی و VPS با systemd و Nginx توضیح داده شده. اجرای دستی برای تست:
mkdir -p logs
gunicorn main:app -c gunicorn.conf.py
اگر اپلیکیشن بالا آمد و روی curl http://127.0.0.1:8000 پاسخ داد، آماده مرحله بعد هستید.
اجرای پایدار با سرویس systemd
اجرای دستی Gunicorn در ترمینال با بسته شدن SSH متوقف میشود. یک unit file در /etc/systemd/system/myapp.service بسازید:
[Unit]
Description=FastAPI app (Gunicorn + Uvicorn workers)
After=network.target
[Service]
User=www-data
WorkingDirectory=/srv/myapp
Environment="PATH=/srv/myapp/venv/bin"
ExecStart=/srv/myapp/venv/bin/gunicorn main:app -c gunicorn.conf.py
Restart=always
RestartSec=3
[Install]
WantedBy=multi-user.target
Restart=always همان بخشی است که یک کرش ناگهانی را به یک خاموشی چند ثانیهای تبدیل میکند، نه یک قطعی کامل. فعالسازی:
sudo systemctl daemon-reload
sudo systemctl enable --now myapp
sudo systemctl status myapp
از این به بعد با هر ریبوت سرور، اپلیکیشن خودش بالا میآید. برای دیدن لاگهای لحظهای: journalctl -u myapp -f.
ریورس پروکسی با Nginx
Gunicorn فقط روی 127.0.0.1:8000 گوش میدهد، یعنی مستقیم از بیرون قابل دسترسی نیست — همین جا Nginx وارد میشود. یک فایل در /etc/nginx/sites-available/myapp بسازید:
server {
listen 80;
server_name api.example.com;
location / {
proxy_pass http://127.0.0.1:8000;
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;
}
}

لینک و فعالسازی:
sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
هدرهای X-Forwarded-* را حذف نکنید؛ بدون آنها FastAPI آیپی واقعی کاربر را نمیبیند و همه درخواستها از سمت Nginx به نظر میرسند. اگر بعد از این تنظیمات با صفحه خطا مواجه شدید، رفع خطای 502 Bad Gateway در Nginx علتهای رایج را پوشش میدهد.
HTTPS رایگان با Let's Encrypt
با دامنهای که به IP سرور اشاره میکند، گرفتن گواهی SSL چند دقیقه کار دارد:
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d api.example.com
Certbot خودش فایل Nginx را ویرایش میکند و ریدایرکت از HTTP به HTTPS را اضافه میکند. گواهی هر ۹۰ روز منقضی میشود اما تمدید خودکار از طریق یک تایمر سیستمی که certbot نصب میکند انجام میشود؛ برای اطمینان میتوانید با sudo certbot renew --dry-run تست کنید.
آپدیت کد بدون قطعی سرویس
بعد از اولین دیپلوی، هر تغییر کد نیاز به یک روش امن برای اعمال دارد. سادهترین حالت کشیدن کد جدید و ریاستارت سرویس است:
cd /srv/myapp
git pull
source venv/bin/activate
pip install -r requirements.txt
sudo systemctl restart myapp
این روش چند ثانیه قطعی دارد چون همه worker ها همزمان بسته و دوباره باز میشوند. اگر این چند ثانیه مهم است، بهجای restart از sudo systemctl reload myapp استفاده کنید؛ Gunicorn با سیگنال HUP worker های قدیمی را یکییکی با نسخه جدید جایگزین میکند و در هر لحظه حداقل یک worker پاسخگو است. برای این کار سیگنال reload باید در unit file تعریف شده باشد:
ExecReload=/bin/kill -HUP $MAINPID
چکلیست قبل از رفتن به Production
قبل از اعلام آدرس به کاربران واقعی، این موارد را چک کنید:
- فایروال فقط پورتهای ۲۲، ۸۰ و ۴۴۳ را باز گذاشته باشد، نه پورت ۸۰۰۰.
- لاگهای Gunicorn (
error.log) بعد از چند درخواست آزمایشی خالی از خطا باشد. - متغیرهای حساس (کلید دیتابیس، توکنها) در فایل
.envباشند، نه داخل کد. - مصرف RAM با
free -hدر حد منطقی باشد؛ اگر هر worker بهتنهایی رم زیادی میخورد، تعداد worker را کم کنید یا پلن سرور را ارتقا دهید. برای محدود کردن سختگیرانهتر مصرف هر سرویس، محدود کردن مصرف CPU و RAM سرویسها با systemd و cgroups در سرور لینوکس روش دقیقتری نشان میدهد.
یک مورد دیگر که راحت فراموش میشود، چرخش لاگ (log rotation) است. فایل access.log روی یک API پرترافیک در چند هفته به چند گیگابایت میرسد و میتواند دیسک سرور را پر کند. یک فایل کوچک در /etc/logrotate.d/myapp این را حل میکند:
/srv/myapp/logs/*.log {
weekly
rotate 4
compress
missingok
notifempty
}
با این تنظیم، لاگهای قدیمی هر هفته فشرده میشوند و فقط ۴ نسخه آخر نگه داشته میشود؛ بقیه حذف میشوند.
اگر منابع سرور فعلی برای تعداد worker مورد نیاز کم است، سادهترین راه ارتقای پلن سرور مجازی آریانت است؛ تغییر پلن بدون نصب دوباره هیچکدام از مراحل بالا انجام میشود.
سؤالات متداول
آیا میتوان بهجای Gunicorn فقط از Uvicorn با چند worker استفاده کرد؟
بله، با uvicorn main:app --workers 4 هم چند پردازه بالا میآید، اما Gunicorn مدیریت worker ها (ریاستارت خودکار یک worker کرشکرده بدون قطع بقیه) را بهتر انجام میدهد. برای production ترکیب Gunicorn و Uvicorn Workers رایجتر است.
چرا باید از Nginx جلوی Gunicorn استفاده کنم؟ Gunicorn برای مدیریت تعداد زیاد اتصال همزمان، فایلهای استاتیک و SSL ساخته نشده. Nginx این کارها را سریعتر انجام میدهد و Gunicorn فقط روی اجرای کد پایتون تمرکز میکند.
روی سرور اختصاصی هم همین مراحل جواب میدهد؟
بله، تمام دستورات همین راهنما مستقل از نوع سرور است. تنها چیزی که تغییر میکند عدد workers در gunicorn.conf.py است، چون به تعداد هسته CPU سرور بستگی دارد.
اگر بعد از ریاستارت سرور اپلیکیشن بالا نیامد چه باید کرد؟
با sudo systemctl status myapp وضعیت سرویس را ببینید و اگر فعال نیست با sudo systemctl enable myapp آن را برای اجرای خودکار در بوت فعال کنید. خطای دقیقتر معمولا در journalctl -u myapp است.




