داکر هاب از ایران: خطای 403، میرور داخلی و digest
docker pull از ایران خطای 403 میدهد؟ تنظیم میرور داخلی داکر هاب در daemon.json، containerd و k3s، نصب داکر از مخزن ایرانی اوبونتو و پین کردن ایمیج با digest.
خطای 403 داکر هاب از ایران یعنی چه
وقتی docker pull یا docker compose up روی سرور یا لپتاپی داخل ایران با 403 Forbidden متوقف میشود، دسترسی به داکر هاب از ایران معمولاً از سمت خود داکر رد شده است. تنظیمات شما مقصر نیست. راه عملی این است که ایمیجها را از یک میرور داخلی بگیرید. برای ایمیجهای مهم هم digest را پین کنید. آن وقت میرور فقط همان ایمیجی را تحویل میدهد که شما از قبل مشخص کردهاید.
کاربران همراه این خطا این متن را دیدهاند:
Since Docker is a US company, we must comply with US export control regulations. In an effort to comply with these, we now block all IP addresses that are located in Cuba, Iran, North Korea, Republic of Crimea, Sudan, and Syria.یعنی: داکر یک شرکت آمریکایی است و باید از مقررات کنترل صادرات آمریکا پیروی کند. به همین دلیل همه نشانیهای IP در کوبا، ایران، کره شمالی، کریمه، سودان و سوریه را مسدود میکند. این متن را یک کاربر در ۳۰ مه ۲۰۲۴ (۱۰ خرداد ۱۴۰۳) در انجمن داکر گزارش کرد. یک وبلاگ هم همان روز همین متن را زیر عنوان 403 Forbidden منتشر کرد. این نقلقول از پست یک کاربر است. صفحهای نیست که خود داکر منتشر کرده باشد. ما صفحهای از داکر پیدا نکردیم که نشان دهد این مسدودسازی امروز هم به همین شکل برقرار است. پس آن را «گزارششده در مه ۲۰۲۴» بدانید و روی سرور خودتان آزمایش کنید.
کدام را داکر رد میکند و کدام را مسیر شبکه
دو مشکل متفاوت ظاهر مشابهی دارند. در اولی، سرور داکر جواب میدهد و جوابش «نه» است. در دومی، اصلاً جواب درستی به دست شما نمیرسد. راهحل این دو یکی نیست، پس اول بفهمید با کدام روبهرو هستید.
docker pull hello-world
curl -sS -o /dev/null -w '%{http_code}\n' https://registry-1.docker.io/v2/
curl -sS -o /dev/null -w '%{http_code}\n' https://download.docker.com/linux/ubuntu/دستور دوم با رجیستری داکر هاب حرف میزند و دستور سوم با مخزن apt داکر. هر کدام فقط کد وضعیت HTTP را چاپ میکند. اگر 403 دیدید، سرور داکر درخواست را دریافت کرده و رد کرده است. اگر 000 دیدید، هیچ پاسخ HTTP دریافت نشده است. در این حالت curl پیش از آن یک خطا چاپ میکند، مثل timeout یا قطع شدن اتصال. علت چنین خطایی در مسیر شبکه است. از بیرون مشخص نیست دقیقاً کجای مسیر.
درباره مخزن apt، یعنی download.docker.com، ما هیچ منبع رسمی پیدا نکردیم که بگوید به IPهای ایرانی 403 میدهد یا نمیدهد. پس فرض نکنید. خروجی دستور سوم و خروجی sudo apt update را روی سرور خودتان ببینید. اگر خطوط مربوط به download.docker.com خطا میدهند، راه دوم همین راهنما برای شماست.
Docker Desktop هم یک نکته جدا دارد. مستندات داکر میگوید: «By default, users can access Docker Desktop without signing in to your organization.» یعنی ورود به حساب بهطور پیشفرض اجباری نیست، مگر اینکه مدیر سازمان آن را اجباری کرده باشد. ما هیچ صفحهای از داکر پیدا نکردیم که بگوید ورود به Desktop از ایران رد میشود. پس چنین چیزی را فرض نکنید. اما Desktop هم ایمیجها را از داکر هاب میگیرد، پس برای pull به همان میرور نیاز دارد.
یک خطای دیگر را با 403 اشتباه نگیرید. داکر هاب برای pull سقف دارد. طبق مستندات داکر، کاربر بدون ورود در هر ۶ ساعت ۱۰۰ pull دارد. این سقف برای هر نشانی IPv4 یا هر زیرشبکه IPv6 با پیشوند /64 حساب میشود. حساب رایگان Personal در همین بازه ۲۰۰ pull دارد. این یک محدودیت نرخ است و ربطی به مسدودسازی کشوری ندارد.
راه اول: میرور داکر هاب در daemon.json
میرور pull-through (میرور واسطه) سروری است که درخواست pull شما را میگیرد. ایمیج را از داکر هاب میآورد، یک نسخه از آن را نگه میدارد و به شما تحویل میدهد. مستندات داکر میگوید چنین رجیستریای به همه درخواستهای معمولی docker pull پاسخ میدهد، اما همه محتوا را بهصورت محلی ذخیره میکند. میرور داخل ایران است، پس ترافیک شما از کشور خارج نمیشود. درخواست شما هم مستقیم به داکر هاب نمیرسد.
یک محدودیت مهم را از اول بدانید. مستندات داکر میگوید: «Only the central Hub can be mirrored.» یعنی کلید registry-mirrors فقط برای داکر هاب کار میکند. ایمیجهای ghcr.io یا quay.io از این تنظیم عبور نمیکنند. برای آنها باید نام ایمیج را عوض کنید (پایینتر توضیح دادهایم).
روی لینوکس، داکر بهطور پیشفرض تنظیماتش را از /etc/docker/daemon.json میخواند. اول ببینید این فایل از قبل چیزی دارد یا نه:
sudo cat /etc/docker/daemon.jsonاگر پیام No such file or directory دیدید، فایل وجود ندارد و میتوانید آن را بسازید. اگر فایل محتوا دارد، فقط کلید registry-mirrors را به همان JSON اضافه کنید. دستوری که در صفحه لیارا آمده کل فایل را بازنویسی میکند. در آن صورت هر تنظیم قبلی شما، مثل log-driver یا data-root، از بین میرود. دستورهای زیر برای فایلی است که هنوز وجود ندارد یا فقط همین یک کلید را لازم دارد:
sudo cp /etc/docker/daemon.json /etc/docker/daemon.json.bak
sudo tee /etc/docker/daemon.json <<'JSON'
{
"registry-mirrors": ["https://docker-mirror.liara.ir"]
}
JSON
sudo systemctl daemon-reload
sudo systemctl restart docker
docker info
docker pull hello-worldاگر فایل از قبل وجود نداشته باشد، دستور اول خطا میدهد. این خطا مشکلی ندارد. در خروجی docker info باید نشانی میرور را در بخش مربوط به registry mirrors ببینید. docker pull hello-world هم باید بدون 403 تمام شود. اگر بعد از ویرایش سرویس داکر بالا نیامد، علت معمولاً یک ویرگول یا گیومه جاافتاده در JSON است، چون داکر با فایل نامعتبر اجرا نمیشود. پیام دقیق را با journalctl -u docker -n 50 ببینید.
در Docker Desktop همین خط را در Settings و بعد Docker Engine اضافه کنید و Apply & Restart را بزنید. این مسیر را صفحه docker.kernel.ir توضیح داده است.
میرورهایی که بررسی کردیم (۱۶ آبان ۱۴۰۵، برابر با ۷ نوامبر ۲۰۲۶)
فهرست Mirava در گیتهاب (github.com/MiravaOrg/Mirava) ۱۳ رجیستری ایرانی داکر را نام میبرد. بخش میرورهای جهانی آن هم هفت میرور غیرایرانی دارد که در توضیح دو مورد، یعنی Huawei Cloud و TUNA دانشگاه Tsinghua، داکر آمده است. این یک فهرست غیررسمی است. برای بیشتر ردیفها فقط برچسب «Docker Registry» آمده است. درباره پوشش داکر هاب یا ghcr.io و تنظیم daemon.json چیزی نمیگوید. برای همین هر میرور را در صفحه خود اپراتور بررسی کردیم:
- لیارا: میرور داکر هاب در
https://docker-mirror.liara.irاست و از طریقregistry-mirrorsتنظیم میشود. صفحه لیارا میگوید حدود ۹۹ درصد ایمیجهای داکر هاب را پوشش میدهد. برایghcr.ioمیرور جدایghcr-mirror.liara.irرا دارد و برای Quay میرورquay-mirror.liara.irرا. مستندات لیارا میگوید این میرورها ایمیجها را «بهصورت کششده» ارائه میکنند. صفحهها تاریخ ندارند. - focker.ir: خودش را میرور رایگان معرفی میکند. هم بهصورت پیشوند نام ایمیج کار میکند (مثل
focker.ir/nginx) و هم بهصورتregistry-mirrorsبا نشانیhttps://focker.ir. منبعهای پشتیبانیشده را داکر هاب، Quay.io، GCR،registry.k8s.ioو GHCR اعلام کرده است. میگوید فقط ایمیجهای عمومی کش میشوند و ایمیجهای خصوصی باید روی داکر هاب بمانند. در صفحه، شرایط استفاده یا هویت اپراتور ندیدیم. - docker.kernel.ir: اپراتور آن را هم برای
registry-mirrorsو هم بهصورت پیشوند (docker pull docker.kernel.ir/nginx) مستند کرده است. صفحه نمیگوید کدام رجیستریها پوشش داده میشوند، پس پوششghcr.ioوquay.ioمشخص نیست. شرایط استفاده و تاریخ هم ندارد. - بررسینشده:
hub.hamdocker.irبه یک رابط وب رفت که فقط لوگوی JFrog را نشان میداد، بدون فهرست رجیستری یا راهنما.docker.mobinhost.comاتصال ما را رد کرد. صفحههای داکر ابرآروان فقط پیام انتقال به سایت را نشان دادند. Mirava این سه را نام میبرد، اما ما راهنمای خود اپراتور را نخواندیم.
صفحههای لیارا و docker.kernel.ir درباره ثبت درخواستها (logging) یا حریم خصوصی چیزی ننوشته بودند. focker.ir در پرسشهای متداولش میگوید فقط ایمیجهای عمومی را کش میکند و هیچ داده شخصی ذخیره نمیکند.
برای ghcr.io و quay.io نام ایمیج را عوض کنید
registry-mirrors فقط داکر هاب را پوشش میدهد. لیارا هم میگوید این روش برای ghcr کار نمیکند. راهش این است که در نشانی ایمیج، ghcr.io را با ghcr-mirror.liara.ir جایگزین کنید. این کار را در فایل Compose و در Dockerfile هم انجام دهید:
docker pull ghcr-mirror.liara.ir/google/cadvisor:v0.60.6services:
cadvisor:
image: ghcr-mirror.liara.ir/google/cadvisor:v0.60.6صفحه Quay لیارا میرور Quay را داخل registry-mirrors نشان میدهد. اما مستندات داکر فقط داکر هاب را قابل میرور میداند. پس روی آن تکیه نکنید و نام ایمیج را با quay-mirror.liara.ir بنویسید. اگر با فایلهای Compose کار میکنید، مبانی Docker Compose روی سرور نشان میدهد کلید image کجا مینشیند و docker compose pull چه میکند.
containerd و k3s: معادل registry-mirrors
containerd
containerd تنظیم میرور را از پوشه /etc/containerd/certs.d میخواند. اول در config.toml این پوشه را معرفی کنید. نام بخش به نسخه containerd بستگی دارد. برای containerd 2.x:
version = 3
[plugins."io.containerd.cri.v1.images".registry]
config_path = "/etc/containerd/certs.d"و برای containerd 1.x:
version = 2
[plugins."io.containerd.grpc.v1.cri".registry]
config_path = "/etc/containerd/certs.d"بعد از ویرایش config.toml، containerd را با sudo systemctl restart containerd ریاستارت کنید تا فایل جدید را بخواند. سپس فایل /etc/containerd/certs.d/docker.io/hosts.toml را بسازید:
server = "https://registry-1.docker.io"
[host."https://docker-mirror.liara.ir"]
capabilities = ["pull"]مستندات containerd میگوید تغییرات داخل این پوشه به ریاستارت نیاز ندارند. نکته اصلی در capabilities است. resolve یعنی تبدیل یک tag (مثل alpine:3.20) به digest. digest شناسه دقیق محتوای ایمیج است. pull یعنی دریافت محتوای یک digest مشخص. مستندات containerd صریح است: «A public mirror should never be trusted to do a resolve action.» یعنی هرگز نباید به میرور عمومی اجازه resolve داد.
در کد containerd، pull با digest فقط به قابلیت pull نیاز دارد. pull با tag به resolve هم نیاز دارد. با فایل بالا، resolve فقط از server بالادستی، یعنی خود داکر هاب، انجام میشود. اگر داکر هاب به شما 403 بدهد، pull با tag شکست میخورد. اما pull با digest از طریق میرور کار میکند، چون digest را خودتان دادهاید. اگر pull با tag لازم دارید، باید capabilities = ["pull", "resolve"] بنویسید. در آن حالت اپراتور میرور تصمیم میگیرد هر tag به کدام ایمیج اشاره کند.
k3s
در k3s تنظیم میرور در /etc/rancher/k3s/registries.yaml است:
mirrors:
docker.io:
endpoint:
- "https://docker-mirror.liara.ir"طبق مستندات k3s، containerd نشانیهای این فهرست و نشانی پیشفرض را امتحان میکند و از اولین نشانیای که کار کند استفاده میکند. نشانی پیشفرض داکر هاب همیشه آخرین گزینه است. پس اگر میرور جواب ندهد، درخواست به داکر هاب میرسد. در این حالت در خطای نهایی باز هم 403 را میبینید، و این 403 یعنی میرور شما جواب نداده است. برای اعمال تغییر، k3s را روی همه نودها ریاستارت کنید. مستندات k3s حداقل سختافزار نود server را ۲ هسته و ۲ گیگابایت RAM ذکر میکند. برای نود agent این حداقل ۱ هسته و ۵۱۲ مگابایت است.
راه دوم: بستههای داکر خود اوبونتو از مخزن ایرانی
اگر مخزن apt داکر روی سرور شما کار نمیکند، از بستههای خود اوبونتو استفاده کنید. بسته موتور داکر docker.io نام دارد و بسته Compose docker-compose-v2. هر دو در بخش universe هستند. Launchpad فهرست رسمی میرورهای Canonical است. در بخش ایران آن ۱۳ میرور مخزن اوبونتو آمده است. دو نمونه با https اینها هستند: https://ir.archive.ubuntu.com/ubuntu/ و https://mirror.arvancloud.ir/ubuntu/. حضور در این فهرست به معنای تأیید Canonical نیست.
اول پیدا کنید نشانی مخزن در کدام فایل نوشته شده است:
grep -rl "ubuntu.com" /etc/apt/فایلی را که پیدا شد با sudo nano باز کنید. نشانی مخزن اصلی را با یکی از میرورهای ایرانی عوض کنید. اگر security.ubuntu.com از سرور شما در دسترس است، آن خط را دست نزنید. مطمئن شوید universe در خط اجزای مخزن هست. بعد:
sudo apt update
apt-cache policy docker.io docker-compose-v2
sudo apt install -y docker.io docker-compose-v2
docker --version
docker compose versionapt-cache policy باید برای هر دو بسته یک نسخه Candidate نشان دهد. اگر (none) دیدید، یا universe فعال نیست یا میرور بهروز نیست. نسخههایی که ما در packages.ubuntu.com دیدیم:
- اوبونتو 24.04 روی amd64:
docker.ioنسخه29.1.3-0ubuntu3~24.04.2. روی arm64 و دیگر معماریهای ports نسخه بسیار قدیمیتر24.0.7-0ubuntu4ارائه میشود. - اوبونتو 26.04 روی amd64 و arm64:
docker.ioنسخه29.1.3-0ubuntu4.1. docker-compose-v2: روی 24.04 نسخه2.40.3+ds1-0ubuntu1~24.04.1از noble-updates، و روی 26.04 نسخه2.40.3+ds1-0ubuntu1.
برای مقایسه، تازهترین نسخه موتور داکر در یادداشتهای انتشار خود داکر 29.9.3 است، با تاریخ ۳ نوامبر ۲۰۲۶. یعنی بسته اوبونتو چند نسخه عقبتر است. بستههای اوبونتو را با بستههای رسمی داکر قاطی نکنید. صفحه نصب داکر میگوید docker.io ممکن است با بستههای رسمی تداخل کند. اگر قبلاً docker-ce نصب کردهاید، پیش از نصب docker.io آن را حذف کنید:
sudo apt remove docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-pluginیک نکته مهم: این راه فقط خود موتور داکر را نصب میکند. ایمیجها همچنان از داکر هاب میآیند، پس راه اول را هم لازم دارید. برای بقیه کار، یعنی کاربر، فایروال و اجرای سرویسها، راهنمای نصب و اجرای داکر روی VPS را ببینید.
راه سوم: DNSهای تحریمشکن، و آنچه میبینند
این سرویسها را معرفی نمیکنیم، چون نتوانستیم صفحه رسمی هیچکدام را باز کنیم. پس نمیدانیم خودشان چه ادعایی دارند، چه چیزی ثبت میکنند، یا با داکر کار میکنند یا نه. اما سازوکار کلی را میشود توضیح داد. شما DNS سیستم را روی resolver آن سرویس میگذارید. وقتی نام دامنهای مثل registry-1.docker.io را میپرسید، resolver بهجای IP واقعی، IP یک سرور واسطه را برمیگرداند. اتصال شما به آن واسطه میرود و واسطه آن را به مقصد میرساند. مقصد IP واسطه را میبیند، نه IP شما را.
اپراتور چنین سرویسی هر نام دامنهای را که سیستم شما میپرسد میبیند، نه فقط دامنههای داکر را. IP شما و زمان هر درخواست را هم میبیند. واسطه هم میبیند به کدام نام وصل شدهاید و چقدر داده رد و بدل کردهاید. خودتان میتوانید ببینید جواب DNS عوض شده است یا نه. resolvectl query registry-1.docker.io را یک بار با DNS عادی و یک بار با DNS سرویس اجرا کنید و IPها را مقایسه کنید.
میرور چه چیزی را میبیند و چه کاری از دستش برمیآید
این مهمترین بخش این راهنماست. میرور برای انجام کارش باید ببیند چه ایمیجی و با چه tagی میخواهید، و درخواست از چه IPی آمده است. این نتیجه طرز کار هر میرور واسطه است. اپراتوری چنین چیزی درباره خودش ننوشته است. صفحههای لیارا و docker.kernel.ir درباره ثبت این اطلاعات چیزی نگفته بودند. focker.ir میگوید هیچ داده شخصی ذخیره نمیکند، اما این ادعای خود اپراتور است و از بیرون نمیشود آن را بررسی کرد. پس فرض کنید فهرست ایمیجهای شما و IP سرورتان نزد اپراتور میماند.
مشکل دوم جدیتر است. وقتی nginx:latest را از میرور میخواهید، میرور تصمیم میگیرد این tag به کدام ایمیج اشاره کند. میرور خراب، قدیمی یا دستکاریشده میتواند ایمیج دیگری تحویل بدهد، و شما از روی tag متوجه نمیشوید. راه دفاع، پین کردن با digest است.
پین کردن ایمیج با digest
مستندات داکر میگوید pull با digest ایمیج را روی همان نسخه «پین» میکند و تضمین میکند ایمیجی که استفاده میکنید همیشه همان باشد. شکلش این است:
docker pull alpine@sha256:21a3deaa0d32a8057914f36584b5288d2e5ecc984380bc0118285c70fa8c9300این digest نمونهای از مستندات داکر است. digest ایمیج خودتان را باید خودتان به دست بیاورید. نکته اصلی همینجاست: digest را از خود میرور نگیرید، چون آن وقت باز هم به همان میرور اعتماد کردهاید. دستور زیر digest را مستقیم از رجیستری میخواند:
docker buildx imagetools inspect alpine:latestخروجی آن یک خط Digest: دارد. این دستور باید جایی اجرا شود که به داکر هاب دسترسی دارد. در صفحه tagهای هر ایمیج در سایت داکر هاب هم میتوانید digest هر tag را انتخاب کنید و جزئیاتش را ببینید. ما بررسی نکردیم که سایت داکر هاب از داخل ایران باز میشود یا نه. اگر باز نمیشود، digest را از کسی بگیرید که دسترسی دارد و به او اعتماد دارید. صفحه انتشار خود پروژه هم منبع خوبی است، اگر digest را منتشر کند. برای ایمیج چندمعماری، خط Digest: digest کل فهرست (index) را نشان میدهد. پس همان را با digest فهرست مقایسه کنید، نه با digest یک معماری خاص.
بعد از pull، digest ایمیجهای محلی را اینطور ببینید:
docker image ls --digestsدر فایل Compose هم بهجای tag از digest استفاده کنید:
services:
web:
image: nginx@sha256:<digest-checked-by-you>این کار یک هزینه دارد. مستندات داکر یادآوری میکند که با پین کردن، داکر نسخههای جدیدتر را دریافت نمیکند تا وقتی digest را عوض کنید. این نسخهها ممکن است بهروزرسانی امنیتی داشته باشند. پس برای بهروزرسانی، digest تازه را از منبع مستقل بگیرید و فایل را عوض کنید. این همان منطق بررسی فایلهای دانلودشده با checksum است: شناسه درست را از جایی غیر از سرور دانلود میگیرید. اگر مقدار digest را در فایل env نگه میدارید، فایلهای env و secrets در Docker Compose نشان میدهد Compose این مقادیر را از کجا میخواند.
FAQ
چرا docker pull از ایران خطای 403 Forbidden میدهد؟
متنی که کاربران در ۳۰ مه ۲۰۲۴ گزارش کردند میگوید داکر برای پیروی از مقررات کنترل صادرات آمریکا، IPهای کوبا، ایران، کره شمالی، کریمه، سودان و سوریه را مسدود میکند. یعنی سرور داکر درخواست را دریافت و رد کرده است. اگر بهجای 403 خطای timeout یا قطع اتصال میبینید، مشکل در مسیر شبکه است. در هر دو حالت، میرور داخلی داکر هاب در /etc/docker/daemon.json راه عملی است.
آیا registry-mirrors برای ghcr.io و quay.io هم کار میکند؟
خیر. مستندات داکر میگوید فقط داکر هاب مرکزی قابل میرور است. برای ghcr.io و quay.io باید نام میزبان را در نشانی ایمیج عوض کنید. مثلاً ghcr-mirror.liara.ir/google/cadvisor:v0.60.6 را بهجای ghcr.io/google/cadvisor:v0.60.6 بنویسید. این کار را در فایل Compose و Dockerfile هم انجام دهید.
آیا میرور داخلی میتواند ایمیج دیگری به من بدهد؟
بله. وقتی با tag درخواست میدهید، میرور تصمیم میگیرد آن tag به کدام ایمیج اشاره کند. ایمیجهای مهم را با digest، یعنی image@sha256:...، pull کنید. digest را از منبعی غیر از میرور بگیرید، مثل صفحه tagهای داکر هاب یا خروجی docker buildx imagetools inspect از جایی که به داکر هاب دسترسی دارد.
اپراتور میرور داکر چه اطلاعاتی از من میبیند؟
میرور واسطه برای کار کردن باید نام و tag هر ایمیجی را که میخواهید ببیند. IP سرور شما و زمان درخواست را هم میبیند. صفحههای لیارا و docker.kernel.ir را در ۱۶ آبان ۱۴۰۵ خواندیم و هیچکدام درباره ثبت این اطلاعات چیزی نگفته بود. focker.ir میگوید هیچ داده شخصی ذخیره نمیکند، اما این ادعای خود اپراتور است و از بیرون نمیشود آن را بررسی کرد. پس فرض کنید این اطلاعات نزد اپراتور ثبت میشود.
آیا Docker Desktop در ایران بدون ورود به حساب کار میکند؟
طبق مستندات داکر، ورود به حساب در Docker Desktop بهطور پیشفرض اجباری نیست، مگر اینکه مدیر سازمان آن را اجباری کند. ما صفحهای از داکر پیدا نکردیم که درباره ورود به Desktop از ایران چیزی بگوید. pull ایمیجها همچنان از داکر هاب انجام میشود. پس میرور را در Settings و بعد Docker Engine اضافه کنید و Apply & Restart را بزنید.