Ubuntu سرور پر Python venv، pipx اور uv کا انتخاب
Ubuntu پر externally-managed-environment ایرر کیوں آتا ہے؟ جانیں کہ کب venv استعمال کریں، کب pipx اور کب uv، تاکہ آپ سسٹم Python کو توڑے بغیر ٹولز انسٹال کر سکیں۔
تازہ Ubuntu سرور پر pip install کیوں ناکام ہوتا ہے
سرور پر Python venv، pipx اور uv کے درمیان انتخاب کا انحصار صرف ایک سوال پر ہے: آپ کیا انسٹال کر رہے ہیں؟ ایپلیکیشن کی ڈیپینڈنسیز (dependencies) کو ایپلیکیشن کی اپنی ڈائریکٹری کے اندر موجود ورچوئل انوائرنمنٹ میں ہونا چاہیے۔ جن کمانڈ لائن ٹولز کو آپ نام سے چلانا چاہتے ہیں، انہیں pipx میں ہونا چاہیے۔ uv یہ دونوں کام کرتا ہے اور ایک lockfile کا اضافہ بھی کرتا ہے، جو اس وقت اہم ہو جاتا ہے جب کسی دوسری مشین پر وہی انوائرنمنٹ دوبارہ بنانا پڑے۔ ان میں سے کوئی بھی سسٹم Python میں انسٹال نہیں کرتا، کیونکہ موجودہ Ubuntu سرور اس کی سختی سے ممانعت کرتا ہے۔
Ubuntu 24.04 پر sudo pip install requests چلائیں تو pip کسی بھی فائل کو ڈاؤن لوڈ کرنے سے پہلے ہی رک جاتا ہے۔
error: externally-managed-environment
× This environment is externally managed
╰─> To install Python packages system-wide, try apt install
python3-xyz, where xyz is the package you are trying to
install.
If you wish to install a non-Debian-packaged Python package,
create a virtual environment using python3 -m venv path/to/venv.
Then use path/to/venv/bin/python and path/to/venv/bin/pip.
If you wish to install a non-Debian packaged Python application,
it may be easiest to use pipx install xyz, which will manage a
virtual environment for you.
note: If you believe this is a mistake, please contact your Python installation or OS distribution provider. You can override this behaviour by passing --break-system-packages.یہ PEP 668 (Python enhancement proposal 668، "externally managed environments") ہے جو اپنا کام کر رہا ہے۔ Debian اور Ubuntu انٹرپریٹر کے ساتھ /usr/lib/python3.12/EXTERNALLY-MANAGED پر ایک مارکر فائل رکھتے ہیں، اور pip کسی بھی ایسے انٹرپریٹر میں لکھنے سے انکار کر دیتا ہے جس میں یہ فائل موجود ہو۔
یہ اصول sys.path ترتیب کی وجہ سے موجود ہے۔ apt لائبریریوں کو /usr/lib/python3/dist-packages میں انسٹال کرتا ہے۔ جب pip کو root کے طور پر سسٹم انٹرپریٹر کے خلاف چلایا جاتا ہے، تو یہ /usr/local/lib/python3.12/dist-packages میں لکھتا ہے، اور Debian کی پیکیجنگ اس ڈائریکٹری کو سرچ پاتھ میں پہلے رکھتی ہے۔ اسے خود python3 -c 'import sys; print(sys.path)' کے ساتھ پرنٹ کریں اور ترتیب کو دیکھیں۔ اس طرح pip کی لکھی ہوئی کاپی، apt کی انسٹال کردہ کاپی پر حاوی ہو جاتی ہے، اور باکس پر موجود ہر وہ پروگرام جو /usr/bin/python3 کے تحت چلتا ہے، بشمول ڈسٹری بیوشن کے اپنے ٹولز، متاثر ہوتا ہے۔ cloud-init اسی انٹرپریٹر سے requests، jinja2 اور PyYAML امپورٹ کرتا ہے۔ اگر آپ pip کے ذریعے ان میں سے کسی کو اپ گریڈ کر کے کسی غیر مطابقت پذیر (incompatible) ریلیز پر چلے جائیں، تو کوئی ایسی چیز جو آپ نے کبھی استعمال نہیں کی، اگلی بوٹ پر فیل ہو جائے گی اور ٹریس بیک (traceback) میں ایک ایسے پیکیج کا نام آئے گا جس کے بارے میں آپ کو علم بھی نہیں تھا کہ وہ چین میں شامل ہے۔ apt اب بھی اپنی انسٹال کردہ ورژن کو ریکارڈ کرتا ہے، لہذا کوئی انتباہ نہیں ملتا، اور مرمت کا واحد طریقہ sudo apt reinstall python3-requests ہے۔
اس کے بعد کا اصول مختصر ہے۔ سسٹم Python ڈسٹری بیوشن کی ملکیت ہے۔ اس میں کچھ انسٹال نہ کریں، pip کے ذریعے اس کی لائبریریوں کو اپ گریڈ نہ کریں، اور پیغام کو ختم کرنے کے لیے EXTERNALLY-MANAGED فائل کو ڈیلیٹ نہ کریں۔ /usr/bin/python3 کو صرف ورچوئل انوائرنمنٹس بنانے کا کام سونپیں۔
venv بمقابلہ pipx بمقابلہ uv: فیصلہ کرنے کا اصول
اس بات کا انتخاب اس بنیاد پر کریں کہ آپ کیا انسٹال کر رہے ہیں، نہ کہ اس بنیاد پر کہ آپ نے حال ہی میں کس ٹول کے بارے میں پڑھا ہے۔
- کوئی ایسی ایپلیکیشن جسے آپ deploy کرتے ہیں اور بطور سروس چلاتے ہیں، جیسے کہ Django یا Flask پروجیکٹ: اس ایپلیکیشن کی ڈائریکٹری کے اندر ایک virtual environment (venv) استعمال کریں۔
- کوئی ایسا کمانڈ لائن ٹول جو آپ اپنے
PATHپر چاہتے ہیں، جیسے کہansibleیاhttpie: اس کے لیے pipx استعمال کریں، جو ہر ٹول کو ایک نجی ماحول اورPATHپر ایک لنک فراہم کرتا ہے۔ - کوئی ایسا پروجیکٹ جسے lockfile، تیز تر تنصیبات، یا Python کے ایسے ورژن کی ضرورت ہو جو distribution میں موجود نہ ہو: اس کے لیے uv استعمال کریں، جو ایک عام venv کے ساتھ ایک
uv.lockفائل تیار کرتا ہے۔ - کوئی ایسی لائبریری جس کی ضرورت کسی distribution ٹول کو ہو، نہ کہ آپ کے کوڈ کو: اس کے لیے
sudo apt install python3-<name>استعمال کریں، جو سسٹم انٹرپریٹر میں کچھ بھی شامل کرنے کا واحد معاون طریقہ ہے۔
pipx اور uv tool install ایک ہی کام کرتے ہیں، لہذا جس مشین پر پہلے سے uv موجود ہو اسے pipx کی ضرورت نہیں ہے۔ آپ نے کون سا ویب فریم ورک منتخب کیا ہے اس سے یہاں کوئی فرق نہیں پڑتا: VPS پر Django اور Flask اس بات میں مختلف ہیں کہ requirements.txt میں کیا جاتا ہے، نہ کہ اس میں کہ ان کے گرد ماحول کیسے تعمیر کیا جاتا ہے۔ نیچے دی گئی ہر چیز Ubuntu 24.04 اور اس کی Python 3.12 کا استعمال کرتی ہے، لہذا اگر آپ کا ورژن مختلف ہو تو paths کے اندر ورژن کو ایڈجسٹ کریں۔
ہر ایپلیکیشن کے لیے venv بنائیں
Ubuntu، venv ماڈیول کو بنیادی Python پیکیج سے الگ رکھتا ہے، اس لیے ایک minimal image پر پہلی کوشش اس پیغام کے ساتھ ناکام ہو جاتی ہے جو واضح طور پر بتاتا ہے کہ کیا چیز غائب ہے۔
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.
apt install python3.12-venvاسے انسٹال کریں، پھر اس صارف کے طور پر ماحول (environment) بنائیں جو کوڈ کا مالک ہوگا۔
sudo apt update
sudo apt install -y python3-venv
sudo install -d -o deploy -g deploy -m 755 /srv/myapp
sudo -u deploy python3 -m venv /srv/myapp/.venv
sudo -u deploy /srv/myapp/.venv/bin/pip install -r /srv/myapp/requirements.txtغور کریں کہ وہاں کیا موجود نہیں ہے: نہ source، نہ activate۔ /srv/myapp/.venv/bin/pip اس ماحول میں انسٹال ہوتا ہے کیونکہ بائنری وہاں موجود ہے، نہ کہ اس لیے کہ آپ نے شیل میں کچھ ایکسپورٹ کیا ہے۔ مزید آگے بڑھنے سے پہلے اس کی تصدیق کریں۔
/srv/myapp/.venv/bin/python -c 'import sys; print(sys.prefix)'یہ /srv/myapp/.venv پرنٹ کرتا ہے۔ اگر یہ /usr پرنٹ کرے، تو آپ سسٹم انٹرپریٹر چلا رہے ہیں اور آپ کے پیکیجز ایسی جگہ چلے گئے ہیں جہاں آپ نہیں بھیجنا چاہتے تھے۔
venv کی دو خصوصیات یہ طے کرتی ہیں کہ آپ بعد میں اس کے ساتھ کیا کر سکتے ہیں۔ ایک venv کو منتقل (relocate) نہیں کیا جا سکتا، کیونکہ bin/ میں موجود ہر اسکرپٹ میں ایک مطلق shebang لائن ہوتی ہے: head -1 /srv/myapp/.venv/bin/pip، #!/srv/myapp/.venv/bin/python کو پڑھتا ہے۔ پیرنٹ ڈائریکٹری کا نام تبدیل کریں تو وہ اسکرپٹس bad interpreter: No such file or directory کے ساتھ ناکام ہو جاتی ہیں۔ ایک venv اس انٹرپریٹر کو بھی پن (pin) کر دیتا ہے جس نے اسے بنایا ہے، جسے /srv/myapp/.venv/pyvenv.cfg میں home لائن کے طور پر ریکارڈ کیا جاتا ہے، اور bin/python3 اس بائنری کا ایک سمبولک لنک (symlink) ہوتا ہے۔ اگر آپ ریلیز کو اپ گریڈ کریں جس سے python3.12 ختم ہو جائے، تو سمبولک لنک کا کوئی ہدف نہیں رہے گا، اور سروس شروع ہوتے ہی No such file or directory کے ساتھ بند ہو جائے گی۔ دونوں صورتوں کا حل ایک ہی ہے: venv کو حذف کریں اور requirements.txt سے نیا بنائیں۔ دوبارہ تعمیر کرنے میں صرف سیکنڈ لگتے ہیں۔ کبھی بھی ایک مشین سے دوسری مشین پر venv کاپی نہ کریں۔
venv کہاں واقع ہو اور اس کا مالک کون ہو
اسے /srv/myapp/.venv پر کوڈ کے ساتھ رکھیں، اور ہر ایپلیکیشن کے لیے ایک الگ venv استعمال کریں۔ اس طرح deployment ایک ہی ڈائریکٹری پر مشتمل ہوتی ہے، systemd یونٹ کو ایک ایسا پاتھ مل جاتا ہے جو کبھی تبدیل نہیں ہوتا، اور دو ایپلیکیشنز ایک مشترکہ dependency اپ گریڈ کے ذریعے کبھی ایک دوسرے کو متاثر نہیں کر سکتیں۔ venv کو ایسی جگہ نہ رکھیں جہاں آپ کا ویب سرور براہ راست فائلیں پبلش کرتا ہو، کیونکہ اس میں آپ کی dependencies اور اکثر آپ کی کنفیگریشن موجود ہوتی ہے۔
ملکیت پر آدھا منٹ غور کرنا ضروری ہے۔ کوڈ اور انوائرمنٹ کا مالک ایک deploy صارف کو بنائیں، اور سروس اکاؤنٹ کو صرف پڑھنے (read) اور چلانے (execute) کی رسائی دیں۔
sudo adduser --system --group --no-create-home myapp
sudo chown -R deploy:myapp /srv/myapp
sudo chmod -R o-rwx /srv/myappاب سروس اپنی dependencies کو امپورٹ تو کر سکتی ہے لیکن انہیں دوبارہ لکھ (rewrite) نہیں سکتی، جس کا مطلب ہے کہ ویب ایپلیکیشن میں موجود کوڈ ایگزیکیوشن کا کوئی بگ خاموشی سے ڈسک پر موجود لائبریری کو تبدیل نہیں کر سکتا اور ری اسٹارٹ کے بعد بھی برقرار نہیں رہ سکتا۔ باقی مشین پر لاگو ہونے والی یہی منطق کم سے کم استحقاق والے صارفین کے طور پر سروسز چلانا میں بیان کی گئی ہے۔
کمانڈ لائن ٹولز کے لیے pipx
pipx لائبریریوں کے بجائے ایپلی کیشنز انسٹال کرتا ہے۔ ہر ٹول کو ~/.local/share/pipx/venvs/<name> کے تحت اپنا الگ ماحول ملتا ہے، اور اس ٹول کی ایگزیکیوٹیبلز کو ~/.local/bin میں لنک کیا جاتا ہے، تاکہ ایک ہی لائبریری کے مختلف ورژنز کی ضرورت رکھنے والے دو ٹولز آپس میں نہ ٹکرائیں۔
sudo apt update
sudo apt install -y pipx
pipx ensurepath
pipx install httpiepipx ensurepath آپ کی شیل اسٹارٹ اپ فائل کو ایڈٹ کر کے PATH میں ~/.local/bin کا اضافہ کرتا ہے۔ یہ اس شیل کو تبدیل نہیں کر سکتا جس میں آپ فی الحال موجود ہیں، لہذا انسٹالیشن کے فوراً بعد http: command not found کا مطلب عام طور پر یہ ہوتا ہے کہ آپ نے ابھی تک لاگ آؤٹ کر کے دوبارہ لاگ ان نہیں کیا ہے۔ Ubuntu کا ڈیفالٹ ~/.profile تب ہی ~/.local/bin کا اضافہ کرتا ہے جب لاگ ان کے وقت وہ ڈائریکٹری پہلے سے موجود ہو، اسی لیے یہ مسئلہ نئے اکاؤنٹ پر ایک بار پیش آتا ہے اور پھر کبھی نہیں۔
اگر آپ pipx کو کسی لائبریری کی طرف اشارہ کریں تو یہ انکار کر دیتا ہے، اور ایک پیغام دیتا ہے جو اس طرح شروع ہوتا ہے:
No apps associated with package requests or its dependencies.یہ ٹول آپ کو بتا رہا ہے کہ یہ غلط آلہ ہے۔ لائبریریاں کسی ایپلی کیشن کے venv میں ہونی چاہئیں۔
سرور پر جو تفصیل اہمیت رکھتی ہے وہ لوکیشن ہے۔ ایک سادہ pipx install سب کچھ ایک صارف کی ہوم ڈائریکٹری کے تحت رکھتا ہے۔ myapp کے طور پر چلنے والی systemd یونٹ اسے نہیں دیکھ سکتی، ایک root cron job اسے نہیں دیکھ سکتی، اور sudo بھی اسے نہیں ڈھونڈ پائے گا، کیونکہ /etc/sudoers میں secure_path، PATH کو ایک فکسڈ لسٹ سے بدل دیتا ہے۔ ایسے ٹول کے لیے جو پوری مشین پر دستیاب ہونا چاہیے، اسے گلوبلی انسٹال کریں۔
sudo pipx install --global ansible
sudo pipx ensurepath --global--global فلیگ ماحول کو /opt/pipx میں رکھتا ہے اور ایگزیکیوٹیبلز کو /usr/local/bin میں لنک کرتا ہے، جو کہ ڈیفالٹ PATH پر اور secure_path کے اندر موجود ہے۔ پہلے pipx --version کے ساتھ اپنا ورژن چیک کریں، کیونکہ Ubuntu 24.04 میں pipx 1.4.3 پیکج ہوتا ہے، جو --global سے پرانا ہے، اور پرانا pipx unrecognized arguments: --global کے ساتھ جواب دیتا ہے۔ اس ورژن پر، دونوں دستاویزی ڈائریکٹریز کو خود سیٹ کریں:
sudo env PIPX_HOME=/opt/pipx PIPX_BIN_DIR=/usr/local/bin pipx install ansible
command -v ansiblecommand -v ansible کو /usr/local/bin/ansible پرنٹ کرنا چاہیے۔ اگر یہ /home کے تحت کوئی پاتھ پرنٹ کرتا ہے، تو ٹول ایک ہی صارف کے اکاؤنٹ میں چلا گیا ہے اور کوئی سروس اسے تلاش نہیں کر سکے گی۔
جب آپ کو lockfile درکار ہو تو uv کا استعمال
uv، Astral کی طرف سے ایک واحد binary ہے جو pip، venv اور pip-tools کے تمام کام انجام دیتی ہے، اور یہ Python interpreters کو بھی ڈاؤن لوڈ کر سکتی ہے۔ یہ اتنی تیز ہے کہ اس کا فرق ایک چھوٹے VPS پر بھی واضح ہوتا ہے، اور یہ ایک حقیقی lockfile تیار کرتی ہے۔
سرکاری انسٹالر uv اور uvx کو ~/.local/bin میں رکھتا ہے:
curl -LsSf https://astral.sh/uv/install.sh | sh
uv --versionسرور پر کسی اسکرپٹ کو براہ راست shell میں پائپ کرنے سے پہلے ایک لمحے کے لیے احتیاط برتیں۔ URL میں ورژن کو پن (pin) کریں اور چلانے سے پہلے فائل کو پڑھ لیں:
curl -LsSf https://astral.sh/uv/0.12.3/install.sh -o uv-install.sh
less uv-install.sh
sh uv-install.shاگر pipx پہلے سے موجود ہو تو pipx install uv بھی کام کرتا ہے۔ uv ایک خود مختار binary ہے جس کی اپنی کوئی Python dependency نہیں ہے، لہذا اسے /usr/local/bin میں کاپی کرنا اسے باکس پر موجود ہر صارف کے ساتھ شیئر کرنے کا ایک درست طریقہ ہے۔
pyproject.toml والے پروجیکٹ کے لیے، ورک فلو چار کمانڈز پر مشتمل ہے، اور صرف آخری کمانڈ سرور پر چلتی ہے۔
uv init myapp
uv add flask gunicorn
uv lock
uv sync --frozen --no-devuv lock، uv.lock لکھتی ہے، جو کہ ایک کراس پلیٹ فارم lockfile ہے جس میں درست resolved ورژنز موجود ہوتے ہیں، اور آپ اسے اپنے کوڈ کے ساتھ commit کرتے ہیں۔ uv sync پروجیکٹ روٹ میں .venv بناتی ہے تاکہ وہ اس سے مطابقت رکھے۔ سرور پر، --frozen وہ اہم فلیگ ہے: دستاویزات کے مطابق یہ lockfile میں موجود ورژنز کو سچائی کا معیار مانتی ہے بجائے اس کے کہ یہ چیک کرے کہ آیا lockfile اپ ٹو ڈیٹ ہے یا نہیں، جو کہ ڈیپلائمنٹ کے لیے مطلوبہ رویہ ہے۔ --no-dev ڈیولپمنٹ ڈیپینڈنسی گروپ کو خارج کر دیتا ہے۔
موجودہ requirements.txt پروجیکٹ کو کسی تبدیلی کی ضرورت نہیں ہے، کیونکہ uv، pip کی زبان سمجھتی ہے:
uv venv /srv/myapp/.venv
uv pip install --python /srv/myapp/.venv/bin/python -r /srv/myapp/requirements.txtاس کے نتیجے میں ایک عام virtual environment بنتا ہے۔ .venv/bin/python بالکل اسی طرح کام کرتا ہے جیسے python3 -m venv نے اسے بنایا ہو، لہذا اس گائیڈ میں بعد میں کچھ بھی تبدیل نہیں ہوتا۔
سرور پر uv استعمال کرنے سے پہلے اس کی ایک ڈیفالٹ سیٹنگ جاننا ضروری ہے۔ اس کی python-preference سیٹنگ ڈیفالٹ طور پر managed ہوتی ہے، جس کا مطلب ہے کہ یہ سسٹم پر پہلے سے موجود interpreters کے بجائے "ان کو منتخب کرتی ہے جو uv کے ذریعے ڈاؤن لوڈ اور انسٹال کیے جاتے ہیں"۔ لہذا، ایسے باکس پر جہاں صرف 3.12 موجود ہو، uv venv --python 3.13 خاموشی سے 3.13 کو ~/.local/share/uv/python میں ڈاؤن لوڈ کر لیتی ہے بجائے اس کے کہ ناکام ہو جائے۔ یہ لیپ ٹاپ پر تو آسان ہے لیکن سرور پر حیران کن ہو سکتا ہے، کیونکہ آپ کی سروس اب ایک ایسے interpreter پر منحصر ہے جو home ڈائریکٹری میں رہتا ہے اور جسے apt upgrade کبھی بھی پیچ (patch) نہیں کرے گا۔ اگر آپ ڈسٹری بیوشن کا interpreter استعمال کرنا چاہتے ہیں تو uv.toml میں python-preference کو only-system پر سیٹ کریں۔ اگر آپ environment کو پروجیکٹ روٹ کے علاوہ کہیں اور رکھنا چاہتے ہیں، تو UV_PROJECT_ENVIRONMENT پروجیکٹ virtual environment کے لیے استعمال ہونے والی ڈائریکٹری کی وضاحت کرتا ہے۔
systemd کو venv انٹرپریٹر پر پوائنٹ کریں، activate پر نہیں
یہ وہ مقام ہے جہاں زیادہ تر Python ڈیپلائمنٹس ناکام ہوتی ہیں، اور اس کی وجہ activate کے کام کو غلط سمجھنا ہے۔
bin/activate ایک شیل اسکرپٹ ہے۔ یہ venv کی bin ڈائریکٹری کو PATH میں شامل کرتا ہے، VIRTUAL_ENV کو سیٹ کرتا ہے، پرانی ویلیوز کو محفوظ کرتا ہے تاکہ deactivate انہیں بحال کر سکے، اور آپ کے پرامپٹ کو تبدیل کرتا ہے۔ اس میں ایسی کوئی چیز نہیں جسے انٹرپریٹر خود پڑھتا ہو۔ ایکٹیویشن صرف ایک انسان کے لیے سہولت ہے جو پرامپٹ پر python ٹائپ کرتا ہے۔
اصل میں ماحول کا انتخاب اس بات سے ہوتا ہے کہ آپ کون سی انٹرپریٹر فائل چلاتے ہیں۔ جب /srv/myapp/.venv/bin/python شروع ہوتا ہے، تو Python کا site ماڈیول ایگزیکیوٹیبل والی ڈائریکٹری اور اس سے ایک درجے اوپر pyvenv.cfg فائل تلاش کرتا ہے۔ /srv/myapp/.venv/pyvenv.cfg مل جانے پر sys.prefix کو venv پر سیٹ کر دیا جاتا ہے، جو اس venv کی site-packages کو sys.path پر لے آتا ہے۔ یہی مکمل میکانزم ہے۔ اسے کسی انوائرمنٹ ویری ایبل یا شیل کی ضرورت نہیں ہوتی۔
اس لیے یہ یونٹ کبھی شروع نہیں ہوتا:
[Service]
ExecStart=source /srv/myapp/.venv/bin/activate && gunicorn app:appmyapp.service: Failed to locate executable source: No such file or directory
myapp.service: Failed at step EXEC spawning source: No such file or directory
myapp.service: Main process exited, code=exited, status=203/EXECExecStart کوئی شیل کمانڈ لائن نہیں ہے۔ systemd براہ راست ایک پروگرام چلاتا ہے، اس لیے وہاں کوئی source بلٹ ان نہیں ہوتا، && کو ایک لٹریل آرگومنٹ کے طور پر بھیجا جاتا ہے، اور کچھ بھی ایکسپینڈ نہیں ہوتا۔
اور یہ یونٹ شروع ہوتا ہے، پھر ختم ہو جاتا ہے:
[Service]
ExecStart=/usr/bin/python3 /srv/myapp/app.pyModuleNotFoundError: No module named 'flask'/usr/bin/python3 سسٹم انٹرپریٹر ہے، اور اس کے sys.path میں کبھی آپ کا venv شامل نہیں رہا۔ وہی کمانڈ آپ کے SSH سیشن میں صرف اس لیے کام کرتی ہے کیونکہ آپ نے وہاں venv کو ایکٹیویٹ کیا تھا، جس کی وجہ سے شیل نے python3 کو PATH کے ذریعے .venv/bin/python3 میں ریزولو کر لیا تھا۔
کمانڈ کو /bin/bash -c 'source ... && gunicorn ...' میں لپیٹنا کام کرتا ہے۔ یہ systemd اور آپ کے پروسیس کے درمیان بلاوجہ ایک شیل بھی شامل کر دیتا ہے، جبکہ ایک مکمل پاتھ (absolute path) اسے حل کر دیتا ہے:
[Unit]
Description=myapp web service
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=myapp
Group=myapp
WorkingDirectory=/srv/myapp
Environment=PYTHONUNBUFFERED=1
Environment=PATH=/srv/myapp/.venv/bin:/usr/local/bin:/usr/bin:/bin
ExecStart=/srv/myapp/.venv/bin/gunicorn --workers 3 --bind 127.0.0.1:8000 app:app
Restart=on-failure
RestartSec=5
NoNewPrivileges=yes
PrivateTmp=yes
ProtectSystem=full
[Install]
WantedBy=multi-user.targetsudo systemctl daemon-reload
sudo systemctl enable --now myapp
systemctl status myapp
journalctl -u myapp -n 50 --no-pagersystemctl status myapp کو active (running) رپورٹ کرنا چاہیے جس میں Main PID آپ کا gunicorn پروسیس ہو۔ اگر کچھ اور ہو تو جرنل پڑھیں۔
Environment=PATH= لائن ExecStart کے لیے نہیں ہے، جس کے پاس پہلے ہی مکمل پاتھ موجود ہے۔ یہ ان پروسیسز کے لیے ہے جو آپ کی ایپلیکیشن شروع کرتی ہے۔ ایک سروس systemd سے ایک مختصر ڈیفالٹ PATH وراثت میں لیتی ہے، اس لیے Python کوڈ جو subprocess.run(["ffmpeg", ...]) کو کال کرتا ہے، یا کوئی مینجمنٹ کمانڈ جو venv سے کنسول اسکرپٹ کو شیل آؤٹ کرتی ہے، اسے وہ نہیں ملے گا جس کی ضرورت ہے۔ venv کی bin ڈائریکٹری کو پہلے رکھنا activate کا وہ واحد حصہ ہے جسے سروس واقعی استعمال کرتی ہے۔ تصدیق کریں کہ یونٹ کو اصل میں کیا ملا، اس کے لیے systemctl show -p Environment myapp استعمال کریں۔
یہی اصول شیڈولڈ کاموں پر بھی لاگو ہوتا ہے۔ cron جابز کو PATH کے /usr/bin:/bin کے ساتھ چلاتا ہے، اس لیے crontab کی لائن جو python3 /srv/myapp/cleanup.py پڑھتی ہے، وہ سسٹم انٹرپریٹر کو چلاتی ہے اور صبح تین بجے ModuleNotFoundError کے ساتھ ناکام ہو جاتی ہے، اور ایرر ایک لوکل میل اسپول میں چلا جاتا ہے جسے کوئی نہیں پڑھ رہا ہوتا۔ وہاں بھی مکمل venv پاتھ لکھیں۔ اس آؤٹ پٹ کو جرنل میں حاصل کرنے اور آخری رن کا ریکارڈ رکھنے کے لیے، ایک systemd سروس اور ٹائمر کا جوڑا وہی ExecStart لائن استعمال کرتا ہے۔
کیا Docker اس فیصلے کی جگہ لے لیتا ہے؟
کنٹینر کا اپنا فائل سسٹم ہوتا ہے، اس لیے یہ سوال ختم ہونے کے بجائے اپنی شکل بدل لیتا ہے۔ python:3.12-slim جیسی آفیشل امیج میں، Python کو /usr/local میں بلٹ ان کیا جاتا ہے اور اس میں کوئی EXTERNALLY-MANAGED مارکر نہیں ہوتا، لہذا pip install بطور root پیکجز شامل کرنے کا درست طریقہ ہے اور venv یہاں بہت کم فائدہ دیتا ہے۔ اس کے برعکس اگر آپ FROM ubuntu:24.04 بلڈ کرتے ہیں تو آپ کو امیج کے اندر دوبارہ externally-managed-environment کا سامنا کرنا پڑے گا، وہی وجہ ہے جو ہوسٹ پر ہوتی ہے: یہ ڈسٹری بیوشن کا انٹرپریٹر ہے جو اپنے ساتھ ڈسٹری بیوشن کی مارکر فائل رکھتا ہے۔
بہت سی امیجز اب بھی venv استعمال کرتی ہیں، کیونکہ یہ ملٹی اسٹیج بلڈ کو آسان بناتا ہے۔ بلڈر اسٹیج /opt/venv میں انسٹالیشن کرتا ہے، اور رن ٹائم اسٹیج صرف اس ایک ڈائریکٹری کو کاپی کر کے کمپائلرز کو پیچھے چھوڑ دیتا ہے۔ ایکٹیویشن کا مسئلہ اس کے ساتھ ہی رہتا ہے۔ ایک RUN source /opt/venv/bin/activate لائن صرف اس بلڈ لیئر کی شیل کو متاثر کرتی ہے، لہذا رن ٹائم پر کنٹینر سسٹم انٹرپریٹر پر شروع ہوتا ہے اور ModuleNotFoundError ایرر دیتا ہے۔ ENV PATH="/opt/venv/bin:$PATH" سیٹ کریں، یا CMD کو مکمل پاتھ /opt/venv/bin/gunicorn دیں۔ یہ وہی بگ ہے جو systemd میں ہوتا ہے، بس فائل مختلف ہے۔
لہذا، کنٹینر انٹرپریٹر کے سوال کو تبدیل کر دیتا ہے، کیونکہ امیج انٹرپریٹر اور اس کے نیچے موجود ہر چیز کو پن (pin) کر دیتی ہے۔ یہ پننگ کے سوال کو ختم نہیں کرتا۔ ایک غیر پن شدہ requirements.txt سے بنی امیج اگلے مہینے مختلف ورژنز ریزولو کر سکتی ہے، جس کا مطلب ہے کہ امیج ٹیگ تو ری پروڈیوس ایبل ہے لیکن وہ بلڈ نہیں جس نے اسے بنایا تھا۔ uv.lock جیسی لاک فائل، یا مکمل طور پر پن شدہ ریکوائرمنٹس فائل، اس خلا کو پر کرتی ہے، چاہے کنٹینر ہو یا نہ ہو۔ اور جب ایک ایپلیکیشن systemd کے تحت ایک VPS پر چلتی ہے، تو کنٹینر زیادہ تر اس فیصلے کو Dockerfile میں منتقل کر دیتا ہے، کیونکہ systemd پہلے ہی فیل ہونے والے پروسیس کو ری اسٹارٹ کرتا ہے اور اس کی آؤٹ پٹ کو جرنل میں محفوظ کر لیتا ہے۔ VPS پر Docker چلانا تب فائدہ مند ہوتا ہے جب آپ چاہتے ہیں کہ بلٹ امیج ہی وہ چیز ہو جسے آپ ڈیپلائے کریں۔
FAQ
کیا میں --break-system-packages کے ساتھ pip install استعمال کر سکتا ہوں؟
ایسے سرور پر ہرگز نہیں جسے آپ نے مسلسل چلانا ہے۔ یہ فلیگ وہی کام کرتا ہے جو اس کا نام بتاتا ہے: یہ حفاظتی رکاوٹ کو ہٹا دیتا ہے، اور pip براہ راست /usr/local/lib/python3.12/dist-packages میں لکھتا ہے، جو sys.path پر apt ڈائریکٹری سے پہلے آتا ہے۔ اس کے بعد آپ کا ورژن /usr/bin/python3 کے تحت چلنے والی ہر سسٹم اسکرپٹ کے لیے ڈسٹری بیوشن کے ورژن کو چھپا دیتا ہے، اور apt بدستور اپنے ورژن کو انسٹال شدہ سمجھتا ہے، لہذا جب تک کوئی چیز خراب نہیں ہوتی، تنازعہ کا پتہ نہیں چلتا۔ کنٹینر امیج کے اندر جسے آپ ہر بار شروع سے دوبارہ بناتے ہیں، نقصان صرف اسی امیج تک محدود رہتا ہے، اس لیے وہاں یہ قابلِ دفاع ہے۔ ایسی مشین پر جسے آپ خود برقرار رکھتے ہیں، venv بنائیں۔ یہ صرف ایک کمانڈ کا کام ہے۔
سرور پر ورچوئل انوائرنمنٹ کہاں ہونا چاہیے؟
ایپلیکیشن کی اپنی ڈائریکٹری کے اندر، /srv/myapp/.venv کے طور پر، جس کا مالک deploy یوزر ہو، اور سروس اکاؤنٹ کے پاس صرف پڑھنے اور چلانے (read and execute) کی رسائی ہو۔ ہر ایپلیکیشن کے لیے ایک الگ venv رکھیں، کیونکہ مشترکہ venv کا مطلب ہے کہ ایک ایپلیکیشن کی اپ گریڈ دوسری کو خراب کر سکتی ہے۔ venv بنانے کے بعد اسے منتقل یا کاپی نہ کریں: اس کی bin/ ڈائریکٹری میں موجود ہر اسکرپٹ کے shebang لائن میں وہ مکمل پاتھ لکھا ہوتا ہے، اس لیے منتقل شدہ venv bad interpreter: No such file or directory کے ساتھ ناکام ہو جاتا ہے۔ اس کے بجائے اسے ڈیلیٹ کریں اور requirements.txt سے دوبارہ بنائیں۔
میری systemd سروس ModuleNotFoundError کے ساتھ کیوں ناکام ہو جاتی ہے؟
کیونکہ یونٹ ایسا انٹرپریٹر چلا رہا ہے جو venv کا نہیں ہے۔ systemctl cat myapp چلائیں اور ExecStart پڑھیں۔ اسے مکمل پاتھ کے ساتھ /srv/myapp/.venv/bin/python، یا اسی bin/ ڈائریکٹری سے کسی کنسول اسکرپٹ کا نام لینا چاہیے۔ یونٹ فائل میں activate کو سورس کرنا کام نہیں کر سکتا، کیونکہ ExecStart شیل نہیں ہے، اور systemd status=203/EXEC کے ساتھ Failed to locate executable source رپورٹ کرتا ہے۔ Environment=PATH=/srv/myapp/.venv/bin:/usr/local/bin:/usr/bin:/bin شامل کریں تاکہ آپ کا کوڈ جو بھی سب پروسیس شروع کرے اسے بھی venv کے ٹولز مل جائیں۔
کیا مجھے venv اور pip کی جگہ uv استعمال کرنا چاہیے؟
uv تب استعمال کریں جب آپ کو lockfile درکار ہو، جب انسٹالیشن کا وقت آپ کے لیے پریشان کن حد تک سست ہو، یا جب آپ کو Python کا ایسا ورژن درکار ہو جو آپ کی ڈسٹری بیوشن فراہم نہیں کرتی۔ یہ ایک عام venv بناتا ہے، لہذا systemd یونٹ اور فائل لے آؤٹ تبدیل نہیں ہوتے، اور uv sync --frozen بالکل وہی انسٹال کرتا ہے جو lockfile میں درج ہوتا ہے۔ اگر کوئی ایک ایپلیکیشن git سے pinned requirements.txt کے ساتھ ڈیپلائے ہوتی ہے اور انسٹالیشن سیکنڈوں میں مکمل ہو جاتی ہے، تو python3 -m venv کافی ہے، اور یہ سرور پر اپ ڈیٹ رکھنے کے لیے ایک کم بائنری ہے۔