همانطور که اطلاع دارید docker hub ایران را تحریم کرده و کسانی که از سرورهای داخل ایران استفاده می کنند مشکلات بسیاری برای استفاده از این سرویس دارند. بنابراین بهترین و حرفه ای ترین راه برای استفاده از داکر در ایران، ساخت یک registry شخصی برای docker است. البته اگر کاربر شرکتی نیستید یا پروژه ای معمولی دارید شاید مقاله تغییر dns های سرور برای استفاده از docker بیشتر برایتان فایده داشته باشد.
برای راه اندازی local registry داکر یک image رسمی به نام registry دارد که از آن استفاده خواهیم کرد. همینطور در محیط production قویا توصیه می شود که از reverse proxy و ssl هم استفاده کنید. آموزش هایی که بود، یا به الزامات محیط production نپرداخته بودند و یا به صورت تمام داکری این کار را انجام نداده بودند. بهرحال، در این آموزش ما از nginx بعنوان reverse proxy استفاده خواهیم کرد و همه چیزهایی که نیاز داریم را در محیط docker پیاده سازی خواهیم کرد.
در ابتدا یک دایرکتوری به نام registry میسازیم که فایل ها و config های لازم آنجا ذخیره شوند:
mkdir registry cd registry
سپس در دایرکتوری ایجاد شده نیاز داریم چند دایرکتوری دیگر نیز بسازیم. پس از ساخت در باره آنها توضیح خواهیم داد:
mkdir auth cerbot data nginx
auth: برای ذخیره سازی اطلاعات مربوط به اعتبار سنجی(username & password) رجیستری شخصی مان از طریق nginx برای اینکه هر کسی از بیرون به آن دسترسی نداشته باشد.
cerbot: برای ذخیره سازی کلیدهای گواهینامه ssl.
data: محل ذخیره image های موجود در رجیستری ما خواهد بود.
nginx: محل ذخیره داکر فایل و کانفیگ nginx است.
برای این که می خواهیم چند container داکر را به طور موازی بالا بیاوریم استفاده از comman line بسیار گیج کننده خواهد شد. بنابراین یک فایل docker-compose.yml ایجاد می کنیم:
nano docker-compose.yml
سپس محتویات زیر را درون آن می نویسیم:
version: "3.9" services: nginx: build: ./nginx restart: always ports: - "80:80" - "443:443" volumes: - ./certbot/www:/var/www/certbot/:ro - ./certbot/conf/:/etc/nginx/ssl/:ro - ./auth:/auth registry: image: registry:latest ports: - "5000:5000" environment: REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY: /data volumes: - ./data:/data certbot: image: certbot/certbot volumes: - ./certbot/www/:/var/www/certbot/:rw - ./certbot/conf/:/etc/letsencrypt/:rw
فایل بالا نکته خاصی ندارد جز این که چند volume مشترک بین سرویس های nginx و cerbot برای اشتراک گذاری اطلاعات ssl ساختیم و در سرویس registry از طریق متغیر محیطی REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY
گفتیم که image ها کجا ذخیره شوند و سپس یک volume به روش bind mount برای آن ساختیم. همه این volume ها را می توانستیم به صورت named volume نیز ایجاد کنیم ولی برای ساده ماندن کار آموزشی این کار را نکردیم.
همچنین دایرکتوری nginx/. را برای ساختن image سرویس nginx معرفی کردیم. این دایرکتوری شامل یک Dockerfile و یک فایل nginx.conf می باشد. اکنون ما در دایرکتوری registry قرار داریم. فلذا به درون دایرکتوری nginx رفته و این فایل ها را ایجاد می کنیم:
cd nginx nano Dockerfile
محتویات Dockerfile:
FROM nginx:1.23-alpine RUN rm /etc/nginx/conf.d/default.conf COPY nginx.conf /etc/nginx/conf.d
سپس به ساختن و پرداختن فایل کانفیگ می پردزایم:
nano nginx.conf
محتویات nginx.conf:
server { listen 80; listen [::]:80; server_name example.com; server_tokens off; location /.well-known/acme-challenge/ { root /var/www/certbot; } location / { return 301 https://example.com$request_uri; } }
توجه داشته باشید که example.com ها را در هر بار ظاهر شدن، با نام دامنه خود عوض کنید. در قسمت location /.well-known/acme-challenge/
گفتیم فایل های مورد نیاز cerbot را serve کن. اما در قسمت location /
گفتیم که درخواست های کاربر به پورت 80 را به همان route ولی پورت 443 ریدایرکت کن. ولی همانطور که ملاحظه می کنید هیچ کانفیگی برای پورت 443 ننوشته ایم. دلیل آن این است این پورت به برخی پیش نیازهایی برای ssl وابسته است که هنوز ایجاد نشده اند و اگر کسی هوس می کرد برای تست کاری که تا اینجا کردیم docker composer up را اجرا کند با خطا مواجه میشد ولی اکنون با خیال راحت می تواند تست کند. پس ابتدا کانتینرها را می سازیم:
docker compose up -d --build
سپس پیش نیازهای ssl را ایجاد می کنیم:
docker compose run --rm certbot certonly --webroot --webroot-path /var/www/certbot/ -d example.com
پس از این مرحله می توانیم به nginx.conf رفته و کانفیگ های مورد نیاز پورت 443 و همچنین هدایت ترافیک از این پورت به رجیستری داکرمان را بنویسیم. پس از این مرحله فایل nginx.conf چنین خواهد بود:
upstream docker-registry { server registry:5000; } server { listen 80; listen [::]:80; server_name panel.brnstudents.ir; server_tokens off; location /.well-known/acme-challenge/ { root /var/www/certbot; } location / { return 301 https://panel.brnstudents.ir$request_uri; } } server { listen 443 default_server ssl http2; listen [::]:443 ssl http2; server_name example.com; ssl_certificate /etc/nginx/ssl/live/example.com/fullchain.pem; ssl_certificate_key /etc/nginx/ssl/live/example.com/privkey.pem; # disable any limits to avoid HTTP 413 for large image uploads client_max_body_size 0; # required to avoid HTTP 411: see Issue #1486 (https://github.com/moby/moby/issues/1486) chunked_transfer_encoding on; auth_basic "Registry realm" auth_basic_user_file /auth/nginx.htpasswd; location / { proxy_pass http://docker-registry; proxy_set_header Host $http_host; # required for docker client's sake proxy_set_header X-Real-IP $remote_addr; # pass on real client's IP proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_read_timeout 900; } }
خب، در ابتدا یک upstream docker-registry
تعریف کردیم و به آن server registry:5000
را معرفی کردیم.(مشابه کاری که برای django و gunicorn می کردیم) در واقع این کار به ما کمک می کند که درخواست هایی که به nginx ما می آیند را به سرور معرفی شده که در اینجا کانتینر registry که ساختیم می باشد، پروکسی کنیم. در ادامه کانفیگ آن را به proxy_pass
معرفی می کنیم. سپس یک سرور دیگر برای استفاده از پورت 443 ساختیم. چند خط اول برای تنظیمات مربوط به ssl هستند. دو خط بعدی نیز دارای توضیحات لازم هستند. اما auth_basic
و auth_basic_user_file
مربوط به اعتبار سنجی پروکسی nginx و رمز گذاری روی registry مان هستند. قبلا در docker-compose.yml برای auth یک volume تعریف کرده بودیم. این نحوه اعتبار سنجی را در مقاله رسمی وبسایت داکر Authenticate proxy with nginx دیدم و به نظر روش بهتری از سایر روشها می نماید بخصوص این که در document رسمی داکر هم به آن اشاره شده. در خطوط بعدی نیز ماژول upstream تعریف شده را به location /
به اصطلاح proxy_pass
کردیم.
حالا می توانید container ها را بسازید:
docker compose up -d --build
مرحله بعدی تعیین username و password برای رجیستری است. با دستور زیر می توانید این کار را انجام دهید:
docker run --rm --entrypoint htpasswd registry:2.7.0 -Bbn testuser testpassword > auth/nginx.htpasswd
testuser و testpassword را با یوزرنیم و پسوورد مورد نظر خودتان عوض کنید. همچنین می بینید که ما از ایمیج registry:2.7.0
استفاده کردیم. دلیل این کار در واقع باگی در image رجیستری داکر است که فقط در tag 2.7.0 وجود ندارد! به هر حال جزییات بیشتر را می توانید در این لینک بخوانید.
حالا باید کانتینر ها را restart کنیم:
docker compose restart
و حالا داکر رجیستری ما آماده است!
حالا که رجیستری ما ساخته شده، می خواهیم push و pull کردن از رجیستری را امتحان کنیم. اولین کاری که می کنیم login کردن با username و password ای است که پیشتر تعریف کردیم:
docker login -u=testuser -p=testpassword myregistrydomain.com
حالا فرض کنید می خواهیم image موسوم به hello-world داکر را که هنگام نصب برای اطمینان از نصب صحیح داکر run کردیم، به رجیستری مان push کنیم. برای این کار ابتدا با tag زدن به شکل زیر آن را به رجیستری خودمان منتقل می کنیم:
docker tag hello-world localhost:5000/hello-world:v1
حالا آن را با دستور زیر push می کنیم:
docker push localhost:5000/hello-world:v1
و تمام! حالا با رفت به آدرس زیر می تونیم لیست image های رجیستری مان را ببینیم:
https://myregistrydomain.com/v2/_catalog
توجه داشته باشید که ما می توانیم تمام image هایی که داریم را به این شکل به registry مان push کنیم. حتی از سرورهای دیگر، فقط کافی است به رجیستری مان login کنیم. برای دیدن لیست image ها هم که می توانیم از docker image ls استفاده کنیم.
برای pull کردن یک image هم دستور زیر را می زنیم:
docker pull myregistrydomain.com/hello-world:v1
توجه کنید همان tag ای که push کردید را می توانید pull کنید. که در این مثال v1 بود.
موفق باشید.