تحریم npm نامی است که توسعه‌دهنده‌های ایرانی روی تقریباً هر خطای نصب پکیج می‌گذارند. پیغام E403 در npm install یکی از آن‌هاست و Read timed out در pip یکی دیگر. راه‌حل پایدار در همه این ابزارها یکی است: نشانی مخزن را به یک میرور داخلی عوض کنید. برای روز قطعی هم یک کش محلی از پکیج‌ها روی سرور نگه دارید.

ابزارتنظیمی که عوض می‌شودفایل یا دستور اصلیپشتیبان آفلاین
ابزار npmکلید registryفایل .npmrc یا npm config setکش npm و Verdaccio
ابزار Yarnکلید npmRegistryServer در نسخه 2 به بعدفایل .yarnrc.ymlپوشه .yarn/cache
ابزار pipکلید index-urlفایل pip.conf یا pip configپوشه wheelhouse
ابزار Composerمخزن packagistفایل composer.json یا config -gکش Composer و پوشه vendor

💡 میرور پکیج یعنی نسخه همگام registry رسمی، داخل شبکه کشور

✅ قبل از عوض کردن هر تنظیمی، با یک دستور curl نوع خطا را مشخص کنید. خطای 403 و Timeout راه‌حل یکسانی ندارند.
✅ هر نشانی میرور را پیش از تنظیم سراسری، یک بار از روی همان سرور تست کنید.

خطای نصب پکیج معمولاً وسط کار سر می‌رسد: کد آماده است اما یک وابستگی کوچک دانلود نمی‌شود و pipeline پشتش می‌ماند. اگر پروژه را روی یک سرور مجازی ایرانی اجرا می‌کنید، به احتمال زیاد همین الگو را دیده‌اید. این راهنما به همان ترتیبی پیش می‌رود که مدیر سرور در عمل کار می‌کند. اول خطا را تشخیص می‌دهیم و بعد نشانی مخزن را در هر ابزار عوض می‌کنیم. آخر سر هم کشی می‌سازیم که نصب‌های تیم را به اینترنت بین‌الملل وابسته نکند.

ترمینال لینوکس روی مانیتور با خروجی نصب پکیج و نمودار مسیر دانلود از مخزن داخلی، تحریم npm

علت خطای نصب پکیج روی سرور ایران چیست؟

پشت این خطاها سه علت متفاوت است: درخواستی که سرور مقصد با 403 رد می‌کند، مسیر بین‌الملل کند یا قطع که به Timeout می‌رسد، و مشکل گواهی SSL. هر کدام دستور تست و راه‌حل خودش را دارد.

بخش بزرگی از چیزی که تحریم npm خوانده می‌شود، شواهدی از مسدودسازی سمت سرویس ندارد. شرکت GitHub (مالک npm) در ژانویه ۲۰۲۱ اعلام کرد برای ارائه خدمات GitHub به توسعه‌دهنده‌های ایران از دولت آمریکا مجوز گرفته است؛ آن اعلامیه نامی از npm نمی‌برد، پس تعمیمش به registry یک استنباط است. در گزارش ۲۰۱۹ کاربران ایرانی در مخزن pypi/warehouse هم خطای pypi.org به شکل Timeout در handshake TLS ثبت شد و در آن گفت‌وگو علت تحریمی تأیید نشد. برای registry رسمی npm، PyPI یا Packagist منبعی پیدا نکردیم که پاسخ 403 به IP ایران را ثبت کرده باشد؛ اگر خودتان چنین پاسخی دیدید، تست زیر آن را مشخص می‌کند.

تشخیص نوع خطا از روی پیغام

