رفتن به محتوا
LoopX

تنظیم میرور برای دانلود image در ایران

توی این درس یاد می‌گیری چرا ممکن است docker pull در ایران خطا بدهد یا خیلی کند باشد، میرور (mirror) چیست و چطور با کلید registry-mirrors در فایل daemon.json تنظیمش می‌کنی، در Docker Desktop و روی لینوکس، چطور تنظیم را با docker info بررسی کنی، و چرا یک میرور خراب می‌تواند کل دانلودهایت را از کار بیندازد. در انتها هم یک روش تست دسترسی به registry داری که در هر شبکه‌ای جواب می‌دهد.

وقتی docker pull nginx را می‌زنی، daemon به registry (به‌صورت پیش‌فرض Docker Hub) وصل می‌شود و image را دانلود می‌کند. اگر آن اتصال از شبکه‌ی تو عبور نکند یا خیلی کند باشد، دو حالت دارد: دانلود بعد از مدتی timeout می‌شود و خطا می‌دهد، یا بسیار آهسته پیش می‌رود. بدون image، کل داکر بلااستفاده است، چون هر docker run به یک image نیاز دارد.

میرور یک registry دیگر است که نسخه‌ی کش‌شده‌ی image های Docker Hub را نگه می‌دارد و از شبکه‌ی تو قابل دسترس‌تر است. مثل یک شعبه‌ی محلی کتابخانه‌ی مرکزی: به‌جای رفتن تا ساختمان اصلی، کتاب را از شعبه‌ی نزدیک می‌گیری.

بدون میرور، daemon مستقیم به Docker Hub می‌رود. با میرور، اول به میرور وصل می‌شود که خودش از Docker Hub می‌گیرد و کش می‌کند.

مثال ۱: تشخیص: registry در دسترس است؟

Section titled “مثال ۱: تشخیص: registry در دسترس است؟”

قبل از هر تنظیمی، مطمئن شو مشکل دسترسی است. API هر registry روی مسیر /v2/ است و جواب 401 یعنی «زنده است و احراز هویت می‌خواهد»؛ پس رسیدن به آن یعنی اتصال برقرار است:

Terminal window
curl -s -o /dev/null -m 15 -w "Docker Hub: HTTP %{http_code}\n" https://registry-1.docker.io/v2/
خروجی
Docker Hub: HTTP 401

401 یعنی رسیدی. اگر بعد از چند ثانیه هیچ جوابی نیامد، یا 000 گرفتی (یعنی اتصال برقرار نشد)، مشکل دسترسی است. در شبکه‌ای که Docker Hub مشکل داشته باشد، خطای docker pull معمولاً از این جنس است (متن دقیق بسته به نسخه و شبکه فرق می‌کند):

خطا (نمونه)
Error response from daemon: Get "https://registry-1.docker.io/v2/": net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)

مثال ۲: فایل daemon.json روی لینوکس

Section titled “مثال ۲: فایل daemon.json روی لینوکس”

روی لینوکس تنظیمات daemon در /etc/docker/daemon.json است (طبق مستندات داکر). فایل را با sudo nano ویرایش می‌کنی؛ من برای تست خودکار با tee می‌نویسم و بعد صحت JSON را می‌سنجم:

Terminal window
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json > /dev/null <<'EOT'
{
"registry-mirrors": ["https://mirror.example.ir"]
}
EOT
cat /etc/docker/daemon.json
python3 -m json.tool /etc/docker/daemon.json > /dev/null && echo "JSON معتبر است"
sudo rm /etc/docker/daemon.json
خروجی
{
"registry-mirrors": ["https://mirror.example.ir"]
}
JSON معتبر است

کلید registry-mirrors یک آرایه است؛ می‌توانی چند میرور بدهی. فایل باید JSON معتبر باشد (ویرگول و گیومه!)؛ یک ویرگول اضافه یا کم، daemon را از بالا آمدن بازمی‌دارد (مثال ۴).

مثال ۳: اعمال تنظیم و دیدنش با docker info

Section titled “مثال ۳: اعمال تنظیم و دیدنش با docker info”

روی یک daemon ایزوله (که نمی‌خواهم داکر اصلی‌ات را تغییر بدهم) تنظیم را اعمال می‌کنیم. اول فایل را می‌نویسیم ولی هنوز daemon را به‌روز نمی‌کنیم:

