آموزش اجرای پروژه پایتون روی سرور مجازی لینوکس
از نصب پایتون تا ساخت محیط مجازی و تبدیل اسکریپت به یک سرویس سیستمی که بعد از راهاندازی مجدد خودش بالا میآید. همه دستورها روی لینوکس اجرا و بررسی شدهاند.
اجرای یک اسکریپت پایتون روی سرور با python main.py شروع میشود و با بستن ترمینال تمام میشود. برای اینکه سرویس واقعاً بماند، سه چیز لازم است: محیط مجازی، سرویس سیستمی، و لاگ. این آموزش از نصب پایتون روی سرور لینوکس شروع میکند و به سرویسی میرسد که بعد از راهاندازی مجدد خودش بالا میآید.
فرض این آموزش اوبونتو یا دبیان است و اینکه کاربر غیر root به نام deploy ساختهاید. اگر نساختهاید، اول بعد از خرید سرور مجازی را ببینید. اگر هنوز سرور ندارید و نمیدانید سرور مجازی دقیقاً چه چیزی در اختیارتان میگذارد، سرور مجازی چیست را بخوانید.
1. پایتون را بررسی کنید
python3 -V
روی نسخههای جدید اوبونتو، پایتون از قبل نصب است. روی اوبونتو 24.04 خروجی چیزی شبیه Python 3.12.3 خواهد بود. دستور python (بدون 3) ممکن است اصلاً وجود نداشته باشد؛ همیشه python3 بنویسید.
پایتون سیستم را حذف یا با نسخه دیگری جایگزین نکنید؛ بخشی از ابزارهای خود سیستمعامل به آن وابستهاند. اگر پروژهتان نسخه دیگری لازم دارد، آن را کنار نسخه سیستم نصب کنید، نه به جایش.
2. بسته venv را نصب کنید
این قدمی است که بیشترین کسان را متوقف میکند. روی اوبونتو و دبیان، پایتون نصب است اما ابزار ساخت محیط مجازی جداگانه بستهبندی شده. اگر این قدم را رد کنید، ساخت محیط مجازی با این خطا متوقف میشود:
The virtual environment was not created successfully because ensurepip is not
available. On Debian/Ubuntu systems, you need to install the python3-venv
package using the following command.
پس اول:
sudo apt update
sudo apt install python3-venv python3-pip
چرا pip install مستقیم خطا میدهد؟
روی اوبونتو 23.04 به بعد و دبیان 12، اگر بیرون از محیط مجازی pip install بزنید، با خطای externally-managed-environment روبهرو میشوید. این خطا عمدی است: طبق مشخصات رسمی بستهبندی پایتون، پایتون سیستم در اختیار مدیر بسته سیستمعامل است و راه پیشنهادی برای نصب کتابخانهها، ساختن محیط مجازی است. گزینه --break-system-packages را برای دور زدنش به کار نبرید؛ اسمش دقیقاً میگوید چه کاری میکند.
3. کد را روی سرور بگذارید
اول کد، بعد محیط مجازی. اگر برعکس عمل کنید، git clone در پوشهای که محیط مجازی در آن ساخته شده با خطای «پوشه خالی نیست» متوقف میشود.
با git، اگر پروژه در یک مخزن است:
git clone https://example.com/user/myapp.git ~/myapp
یا با scp از کامپیوتر خودتان:
scp -r ./myapp deploy@203.0.113.10:~/
اگر پوشه .venv روی کامپیوتر خودتان هم هست، آن را منتقل نکنید؛ محیط مجازی قابل جابهجایی نیست و روی سرور از نو ساخته میشود.
4. محیط مجازی بسازید
محیط مجازی، وابستگیهای پروژه شما را از بستههای سیستم جدا نگه میدارد. بدون آن، نصب یک کتابخانه برای یک پروژه میتواند پروژه دیگر یا حتی ابزارهای خود سیستم را از کار بیندازد.
cd ~/myapp
python3 -m venv .venv
برای فعال کردنش در جلسه فعلی:
source .venv/bin/activate
وقتی فعال باشد، نام محیط در ابتدای خط فرمان نمایش داده میشود. طبق مستندات venv، محیط مجازی یکبارمصرف است: کد پروژه را داخلش نمیگذارید، آن را جابهجا یا کپی نمیکنید، و هر وقت لازم شد پاکش میکنید و از روی requirements.txt دوباره میسازید. اگر پروژه با git مدیریت میشود، .venv/ را به فایل .gitignore اضافه کنید.
5. وابستگیها را نصب کنید
pip install -r requirements.txt
اگر فایل requirements.txt ندارید، بستهها را مستقیم نصب کنید و بعد فهرستشان را بسازید:
pip install requests
pip freeze > requirements.txt
6. یک بار دستی اجرا کنید
قبل از ساختن سرویس، مطمئن شوید برنامه با مفسر محیط مجازی و بدون فعالسازی اجرا میشود — همان کاری که systemd قرار است انجام دهد:
~/myapp/.venv/bin/python ~/myapp/main.py
اگر اینجا خطا میدهد، در سرویس هم خطا خواهد داد؛ اینجا رفع کردنش سادهتر است. با Ctrl + C متوقفش کنید.
7. آن را به یک سرویس سیستمی تبدیل کنید
این چیزی است که اسکریپت را از یک برنامه در حال اجرا به یک سرویس تبدیل میکند: بعد از راهاندازی مجدد سرور خودش بالا میآید و اگر خطا داد، دوباره اجرا میشود.
اگر برنامه کلید یا رمزی لازم دارد، اول آن را در یک فایل جدا بگذارید که فقط صاحبش بتواند بخواندش:
nano ~/myapp/.env
chmod 600 ~/myapp/.env
محتوای فایل، هر متغیر در یک خط، مثلاً API_KEY=.... بعد فایل /etc/systemd/system/myapp.service را بسازید:
[Unit]
Description=My Python service
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=deploy
WorkingDirectory=/home/deploy/myapp
EnvironmentFile=/home/deploy/myapp/.env
Environment=PYTHONUNBUFFERED=1
ExecStart=/home/deploy/myapp/.venv/bin/python /home/deploy/myapp/main.py
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
چند نکته که معمولاً اشتباه نوشته میشوند:
- مسیرها باید کامل باشند. systemd متغیرهای محیط پوسته شما را ندارد و
~را هم در ExecStart باز نمیکند. - مفسر پایتون را از داخل محیط مجازی صدا بزنید — همان
.venv/bin/python. با این کار نیازی به فعال کردن محیط نیست. - نام کاربر را بنویسید. بدون
User، سرویس با دسترسی root اجرا میشود. - فایل EnvironmentFile باید وجود داشته باشد. اگر نباشد، سرویس اجرا نمیشود. اگر برنامهتان راز یا تنظیمی ندارد، آن خط را حذف کنید، یا با گذاشتن
-در ابتدای مسیر (EnvironmentFile=-/home/deploy/myapp/.env) نبودن فایل را مجاز کنید. PYTHONUNBUFFERED=1را فراموش نکنید. وقتی خروجی پایتون به ترمینال نمیرود، چاپها بافر میشوند و لاگ با تأخیر یا بعد از کرش ناقص میرسد. طبق مستندات پایتون، این متغیر همان اثر گزینه-uرا دارد.Restart=alwaysیاon-failure؟ طبق مستندات systemd،on-failureفقط وقتی دوباره اجرا میکند که برنامه با خطا خارج شود؛alwaysحتی خروج عادی را هم دوباره اجرا میکند. برای ربات یا سرویسی که نباید هیچوقت متوقف شود،alwaysمناسبتر است. توقفی که خودتان باsystemctl stopانجام دهید، در هر دو حالت دوباره اجرا نمیشود.
حالا سرویس را فعال و اجرا کنید. --now یعنی هم برای راهاندازی بعدی فعال شود و هم همین حالا اجرا شود:
sudo systemctl daemon-reload
sudo systemctl enable --now myapp
و وضعیتش را ببینید:
systemctl status myapp
هر بار فایل .service را ویرایش کردید، دوباره daemon-reload و بعد restart لازم است؛ وگرنه systemd هنوز نسخه قبلی را اجرا میکند.
8. لاگها
هر چیزی که برنامه چاپ کند در ژورنال سیستم ثبت میشود:
journalctl -u myapp -f
گزینه -f یعنی همانطور که خطوط جدید میآیند نمایش بده. برای دیدن 50 خط آخر، یا هر چه در یک ساعت اخیر ثبت شده:
journalctl -u myapp -n 50 --no-pager
journalctl -u myapp --since "1 hour ago"
به فیلتر -p err برای پیدا کردن خطاهای پایتون تکیه نکنید: systemd خروجی برنامه را، چه stdout و چه stderr، بهطور پیشفرض با سطح info ثبت میکند، پس traceback پایتون با این فیلتر دیده نمیشود.
9. بعد از هر بهروزرسانی کد
cd ~/myapp
git pull
.venv/bin/pip install -r requirements.txt
sudo systemctl restart myapp
اگر سرویس بالا نیامد
| نشانه | علت محتمل |
|---|---|
status=203/EXEC در خروجی status | مسیر ExecStart اشتباه است یا فایل مفسر وجود ندارد؛ مسیر .venv/bin/python را با ls بررسی کنید |
status=200/CHDIR | پوشه WorkingDirectory وجود ندارد یا کاربر به آن دسترسی ندارد |
ModuleNotFoundError در لاگ | کتابخانه در محیط مجازی نصب نشده، یا ExecStart به جای پایتون محیط مجازی به پایتون سیستم اشاره میکند |
Permission denied در لاگ | فایلها متعلق به rootاند؛ با sudo chown -R deploy:deploy /home/deploy/myapp صاحبشان را درست کنید |
| لاگ دیر یا ناقص میرسد | خروجی بافر میشود؛ PYTHONUNBUFFERED=1 را اضافه کنید |
| سرویس مدام بالا و پایین میرود | برنامه در شروع کرش میکند و Restart دوباره اجرایش میکند؛ علت را در journalctl -u myapp -n 50 ببینید |
چند نکته عملی
- حافظه را نگاه کنید. با
free -hببینید چقدر باقی مانده. سرویس پایتونی که حافظهاش تمام شود، معمولاً بدون پیام روشنی کشته میشود. - کلیدها و رمزها را در کد ننویسید. از فایل
EnvironmentFileبا دسترسی600استفاده کنید و آن را در مخزن git قرار ندهید. مستندات systemd یادآوری میکند که متغیرهای محیطی روش کاملاً امنی برای رازها نیستند؛ برای موارد حساستر، گزینهLoadCredential=در systemd ساخته شده است. - اگر برنامه وب است، آن را مستقیم روی پورت 80 اجرا نکنید. روی
127.0.0.1و یک پورت محلی اجرا کنید و یک وبسرور جلویش بگذارید.
مشخصات پلنهای در دسترس را در خرید سرور مجازی میبینید. اگر سرویس شما سبک است و مطمئن نیستید کدام اندازه کافی است، سرور مجازی با رم 1 یا 2 گیگابایت را ببینید، و اگر پروژهتان ربات تلگرام است، سرور مجازی برای ربات تلگرام نکات مخصوص آن را دارد.