پیغام خطامعنای محتملاقدام درست
کد E403 یا عبارت 403 Forbiddenسرور مقصد درخواست را رد کرده است؛ به‌خاطر توکن، دسترسی پکیج یا سیاست سرویستست با curl بدون توکن، سپس تغییر registry
عبارت ETIMEDOUT یا Read timed outپاسخی در زمان مجاز نرسیده؛ معمولاً مسیر بین‌الملل کند یا قطع استتنظیم میرور داخلی
عبارت ECONNRESET یا handshake ناتماماتصال وسط راه قطع شده استتست curl روی میرور داخلی؛ با کد 200 آن را تنظیم کنید
عبارت CERTIFICATE_VERIFY_FAILED یا UNABLE_TO_GET_ISSUER_CERTگواهی میرور شناخته نمی‌شود یا ساعت سرور غلط استاصلاح ساعت و معرفی CA (بخش عیب‌یابی پایین‌تر)
کد EINTEGRITY یا hashes do not matchفایل دریافتی با هش ثبت‌شده یکی نیست؛ کش خراب، فایل دست‌کاری‌شده یا tarball بازسازی‌شدهپاک کردن کش و نصب دوباره

آزمون دسترسی قبل از تغییر تنظیمات

این سه دستور کد پاسخ HTTP و زمان کل را برای registry رسمی هر ابزار نشان می‌دهند. کد 200 با زمان کم یعنی مشکل در تنظیمات پروژه است، نه شبکه. کد 403 یعنی رد شدن درخواست و کد 000 همراه با خطای زمان یعنی مسیر باز نیست.

curl -sS -o /dev/null -m 20 -w '%{http_code} %{time_total}s\n' https://registry.npmjs.org/lodash
curl -sS -o /dev/null -m 20 -w '%{http_code} %{time_total}s\n' https://pypi.org/simple/requests/
curl -sS -o /dev/null -m 20 -w '%{http_code} %{time_total}s\n' https://repo.packagist.org/packages.json

علت خطای نصب پکیج روی سرور ایران چیست؟

تنظیم registry جایگزین برای npm و Yarn

ابزار npm همه پکیج‌ها را از نشانی تنظیم registry می‌گیرد که مقدار پیش‌فرضش https://registry.npmjs.org/ است. چه علت خطا تحریم npm باشد چه اختلال مسیر، با عوض کردن همین نشانی، npm install و npm ci بدون تغییر در کد پروژه از میرور دانلود می‌کنند.

میرورهای مستند و تست آن‌ها

جدول زیر میرورهایی را نشان می‌دهد که سرویس‌دهنده‌شان نشانی و دستور تنظیم را در مستندات رسمی خود منتشر کرده است. این نشانی‌ها از بیرون ایران قابل تست نبودند؛ پس قبل از استفاده، دستور تست هر ردیف را روی سرور خودتان اجرا کنید. فهرست کامل‌تر سرویس‌دهنده‌ها در فهرست متن‌باز Mirava آمده است، اما نشانی دقیق را همیشه از سند رسمی خود سرویس‌دهنده بردارید.

اکوسیستمسرویس‌دهندهسند رسمی
مخزن npmلیارامستندات میرور npm لیارا
مخزن PyPIلیارامستندات میرور PyPI لیارا
مخزن PyPIرانفلرمستندات میرور PyPI رانفلر
مخزن Composerلیارامستندات میرور Composer لیارا

نشانی‌های منتشرشده در همین اسناد و دستور تست هر کدام این‌هاست. کد 200 یعنی میرور از روی سرور شما پاسخ می‌دهد:

curl -sS -o /dev/null -m 20 -w '%{http_code}\n' https://package-mirror.liara.ir/repository/npm/lodash
curl -sS -o /dev/null -m 20 -w '%{http_code}\n' https://package-mirror.liara.ir/repository/pypi/simple/requests/
curl -sS -o /dev/null -m 20 -w '%{http_code}\n' https://mirror-pypi.runflare.com/simple/requests/
curl -sS -o /dev/null -m 20 -w '%{http_code}\n' https://package-mirror.liara.ir/repository/composer/packages.json

دستورهای npm برای تغییر registry

دستور زیر نشانی را در فایل ~/.npmrc کاربر ذخیره می‌کند. اگر فقط یک پروژه باید عوض شود، خط registry=... را در فایل .npmrc ریشه همان پروژه بنویسید تا همراه مخزن کد منتقل شود.