Terminal window
cat > /etc/docker/daemon.json <<'EOT'
{
"registry-mirrors": ["https://mirror.example.ir"]
}
EOT
echo "قبل از reload:"; docker info 2>/dev/null | grep -A1 "Registry Mirrors" || echo "(هیچ میرور فعالی نیست)"
kill -HUP $(pidof dockerd); sleep 2
echo "بعد از reload:"; docker info 2>/dev/null | grep -A1 "Registry Mirrors"
خروجی
قبل از reload:
(هیچ میرور فعالی نیست)
بعد از reload:
Registry Mirrors:
https://mirror.example.ir/

نکته‌ی مهم: نوشتن فایل کافی نیست. daemon فقط وقتی تنظیم را می‌خواند که به آن بگویی. روی لینوکس معمولاً sudo systemctl restart docker می‌زنی (روی سیستم خودت؛ داخل این کانتینر آزمایشی systemd نیست و از سیگنال reload استفاده کردم)، و در Docker Desktop دکمه‌ی Apply & restart همین کار را می‌کند.

⚡ بررسی سریع

فایل daemon.json را با میرور ویرایش و ذخیره کردی ولی docker info هیچ میرور نشان نمی‌دهد. محتمل‌ترین علت؟

مثال ۴: قبل از restart، فایل را بسنج

Section titled “مثال ۴: قبل از restart، فایل را بسنج”

یک JSON خراب باعث می‌شود daemon بالا نیاید و همه‌ی کانتینرها از کار بیفتند. daemon خودش یک دستور اعتبارسنجی دارد:

Terminal window
echo '{ "registry-mirrors": ["https://mirror.example.ir" ' > /var/tmp/bad.json
dockerd --validate --config-file /var/tmp/bad.json 2>&1 | head -2
echo '{"registry-mirrors":["https://mirror.example.ir"]}' > /var/tmp/good.json
dockerd --validate --config-file /var/tmp/good.json 2>&1 | head -2
خروجی
unable to configure the Docker daemon with file /var/tmp/bad.json: invalid JSON: unexpected end of JSON input
configuration OK

اول invalid JSON، دومی configuration OK. روی سرور واقعی قبل از systemctl restart docker همین را بزن. (این دستور را با sudo dockerd --validate --config-file /etc/docker/daemon.json روی لینوکس خودت اجرا کن.)

مثال ۵: Docker Desktop (مک و ویندوز)

Section titled “مثال ۵: Docker Desktop (مک و ویندوز)”

در Docker Desktop فایلی برای ویرایش دستی لازم نیست. طبق مستندات داکر: Settings ← Docker Engine را باز کن، همان JSON را در ویرایشگر داخل برنامه می‌بینی. کلید registry-mirrors را اضافه کن و Apply & restart را بزن. بعد با docker info در ترمینال بررسی کن (مثال ۳).

{
"registry-mirrors": ["https://mirror.example.ir"]
}

اگر ویرایشگر JSON از قبل کلیدهای دیگری دارد (مثل builder)، آن‌ها را نگه دار و فقط کلید جدید را اضافه کن؛ بین کلیدها ویرگول لازم است.

مثال ۶: میرور نامعتبر چه می‌کند؟ (اجرای واقعی)

Section titled “مثال ۶: میرور نامعتبر چه می‌کند؟ (اجرای واقعی)”

روی همان daemon ایزوله، یک میرور که وجود ندارد (آدرس نمونه) تنظیم کرده‌ایم. ببین pull چه می‌شود و چقدر طول می‌کشد:

Terminal window
docker rmi alpine >/dev/null 2>&1
start=$(date +%s)
docker pull alpine 2>&1 | tail -1
echo "زمان: $(( $(date +%s) - start )) ثانیه"
خروجی
docker.io/library/alpine:latest
زمان: 11 ثانیه

در این اجرا pull بالاخره موفق شد (daemon بعد از شکست میرور به Docker Hub برگشت)، ولی مجبور شد اول به میرور مرده سر بزند. در یک اجرای دیگرِ همان تست، با همین تنظیم یک بار خطای سخت گرفتم (متن ثبت‌شده در همان تست، نه خروجی اجرای بالا):

خطا (ثبت‌شده در یک اجرای تست)
Error response from daemon: failed to resolve reference "docker.io/library/alpine:latest": failed to do request: Head "https://mirror.example.ir/v2/library/alpine/manifests/latest?ns=docker.io": dial tcp: lookup mirror.example.ir: no such host

یعنی رفتار یک میرور مرده قابل‌پیش‌بینی نیست: ممکن است تأخیر بیندازد یا pull را شکست بدهد. نتیجه: میرور را قبل از گذاشتن تست کن.

