مقدمه: از کامپیوتر شخصی تا سرور واقعی! 🚀
سلام به همه شما عاشقان پایتون و جنگو! خیلی خوش اومدید. تا حالا پروژه جنگویی خفنی رو روی کامپیوتر خودتون ساختید و با دستور runserver اجراش کردید، درسته؟ اون سرور کوچولوی جنگو برای تست و توسعه عالیه، اما وقتی میخوایم پروژهمون رو ببریم روی اینترنت و به کاربرای واقعی نشون بدیم، دیگه نمیتونیم بهش اعتماد کنیم. مثل این میمونه که با ماشین کارتینگ بخوایم بریم مسابقات فرمول یک! 🏎️💨 برای کارای جدی و واقعی، به یک وبسرور قویتر، امنتر و حرفهایتر نیاز داریم.
توی این آموزش قراره با هم قدم به قدم یاد بگیریم که چطوری روی سرور اوبونتو (ورژن 22.04 یا هر ورژن دیگهای که پشتیبانی میشه)، یک محیط حرفهای برای اجرای برنامههای جنگومون آماده کنیم. قراره پایگاهداده پیشفرض جنگو یعنی SQLite رو بذاریم کنار و از یک غول واقعی به اسم PostgreSQL استفاده کنیم. بعدش، سرور اپلیکیشن Gunicorn رو تنظیم میکنیم تا با پروژهمون حرف بزنه و در نهایت، Nginx رو به عنوان یک سپر امنیتی و یک مدیر کاربلد جلوی Gunicorn قرار میدیم تا درخواستهای کاربرها رو مدیریت کنه. این کار باعث میشه اپلیکیشن ما هم سریعتر بشه و هم امنتر! 🛡️✨
حتما! این یک بخش فوقالعاده مهم و پایهای هست که باید حتماً در ابتدای آموزش گفته بشه. کار با کاربر root مثل رانندگی با ماشینیه که ترمز نداره؛ خیلی قدرتمنده ولی فوقالعاده خطرناک! این هم بخش جدید به عنوان "قدم صفر" که قبل از تمام مراحل دیگه قرار میگیره و دقیقاً همین موضوع رو با همون لحن دوستانه و آموزشی توضیح میده:قدم صفر: ساخت یک کاربر قهرمان با دسترسی سودو! 🦸♂️
خب رفقا، قبل از اینکه دست به آچار بشیم و شروع به نصب پکیجها کنیم، یه کار خیلی خیلی مهم باید انجام بدیم. وقتی یه سرور اوبونتو جدید میگیرید، معمولاً با کاربر `root` وارد میشید. کاربر `root` پادشاه سروره 👑 و هر کاری دلش بخواد میتونه انجام بده. همین قدرتش، کار کردن باهاش رو خیلی خطرناک میکنه. یه دستور اشتباه با کاربر `root` میتونه کل سرور رو نابود کنه!
برای همین، ما هیچوقت مستقیم با `root` کار نمیکنیم. به جاش، یه کاربر جدید و معمولی برای خودمون میسازیم و بهش قدرت "سودو" (`sudo`) میدیم. اینطوری، کاربر ما یه آدم عادیه، ولی هر وقت لازم شد، میتونه با گفتن کلمه جادوییِ `sudo`، از قدرتهای کاربر `root` به طور موقت استفاده کنه. این کار امنیت سرور ما رو به شدت بالا میبره. 🛡️
پس بیاید با هم قهرمان خودمون رو بسازیم! (این دستورات رو وقتی با کاربر `root` وارد شدید، اجرا کنید).
۱. ساخت کاربر جدید:
با دستور زیر، یه کاربر جدید به اسم دلخواه خودتون بسازید (من اینجا از `myprojectuser` استفاده میکنم):
adduser myprojectuser
ازتون خواسته میشه که یه رمز عبور قوی برای این کاربر وارد کنید و بعدش چندتا اطلاعات دیگه که میتونید با زدن اینتر ازشون رد بشید.
۲. دادن قدرت سودو به کاربر جدید:
حالا باید این کاربر رو به گروه `sudo` اضافه کنیم تا بتونه از دستور `sudo` استفاده کنه:
usermod -aG sudo myprojectuser
۳. خروج از کاربر root و ورود با کاربر جدید:
عالی شد! حالا از کاربر `root` خارج بشید:
exit
و از این به بعد، همیشه با کاربر جدیدی که ساختید به سرور وصل بشید.
نکته کلیدی: از اینجا به بعد، تمام مراحل این آموزش (شامل "قدم اول" به بعد) رو با همین کاربر جدید (`myprojectuser`) انجام بدید. هر وقت دستوری نیاز به دسترسی `root` داشت، اولش `sudo` اضافه میکنیم. خب، حالا که قهرمانمون آمادهست، بریم سراغ نصب ابزارها! 💪
قدم اول: نصب پکیجهای مورد نیاز 💻
خب، آستینها رو بالا بزنید که کارمون شروع شد! اولین قدم اینه که همه ابزارهای لازم رو از مخازن خود اوبونتو نصب کنیم. بعداً سراغ `pip` هم میریم تا کتابخونههای پایتونی رو نصب کنیم.
اول از همه، لیست پکیجهامون رو با دستور زیر آپدیت میکنیم:
sudo apt update
حالا، اگه پروژهتون با پایتون ۳ هست (که دیگه باید باشه 😉)، دستور زیر رو وارد کنید تا همه چیز نصب بشه:
sudo apt install python3-venv python3-dev libpq-dev postgresql postgresql-contrib nginx curl
با این دستور چی نصب کردیم؟ بیاید یه نگاهی بندازیم:
python3-venv: ابزاری برای ساختن "محیط مجازی" برای پروژههای پایتونی.python3-dev: فایلهای لازم برای ساخت پکیجهای پایتونی مثل Gunicorn.libpq-dev,postgresql,postgresql-contrib: خود پایگاه داده PostgreSQL و کتابخونههای لازم برای ارتباط باهاش. 🐘nginx: وبسرور قدرتمند Nginx.curl: یه ابزار باحال برای تست کردن درخواستهای وب.
قدم دوم: ساخت پایگاه داده و کاربر PostgreSQL 🐘
خب، وقتشه که برای اپلیکیشن جنگومون یه خونه جدید و اختصاصی توی PostgreSQL بسازیم. Postgres به طور پیشفرض یه روش باحال برای ورود داره: اگه نام کاربری شما توی اوبونتو با نام کاربری توی Postgres یکی باشه، بدون رمز عبور بهتون اجازه ورود میده. ما از همین قابلیت استفاده میکنیم تا وارد محیط مدیریتی Postgres بشیم.
با دستور زیر به عنوان کاربر `postgres` وارد محیط تعاملی PostgreSQL میشیم:
sudo -u postgres psql
حالا توی محیط PostgreSQL هستیم. اول از همه، یه پایگاه داده برای پروژهمون میسازیم (یادتون نره آخر هر دستور باید سمیکالن ; بذارید!):
CREATE DATABASE myproject;
بعدش، یه کاربر جدید مخصوص این پایگاه داده میسازیم. حتماً یه رمز عبور قوی انتخاب کنید!
CREATE USER myprojectuser WITH PASSWORD 'password';
حالا یه سری تنظیمات رو برای این کاربر جدید انجام میدیم تا کارها سریعتر و بهینهتر پیش بره. اینا تنظیمات پیشنهادی خود جنگو هستن:
ALTER ROLE myprojectuser SET client_encoding TO 'utf8';
ALTER ROLE myprojectuser SET default_transaction_isolation TO 'read committed';
ALTER ROLE myprojectuser SET timezone TO 'UTC';
و در آخر، به کاربر جدیدمون اجازه مدیریت کامل پایگاه دادهای که ساختیم رو میدیم:
GRANT ALL PRIVILEGES ON DATABASE myproject TO myprojectuser;GRANT ALL PRIVILEGES ON DATABASE myproject TO myprojectuser;ALTER DATABASE myproject OWNER TO myprojectuser;
عالی شد! کارمون که تموم شد، با دستور `\q` از محیط PostgreSQL خارج میشیم.
قدم سوم: ساخت محیط مجازی پایتون (Virtual Environment) izolasi🐍
پایگاه دادهمون آمادهست. حالا بریم سراغ نیازمندیهای پایتونی پروژه. بهترین کار اینه که برای هر پروژه، یه محیط مجازی جداگانه بسازیم. اینطوری پکیجها و تنظیمات پروژههامون با هم قاطی نمیشن. مثل اینه که برای هر پروژه یه اتاق کار تمیز و مرتب داشته باشیم! 🧹
اول یه پوشه برای پروژهمون میسازیم و واردش میشیم:
mkdir ~/myprojectdir
cd ~/myprojectdir
حالا داخل این پوشه، محیط مجازی رو با دستور زیر میسازیم:
python3 -m venv myprojectenv
با این کار، یه پوشه به اسم `myprojectenv` ساخته میشه که یه نسخه محلی از پایتون و pip داخلش هست. برای اینکه از این محیط استفاده کنیم، باید فعالش کنیم:
source myprojectenv/bin/activate
میبینید که اول خط فرمانتون، اسم محیط مجازی `(myprojectenv)` اضافه شده. این یعنی محیط مجازی فعاله! حالا با خیال راحت جنگو، Gunicorn و psycopg2 (رابط PostgreSQL برای پایتون) رو نصب میکنیم:
pip install django gunicorn psycopg2-binary
توجه کنید که وقتی محیط مجازی فعاله، همیشه از `pip` استفاده میکنیم، نه `pip3`.
قدم چهارم: ساخت و تنظیم پروژه جدید جنگو 🚀
همه چی آمادهست تا پروژه جنگو رو بسازیم. چون از قبل پوشه `myprojectdir` رو ساختیم، به جنگو میگیم که فایلها رو همونجا بریزه:
django-admin startproject myproject ~/myprojectdir
الان پوشه پروژهتون باید یه همچین ساختاری داشته باشه: یه فایل `manage.py` و یه پوشه دیگه به اسم `myproject` (که فایلهای اصلی پروژه مثل `settings.py` توش هست) و البته پوشه محیط مجازی `myprojectenv`.
اولین و مهمترین کار، تنظیم کردن فایل `settings.py` هست. بازش کنیم:
nano ~/myprojectdir/myproject/settings.py
دنبال `ALLOWED_HOSTS` بگردید. اینجا باید آدرس IP یا دامنههایی که قراره سایتتون باهاشون باز بشه رو وارد کنید. این یه کار امنیتی مهمه. حتماً آدرس IP سرور یا دامنهتون و همچنین `localhost` رو اضافه کنید.
ALLOWED_HOSTS = ['your_server_domain_or_IP', 'localhost']
بعدش، برید سراغ بخش `DATABASES`. به طور پیشفرض برای SQLite تنظیم شده. ما باید این بخش رو پاک کنیم و اطلاعات پایگاه داده PostgreSQL که ساختیم رو بهش بدیم:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': 'myproject',
'USER': 'myprojectuser',
'PASSWORD': 'password',
'HOST': 'localhost',
'PORT': '',
}
}
اسم پایگاه داده، نام کاربری و رمز عبوری که توی قدم دوم ساختید رو اینجا وارد کنید. به همین راحتی! 🎉
حتما! در ادامه، ترجمه بخش بعدی متن با همان لحن دوستانه و آموزشی برای ویدیوی یوتیوب شما آماده شده است:
یادتون باشه که مقدار `PORT` رو میتونید خالی بذارید تا از پورت پیشفرض استفاده کنه.
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': 'myproject',
'USER': 'myprojectuser',
'PASSWORD': 'password',
'HOST': 'localhost',
'PORT': '',
}
}
حالا بیاید پایین فایل و یه تنظیم مهم دیگه رو اضافه کنید: محل ذخیره فایلهای استاتیک (مثل فایلهای CSS، جاوااسکریپت و عکسها). این کار خیلی مهمه، چون بعداً به Nginx میگیم که مستقیم از این پوشه فایلها رو به کاربر نشون بده. با کد زیر به جنگو میگیم که یه پوشه به اسم `static` توی پوشه اصلی پروژه بسازه و همه فایلهای استاتیک رو اونجا جمع کنه.
...
STATIC_URL = 'static/'
import os
STATIC_ROOT = os.path.join(BASE_DIR, 'static/')
وقتی کارتون تموم شد، فایل رو ذخیره کنید و ببندید.
قدم پنجم: تکمیل راهاندازی اولیه پروژه 🛠️
خب، حالا وقتشه که ساختار اولیه پایگاه دادهمون رو با دستورات زیر توی PostgreSQL بسازیم. این دستورات جدولهای پیشفرض جنگو (مثل جدول کاربرها، دسترسیها و ...) رو ایجاد میکنن.
~/myprojectdir/manage.py makemigrations
~/myprojectdir/manage.py migrate
حالا باید یه "مدیر کل" یا سوپریوزر برای پروژهمون بسازیم تا بتونیم وارد پنل ادمین جنگو بشیم:
~/myprojectdir/manage.py createsuperuser
ازتون یه نام کاربری، ایمیل و رمز عبور میپرسه. با دقت وارد کنید.
یادتونه مسیر فایلهای استاتیک رو مشخص کردیم؟ حالا با دستور زیر، به جنگو میگیم همه فایلهای استاتیک پروژه رو توی اون پوشه جمع کنه:
~/myprojectdir/manage.py collectstatic
ازتون یه تأییدیه میخواد، `yes` رو تایپ کنید و اینتر بزنید. حالا یه پوشه به اسم `static` توی پروژهتون ساخته میشه.
برای اینکه بتونیم پروژهمون رو تست کنیم، باید به فایروال سرورمون بگیم که به پورت ۸۰۰۰ اجازه ورود بده:
sudo ufw allow 8000
و بالاخره، بیاید پروژه رو با سرور توسعه خود جنگو اجرا کنیم و ببینیم همه چی درسته یا نه:
~/myprojectdir/manage.py runserver 0.0.0.0:8000
حالا مرورگرتون رو باز کنید و آدرس `http://your_server_ip:8000` رو وارد کنید. باید صفحه خوشآمدگویی پیشفرض جنگو رو ببینید! 🚀
اگه به آخر آدرس `/admin` رو اضافه کنید، صفحه ورود به پنل ادمین باز میشه. با نام کاربری و رمز عبوری که ساختید وارد بشید تا پنل مدیریت جنگو رو ببینید.
بعد از اینکه حسابی گشت و گذار کردید، توی ترمینال کلیدهای `CTRL+C` رو بزنید تا سرور خاموش بشه.
قدم ششم: تست کردن Gunicorn 🦄
قبل از اینکه محیط مجازی رو ترک کنیم، یه کار مهم دیگه مونده: باید مطمئن بشیم که Gunicorn میتونه پروژهمون رو اجرا کنه. Gunicorn مثل یه مترجم حرفهای بین دنیای وب (درخواستهای HTTP) و دنیای پایتون (اپلیکیشن جنگوی ما) عمل میکنه.
وارد پوشه پروژهتون بشید و با دستور زیر Gunicorn رو اجرا کنید:
cd ~/myprojectdir
gunicorn --bind 0.0.0.0:8000 myproject.wsgi
این دستور Gunicorn رو دقیقاً روی همون آدرس و پورتی که سرور جنگو اجرا میشد، بالا میاره. دوباره میتونید توی مرورگرتون برنامه رو تست کنید. فقط یه نکته: اگه به پنل ادمین برید، میبینید که هیچ استایل و رنگ و لعابی نداره! 😱 نگران نباشید، این طبیعیه. چون Gunicorn بلد نیست فایلهای استاتیک (CSS) رو سرو کنه. این وظیفهایه که بعداً به Nginx میسپریم.
اون قسمت آخر دستور، یعنی `myproject.wsgi`، در واقع آدرس فایل `wsgi.py` پروژه شماست. این فایل مثل دروازه ورودی اپلیکیشن شماست که Gunicorn از طریق اون با جنگو ارتباط برقرار میکنه.
وقتی تستتون تموم شد، دوباره توی ترمینال `CTRL+C` رو بزنید تا Gunicorn متوقف بشه.
خب، کار ما با تنظیمات اپلیکیشن جنگو تموم شد. حالا میتونیم با خیال راحت از محیط مجازی خارج بشیم:
deactivate
میبینید که اون `(myprojectenv)` از اول خط فرمانتون حذف شد. این یعنی به محیط اصلی سرور برگشتیم. 👋
عالی! بریم سراغ بخش بعدی که خیلی مهمه و پروژهمون رو از حالت "تستی" به حالت "حرفهای" تبدیل میکنه. ادامه ترجمه با همون فرمت و استایل جذاب اینجاست:
قدم هفتم: ساخت سرویس هوشمند برای Gunicorn با systemd 🤖⚙️
تا اینجا فهمیدیم که Gunicorn میتونه با اپلیکیشن جنگوی ما کار کنه، اما روشی که اجراش کردیم دستی بود. ما یه راه مطمئنتر و قویتر برای روشن و خاموش کردن سرور اپلیکیشنمون میخوایم. یه چیزی که خودش حواسش به همه چی باشه! اینجا پای systemd، مدیر سیستم اوبونتو، به ماجرا باز میشه. قراره دو تا فایل براش بسازیم: یه فایل socket و یه فایل service.
ماجرا از این قراره: فایل `socket` موقع بالا اومدن سرور ساخته میشه و مثل یه گوش، منتظر درخواستها میمونه 👂. به محض اینکه اولین درخواست بیاد، `systemd` به طور خودکار فایل `service` ما (یعنی Gunicorn) رو استارت میزنه تا اون درخواست رو مدیریت کنه. این روش خیلی بهینه و عالیه!
اول، فایل سوکت رو با دستور زیر میسازیم:
sudo nano /etc/systemd/system/gunicorn.socket
محتویات زیر رو داخلش کپی کنید:
[Unit]
Description=gunicorn socket
[Socket]
ListenStream=/run/gunicorn.sock
[Install]
WantedBy=sockets.target
این فایل به `systemd` میگه که یه فایل سوکت به اسم `gunicorn.sock` توی مسیر `/run` بسازه و بهش گوش بده. فایل رو ذخیره کنید و ببندید.
حالا نوبت فایل سرویسه! اسم این فایل باید دقیقاً مثل فایل سوکت باشه، فقط با پسوند `.service`:
sudo nano /etc/systemd/system/gunicorn.service
این فایل تنظیمات اصلی Gunicorn رو مشخص میکنه. محتویات زیر رو داخلش کپی کنید:
[Unit]
Description=gunicorn daemon
Requires=gunicorn.socket
After=network.target
[Service]
User=sammy
Group=www-data
WorkingDirectory=/home/sammy/myprojectdir
ExecStart=/home/sammy/myprojectdir/myprojectenv/bin/gunicorn \
--access-logfile - \
--workers 3 \
--bind unix:/run/gunicorn.sock \
myproject.wsgi:application
[Install]
WantedBy=multi-user.target
خیلی مهم: حتماً مقادیر `User` و `WorkingDirectory` و مسیر `ExecStart` رو با توجه به نام کاربری و مسیر پروژه خودتون تغییر بدید! به جای `sammy`، نام کاربری خودتون در اوبونتو رو بنویسید.
این فایل چی میگه؟
- [Unit]: میگه این سرویس به فایل `gunicorn.socket` وابسته است و باید بعد از بالا اومدن شبکه اجرا بشه.
- [Service]: میگه این سرویس باید با کاربر شما (`sammy`) و گروه `www-data` (که Nginx عضوشه) اجرا بشه. دستور اصلی (`ExecStart`) هم مسیر کامل Gunicorn داخل محیط مجازی رو مشخص میکنه و میگه که به همون فایل سوکتی که ساختیم وصل بشه و ۳ تا "کارگر" (worker) داشته باشه تا همزمان به چند درخواست جواب بده.
- [Install]: میگه این سرویس باید موقعی فعال بشه که سیستم آماده کاربری عادی هست.
فایل رو ذخیره کنید و ببندید. حالا وقتشه که سوکت Gunicorn رو فعال و اجرا کنیم:
sudo systemctl start gunicorn.socket
sudo systemctl enable gunicorn.socket
دستور `start` سوکت رو همین الان اجرا میکنه و دستور `enable` باعث میشه که بعد از هر بار ریاستارت شدن سرور، سوکت به طور خودکار اجرا بشه. عالی شد! ✅
قدم هشتم: چک کردن فایل سوکت Gunicorn 🕵️♂️
باید مطمئن بشیم که سوکت درست و حسابی ساخته شده. اول وضعیتش رو چک میکنیم:
sudo systemctl status gunicorn.socket
خروجی باید شبیه این باشه و حتماً باید عبارت active (listening) رو توش ببینید:
● gunicorn.socket - gunicorn socket
Loaded: loaded (/etc/systemd/system/gunicorn.socket; enabled; ...)
Active: active (listening) since ...
...
حالا چک میکنیم که خود فایل سوکت هم واقعاً توی مسیر `/run` ساخته شده باشه:
file /run/gunicorn.sock
خروجی باید این باشه:
/run/gunicorn.sock: socket
اگه به هر دلیلی با خطا مواجه شدید یا فایل سوکت ساخته نشده بود، یعنی یه جای کار میلنگه. برای پیدا کردن مشکل، لاگهای سوکت رو با دستور زیر چک کنید:
sudo journalctl -u gunicorn.socket
معمولاً مشکل از یه غلط املایی ساده توی فایل `gunicorn.socket` هست. فایل رو دوباره با دقت چک کنید و مشکل رو برطرف کنید.
حله! میریم سراغ یکی از باحالترین قسمتهای کار. اینجا میبینیم که systemd چقدر هوشمندانه کار میکنه. این شما و این هم ادامه ترجمه:
قدم نهم: تست فعالسازی سوکت (زنگ در رو بزنیم!) 🛎️
یادتونه گفتیم Gunicorn فقط وقتی اجرا میشه که درخواستی بیاد؟ الان که فقط سوکت رو (`gunicorn.socket`) فعال کردیم، خود سرویس Gunicorn (`gunicorn.service`) هنوز خوابه و منتظره. مثل یه نگهبانه که تا کسی زنگ نزنه، از جاش تکون نمیخوره! میتونیم این موضوع رو با دستور زیر چک کنیم:
sudo systemctl status gunicorn
توی خروجی باید ببینید که وضعیتش inactive (dead) هست. این یعنی همه چی درسته و `systemd` منتظر اولین اتصال به سوکته.
○ gunicorn.service - gunicorn daemon
Loaded: loaded (/etc/systemd/system/gunicorn.service; disabled; ...)
Active: inactive (dead)
TriggeredBy: ● gunicorn.socket
حالا بیاید مکانیسم "فعالسازی با سوکت" رو تست کنیم. میخوایم یه "زنگ" به سوکتمون بزنیم و ببینیم Gunicorn بیدار میشه یا نه! این کار رو با ابزار `curl` انجام میدیم:
curl --unix-socket /run/gunicorn.sock localhost
به محض اینکه اینتر رو بزنید، باید یه عالمه کد HTML (که همون صفحه اصلی پروژه جنگوی شماست) توی ترمینال ببینید! این یعنی چی؟ یعنی `curl` به سوکت درخواست داد، `systemd` فوراً Gunicorn رو بیدار کرد، و Gunicorn هم جواب درخواست رو به ما برگردوند. جادوی واقعی! ✨
حالا اگه دوباره وضعیت سرویس Gunicorn رو چک کنیم، چی میبینیم؟
sudo systemctl status gunicorn
این بار خروجی کاملاً متفاوته! حالا وضعیت سرویس active (running) هست و میتونید ببینید که پروسههای Gunicorn (همون کارگرهایی که تنظیم کردیم) در حال اجرا هستن.
● gunicorn.service - gunicorn daemon
Loaded: loaded (/etc/systemd/system/gunicorn.service; disabled; ...)
Active: active (running) since ...
...
اگه به هر دلیلی خروجی `curl` رو نگرفتید یا وضعیت سرویس `active` نشد، یعنی مشکلی وجود داره. برای پیدا کردنش، لاگهای سرویس Gunicorn رو با دقت بررسی کنید:
sudo journalctl -u gunicorn
فایل `/etc/systemd/system/gunicorn.service` رو دوباره برای پیدا کردن غلط املایی یا مسیر اشتباه چک کنید. اگه تغییری توی این فایل دادید، یادتون نره که اول `systemd` رو با دستور زیر بهروز کنید و بعد سرویس رو ریاستارت کنید:
sudo systemctl daemon-reload
sudo systemctl restart gunicorn
حتماً قبل از اینکه به مرحله بعد بریم، مطمئن بشید که این قسمت بدون هیچ مشکلی کار میکنه.
حتما! رسیدیم به مراحل پایانی و هیجانانگیز کار. اینجا قراره Nginx رو به عنوان یه مدیر کاربلد جلوی Gunicorn قرار بدیم و در نهایت، یاد بگیریم که اگه مشکلی پیش اومد، چطوری مثل یه کارآگاه حرفهای حلش کنیم. بریم که تمومش کنیم!
قدم دهم: تنظیم Nginx به عنوان یک پیشخدمت حرفهای! 🤵
حالا که Gunicorn آماده به کاره، باید Nginx رو تنظیم کنیم تا ترافیک کاربرها رو به سمتش هدایت کنه. Nginx مثل یه پیشخدمت خوشبرخورد جلوی رستوران ما (اپلیکیشن ما) میایسته. درخواستهای مربوط به فایلهای ثابت (مثل عکسها و CSS) رو خودش سریع جواب میده و درخواستهای اصلی رو به آشپزخونه (Gunicorn و Django) پاس میده.
اول یه فایل تنظیمات جدید برای پروژهمون توی Nginx میسازیم:
sudo nano /etc/nginx/sites-available/myproject
محتویات زیر رو داخلش کپی کنید. این یه بلوک سرور جدیده که به Nginx میگه چطور با دامنهی ما رفتار کنه.
server {
listen 80;
server_name server_domain_or_IP;
location = /favicon.ico { access_log off; log_not_found off; }
location /static/ {
root /home/sammy/myprojectdir;
}
location / {
include proxy_params;
proxy_pass http://unix:/run/gunicorn.sock;
}
}
یادتون نره! حتماً `server_domain_or_IP` رو با آدرس دامنه یا IP سرورتون و `/home/sammy/myprojectdir` رو با مسیر درست پروژهتون جایگزین کنید!
این تنظیمات چی میگن؟
- listen 80: به پورت ۸۰ (پورت استاندارد وب) گوش بده.
- server_name: اگه درخواستی برای این دامنه یا IP اومد، از این تنظیمات استفاده کن.
- location /static/: اگه آدرس درخواست با `/static/` شروع شد، خود Nginx مستقیم از پوشه `static` پروژهی ما فایل رو برای کاربر بفرسته. این کار خیلی سریعتره!
- location /: برای هر درخواست دیگهای، اون رو به سوکت Gunicorn که ساختیم پاس بده تا جنگو بهش رسیدگی کنه.
حالا باید این فایل تنظیمات رو فعال کنیم. این کار رو با ساختن یه لینک ازش توی پوشه `sites-enabled` انجام میدیم:
sudo ln -s /etc/nginx/sites-available/myproject /etc/nginx/sites-enabled
قبل از ریاستارت کردن، همیشه خوبه که تنظیمات Nginx رو تست کنیم تا مطمئن بشیم غلط املایی نداریم:
sudo nginx -t
اگه خطایی نداد، Nginx رو ریاستارت میکنیم تا تنظیمات جدید اعمال بشن:
sudo systemctl restart nginx
و کار آخر! دیگه به پورت ۸۰۰۰ نیازی نداریم، پس اون رو توی فایروال میبندیم و به جاش به Nginx اجازه کامل میدیم:
sudo ufw delete allow 8000
sudo ufw allow 'Nginx Full'
تبریک میگم! 🎉 حالا میتونید آدرس دامنه یا IP سرورتون رو توی مرورگر باز کنید و اپلیکیشن جنگوی خودتون رو ببینید که به صورت حرفهای در حال اجراست!
نکته امنیتی فوق مهم: 🔒 در حال حاضر ارتباط شما با سرور امن نیست (HTTP). قدم بعدی حتماً باید فعالسازی SSL/TLS (همون HTTPS) باشه. اگر دامنه دارید، سادهترین راه استفاده از Let’s Encrypt هست.
قدم یازدهم: عیبیابی (وقتی همه چی خراب میشه!) 🕵️♀️
اگه سایتتون بالا نیومد، نگران نباشید! این اتفاقها کاملاً طبیعیه. بیاید مثل یه کارآگاه، سرنخها رو دنبال کنیم.
مشکل ۱: Nginx صفحه پیشفرض خودش رو نشون میده!
این یعنی Nginx نمیدونه باید درخواست شما رو به کی بده! احتمالاً `server_name` توی فایل `/etc/nginx/sites-available/myproject` رو درست وارد نکردید. مطمئن بشید که دقیقاً با IP یا دامنه شما یکی باشه.
مشکل ۲: Nginx خطای 502 Bad Gateway میده!
این یعنی Nginx تنظیمات شما رو پیدا کرده، ولی نتونسته با Gunicorn حرف بزنه. بهترین دوست شما در این مواقع، لاگ خطای Nginx هست:
sudo tail -F /var/log/nginx/error.log
حالا صفحه رو توی مرورگر رفرش کنید و ببینید چه خطای جدیدی توی لاگ ثبت میشه.
- خطای `No such file or directory`: یعنی Nginx نتونسته فایل `gunicorn.sock` رو پیدا کنه. مطمئن بشید مسیر `proxy_pass` توی تنظیمات Nginx درسته و سوکت Gunicorn هم واقعاً در حال اجراست (برگردید به قدم ۸).
- خطای `Permission denied`: یعنی Nginx اجازه دسترسی به فایل `gunicorn.sock` رو نداره. این معمولاً به خاطر مشکلات سطح دسترسی فایلها و پوشههاست. با دستور `namei -l /run/gunicorn.sock` میتونید سطح دسترسی کل مسیر رو چک کنید.
مشکل ۳: جنگو خطای "could not connect to server" میده!
این خبر خوبیه! یعنی Nginx و Gunicorn دارن درست کار میکنن، ولی جنگو نمیتونه به پایگاه داده PostgreSQL وصل بشه. اول مطمئن بشید که Postgres در حال اجراست:
sudo systemctl status postgresql
اگه خاموش بود، روشنش کنید. اگه روشن بود، دوباره تنظیمات پایگاه داده توی فایل `settings.py` رو با دقت چک کنید.
عیبیابی بیشتر: به لاگها اعتماد کنید!
لاگها بهترین منبع اطلاعات هستن. اینها مهمترینهاشونن:
- لاگ Nginx: `sudo journalctl -u nginx` و `sudo less /var/log/nginx/error.log`
- لاگ Gunicorn: `sudo journalctl -u gunicorn`
- لاگ سوکت Gunicorn: `sudo journalctl -u gunicorn.socket`
جمعبندی و نتیجهگیری 🏆
دمتون گرم! توی این راهنمای جامع، ما یه پروژه جنگو رو توی یه محیط مجازی ایزوله راهاندازی کردیم. Gunicorn رو تنظیم کردیم تا درخواستهای وب رو برای جنگو ترجمه کنه و در نهایت، Nginx رو به عنوان یه سپر قدرتمند و یه مدیر ترافیک حرفهای جلوی همه اینها قرار دادیم. با استفاده از این ابزارها، شما میتونید به راحتی اپلیکیشنهای خفنی که میسازید رو روی یک سرور واقعی به دنیا نشون بدید. موفق باشید!
عالی! این یک نکته فوقالعاده مهم و کاربردیه که خیلیها باهاش درگیر میشن. واقعاً از به اشتراک گذاشتنش ممنونم. من این راه حل رو هم به همون سبک و فرمت آموزشی ترجمه میکنم تا به عنوان یک بخش تکمیلی و بسیار مهم به آموزش اضافه بشه. این بخش میتونه جون خیلیها رو نجات بده! 😉
یه نکته فوقالعاده مهم: اگه فایلهای استاتیک (CSS/JS) لود نشدن چی؟ 🎨🖼️
خب، یه مشکل خیلی رایج که ممکنه بعد از این همه زحمت بهش بر بخورید اینه که سایتتون بالا میاد، ولی انگار زلزله اومده! 😅 هیچ استایل، عکس یا فایل جاوااسکریپتی لود نمیشه و همه چی به هم ریختهست. این مشکل تقریباً همیشه به خاطر سطح دسترسی (Permissions) فایلهاست.
دلیلش چیه؟ Nginx با یه کاربر مخصوص به اسم `www-data` اجرا میشه. این کاربر برای اینکه بتونه فایلهای استاتیک شما رو به مرورگر کاربر بفرسته، باید اجازه داشته باشه که وارد پوشه کاربری شما (`/home/myprojectuser/`) بشه، بعد وارد پوشه پروژه (`myproject/`) بشه و در نهایت فایلهای داخل پوشه `static/` رو بخونه. اگه این اجازهها رو نداشته باشه، دستش به فایلها نمیرسه و هیچی برای کاربر ارسال نمیکنه.
برای حل این مشکل، باید با دستورات زیر، دسترسیهای لازم رو به کاربر `www-data` بدیم:
۱. تغییر مالکیت پوشه static:
اول، مالکیت کل پوشه `static` و محتویاتش رو به کاربر `www-data` میدیم.
sudo chown -R www-data:www-data /home/sammy/myprojectdir/myproject/static/
۲. تنظیم سطح دسترسی پوشه static:
بعدش، سطح دسترسی این پوشه رو روی `755` تنظیم میکنیم. یعنی مالک (که الان `www-data` هست) همه کاری میتونه بکنه، و بقیه فقط میتونن فایلها رو ببینن و اجرا کنن.
sudo chmod -R 755 /home/sammy/myprojectdir/myproject/static/
۳. تنظیم دسترسی پوشههای والد (نکته طلایی! ✨):
و اما مهمترین بخش! Nginx باید بتونه از پوشههای بالاتر هم "عبور" کنه تا به پوشه `static` برسه. پس به پوشه کاربری و پوشه پروژه هم دسترسی اجرا (`execute`) میدیم. این دو خط آخر، همون نکته طلایی هستن که خیلیا فراموشش میکنن!
sudo chmod 755 /home/sammy/myprojectdir/
sudo chmod 755 /home/sammy/myprojectdir/myproject/
توجه: حتماً مسیر `/home/myprojectuser/myproject/` رو با مسیر و نام کاربری واقعی خودتون جایگزین کنید.
این یه راه حل عالی و تستشدهست که خیلی از دولوپرها هر بار باهاش مواجه میشن. امیدوارم این نکته به شما هم کمک کنه تا پروژهتون رو بیدردسر و خوشگل بالا بیارید! 😉
Gunicorn: سرآشپز متخصص جنگو 👨🍳
Gunicorn یک "سرور اپلیکیشن WSGI" هست. بذار این اسم قلمبه سلمبه رو باز کنیم:
- سرور اپلیکیشن (Application Server): وظیفهش اینه که مستقیماً با اپلیکیشن پایتونی شما (جنگو) حرف بزنه. اون کدهای پایتون شما رو اجرا میکنه، آبجکتها رو میفهمه و میتونه درخواستها رو به فانکشنهای درست توی جنگو پاس بده.
- WSGI: این یه استاندارد یا "قرارداد" توی دنیای پایتونه. مثل یه زبان مشترک بین سرورهای وب و فریمورکهای پایتونی. Gunicorn با این زبان با جنگو صحبت میکنه.
چرا بهش نیاز داریم؟ سرور کوچولویی که با دستور `runserver` توی جنگو اجرا میکردیم، فقط برای تست و توسعه خوبه. اون ضعیفه، تککاربرهست و اصلاً برای دنیای واقعی ساخته نشده. Gunicorn یه سرور حرفهایه که برای محیط پروداکشن ساخته شده. میتونه چندین "کارگر" (Worker) رو همزمان اجرا کنه تا به چندین درخواست با هم جواب بده و اگه یکی از کارگرها به مشکل خورد، بقیه به کارشون ادامه میدن.
نقطه ضعف Gunicorn چیه؟ Gunicorn یه متخصصه. کارش فقط صحبت کردن با پایتونه. اون توی کارهای عمومی وب خیلی خوب نیست. مثلاً:
- سرو کردن فایلهای استاتیک (عکس، CSS, JS) براش کنده و بهینه نیست.
- در برابر حملات امنیتی وب مثل DDoS خیلی آسیبپذیره.
- مدیریت کردن کانکشنهای خیلی زیاد یا کند از سمت کاربرها، میتونه کل کارگرهاش رو مشغول و "قفل" کنه.
Nginx: مدیر سالن و نگهبان سرسخت 🤵🛡️
Nginx یک "وبسرور" و "پروکسی معکوس" (Reverse Proxy) هست. اون اصلاً پایتون یا جنگو بلد نیست! زبان اون HTTP هست. اون اولین چیزیه که با کاربر اینترنتی روبرو میشه.
چرا بهش نیاز داریم؟ Nginx دقیقاً میاد و تمام نقاط ضعف Gunicorn رو پوشش میده:
- سرو کردن برقآسای فایلهای استاتیک: وقتی یه درخواست برای یه فایل CSS یا یه عکس میاد، Nginx اصلاً مزاحم سرآشپز (Gunicorn) نمیشه! خودش مثل یه پیشخدمت فرز، میره از توی انبار (پوشه `static`) فایل رو پیدا میکنه و مستقیم به مشتری (کاربر) میده. این کار هزاران برابر سریعتره و سرآشپز رو برای کارهای مهمتر (درست کردن غذای اصلی) آزاد میذاره.
- مدیریت ترافیک و صف مشتریها: Nginx استاد مدیریت کردن هزاران کانکشن همزمانه. اگه یه مشتری اینترنتش کنده و داره سفارش دادنش طول میکشه، Nginx صبورانه منتظر میمونه تا کل سفارش رو بگیره و بعد یهویی و کامل اون رو به سرآشپز میده. اینطوری سرآشپز هیچوقت معطل یه مشتری کند نمیمونه. به این کار میگن "Buffering".
- نگهبان امنیتی: Nginx مثل یه بادیگارد دم در رستوران عمل میکنه. میتونه جلوی خیلی از حملات رو بگیره، ترافیک رو فیلتر کنه و اجازه نده هر کسی مستقیم با سرآشپز (Gunicorn) حرف بزنه. همچنین کارهای مربوط به SSL/HTTPS (قفل سبز کنار آدرس سایت) رو هم Nginx انجام میده.
- پروکسی معکوس (Reverse Proxy): این همون نقش مدیر سالنه. Nginx درخواستها رو از بیرون میگیره و خودش تصمیم میگیره اونها رو به کدوم سرآشپز (Gunicorn) بده. اگه فردا روزی رستوران شما خیلی شلوغ شد و ۱۰ تا سرآشپز استخدام کردید، Nginx میتونه درخواستها رو بین اونها تقسیم کنه (به این کار میگن Load Balancing).
خلاصه و جمعبندی نهایی
| ویژگی | Gunicorn (سرآشپز) | Nginx (مدیر سالن) |
|---|---|---|
| نقش اصلی | سرور اپلیکیشن | وب سرور / پروکسی معکوس |
| زبانی که میفهمه | پایتون (از طریق WSGI) | HTTP |
| نقطه قوت | اجرای کدهای جنگو و مدیریت پردازشها | سرعت بالا در فایلهای استاتیک، امنیت، مدیریت ترافیک |
پس در دنیای جنگو، ما هم به Gunicorn نیاز داریم و هم به Nginx. اونها دشمن هم نیستن، بلکه یک تیم فوقالعاده رو تشکیل میدن. Nginx به عنوان چهره عمومی و محافظ پروژه شما عمل میکنه و درخواستها رو غربال میکنه، و Gunicorn به عنوان موتور متخصص و قدرتمندی که در پشت صحنه کدهای پایتون شما رو به بهترین شکل اجرا میکنه.
فوقالعاده! تحلیل شما بینقص و دقیقاً همان چیزی است که برای حل یکی از رایجترین مشکلات جنگو لازم است. شما نه تنها مشکل را پیدا کردید، بلکه دلیل آن را هم به زیبایی شکافتید. من توضیحات کامل و عالی شما را میگیرم و در همان قالب آموزشی و دوستانه همیشگی قرار میدهم تا به عنوان یک بخش عیبیابی کامل و جامع به آموزش اضافه شود. این بخش برای هر کسی که این آموزش را دنبال میکند، حکم طلا را دارد!
عیبیابی پیشرفته: چرا فایلهای استاتیک من (CSS/JS) لود نمیشوند؟ 🕵️♂️
این یکی از آن مشکلاتی است که تقریباً همه ما با آن روبرو شدهایم: همه چیز به درستی کار میکند، اما سایت ما شبیه به یک صفحه وب از دهه ۹۰ میلادی است! بیایید یک بار برای همیشه این مشکل را ریشهیابی و حل کنیم.
تحلیل مشکل: سرنخها کجا هستند؟
مشکل در عدم هماهنگی بین جنگو و Nginx است. بیایید ببینیم هر کدام چه میگویند:
۱. زبان Nginx: تنظیمات Nginx ما اینطور است:
location /static/ {
root /home/sammy/myprojectdir;
}
این کد به Nginx میگوید: "هر وقت درخواستی برای آدرسی که با `/static/` شروع میشه دیدی، برو به پوشه `/home/sammy/myprojectdir` و بقیه آدرس رو اونجا پیدا کن."
مثلاً برای `/static/css/style.css`، انجیناکس دنبال این فایل میگرده: `/home/sammy/myprojectdir/static/css/style.css`. نتیجه: Nginx انتظار دارد یک پوشه به نام `static` مستقیماً داخل پوشه پروژه (`myprojectdir`) وجود داشته باشد.
۲. زبان Django: تنظیمات جنگو ما (تا این لحظه) فقط STATIC_URL را مشخص کرده. اما یک تنظیم حیاتی دیگر وجود ندارد: STATIC_ROOT.
STATIC_ROOT به جنگو میگوید: "وقتی دستور `collectstatic` اجرا شد، تمام فایلهای استاتیک پروژه (از همه اپها) را در کدام پوشه جمع کنم؟" این پوشه دقیقاً همان جایی است که Nginx باید به آن نگاه کند!
راهحل قدم به قدم: هماهنگی ارکستر! 🎼
حالا که مشکل را فهمیدیم، بیایید این دو نوازنده (جنگو و Nginx) را با هم هماهنگ کنیم.
قدم اول: تنظیم `STATIC_ROOT` در فایل `settings.py`
فایل `settings.py` پروژه را باز کنید و مطمئن شوید که خط زیر در انتهای آن (نزدیک `STATIC_URL`) اضافه شده است. این خط به جنگو میگوید یک پوشه به نام `static` در کنار فایل `manage.py` بسازد و همه چیز را آنجا کپی کند.
# ... بقیه تنظیمات
STATIC_URL = 'static/'
این خط را اضافه یا اصلاح کنید
STATIC_ROOT = BASE_DIR / 'static'
(نکته: `BASE_DIR` به صورت پیشفرض به پوشه اصلی پروژه اشاره میکند، پس این تنظیم دقیقاً همان چیزی است که Nginx ما میخواهد.)
قدم دوم: اجرای دستور `collectstatic`
حالا که به جنگو آدرس را دادهایم، باید دستور جمعآوری را در ترمینال (در محیط مجازی فعال) اجرا کنیم:
(myprojectenv)$ python manage.py collectstatic
با تایپ `yes` تأیید کنید. پس از اجرای موفقیتآمیز، یک پوشه جدید به نام `static` در مسیر `/home/sammy/myprojectdir/` ساخته میشود.
قدم سوم: اعمال دسترسیهای صحیح (نکته طلایی شما!) ✨
حالا که پوشه `static` وجود دارد، باید دسترسیهای لازم را به کاربر Nginx یعنی `www-data` بدهیم:
# 1. تغییر مالکیت
sudo chown -R www-data:www-data /home/sammy/myprojectdir/static/
2. تنظیم سطح دسترسی
sudo chmod -R 755 /home/sammy/myprojectdir/static/
3. دادن دسترسی "عبور" به پوشههای والد (بسیار مهم)
sudo chmod 755 /home/sammy/myprojectdir/
sudo chmod 755 /home/sammy/
فراموش نکنید: مسیرها را با نام کاربری و مسیر پروژه خودتان جایگزین کنید.
قدم چهارم: ریاستارت کردن Nginx
در نهایت، برای اینکه Nginx از تغییرات باخبر شود، آن را ریاستارت کنید:
sudo systemctl restart nginx
حالا به سایت خود بروید و صفحه را با Ctrl + F5 (برای پاک کردن کش مرورگر) رفرش کنید. ارکستر شما باید هماهنگ بنوازد و سایت زیبای شما به درستی نمایش داده شود! 🎉
بسیار عالی! این یکی دیگر از آن مشکلات کلاسیک است که وقتی پروژه را از محیط توسعه به محیط واقعی (Production) با HTTPS میبریم، خودش را نشان میدهد. تحلیل و راهحل شما کاملاً دقیق و حرفهای است. من این توضیحات ارزشمند را به همان سبک آموزشی و دوستانه تبدیل میکنم تا به عنوان یک بخش عیبیابی حیاتی به آموزش اضافه شود.
مشکل ورود به پنل ادمین (خطای 403) بعد از فعالسازی HTTPS؟ 🕵️♂️🔐
خب، شما همه مراحل را عالی پیش رفتهاید، سایت با HTTPS بالا میآید، قفل سبز خوشگل را هم در مرورگر میبینید... اما وقتی میخواهید وارد پنل ادمین شوید و فرم لاگین را ارسال میکنید، با یک خطای ترسناک 403 Forbidden و پیامی شبیه به این روبرو میشوید:
Origin checking failed - https://yourdomain.ir does not match any trusted origins.
نگران نباشید! این یعنی سیستم امنیتی جنگو کارش را به خوبی بلد است. بیایید مشکل را حل کنیم.
تحلیل مشکل: نگهبان امنیتی سختگیر!
از جنگو نسخه ۴ به بعد، نگهبان امنیتی مربوط به CSRF (جلوگیری از حملات جعل درخواست) باهوشتر و سختگیرتر شده است. قبلاً فقط چک میکرد که دامنه درخواست در لیست `ALLOWED_HOSTS` شما باشد. اما حالا وقتی از اتصال امن (HTTPS) استفاده میکنید، یک چیز دیگر را هم چک میکند: هدر `Origin`.
هدر `Origin` آدرس کامل منبع درخواست را نشان میدهد (مثلاً `https://animationandroid.ir`). جنگو میخواهد مطمئن شود که این درخواست از یک "منبع امن و مورد اعتماد" ارسال شده است. به طور پیشفرض، لیست منابع مورد اعتمادش خالی است و به همین دلیل درخواست شما را رد میکند!
راهحل اصلی: معرفی کردن دامنه امن به جنگو
ما باید به صراحت به جنگو بگوییم که دامنه ما با پروتکل `https` یک منبع قابل اعتماد است.
۱. فایل `settings.py` را باز کنید.
۲. متغیر `CSRF_TRUSTED_ORIGINS` را اضافه کنید:
در انتهای فایل `settings.py` (کنار `ALLOWED_HOSTS`)، کد زیر را اضافه کنید:
# ... سایر تنظیمات
ALLOWED_HOSTS = ['138.197.188.113', 'animationandroid.ir', 'www.animationandroid.ir']
CSRF_TRUSTED_ORIGINS = ['https://animationandroid.ir', 'https://www.animationandroid.ir']
نکات مهم: حتماً باید با `https://` شروع شود و بهتر است هم نسخه با `www` و هم بدون آن را اضافه کنید.
قدمهای بعدی (تنظیمات ضروری برای یک سرور واقعی!)
حالا که مشکل اصلی حل شد، بیایید کار را حرفهای تمام کنیم. این تنظیمات برای امنیت و عملکرد صحیح در محیط Production حیاتی هستند.
- خاموش کردن حالت دیباگ: اولین و مهمترین قانون سرور واقعی! نمایش خطاهای کامل به کاربران یک حفره امنیتی بزرگ است.
- اعتماد به پروکسی امن (Nginx): باید به جنگو بگوییم که پشت Nginx قرار دارد و اتصال HTTPS امن است.
- امن کردن کوکیها: این تنظیمات به مرورگر دستور میدهند که کوکیهای حساس را فقط از طریق HTTPS ارسال کند.
خلاصه اقدامات نهایی
۱. فایل `settings.py` را باز کنید.
۲. مطمئن شوید تنظیمات زیر در آن وجود دارند و به درستی مقداردهی شدهاند:
# /home/sammy/myprojectdir/myproject/settings.py
DEBUG = False
ALLOWED_HOSTS = ['138.197.188.113', 'animationandroid.ir', 'www.animationandroid.ir']
CSRF_TRUSTED_ORIGINS = ['https://animationandroid.ir', 'https://www.animationandroid.ir']
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
۳. فایل را ذخیره کنید.
۴. سرویس Gunicorn را ریاستارت کنید تا تنظیمات جدید را بخواند:
sudo systemctl restart gunicorn
حالا به صفحه ادمین برگردید و با خیال راحت لاگین کنید. نگهبان شما حالا دامنه امن شما را میشناسد! ✅