npm view lodash version --registry https://package-mirror.liara.ir/repository/npm/
npm config set registry https://package-mirror.liara.ir/repository/npm/
npm config get registry
# برگشت به حالت پیش‌فرض
npm config delete registry

فایل package-lock و گزینه replace-registry-host

فایل package-lock.json نشانی کامل هر tarball را در فیلد resolved نگه می‌دارد. طبق مستندات npm، مقدار پیش‌فرض replace-registry-host برابر npmjs است؛ یعنی فقط نشانی‌هایی که از registry رسمی آمده‌اند با registry تنظیم‌شده جایگزین می‌شوند. اگر lockfile با میرور دیگری ساخته شده باشد، npm سراغ همان میرور قدیمی می‌رود. در این حالت خط replace-registry-host=always را در .npmrc همان پروژه بگذارید تا روی بقیه پروژه‌ها اثر نکند.

تنظیم registry در Yarn

در Yarn نسخه 2 به بعد، تنظیم معادل npmRegistryServer است؛ پیش‌فرض آن طبق مستندات Yarn نشانی https://registry.yarnpkg.com است. این دستور در فایل .yarnrc.yml پروژه می‌نویسد و گزینه -H آن را برای کل کاربر ثبت می‌کند. نسخه کلاسیک 1.22 کلید registry را می‌خواند.

# نسخه 2 به بعد
yarn config set npmRegistryServer https://package-mirror.liara.ir/repository/npm/
# نسخه کلاسیک 1.22
yarn config set registry https://package-mirror.liara.ir/repository/npm/

برای پکیج‌های scope خصوصی، registry شرکت را جدا نگه دارید و فقط پکیج‌های عمومی را به میرور بفرستید. در npm این کار با خطی مثل @mycorp:registry=... در .npmrc انجام می‌شود و در Yarn با کلید npmScopes.

تنظیم registry جایگزین برای npm و Yarn

تنظیم index-url در pip و میرور Packagist در Composer

ابزار pip پکیج‌ها را از نشانی index-url می‌خواند و Composer از مخزنی به نام packagist. با عوض کردن همین دو مقدار، نصب پکیج‌های پایتون و PHP هم مثل npm از میرور داخلی انجام می‌شود.

روش موقت و دائمی تغییر index-url در pip

برای یک نصب تکی، گزینه -i کافی است. دستور pip config مقدار را در فایل کاربر می‌نویسد؛ طبق مستندات pip 26.2 این فایل در لینوکس $HOME/.config/pip/pip.conf است و هر virtualenv هم فایل $VIRTUAL_ENV/pip.conf خودش را دارد.

# یک بار
pip install -i https://package-mirror.liara.ir/repository/pypi/simple django
# دائمی برای کاربر فعلی
pip config --user set global.index-url https://package-mirror.liara.ir/repository/pypi/simple
pip config debug
# برگشت به PyPI رسمی
pip config --user unset global.index-url

متغیر محیطی PIP_INDEX_URL همین کار را بدون فایل روی دیسک می‌کند و برای Dockerfile و CI مناسب‌تر است. مراحل نصب خود Docker در آموزش نصب Docker روی اوبونتو 24.04 آمده است.

تفاوت index-url با extra-index-url و گزینه trusted-host

گزینه extra-index-url مخزن دوم را کنار مخزن اصلی می‌گذارد و pip از هر دو جست‌وجو می‌کند. مستندات pip هشدار می‌دهد که این کار برای پکیج‌های خصوصی خطر dependency confusion دارد: پکیجی هم‌نام در مخزن عمومی ممکن است به‌جای پکیج شما انتخاب شود. در پروژه‌ای که هم با تحریم npm و هم با pip درگیر است، میرور را جایگزین کنید.

تعریف pip برای trusted-host این است که میزبان «حتی بدون HTTPS معتبر» قابل اعتماد علامت می‌خورد. صفحه رانفلر این گزینه را هم در دستورهایش آورده است؛ اما وقتی میرور با HTTPS و گواهی معتبر پاسخ می‌دهد، این گزینه لازم نیست و بهتر است تنظیمش نکنید. در پروژه‌های حساس، requirements را با هش قفل کنید؛ pip فقط با گزینه --require-hashes یا وجود --hash در فایل، هش هر فایل را بررسی می‌کند.