حالا برش گردانیم. نکته‌ی مهم: حذف خود فایل و reload، میرور را پاک نمی‌کند (daemon فایل ناموجود را نمی‌خواند و تنظیم قبلی را نگه می‌دارد). باید فایل را با آرایه‌ی خالی بنویسی (یا daemon را کامل restart کنی):

Terminal window
echo '{"registry-mirrors":[]}' > /etc/docker/daemon.json
kill -HUP $(pidof dockerd); sleep 2
echo "میرورهای فعال: $(docker info 2>/dev/null | grep -c 'Registry Mirrors')"
خروجی
میرورهای فعال: 0

شمارنده 0 است؛ دیگر میرور فعالی نیست.

پشت پرده: میرور چطور کار می‌کند؟

Section titled “پشت پرده: میرور چطور کار می‌کند؟”

میرور یک pull-through cache است. مستندات رسمی داکر می‌گوید: اولین باری که یک image را از میرور محلی بخواهی، میرور آن را از registry عمومی می‌گیرد، نزد خودش ذخیره می‌کند و بعد به تو می‌دهد. دفعات بعد از کش جواب می‌دهد. در خطای مثال ۶ هم ردش را دیدی: daemon به آدرس https://mirror.example.ir/v2/library/alpine/manifests/latest?ns=docker.io درخواست زد؛ پارامتر ns=docker.io به میرور می‌گوید «این از Docker Hub است».

مهم است بدانی:

  • registry-mirrors فقط برای image های Docker Hub است. image هایی که آدرس registry دیگری دارند (مثل ghcr.io/...) از میرور نمی‌گذرند.
  • میرور باید HTTPS باشد (یا به‌عنوان insecure-registries اعلام شود که توصیه نمی‌شود).
  • میرور شخص ثالث می‌تواند نظریاً image را دستکاری کند. برای image های مهم، از digest استفاده کن (image@sha256:...) تا مطمئن باشی همان چیزی را گرفته‌ای که انتظار داری. (در درس «محدودیت Docker Hub و راه‌حل‌ها» بیشتر می‌گوییم.)
سیستم محل تنظیم اعمال تغییر
لینوکس (Engine) /etc/docker/daemon.json sudo systemctl restart docker
Docker Desktop (مک و ویندوز) Settings ← Docker Engine Apply & restart
دستور کاربرد
curl -s -o /dev/null -w "%{http_code}\n" https://registry-1.docker.io/v2/ تست رسیدن به Docker Hub (401 = رسیده)
docker info | grep -A3 Mirrors دیدن میرورهای فعال
dockerd --validate --config-file FILE اعتبارسنجی daemon.json قبل از restart
python3 -m json.tool FILE بررسی ساده‌ی صحت JSON
Terminal window
echo '{"registry-mirrors": ["https://mirror.example.ir",]}' > /var/tmp/trailing.json
dockerd --validate --config-file /var/tmp/trailing.json 2>&1 | head -2
خروجی
unable to configure the Docker daemon with file /var/tmp/trailing.json: invalid JSON: invalid character ']' looking for beginning of value

ویرگول اضافه بعد از آخرین عضو، JSON را نامعتبر می‌کند. راه‌حل: همیشه با --validate یا python3 -m json.tool بسنج.

۲) میرور مرده یا نامعتبر

Section titled “۲) میرور مرده یا نامعتبر”

مثال ۶ را ببین: میرور غیرقابل‌دسترس تأخیر می‌اندازد یا pull را شکست می‌دهد. راه‌حل: قبل از گذاشتن، با curl آدرس /v2/ میرور را تست کن (جواب 200 یا 401 یعنی زنده است)، و اگر مشکل‌ساز شد فوراً برش دار.

فایل را ذخیره کردی ولی تنظیم اعمال نشده (مثال ۳، «قبل از reload»). راه‌حل: restart (لینوکس) یا Apply & restart (Docker Desktop)، بعد docker info.

۴) فکر کردن «حذف فایل، میرور را پاک می‌کند»

Section titled “۴) فکر کردن «حذف فایل، میرور را پاک می‌کند»”

حذف daemon.json و reload کافی نیست (آخر مثال ۶). راه‌حل: آرایه‌ی خالی بنویس یا daemon را restart کن، بعد docker info را بررسی کن.

۵) ویرایش با ویرایشگر اشتباه روی Docker Desktop

Section titled “۵) ویرایش با ویرایشگر اشتباه روی Docker Desktop”

در Docker Desktop فایل daemon.json را از Finder یا Explorer ویرایش نکن؛ از Settings ← Docker Engine استفاده کن تا تغییر درست اعمال شود.

✎ تمرینآسان

بدون هیچ تنظیمی، با curl ثابت کن به Docker Hub دسترسی داری (فقط کد HTTP را چاپ کن).

دیدن جواب
Terminal window
curl -s -o /dev/null -m 15 -w "%{http_code}\n" https://registry-1.docker.io/v2/
خروجی
401

401 یعنی رسیدی. اگر به اینجا نرسیدی، به بخش «داکر در ایران» برگرد.

✎ تمرینمتوسط

روی daemon آزمایشی، یک میرور نمونه را در daemon.json بگذار، daemon را reload کن و فقط خطوط مربوط به میرور را از docker info نشان بده. بعد برش دار.

دیدن جواب
Terminal window
echo '{"registry-mirrors":["https://mirror.example.ir"]}' > /etc/docker/daemon.json
kill -HUP $(pidof dockerd); sleep 2
docker info 2>/dev/null | grep -A1 "Registry Mirrors"
echo '{"registry-mirrors":[]}' > /etc/docker/daemon.json; kill -HUP $(pidof dockerd); sleep 2
docker info 2>/dev/null | grep -c "Registry Mirrors"
خروجی
Registry Mirrors:
https://mirror.example.ir/
0
✎ تمرینسخت

روی daemon آزمایشی یک فایل با دو میرور نامعتبر بنویس. ثابت کن --validate آن را «OK» می‌داند (چون ساختار درست است) و docker info هر دو را نشان می‌دهد. بعد توضیح بده چرا --validate برای سلامت میرور کافی نیست و آخر کار تنظیم را پاک کن.

دیدن جواب
Terminal window
cat > /var/tmp/two.json <<'EOT'
{"registry-mirrors":["https://mirror-a.example.ir","https://mirror-b.example.ir"]}
EOT
dockerd --validate --config-file /var/tmp/two.json 2>&1 | head -1
cp /var/tmp/two.json /etc/docker/daemon.json; kill -HUP $(pidof dockerd); sleep 2
docker info 2>/dev/null | grep -A2 "Registry Mirrors"
echo '{"registry-mirrors":[]}' > /etc/docker/daemon.json; kill -HUP $(pidof dockerd); sleep 2
docker info 2>/dev/null | grep -c "Registry Mirrors"
خروجی
configuration OK
Registry Mirrors:
https://mirror-a.example.ir/
https://mirror-b.example.ir/
0

--validate فقط ساختار فایل (JSON و کلیدها) را می‌سنجد، نه اینکه آدرس‌ها زنده‌اند. برای سلامت میرور باید جداگانه با curl به مسیر /v2/ هر کدام درخواست بزنی (کد 200 یا 401 یعنی زنده).

؟ آزمونک
  1. میرور (mirror) چیست؟

  2. کلید تنظیم میرور در daemon.json کدام است؟

  3. کد HTTP «401» از https://registry-1.docker.io/v2/ یعنی چه؟

  4. اگر daemon.json را پاک کنی و daemon را reload کنی، میرور قبلی…

  5. قبل از restart کردن daemon بهتر است چه کنی؟

  • در شبکه‌ای که Docker Hub مشکل دارد، docker pull timeout می‌شود؛ اول با curl .../v2/ تشخیص بده.
  • میرور یک registry کش‌دار برای image های Docker Hub است؛ در daemon.json با کلید registry-mirrors (آرایه) تنظیم می‌شود.
  • لینوکس: /etc/docker/daemon.json + sudo systemctl restart docker؛ Docker Desktop: Settings ← Docker Engine ← Apply & restart.
  • با docker info میرور فعال را ببین و با dockerd --validate فایل را قبل از restart بسنج.
  • میرور مرده تأخیر می‌اندازد یا pull را شکست می‌دهد؛ آدرس را قبل از استفاده تست کن. برای پاک کردن میرور، آرایه‌ی خالی بنویس (فقط حذف فایل کافی نیست).
  • میرور شخص ثالث را با احتیاط انتخاب کن و برای image های مهم از digest استفاده کن.
برگه‌ی تقلب این درس
دستورکاری که می‌کند
sudo nano /etc/docker/daemon.jsonویرایش تنظیمات daemon (لینوکس)
{"registry-mirrors":["https://HOST"]}کلید میرور در daemon.json
sudo dockerd --validate --config-file /etc/docker/daemon.jsonاعتبارسنجی فایل
sudo systemctl restart dockerاعمال تغییرات (لینوکس)
docker info | grep -A3 Mirrorsدیدن میرورهای فعال
curl -s -o /dev/null -w "%{http_code}\n" https://registry-1.docker.io/v2/تست دسترسی به Docker Hub
python3 -m json.tool FILEبررسی صحت JSON