میرور Packagist در Composer و دانلود از GitHub

در Composer نسخه 2.10، دستور زیر مخزن پیش‌فرض را با میرور عوض می‌کند. قالب این دستور و نشانی میرور از مستندات Composer لیارا آمده است؛ خود صفحه packagist.org/mirrors دستوری نمی‌دهد و کاربر را به سایت هر میرور ارجاع می‌دهد.

composer config -g repos.packagist composer https://package-mirror.liara.ir/repository/composer/
composer config --list --global
composer diagnose
# برگشت به Packagist رسمی
composer config -g --unset repos.packagist

صفحه رسمی Packagist میرورهای فهرست‌شده‌اش را «میرور متادیتا» می‌نامد. در متادیتای رسمی، نشانی dist بسیاری از پکیج‌ها به GitHub اشاره دارد؛ برای نمونه، هنگام نگارش این مقاله dist پکیج monolog/monolog در repo.packagist.org نشانی api.github.com را داشت. پس اگر میرور فقط متادیتا را برگرداند، فهرست نسخه‌ها سریع می‌آید ولی فایل zip همچنان از GitHub دانلود می‌شود. با اجرای composer install -vvv نشانی هر دانلود در خروجی دیده می‌شود. اگر هنوز GitHub دیده شد، کش Composer روی سرور build (بخش بعد) این وابستگی را برای نصب‌های بعدی برمی‌دارد.

عیب‌یابی خطای SSL و Timeout بعد از تغییر میرور

اختلاف چند دقیقه‌ای ساعت سرور کافی است تا گواهی معتبر هم رد شود. ساعت را بررسی کنید و همگام‌سازی NTP را روشن کنید؛ اگر سرور NTP خارجی در دسترس نیست، یک سرور NTP داخلی را در /etc/systemd/timesyncd.conf بگذارید.

timedatectl status
sudo timedatectl set-ntp true

اگر میرور گواهی صادرشده از یک CA داخلی دارد، فایل CA را به هر ابزار معرفی کنید. در Composer گزینه معادل cafile است. خاموش کردن strict-ssl فقط خطا را پنهان می‌کند و راه دریافت پکیج دست‌کاری‌شده را باز می‌گذارد.

npm config set cafile /etc/ssl/certs/internal-ca.pem
pip config --user set global.cert /etc/ssl/certs/internal-ca.pem
composer config -g cafile /etc/ssl/certs/internal-ca.pem

برای Timeout، پیش‌فرض‌ها را بشناسید: طبق مستندات npm، fetch-timeout پنج دقیقه و fetch-retries دو است. در pip، --timeout پیش‌فرض ۱۵ ثانیه دارد و --retries حداکثر پنج تلاش برای برقراری اتصال است. بالا بردن این اعداد روی مسیر کند کمک می‌کند؛ روی مسیر قطع فقط انتظار را بیشتر می‌کند.

تنظیم index-url در pip و میرور Packagist در Composer

ساخت کش محلی و نسخه آفلاین پکیج‌ها برای تیم توسعه

کش محلی یعنی هر پکیج فقط یک بار از بیرون دانلود شود و بعد از دیسک یک سرور داخل شبکه خودتان تحویل داده شود. برای تیمی که چند توسعه‌دهنده و یک CI دارد، این لایه قابل‌اتکاتر از هر میرور عمومی است، چون کنترلش دست خودتان است.

وقتی پنج نفر و یک CI هر روز npm ci می‌زنند، با کش فقط اولین درخواست بیرون می‌رود. چه مشکل از تحریم npm باشد چه از قطعی مسیر، نصب‌های بعدی از دیسک داخلی انجام می‌شوند.

راه‌اندازی Verdaccio به‌عنوان registry داخلی npm

ابزار Verdaccio یک registry سبک npm است که درخواست‌ها را به مخزن بالادستی (uplink) می‌فرستد و فایل‌ها را در storage خودش نگه می‌دارد. طبق مستندات Verdaccio، نسخه 6 به Node.js 22 یا بالاتر نیاز دارد، به‌طور پیش‌فرض فقط روی localhost:4873 گوش می‌دهد و گزینه cache آن به‌طور پیش‌فرض روشن است.

npm install -g verdaccio
verdaccio   # فایل config.yaml در اولین اجرا ساخته می‌شود

در فایل config.yaml دو تغییر لازم است: listen برای دسترسی بقیه سیستم‌ها و نشانی uplink پیش‌فرض. نام npmjs را عوض نکنید؛ بخش packages در فایل پیش‌فرض به همین نام ارجاع می‌دهد.

listen: 0.0.0.0:4873
uplinks:
  npmjs:
    url: https://package-mirror.liara.ir/repository/npm/
    timeout: 30s
    maxage: 10m

پورت 4873 را فقط برای IPهای تیم و CI در فایروال باز کنید. روی سیستم هر توسعه‌دهنده هم نشانی سرور را registry بگذارید:

# نشانی سرور Verdaccio خودتان را بگذارید
npm config set registry http://10.0.0.5:4873/

رفتار Verdaccio هنگام قطع کامل uplink به maxage، fail_timeout و max_fails بستگی دارد؛ پیش از اتکا، یک بار uplink را عمداً قطع کنید و نصب را تست کنید.

ساخت wheelhouse آفلاین برای pip

دستور pip download همه وابستگی‌ها را در یک پوشه می‌ریزد و طبق مستندات pip، همان پوشه بعداً به --find-links داده می‌شود. با --no-index و یک پوشه محلی، pip سراغ هیچ مخزن شبکه‌ای نمی‌رود.

pip download -r requirements.txt -d /srv/wheelhouse
pip install --no-index --find-links=/srv/wheelhouse -r requirements.txt

با هر تغییر requirements دستور اول را دوباره اجرا کنید. نسخه پایتون و معماری سرور مقصد باید با سرور دانلودکننده یکی باشد، وگرنه wheelهای باینری نصب نمی‌شوند.

کش Composer و پوشه vendor

ابزار Composer فایل‌های dist را در پوشه کشی نگه می‌دارد که با متغیر COMPOSER_CACHE_DIR یا گزینه cache-dir جابه‌جا می‌شود. اگر این پوشه روی دیسک مشترک CI باشد، نصب‌های بعدی از کش انجام می‌شوند. برای سرور تولید، مطمئن‌تر این است که پوشه vendor روی سرور build ساخته و همان خروجی منتقل شود.

export COMPOSER_CACHE_DIR=/srv/composer-cache
composer install --no-dev --prefer-dist --optimize-autoloader

سرور همیشه‌روشن برای کش مشترک تیم

کش مشترک به سروری نیاز دارد که هم توسعه‌دهنده‌ها و هم CI به آن برسند. سرور مجازی ایران وب‌داده در دیتاسنتر تهران و روی شبکه رسپینا میزبانی می‌شود و IP مستقیمی دارد که از اینترنت بین‌الملل هم در دسترس است. پنل آن اسنپ‌شات و بازگردانی دارد؛ پس قبل از ارتقای Verdaccio یا پاک کردن کش، یک اسنپ‌شات می‌گیرید و اگر چیزی خراب شد برمی‌گردید. معیارهای انتخاب میزبانی داخل یا خارج در مقایسه سرور مجازی ایران و خارج آمده است.

پرسش‌های رایج درباره خطای نصب پکیج روی سرورهای داخلی

آیا npm واقعاً کاربران ایران را مسدود کرده است؟

سند رسمی‌ای از npm درباره مسدودسازی ایران پیدا نکردیم. GitHub در ژانویه ۲۰۲۱ برای ارائه خدمات GitHub در ایران مجوز دولت آمریکا را گرفت، هرچند آن اعلامیه npm را نام نمی‌برد. بیشتر گزارش‌ها Timeout یا قطع اتصال است؛ اگر 403 بدون توکن دیدید، registry را عوض کنید.

اولی مخزن اصلی را جایگزین می‌کند و دومی مخزن دیگری کنار آن می‌گذارد. برای میرور، index-url را تنظیم کنید؛ extra-index-url در پروژه‌های دارای پکیج خصوصی خطر dependency confusion دارد.

در npm دستور npm config delete registry، در pip دستور pip config –user unset global.index-url و در Composer دستور composer config -g –unset repos.packagist. اگر تنظیم را در فایل پروژه نوشته‌اید، همان خط را حذف کنید.

به نوع بررسی شما بستگی دارد. در npm، وقتی package-lock.json فیلد integrity دارد، فایل ناسازگار با EINTEGRITY رد می‌شود. pip فقط با –require-hashes هش را بررسی می‌کند و Composer برای zipballهای GitHub معمولاً هشی در lockfile ندارد. نصب اول بدون lockfile هم محافظتی ندارد؛ پس میرورهایی با HTTPS معتبر و سرویس‌دهنده شناخته‌شده انتخاب کنید.

چون نشانی فایل zip در متادیتای بسیاری از پکیج‌ها به GitHub اشاره دارد و میرور متادیتا آن را عوض نمی‌کند. خروجی composer install -vvv را ببینید؛ کش Composer روی سرور build نصب‌های بعدی را از این وابستگی جدا می‌کند.

بعضی پکیج‌ها در مرحله postinstall فایل باینری را از GitHub یا سایت سازنده می‌گیرند و این دانلود از registry عبور نمی‌کند. نشانی دانلود را در خروجی خطا پیدا کنید؛ معمولاً README پکیج یک متغیر محیطی برای تغییر آن معرفی کرده است.

بله؛ میرور Docker Hub با کلید registry-mirrors در فایل daemon.json داکر تنظیم می‌شود و به npm و pip ربطی ندارد.

جمع‌بندی؛ تحریم npm را قبل از روز اختلال مدیریت کنید خطای نصب پکیج روی سرور داخلی سه علت جدا دارد و اولین کار، تشخیص با یک دستور curl است. پاسخ 403 یا Timeout یعنی باید نشانی مخزن را عوض کنید: registry در npm و Yarn، index-url در pip و repos.packagist در Composer. برای خطای گواهی، ساعت سرور و CA را درست کنید؛ خاموش کردن SSL راه‌حل نیست. بعد از تنظیم، lockfileهای ساخته‌شده با میرور دیگر و میرورهای Composer را که فقط متادیتا دارند بررسی کنید. برای تیم و CI هم یک کش داخلی بسازید؛ Verdaccio برای npm، پوشه wheelhouse برای pip و کش Composer یا پوشه vendor آماده. هر نشانی میرور را با دستور تست همین مقاله روی سرور خودتان امتحان کنید و دستورهای برگشت را کنارش یادداشت کنید تا در اختلال بعدی فقط یک دستور فاصله داشته باشید.

🚀 سرور لینوکس داخلی برای build و کش پکیج

سرور مجازی ایران وب‌داده بعد از پرداخت به‌صورت خودکار تحویل می‌شود و قبل از خرید می‌توانید آن را ساعتی و رایگان تست کنید تا تنظیمات میرور و کش را روی محیط واقعی بسنجید.

اگر به خطای خاصی برخوردید، متن آن را در بخش نظرات بنویسید.

منابع

وب داده
وب داده

جدید ترین مطالب آموزشی و کاربردی را در اینجا بخوانید !
ما با بهره‌گیری از دانش روز و تجربه متخصصان حوزه فناوری، مجموعه‌ای از آموزش‌های کاربردی و مقالات تخصصی را گردآوری کرده‌ایم که هر یک، پاسخی دقیق به پرسش‌های شماست. ؛ از مفاهیم پایه تا پیچیده‌ترین تکنیک‌های حرفه‌ای. اینجا، دانش با زبانی ساده اما عمیق در اختیار شما قرار می‌گیرد.

مقاله‌ها: 229
پاسخی بگذارید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *