مطالب
مقدمه ای بر Docker، قسمت پنجم
  در قسمت‌های قبل با کلیات مفاهیم داکر آشنا شدیم. اما بنا داریم در این قسمت با اصول اولیه‌ی تهیه‌ی docker-compose آشنا شده و دستورالعمل اجرای کانتینر‌های مختلف را درون یک فایل نوشته و مدیریت نماییم. در واقع، compose ابزاری است برای تعریف و اجرای اپلیکیشن‌های multi-container.
با استفاده از YAML، دستورالعمل‌های سرویس‌های مختلف را نوشته و با یک دستور همه‌ی آنها را با هم اجرا مینماییم. از compose در تمامی مراحل production, staging, development, testing و همچنین CI workflow استفاده میشود.

برای استفاده از compose سه عمل زیر باید انجام شود:
1- ساخت  و تعریف dockerfile برای هر سرویس.
2- ساخت و تعریف docker-compose.yml. بنابراین هر سرویس میتواند در محیط ایزوله‌ی خود اجرا شود.
3- اجرای دستور docker-compose up.

در قسمت‌های قبلی مراحل ساخت و اجرای image‌ها درون کانتینر و همچنین متصل کردن آن‌ها را به شبکه، بررسی کرده ایم؛ اما در این قسمت میخواهیم با استفاده از docker-compose مدیریت build و اجرای همه‌ی image‌ها را بر عهده بگیریم.
عملا با این ساختار، قابلیت ایجاد شبکه، volume و غیره را خواهیم داشت. بنابراین با استفاده از این config توانایی توزیع برنامه را فقط با یک فایل YAML، خواهیم داشت.


ایجاد پروژه:

فرض کنید نرم افزار ما از دو سرویس Nodejsی همچنین یک دیتابیس Mongo تشکیل شده است. در نهایت باید به چیزی شبیه به تصویر زیر برسیم:


دایرکتوری root این پروژه از دو پوشه به نام‌های nodeapp1 و nodeapp2 تشکیل شده است که داخل هر کدام یک فایل index.js و همچنین package.json و dockerfile وجود دارد؛ همانند مطالب پیشین.
اما چیزی که اینجا اضافه شده است، فایل docker-compose.yml جهت مدیریت و اجرای این برنامه میباشد که حاوی ساختار زیر است:
version: '3'
networks:
  shared-network:
services:
  nodeapp1:
    image: nodeapp1
    build:
      context: nodeapp1
      dockerfile: dockerfile
    ports:
     - "8181:80"
    networks:
     - shared-network
  nodeapp2:
    image: nodeapp2
    build:
      context: nodeapp2
      dockerfile: dockerfile
    ports:
     - "8182:80"
    networks:
     - shared-network
  mongo:
    image: mongo
    ports:
      - "27017:27017"
    networks:
      - shared-network
1) ابتدا یک شبکه از نوع bridge را به نام shared-network میسازیم.
2) برای مشخص کردن سرویس‌های این برنامه از services استفاده کرده و آن‌ها را تعریف مینماییم.
3) سرویس nodeapp1 که قرار است تصویری به نام nodeapp1 را ایجاد کند (هدف آن build کردن اولین سرویس میباشد. همانطور که مشخص است context برنامه، اسم پوشه‌ی nodeapp1 درون ریشه‌ی پروژه است. ضمن اینکه نام dockerfile را هم درون آن پوشه بدان اضافه کرده‌ایم).
4) پورت 8181 را بر روی پورت 80 درون این کانتینر هدایت می‌کنیم.
5) این سرویس، درون شبکه‌ی ایجاد شده‌ی shared-network قرار می‌گیرد.
5) سرویس nodeapp2 را هم به همین شکل اضافه می‌کنیم.
6) سرویس mongo قرار نیست هیچ کدی را build کند و هدف، فقط اجرای mongo درون شبکه‌ی shared-network است که بقیه سرویس‌ها بتوانند بدان وصل شوند.


برای ساخت و اجرا نیز در ریشه‌ی این پروژه، ترمینال خود را باز کرده و دستورات زیر را وارد مینماییم:
برای build کردن:
 docker-compose build
برای اجرا کردن:
 docker-compose up
برای حذف کردن:
 docker-compose down
برای stop کردن موقتی:
 docker-compose stop
برای start کردن مجدد:
 docker-compose start

و اگر بخواهیم بعد از build کردن، بصورت خودکار نیز اجرا شود، از دستور زیر استفاده میکنیم:
 docker-compose run --build

dockerfile هر دو سرویس نیز بصورت ساده همانند مطالب پیشین در نظر گرفته شده‌است:
FROM node
COPY . /var/www
WORKDIR /var/www
RUN npm i
EXPOSE 80
ENTRYPOINT node index

در صورتیکه بخواهیم نگاهی هم به کد‌های نوشته شده بیندازیم، نکته‌ی جالبی مد نظر قرار میگیرد؛ بطور مثال از آنجائیکه همه‌ی کانتینر‌های اجرا شده، درون یک شبکه هستند، برای فراخوانی سرویس‌های دیگر کافیست با نامشان صدا زده شوند. بطور مثال در nodeapp1 برای فراخوانی nodeapp2 به راحتی با نام صدا زده شده است و احتیاجی به فراخوانی با ip نیست. کدهای زیر مربوط به فایل index.js در سرویس nodeapp1 میباشند (بدلیل اینکه روی پورت 80 درون کانتینر قرار گرفته‌است، دیگر لازم به وارد نمودن پورت نبودیم؛ در غیر اینصورت بطور مثال باید درخواستی بصورت http://nodeapp2:5000 را ارسال مینمودیم):
const express = require('express');
const fetch = require('node-fetch');
const app = express();

app.get('/', async (req, res) => {
    let response = await fetch("http://nodeapp2/");
    text = await response.text();
    res.send(text);
})

app.listen(80, () => console.log(`listening on port 80!`))
 بعد از اجرا کردن docker-compose، به راحتی سرویس‌های ما از طریق پورت 8181 و 8182 قابلیت فراخوانی را دارند. 

نکته: override کردن compose‌ها نیز قابل انجام است. بدین معنا که شما یک نسخه برای build و استفاده در محیط development و نسخه‌های دیگری بطور مثال برای production خود تعریف مینمایید؛ مثلا روی پروداکشن، environment variables‌های متفاوتی را در نظر میگیرید. YAML زیر را مشاهده کنید:
 version: '3'

services:
nodeapp1:
  environment:
- PRODUCTION: 'true'
nodeapp2:
  environment:
- PRODUCTION: 'true'

فرض کنید که قرار است YAML فوق بر روی فایل قبلی، بازنویسی شود؛ با استفاده از دستور زیر:
 docker-compose -f docker-compose.yml -f docker-compose.prod.yml up

تمام کد‌های فوق از اینجا «node.rar» قابل دریافت میباشد.
نظرات مطالب
کار با Docker بر روی ویندوز - قسمت دوم - نصب Docker
من نمی‌توانم هیچ یک از image هارو اجرا کنم. حتی hello-world و همین microsoft/iis:nanoserver 
خطایی که دریافت میکنم
docker: Error response from daemon: hcsshim::CreateComputeSystem 1c11dcf156f978c77f095a247697e61046a057cbbe6e0f4ce379ac3c27a8893e: 
The virtual machine could not be started because a required feature is not installed.
(extra info: {"SystemType":"Container","Name":"1c11dcf156f978c77f095a247697e61046a057cbbe6e0f4ce379ac3c27a8893e","Owner":"docker","IgnoreFlushesDuringBoot":true,"LayerFolderPath":"C:\\ProgramData\\Docker\\windowsfilter\\1c11dcf156f978c77f095a247697e61046a057cbbe6e0f4ce379ac3c27a8893e","Layers":[{"ID":"07980919-a64b-5c53-a9f5-90d601652283","Path":"C:\\ProgramData\\Docker\\windowsfilter\\bafa745f07de7a1225fde00e2a023ef3f3506b0aa860533d698274db8b6470bf"},{"ID":"a61fb5af-c609-53d4-bc44-60dd64782af7","Path":"C:\\ProgramData\\Docker\\windowsfilter\\19fa1b0fe7a7ae21a6759065a777bb97258999e19e807e7585cc04294d280415"},{"ID":"9ab68230-2445-5771-ab79-381a25f39628","Path":"C:\\ProgramData\\Docker\\windowsfilter\\878443b99b40a380ab046bdde15c2e4e3fd550de9bac9dda965608eade6eb57e"},{"ID":"1c3d5a81-0cc7-57fa-80a1-e730452d057c","Path":"C:\\ProgramData\\Docker\\windowsfilter\\4ae8898165beb41737d803086903772e04dd1c4f5bfc3a07f4609066ccf67762"},{"ID":"2f086665-fabf-5ca3-b14b-a36bad45c008","Path":"C:\\ProgramData\\Docker\\windowsfilter\\6f383e273091a5a33427f644820696e6356b1b1cfa7cd662f8477445d2564676"}],"HostName":"1c11dcf156f9","HvPartition":true,"EndpointList":["7C713800-DCBE-4525-99E7-DE82C06F285C"],"HvRuntime":{"ImagePath":"C:\\ProgramData\\Docker\\windowsfilter\\4ae8898165beb41737d803086903772e04dd1c4f5bfc3a07f4609066ccf67762\\UtilityVM"},"AllowUnqualifiedDNSQuery":true}).

برداشت خودم اینه که من نمی‌تونم با این سیستم داکر اجرا کنم. اگر درست فکر میکنم بهم بگید و یا لطفا یک راه حل برای اجرای داکر(با windows image) بهم بگید. ایمیج لینوکس می‌دونم نمیشه کلا.

البته من WSL را نصب دارم. تداخلی ایجاد نمیکنه؟

مطالب
کار با Docker بر روی ویندوز - قسمت هفتم - مدیریت اجرای چندین کانتینر به هم وابسته
تا اینجا نحوه‌ی اجرای برنامه‌ها، وب سرورها و حتی بانک‌های اطلاعاتی را توسط داکر بررسی کردیم. در این قسمت می‌خواهیم یک برنامه و بانک اطلاعاتی مخصوص آن‌را داخل یک کانتینر اجرا کنیم و برای این منظور از ابزار ساده کننده‌ی docker-compose استفاده خواهیم کرد.


docker-compose چیست؟

فرض کنید برنامه‌ی ما، از یک قسمت منطق خود برنامه و قسمت دیگر بانک اطلاعاتی آن تشکیل شده‌است. در این حالت برای توزیع آن توسط کانتینرها، نیاز به دو کانتینر مجزا خواهد بود؛ یکی برای برنامه و دیگری برای بانک اطلاعاتی:
dockerrun --name db`
-d `
-p 3306:3306 `
-e MYSQL_ROOT_PASSWORD=my-secret-pw `
-v db:/var/lib/mysql`
mysql

dockerinspect db # extract ipaddress

dockerrun --name web `
-d `
-p 8080:80 `
-e MY_DB_PORT=3306 `
-e MY_DB_HOST=? `
-v /my/php/app:/usr/share/nginx/html `
nginx
تمام این دستورات را به همراه یک ` نیز مشاهده می‌کنید. این روشی است که از آن برای چندسطری کردن دستورات در PowerShell استفاده می‌شود.
- دستور اول مطابق توضیحات قسمت قبل، یک بانک اطلاعاتی MySQL را در پس زمینه، با نام db که در آن، پورت 3306 میزبان به پورت 3306 کانتینر نگاشت شده‌است و همچنین بانک اطلاعاتی آن در یک volume نامدار به نام db با مسیر نگاشت شده‌ی به /var/lib/mysql/ داخل کانتینر ایجاد می‌شود، اجرا می‌کند.
- دستور دوم کار استخراج اطلاعات این کانتینر را انجام می‌دهد که شامل آدرس IP آن نیز می‌باشد. از این IP در برنامه‌ی وب استفاده خواهیم کرد.
- دستور سوم مطابق توضیحات قسمت پنجم، یک وب سرور nginx را برای هاست یک برنامه‌ی PHP که در آن پورت 8080 میزبان به پورت 80 کانتینر نگاشت شده‌است و همچنین فایل‌های آن از مسیر /my/php/app/ میزبان به مسیر /usr/share/nginx/html/ داخل کانتینر نگاشت و تامین می‌شوند، اجرا می‌کند. در اینجا از پارامتر e برای تعریف یک سری متغیر محیطی مانند شماره‌ی پورت و IP کانتینر اجرا کننده‌ی mysql، استفاده شده‌است.

در این مثال دو کانتینر به هم وابسته را اجرا کرده‌ایم و برای اجرای کانتینر دوم، نیاز است حداقل IP کانتینر اول را دانست و در قسمت MY_DB_HOST مقدار دهی کرد. روش دیگری نیز برای مدیریت ساده‌تر اجرای چندین کانتینر به هم وابسته توسط ابزاری به نام docker-compose وجود دارد. اگر از Dockerfile (که آن‌را در قسمت پنجم معرفی کردیم) جهت ایجاد Imageهای سفارشی بکار می‌رود، فایل docker-compose.yml، کار خودکار سازی ایجاد و اجرای چندین کانتینر را انجام می‌دهد که با قالب YAML تعریف می‌شود:
version: '2'
services:
    db:
         image: mysql
         ports:
             -3306:3306
         environment:
             -MYSQL_ROOT_PASSWORD=my-secret-pw
         volumes:
             -db:/var/lib/mysql

    web:
         image: nginx
         ports:
             -8080:80
         environment:
             -MY_DB_PORT=3306
             -MY_DB_HOST=db
         volumes:
             -/my/php/app:/usr/share/nginx/html
همانطور که مشاهده می‌کنید، هرچند قالب آن اندکی متفاوت شده‌است، اما در اصل با دستورات docker ای که در ابتدا معرفی کردیم، تفاوتی ندارد. محتوای فوق را در یک فایل متنی ویژه به نام docker-compose.yml ذخیره و آن‌را به ابزار خط فرمان دیگری به نام docker-compose معرفی می‌کنیم.
در ابتدای این فایل، شماره نگارش قالب YAML مورد استفاده، مشخص شده‌است. در این نگارش، به کانتینرها، services گفته می‌شود که در اینجا دو سرویس db و web را مشاهده می‌کنید. در فایل‌های yml، فضاهای خالی و indentations مهم هستند و بر این اساس است که کانتینرها و سپس مشخصات این کانتینرها، تمیز داده می‌شوند.


راه اندازی TeamCity به کمک فایل docker-compose.yml آن

در اینجا محتویات فایل docker-compose.yml مخصوص راه اندازی TeamCity را مشاهده می‌کنید که از سه کانتینر تشکیل شده‌است و از بانک اطلاعاتی postgres استفاده می‌کند:
version: '2'
services:
    teamcity:
        image: sjoerdmulder/teamcity
        ports:
            - 8111:8111
    teamcity-agent:
        image: sjoerdmulder/teamcity-agent
        environment:
            - TEAMCITY_SERVER=http://teamcity:8111
    postgres:
        image: postgres
        environment:
            - POSTGRES_DB=teamcity
در این تنظیمات، پورت 8111 میزبان به پورت 8111 کانتینر teamcity نگاشت شده‌است. در ادامه teamcity-agent نیاز به IP این کانتینر را دارد (یک build-server است). زمانیکه چندین کانتینر را توسط فایل docker-compose.yml راه اندازی می‌کنیم، داکر یک شبکه‌ی ایزوله (private network) را نیز برای اینکار مهیا می‌کند. داخل این شبکه‌ی ایزوله، یک DNS سرور توکار نیز وجود دارد که امکان دسترسی به کانتینرهای مختلف را از طریق نام کانتینرهای آن‌ها میسر می‌کند. به همین جهت است که مقدار TEAMCITY_SERVER، به http://teamcity:8111 تنظیم شده‌است و دقیقا از نام کانتینر teamcity برای یافتن IP آن استفاده می‌کند. همچنین باید دقت داشت در این آدرس، عدد 8111 به پورت داخل کانتینر teamcity اشاره می‌کند و نه به پورت میزبان. کانتینرها از طریق آدرس IP و پورت خودشان با هم در تماس هستند. پورت میزبان 8111، صرفا جهت فراخوانی teamcity از طریق سیستم میزبان مشخص شده‌است.


در ادامه برای کار با آن، ابتدا این محتویات را به صورت یک فایل متنی docker-compose.yml ذخیره کنید. سپس از طریق خط فرمان به پوشه‌ی آن وارد شده و دستور docker-compose up را صادر کنید. docker-compose یکی دیگر از ابزارهای خط فرمان نصب شده‌ی به همراه داکر است و پارامتر up آن کار راه اندازی و اجرای کانتینرهای ذکر شده‌ی در فایل yml موجود را انجام می‌دهد. نام پوشه‌ای که این فایل در آن قرار دارد، به عنوان نام پروژه‌ی مشترک بین این کانتینرها در گزارشات آن مورد استفاده قرار می‌گیرد.
پس از صدور این فرمان، ابتدا تمام imageهای ذکر شده‌ی در فایل yml دریافت می‌شوند (سه image در اینجا) و هر سه کانتینر راه اندازی می‌شوند. اکنون می‌توان در سیستم میزبان به آدرس http://localhost:8111 مراجعه کرد و از برنامه‌ی teamcity استفاده نمود. البته صفحه‌ی ابتدایی آن کار تنظیمات بانک اطلاعاتی آن‌را انجام می‌دهد و جائیکه در مورد database type سؤال می‌پرسد می‌توان postgres را انتخاب کرد و سپس در ذیل آن مقدار database host را نیز postgres وارد می‌کنیم. علت آن‌را نیز پیشتر توضیح دادیم. postgres در اینجا نام کانتینر نیز هست و ذکر نام آن، با ذکر IP مرتبط با آن کانتینر، یکی است. نام بانک اطلاعاتی را teamcity وارد کنید (مطابق تنظیمات فایل yml فوق) و نام کاربری آن نیز postgres است؛ بدون کلمه‌ی عبور. البته می‌شد در فایل yml فوق، متغیر محیطی POSTGRES_PASSWORD=xyz را نیز تنظیم کرد و سپس از آن در اینجا استفاده نمود.


docker-compose و ایجاد شبکه‌های ایزوله

توسط دستور docker network ls می‌توان لیست شبکه‌های مجازی ایجاد شده‌ی توسط docker را مشاهده کرد (و همچنین سایر network adapters موجود). اگر این دستور را اجرا کنید، کارت شبکه‌ی مجازی متناظر با شبکه‌ی خصوصی teamcity_default را که پیشتر در مورد آن توضیح داده شد، می‌توانید مشاهده کنید. این teamcity در اینجا همان نام پروژه و یا در اصل نام پوشه‌ای است که فایل docker-compose را از آنجا اجرا کردیم.
برای دریافت اطلاعات بیشتری در مورد این کارت شبکه‌ی به خصوص، می‌توان دستور docker network inspect teamcity_default را صادر کرد. یکی از قسمت‌های خروجی این گزارش، لیست کانتینرهایی است که هم اکنون به این شبکه متصل هستند؛ که در اینجا teamcity و بانک اطلاعاتی آن است.
مزیت ایجاد یک شبکه‌ی خصوصی مخصوص کانتینرهای به هم پیوسته، علاوه بر سادگی تشکیل فایل docker-compose آن‌ها با اشاره‌ی به نام کانتینرها، بجای ذکر مستقیم آدرس IP هر کدام، ایزوله ساختن این شبکه، از شبکه‌ی پیش‌فرض docker و بالا بردن میزان امنیت سایر کانتینرهایی است که هم اکنون از آن شبکه استفاده می‌کنند.


docker-compose و ایجاد DNS Server توکار

همانطور که عنوان شد، در این شبکه‌ی خصوصی ویژه‌ی کانتینرهای به هم پیوسته که توسط docker-compse اجرا و مدیریت شده‌اند، می‌توان از نام containerها بجای آدرس IP آن‌ها استفاده کرد و این مورد با وجود یک DNS Server توکار در این شبکه میسر شده‌است. برای آزمایش بیشتر این قابلیت، ابتدا دستور docker ps را صادر می‌کنیم تا نام کانتینرهای در حال اجرا را بدست بیاوریم. سپس سعی می‌کنیم پروسه‌ی bash shell داخل کانتینر بانک اطلاعاتی را اجرا کنیم:
docker ps
docker exec -it teamcity_postgres_1 bash
#exit
استفاده از docker exec یک روش است برای دسترسی به shell این کانتینر در حال اجرا؛ روش دیگر، استفاده از خود docker-compse می‌باشد که در این حالت، کار با آن ساده‌تر است:
 docker-compose exec postgres bash
در اینجا نیازی به ذکر سوئیچ it نیست؛ چون مقدار پیش‌فرض آن است و همچنین دیگر نیازی به اجرای docker ps برای یافتن نام کانتینری هم نیست و مستقیما می‌توان از نام‌های سرویس‌هایی که در فایل docker-compose.yml تعریف شده‌اند، استفاده کرد.
پس از دسترسی به شل، دستور زیر را اجرا کنید:
#ping teamcity
#exit
مشاهده خواهید کرد که این کانتینر می‌تواند کانتینر دیگری را صرفا با ذکر نام آن، ping کند.

یک نکته: اگر بخواهیم از وضعیت بانک‌های اطلاعاتی postgres توسط برنامه‌ی psql آن گزارش بگیریم نیز روش اجرای آن به همین صورت است:
docker-compose exec postgres psql -U postgres
postgres=#\l
postgres=#\q


اتصال یک کانتینر خارج از شبکه‌ی مجازی ایجاد شده‌ی توسط docker-compose به آن

فرض کنید می‌خواهید کانتینر کم حجم لینوکس alpine را اجرا کنید و توسط آن به شبکه‌ی مجازی ایجاد شده‌ی توسط docker-compose متصل شوید. روش آن به صورت زیر است:
 docker run --name apline -it --rm --net teamcity_default alpine sh
در اینجا توسط سوئیچ net، می‌توان نام شبکه‌ی مجازی را که نیاز است به آن متصل شویم، ذکر کرد. نام teamcity_default نیز از طریق اجرای دستور docker netwrok ls بدست آمده‌است.
این دستور، کانتینر لینوکس alpine را در حالت interactive جهت اجرای shell آن، راه اندازی می‌کند. سپس به شبکه‌ی مجازی teamcity_default متصل می‌شود. برای آزمایش این اتصال، در این shell راه اندازی شده، دستور ping teamcity را می‌توان صادر کرد. همچنین از داخل کانتینر teamcity نیز می‌توان این کانتینر را با نام آن ping کرد.


راه اندازی مجدد کانتینرها توسط docker-compose

اگر دستور docker-compose ps را دقیقا در پوشه‌ای که فایل yml آن قرار دارد اجرا کنیم، می‌توان گزارشی را صرفا از وضعیت کانتینرهای مرتبط با این فایل yml بدست آورد. دستور docker ps، لیست وضعیت تمام کانتینرهای در حال اجرای موجود را بر می‌گرداند. اکنون فرض کنید یکی از کانتینرهای اجرای شده‌ی توسط docker-compose، دیگر در حال اجرا نیست. برای راه اندازی مجدد آن می‌توان از دستور docker-compose start teamcity-agent استفاده کرد. همچنین دستور docker-compose logs teamcity-agent، لیست آخرین لاگ‌های مرتبط با یک کانتینر را بر می‌گرداند که می‌تواند برای رفع اشکال بسیار مفید باشد.


حذف کانتینرهای به هم پیوسته‌ی ایجاد شده‌ی توسط docker-compose

در ذیل ابتدا یک سری دستور را جهت مشاهده‌ی وضعیت سیستم مشاهده می‌کنید. سپس دستور docker-compose stop، کار متوقف کردن کانتینرهای مرتبط با فایل yml آن‌را انجام می‌دهد. دستور docker-compose rm -v، علاوه بر حذف این کانتینرها، volumeهای بانک‌های اطلاعاتی مرتبط را نیز حذف می‌کند. در آخر دستور docker-compose down، شبکه‌ی مجازی مرتبط را نیز حذف خواهد کرد. سپس مجددا از وضعیت سیستم گزارش گیری شده‌است.
 docker ps
docker-compose ps
docker volume ls
docker network ls

docker-compose stop
docker-compose rm -v
docker-compose down

docker ps -a
docker volume ls
docker network ls


اجرای پروژه‌ی ASP.NET Core Music Store توسط docker-compose

پروژه‌ی معروف Music Store مایکروسافت را به همراه فایل docker-compose.windows.yml آن، در اینجا می‌توانید مشاهده کنید. محتوای این فایل نیز به صورت زیر است:
version: '3'
services:
  db:
    image: microsoft/mssql-server-windows-developer
    environment:
      sa_password: "Password1"
      ACCEPT_EULA: "Y"
    ports:
      - "1433:1433" # REMARK: This is currently required, needs investigation
    healthcheck:
      test: [ "CMD", "sqlcmd", "-U", "sa", "-P", "Password1", "-Q", "select 1" ]
      interval: 1s
      retries: 30
web:
    build:
      context: .
      dockerfile: Dockerfile.windows
    environment:
      - "Data:DefaultConnection:ConnectionString=Server=db,1433;Database=MusicStore;User Id=sa;Password=Password1;MultipleActiveResultSets=True"
    depends_on:
      - db
    ports:
      - "5000:5000"
در اینجا تعریف دو کانتینر را مشاهده می‌کنید:
- کانتینر db که بر اساس image مخصوص mssql-server-windows-developer راه اندازی می‌شود. تنظیمات آن نیز بسیار شبیه به مطلب «کار با Docker بر روی ویندوز - قسمت ششم - کار با بانک‌های اطلاعاتی درون Containerها» است که پیشتر در مورد آن بحث کردیم.
- کانتینر web آن که از فایل Dockerfile.windows برای build سپس publish و در آخر run خودکار این برنامه‌ی ASP.NET Core، کمک می‌گیرد. در اینجا context به پوشه‌ی جاری اشاره می‌کند. در قسمت تنظیمات بانک اطلاعاتی آن، استفاده‌ی از نام کانتینر db را در قسمت رشته‌ی اتصالی مشاهده می‌کنید. قسمت depends_on آن ترتیب اجرای این کانتینرها را مشخص می‌کند. یعنی ابتدا باید کانتینر db اجرا شود و سپس web.
محتوای فایل Dockerfile.windows آن نیز به صورت زیر است که بر اساس دستورات NET Core CLI. تهیه شده‌است:
FROM microsoft/dotnet-nightly:2.0-sdk-nanoserver
SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]
ENV NUGET_XMLDOC_MODE skip
ENV DOTNET_SKIP_FIRST_TIME_EXPERIENCE 1
RUN New-Item -Path \MusicStore\samples\MusicStore -Type Directory
WORKDIR app
ADD samples/MusicStore/MusicStore.csproj samples/MusicStore/MusicStore.csproj
ADD build/dependencies.props build/dependencies.props
ADD NuGet.config .
RUN dotnet restore --runtime win10-x64 .\samples\MusicStore
ADD samples samples
RUN dotnet publish --output /out --configuration Release --framework netcoreapp2.0 --runtime win10-x64 .\samples\MusicStore
FROM microsoft/dotnet-nightly:2.0-runtime-nanoserver
WORKDIR /app
COPY --from=0 /out .
EXPOSE 5000
ENV ASPNETCORE_URLS http://0.0.0.0:5000
CMD dotnet musicstore.dll
روش اجرای آن‌را نیز به صورت زیر بیان کرده‌است:
docker-compose -f .\docker-compose.windows.yml build
docker-compose -f .\docker-compose.windows.yml up
البته باید دقت داشت که اجرای فرمان up، به صورت خودکار کار build را هم انجام می‌دهد. پس از اجرای آن، برنامه را بر روی پورت 5000 می‌توانید مشاهده کنید.
مطالب
کار با Docker بر روی ویندوز - قسمت چهارم - اجرای برنامه‌های خط فرمان درون Containerها
یکی از مزایای مهم کار با Docker، امکان اجرای برنامه‌ای، بدون اینکه بدانیم چگونه باید آن‌را نصب کرد، می‌باشد. در اینجا فقط کافی است دستور docker run را صادر کنیم و ... آن برنامه اجرا می‌شود. در این قسمت سعی خواهیم کرد تعدادی برنامه‌ی خط فرمان را توسط Docker اجرا کنیم تا بتوانیم با جزئیات بیشتری از آن آشنا شویم.


داخل یک Image چیست؟

در قسمت قبل دریافتیم که یک Image، از کل User Space مورد نیاز جهت اجرای یک برنامه تشکیل می‌شود. در ادامه می‌خواهیم بررسی کنیم، این ادعا تا چه حد صحت دارد و یک Image‌، واقعا از چه اجزائی تشکیل می‌شود.
با استفاده از دستور docker images می‌توان لیست imageهای دریافت شده را به همراه tagهای مرتبط با هر کدام، مشاهده کرد. سپس با استفاده از دستور docker save می‌توان image ای خاص را export کرد؛ برای مثال:
docker save microsoft/iis:nanoserver -o iis.tar
این دستور، image مربوط به iis با tag ای به نام nanoserver را در فایل c:\Users\Vahid\iis.tar ذخیره می‌کند.
البته ویندوز در حالت استاندارد آن، قابلیت کار با فایل‌های tar را ندارد. هرچند می‌توان از نرم افزارهای ثالثی مانند win-rar و یا 7zip برای انجام اینکار استفاده کرد، اما تمام Linux Containers، به همراه ابزار کمکی tar، برای استخراج محتوای این نوع فایل‌ها نیز هستند. بنابراین نیاز است از حالت Windows Containers به Linux Containers سوئیچ کرد. برای اینکار فقط کافی بر روی آیکن Docker در قسمت Tray Icons ویندوز کلیک راست نمود و گزینه‌ی Switch to Linux Containers را انتخاب کرد. اکنون اگر دستور docker images را صادر کنیم، تنها لیست imageهای لینوکسی از پیش دریافت شده را می‌توان مشاهده کرد.
در ادامه، image یکی از سبک‌ترین توزیع‌های لینوکسی را به نام alpine، دریافت می‌کنیم که فقط 5 مگابایت حجم دارد؛ چون آنچنان فایلی در user space آن وجود ندارد. به همین جهت برای دریافت آن، دستور docker pull alpine را صادر می‌کنیم. اکنون اگر دستور docker images را صادر کنیم، این image جدید در لیست خروجی آن قابل مشاهده‌است.
سپس چون می‌خواهیم یک shell را در داخل این container اجرا کنیم (مانند اجرای کنسول PowerShell قسمت قبل)، نیاز است دستور docker run آن‌را با سوئیچ it اجرا کنیم:
docker run -it alpine sh
در اینجا sh، همان shell داخل این کانتینر است. پس از اجرای این دستور، به command prompt آن منتقل می‌شویم. برای نمونه در اینجا دستور ps را صادر کنید تا بتوانید لیست پروسه‌های در حال اجرای آن‌را مشاهده کنید و یا دستور tar را صادر کنید؛ مشاهده خواهید کرد که این ابزار در داخل این توزیع لینوکسی وجود دارد.
اکنون می‌خواهیم به فایل iis.tar ای که export کردیم از اینجا دسترسی پیدا کنیم. این فایل نیز بر روی فایل سیستم ویندوز 10 تولید شده‌است. به همین جهت نیاز است بتوان به این فایل خارج از container جاری دسترسی پیدا کرد.


روش به اشتراک گذاری فایل سیستم میزبان با کانتینرها

برای اینکه از داخل یک container به فایلی در خارج از آن دسترسی پیدا کنیم، باید درایو آن فایل خارجی را اصطلاحا mount کرد؛ دقیقا مانند اتصال یک USB Stick به سیستم و اضافه شدن آن به صورت یک درایو جدید. در Docker، اینکار توسط مفهومی به نام Volumes انجام می‌شود. برای این منظور بر روی آیکن Docker در قسمت Tray Icons ویندوز کلیک راست کرده و گزینه‌ی Settings را انتخاب کنید. البته این تصویر را در حالت کار با Linux Containers مشاهده می‌کنید:


در اینجا درایو C را انتخاب و Apply کنید و سپس نام کاربری و کلمه‌ی عبور ویندوز خود را وارد نمائید، تا مجوز دسترسی به این درایو، به این Container داده شود.
سپس دستور زیر را اجرا کنید:
docker run --rm -v c:/Users:/data alpine ls /data
در این دستور:
- سوئیچ rm به این معنا است که Container، پس از پایان کار، به طور کامل حذف می‌شود.
- کار mount، با سوئیچ v که به معنای volume mount است، انجام می‌شود.
- عبارت c:/Users:/data به این معنا است که پوشه‌ی C:\users ویندوز را به مسیر data/ داخل container لینوکسی نگاشت کن. به همین جهت بین این دو قسمت یک : وجود دارد و مسیرهای لینوکسی فاقد نام درایو خاصی هستند.
در این دستور می‌توان پوشه‌ای خاص مانند c:/Users/Vahid:/data و یا فایلی خاص را مانند c:/Users/Vahid/iis.tar:/data نیز نگاشت کرد. مزیت آن کاهش سطح دسترسی Container به فایل‌های سیستم میزبان است.
- این دستور با اجرای دستور ls، محتوای پوشه‌ی C:\users را لیست می‌کند.


کار با فایل‌های سیستم میزبان از داخل یک Container

در ادامه دستور فوق را به صورت زیر اجرا می‌کنیم که در آن فایل iis.tar میزبان، در داخل container در مسیر data/ آن قابل دسترسی شده‌است و همچنین بجای ls، دستور sh صادر شده‌است تا به shell آن دسترسی پیدا کنیم. به همین جهت نیاز به سوئیچ it نیز وجود دارد:
docker run --rm -it -v c:/Users/vahid/iis.tar:/data alpine sh
اکنون که به shell دسترسی پیدا کرده‌ایم، از ابزار tar برای چاپ محتویات داخل فایل iis.tar استفاده می‌کنیم:
tar -tf /data/iis.tar


در اینجا حداقل هفت پوشه را مشاهده می‌کنید که داخل هر کدام فایلی به نام layer.tar قرار دارد؛ لایه‌هایی که این image را ایجاد می‌کنند.
پس از این، اگر دستور استخراج این فایل‌ها را صادر کنیم (t برای نمایش لیست محتویات است و x برای استخراج آن‌ها):
mkdir /data/extract
tar -xf /data/iis.tar -C /data/extract
این فایل‌ها در پوشه‌ی /data/extract/ استخراج خواهند شد.


انتقال فایل‌ها از Container به سیستم میزبان

البته نکته‌ی مهم اینجا است که این پوشه‌ی /data/extract/ صرفا داخل دیسک مجازی این container ایجاد شده‌است و در سمت ویندوز قابل دسترسی و مشاهده نیست.
برای رفع این مشکل، اینبار دستور tar -xf را به صورت زیر اجرا می‌کنیم:
 docker run --rm -it -v c:/Users/vahid/:/data alpine tar -xf /data/iis.tar -C /data/iis
در این دستور بجای صرفا نگاشت تک فایل c:/Users/vahid/iis.tar میزبان، کل پوشه‌ی c:/Users/vahid میزبان، به مسیر /data/ کانتینر نگاشت شده‌است. در ادامه پس از ذکر نام image، اصل فرمان را مستقیما صادر کرده‌ایم. درست مانند اینکه یک فرمان لینوکسی را مستقیما داخل ویندوز صادر کرده باشیم؛ بدون اینکه نیازی باشد به shell آن توزیع لینوکسی مراجعه کنیم. در این حالت اگر در مسیر c:/users/vahid، پوشه‌ی جدید iis را ایجاد کنیم (در سمت میزبان ویندوزی) و سپس دستور فوق را اجرا کنیم، این فایل‌ها در پوشه‌ی c:\users\vahid\iis نیز ظاهر می‌شوند و دقیقا مانند این است که پوشه‌ی /data/ کانتینر، با میزبان ویندوزی به اشتراک گذاشته شده‌است.
در اینجا اگر دستور tar را بر روی این لایه‌ها اجرا کنیم، در نهایت به یک چنین خروجی‌هایی خواهیم رسید:



که بسیار شبیه به درایو C یک سیستم ویندوزی است. بنابراین این لایه، همان OS Apps & Libs را به همراه دارد که در قسمت قبل با آن آشنا شدیم.


یک مثال دیگر: اجرای ffmpeg توسط docker

اگر خاطرتان باشد بحث docker را در قسمت اول با معرفی ffmpeg و سخت بودن اجرای آن آغاز کردیم. در ادامه می‌خواهیم image آن‌را دریافت و سپس با تبدیل آن به یک container، کار تبدیل فرمت‌های ویدیویی را انجام دهیم:
 docker run --rm --volume ${pwd}:/output jrottenberg/ffmpeg -i http://site.com/file.mp4 /output/file.gif
در این دستور:
- سوئیچ rm کار حذف container ایجاد شده را پس از اتمام کار آن انجام می‌دهد.
- سپس مسیرجاری (پوشه‌ی جاری در سیستم میزبان که دستور فوق را اجرا می‌کند) به مسیر output/ کانتینر نگاشت شده‌است.
- در ادامه مخزن dockerhub ای که ffmpeg قرار است از آن دریافت و اجرا شود، مشخص شده‌است.
- در آخر پارامترهای کار با ffmpeg برای دریافت یک فایل mp4 آنلاین و تبدیل آن به یک فایل animated gif محلی، ذکر شده‌اند. این فایل در مسیر output کانتینر ایجاد می‌شود که در نهایت با میزبان، به اشتراک گذاشته خواهد شد.
- خروجی نهایی تولید شده را در سیستم میزبان در همان پوشه‌ای که دستور فوق را صادر کرده‌اید، مشاهده خواهید کرد.
مطالب
کار با Docker بر روی ویندوز - قسمت اول - Container چیست؟
نصب بسیاری از نرم افزارها، کاری مشکل است

فرض کنید می‌خواهید یک فایل ویدیویی با قالب m4v را بر روی تلویزیون خود نمایش دهید؛ اما تلویزیون شما تنها از فایل‌های mp4، پشتیبانی می‌کند. برای رفع این مشکل نیاز به یک نرم افزار تبدیل کننده‌ی فرمت‌های ویدیویی را داریم و یکی از قوی‌ترین‌های آن‌ها، FFmpeg است. اگر به سایت آن مراجعه کنید، لینک دانلود آن به یک فایل tar.bz2 ختم می‌شود که حاوی سورس آن است! هرچند در قسمتی از آن، فایل‌های نهایی کامپایل شده‌ی مخصوص سیستم عامل‌های مختلف را نیز می‌توانید پیدا کنید، اما باز هم با انبوهی از لینک‌ها مواجه خواهید شد که دقیقا مشخص نیست کدام را باید دریافت کرد و آیا نگارش دریافت شده، با سیستم عامل فعلی سازگار است یا خیر.
همانطور که مشاهده می‌کنید، هنوز هم شروع به کار با نرم افزارهای مختلف برای بسیاری از کاربران، کاری مشکل و طاقت‌فرسا است. در اینجا شاید این سؤال مطرح شود که این موضوع چه ربطی به Docker (Docker) و کانتینرها (Containers) دارد؟ تمام هیاهویی که پیرامون Docker ایجاد شده‌است، در اصل جهت ساده سازی نصب، راه اندازی و تعامل با نرم افزارهای مختلف است.


چالش‌های پیش روی یافتن نرم افزارهای مناسب


این روزها بیشتر نرم افزارهای مورد نیاز خود را از اینترنت تهیه می‌کنیم. اولین مرحله‌ی آن و اولین چالشی که در اینجا وجود دارد، یافتن نرم افزاری با مشخصات مدنظر است. برای نمونه حتی اگر با FFmpeg آشنا نیز باشید، به سادگی مشخص نیست که برای سیستم عامل و معماری خاص پردازنده‌ی آن، دقیقا کدام نگارش آن‌را از چه آدرسی می‌توان دریافت کرد. پس از یافتن نرم افزار و نگارش مدنظر، مرحله‌ی بعد، استخراج محتویات آن از یک فایل zip و یا اجرای برنامه‌ی نصاب آن است و مرحله‌ی آخر، اجرای این برنامه می‌باشد.
بنابراین اولین چالش، یافتن محلی برای دریافت نرم افزار است:
-  این روزها برای بعضی از سکوهای کاری، App Storeهایی وجود دارند که می‌توان از آنجا شروع کرد؛ اما چنین قابلیتی برای تمام سکوهای کاری پیش بینی نشده‌است.
- در لینوکس قابلیت دیگری به نام Package manager وجود دارد که کار یافتن و نصب نرم افزارها را ساده می‌کند؛ اما گاهی از اوقات اطلاعات آن، آنچنان به روز نیست. همچنین اگر بسته‌ای برای توزیع خاصی از لینوکس وجود داشته باشد، الزاما به این معنا نیست که این بسته، قابلیت استفاده‌ی در سایر توزیع‌های لینوکس را نیز به همراه دارد. در ویندوز نیز وضعیت مشخص است! فاقد یک Package manager توکار و استاندارد است. هرچند یک App Store برای آن از طرف مایکروسافت ارائه شده‌است، اما آنچنان محبوبیتی پیدا نکرده‌است.
- و روش متداول دیگری که وجود دارد، مراجعه‌ی مستقیم به سایت اصلی سازنده‌ی نرم افزار است.

- علاوه بر این‌ها داشتن یک سری متادیتا و آمار نیز در مورد نرم افزارها بسیار مفید هستند تا بتوانند در مورد تصمیم به استفاده‌، یا عدم استفاده‌ی از نرم افزار، راهنمای کاربران باشند؛ مانند میزان محبوبیت، تعداد بار دریافت، تعداد مشکلاتی که کاربران با آن داشته‌اند و آخرین باری که نرم افزار به روز شده‌است. اما با توجه به پراکندگی روش‌های دریافت نرم افزار که ذکر شدند، عموما یک چنین آمارهایی را مشاهده نمی‌کنیم.
- چالش دیگر، مشکل سخت اطمینان کردن به روش‌های مختلف توزیع نرم افزارها است. آیا سایتی که این نرم افزار را ارائه می‌دهد، واقعا مرتبط با نویسنده‌ی اصلی آن است؟ همچنین آیا خود نرم افزار مشکلات امنیتی را به همراه ندارد؟ چه کاری را انجام می‌دهد؟
- مشکل بعدی، در دسترس بودن سایت توزیع کننده‌ی نرم افزار است. آیا زمانیکه به برنامه‌ای نیاز داریم، پهنای باند سایت توزیع کننده‌ی آن تمام نشده‌است و می‌توان به آن دسترسی داشت؟
- چالش دیگر، چگونگی پرداخت مبلغی برای دسترسی به نرم افزار است. به نظر تا به اینجا تنها App Storeها موفق شده‌اند روشی یک دست را برای خرید برنامه‌ها و همچنین ارائه‌ی مجوزی برای استفاده‌ی از آن‌ها، ارائه دهند.


چالش‌های پیش روی نصب نرم افزارها

زمانیکه به مرحله‌ی نصب نرم افزار می‌رسیم، هر نرم افزار، روش نصب و تنظیمات آغازین خاص خودش را دارد.
- اولین چالش پس از دریافت نرم افزار، بررسی سازگاری آن با سیستم عامل و پردازنده‌ی فعلی است. شاید این مسایل برای توسعه دهندگان نرم افزارها پیش‌پا افتاده به نظر برسند، اما برای عموم کاربران، چالشی جدی به شما می‌روند.
- پس از مشخص شدن سازگاری یک نرم افزار با سیستم فعلی، قالب ارائه‌ی آن نرم افزار نیز می‌توان مشکل‌زا باشد. بعضی از برنامه‌ها صرفا از طریق سورس کد منتشر می‌شوند. بعضی از آن‌ها توسط یک فایل exe متکی به خود ارائه می‌شوند و بعضی دیگر به همراه یک فایل exe و تعدادی dll به همراه آن‌ها. گاهی از اوقات این برنامه‌ها نیاز به نصب جداگانه‌ی NET Runtime. و یا Java Runtime را برای اجرا دارند و یا وابستگی آن‌ها صرفا به نگارش خاصی از این کتابخانه‌ها و فریم ورک‌های ثالث است. هرچند اگر برنامه‌ای به همراه بسته‌ی نصاب آن باشد، به احتمال زیاد این وابستگی‌ها را نیز نصب می‌کند؛ اما تمام برنامه‌ها اینگونه ارائه نمی‌شوند. به علاوه خیلی‌ها علاقه‌ای به کار با برنامه‌های نصاب ندارند و از ایجاد تغییرات بسیاری که آن‌ها در سیستم ایجاد می‌کنند، خشنود نیستند.
- پس از نصب نرم افزار، مشکل بعدی، نحوه‌ی به روز رسانی آن‌ها است. چگونه باید اینکار انجام شود؟ (تمام مراحل و چالش‌هایی را که تاکنون بررسی کردیم، یکبار دیگر از ابتدا مرور کنید!)

بنابراین همانطور که مشاهده می‌کنید، نصب، راه اندازی و به روز رسانی نرم افزارها این روزها بسیار پیچیده شده‌اند و بسیاری از کاربران به سادگی از عهده‌ی آن‌ها بر نمی‌آیند.


چالش‌های پیش روی کار با نرم افزارها

مرحله‌ی بعد، نیاز به مستندات کافی برای کار با برنامه است. این مستندات را از کجا می‌توان تهیه کرد؟ آخرین باری که به روز شده، چه زمانی بوده‌است؟ بسیاری از اوقات بین مستندات تهیه شده و آخرین نگارش نرم افزار، ناسازگاری وجود دارد و به سختی قابل استفاده‌است. آیا نیاز است برنامه را به PATH اضافه کرد؟ آیا نیاز است به صورت سرویس نصب شود؟ اگر بله، چگونه باید این مراحل را انجام داد؟ مجوز کار کردن با آن‌ها چگونه است؟
مشکل مهم دیگری که حین کار با نرم افزارها، در حالت متداول آن‌ها وجود دارد، دسترسی کامل آن‌ها به تمام اجزای سیستم و شبکه است و درون یک sandbox (قرنطینه) امنیتی اجرا نمی‌شوند.
مشکل بعدی، به روز رسانی اجزای ثالث سیستم و یا حتی خود سیستم عامل، مانند به روز رسانی OpenSSL نصب شده و پس از آن، از کار افتادن برنامه‌ای خاص است که وابستگی به نگارشی خاص از این کتابخانه را دارد.


کانتینرها در مورد برنامه‌ها هستند و نه مجازی سازی

خوب، تا اینجا دریافتیم که مدیریت توزیع، نصب و استفاده‌ی از برنامه‌ها، کار ساده‌ای نیست. اما این‌ها چه ارتباطی با Docker دارند؟ در بسیاری از اوقات، زمانیکه صحبت از Docker می‌شود، تصور بسیاری از آن، ارائه‌ی جایگزینی برای ماشین‌های مجازی است. اما ... اینگونه نیست. کانتینرها در مورد نرم افزارها هستند. برای مثال در آینده در مورد ایمیج‌های (Images) کانتینرها بیشتر بحث خواهیم کرد. این ایمیج‌ها در اصل یک بسته‌ی حاوی برنامه‌ها هستند. بنابراین بیشتر شبیه به فایل zip ای است که از یک وب سایت دریافت می‌کنیم (در قسمت یافتن نرم افزار).


یک کانتینر (Container) چیست؟

برای درک بهتر مواردی که تاکنون بحث شدند و همچنین بررسی مفهوم Containers، ابتدا MonogoDB را به صورت معمول نصب می‌کنیم. سپس نحوه‌ی نصب آن‌را درون یک Container بررسی خواهیم کرد. البته هدف اصلی در اینجا، بررسی مفهومی این مراحل و مقایسه‌ی آن‌ها با هم هستند و در قسمت‌های بعدی کار نصب و استفاده‌ی از Docker را قدم به قدم بررسی خواهیم کرد.
 
مراحل نصب محلی MongoDB به صورت متداول:
- ابتدا برای مثال به سایت گوگل مراجعه کرده و mongodb را جستجو می‌کنیم تا بتوانیم به سایت اصلی و محل دریافت بسته‌ی آن، هدایت شویم.
- پس از ورود به سایت mongodb، در بالای صفحه اصلی آن، لینک به صفحه‌ی دریافت بسته‌ی mongodb را می‌توان مشاهده کرد.
- با انتخاب آن، به صفحه‌ی دریافت بسته‌ی mongodb بر اساس سیستم عامل‌های مختلفی هدایت می‌شویم. برای مثال در ویندوز، بسته‌ی msi آن‌را دریافت می‌کنیم.
- به نظر می‌رسد که بسته‌ی نصاب msi آن تمام کارهای لازم برای راه اندازی اولیه‌ی mongodb را انجام می‌دهد. به همین جهت آن‌را اجرا کرده و پس از چندبار انتخاب گزینه‌ی next، نصب آن به پایان می‌رسد.
- پس از پایان نصب، ابتدا به کنسول service.msc ویندوز مراجعه می‌کنیم تا مطمئن شویم که سرویس آن، توسط نصاب msi نصب شده‌است یا خیر؟ و ... خیر! این نصاب، سرویس آن‌را نصب نکرده‌است.
- به همین جهت به مستندات نصب آن در سایت mongodb مراجعه می‌کنیم (لینک Installation instructions در همان صفحه‌ی دریافت بسته‌ی msi وجود دارد). پس از پایان مراحل نصب، عنوان کرده‌است که باید دستور md \data\db را اجرا کنید تا مسیر پیش فرض اطلاعات آن به صورت دستی ایجاد شود. اما ... این مسیر دقیقا به کجا اشاره می‌کند؟ چون شبیه به مسیرهای ویندوزی نیست.
- در ادامه برای آزمایش، به پوشه‌ی program files ویندوز رفته، monogodb نصب شده را یافته و سپس فایل mongod.exe را از طریق خط فرمان اجرا می‌کنیم (برنامه‌ی سرور). اگر این کار را انجام دهیم، این پروسه با نمایش خطای یافت نشدن مسیر c:\data\db، بلافاصله خاتمه پیدا می‌کند. به همین جهت در همین مسیری که در خط فرمان قرار داریم (جائیکه فایل mongod.exe قرار دارد)، دستور md \data\db را اجرا می‌کنیم. اجرای این دستور در این حالت، همان پوشه‌ی c:\data\db را ایجاد می‌کند. نکته‌ای که شاید خیلی‌ها با آن آشنایی نداشته باشند.
- اکنون اگر مجددا فایل mongod.exe را اجرا کنیم، اجرای آن موفقیت آمیز خواهد بود و پیام منتظر دریافت اتصالات بودن از طریق پورت 27017 را نمایش می‌دهد.
- مرحله‌ی بعد، اجرای فایل mongo.exe است تا به این دیتابیس سرور در حال اجرا متصل شویم (برنامه‌ی کلاینت). در اینجا برای مثال می‌توان دستور show dbs را اجرا کرد تا لیست بانک‌های اطلاعاتی آن‌را نمایش دهد.
 

مراحل نصب MongoDB به صورت Container توسط Docker:
- ابتدا برای مثال به سایت گوگل مراجعه کرده و اینبار mongodb docker را جستجو می‌کنیم تا بتوانیم به محل دریافت image آن هدایت شویم. با ورود به آن، در بالای صفحه عنوان شده‌است که official repository است که سبب اطمینان از بسته‌ی ارائه شده‌ی توسط آن می‌شود. بنابراین در اینجا بجای مراجعه به سایت متکی به خود mongodb، به docker hub برای دریافت آن مراجعه کرده‌ایم. در اینجا با جستجوی یک برنامه، متادیتا و اطلاعات آماری بسیاری را نیز می‌توان در مورد برنامه‌های مختلف، مشاهده کرد که در سایت متکی به خود نرم افزارهای مختلف، در دسترس نیستند. همچنین در اینجا اگر بر روی برگه‌ی Tags یک مخزن کلیک کنید، مشاهده می‌کنید که تمام فایل‌های موجود در آن توسط docker hub از لحاظ مشکلات امنیتی پیشتر اسکن شده‌اند و گزارش آن‌ها قابل مشاهده‌است. علاوه بر این‌ها docker hub به همراه یک docker store برای برنامه‌های غیر رایگان نیز هست و این مورد فرآیند کار با نرم افزارهای تجاری را یک دست می‌کند.
- مرحله‌ی بعد، دریافت یک کپی از mongodb از docker hub است. اینبار بجای دریافت مستقیم یک فایل zip یا msi، از دستور docker pull mongo استفاده می‌شود که یک image را در نهایت دریافت می‌کند. این image، حاوی برنامه‌ی مدنظر و تمام وابستگی‌های آن است.
- پس از دریافت image، مرحله‌ی بعد، اجرای mongodb به همراه آن است. در حالت متداول، ابتدا نرم افزار داخل فایل zip یا msi استخراج شده و سپس بر روی سیستم اجرا می‌شوند، اما در اینجا مفهوم معادل نصب نرم افزار دریافت شده‌ی از بسته‌ی zip همراه آن، یک container است. یک container دقیقا مانند یک نرم افزار از پیش نصب شده، عمل می‌کند و معادل اجرای فایل exe مانگو دی بی در اینجا، اجرای container آن است. بنابراین docker، از image دریافت شده، یک container را ایجاد می‌کند که دقیقا معادل یک نرم افزار از پیش نصب شده، رفتار خواهد کرد.
- پس از دریافت image، جهت اجرای آن به عنوان یک container، برای استفاده از نرم افزاری که دریافت کرده‌ایم، تنها یک دستور است که باید با آن آشنا باشیم: docker run mongo. این دستور را در همان صفحه‌ی docker hub مربوطه نیز می‌توانید مشاهده کنید. پس از اجرای این دستور، دقیقا همان خروجی و پیام منتظر دریافت اتصالات بودن از طریق پورت 27017 را مشاهده خواهیم کرد. برای اجرای کلاینت آن نیز دستور docker exec -it 27 mongo را می‌توان اجرا کرد. docker exec کار اجرای چندباره‌ی یک نرم افزار نصب شده را انجام می‌دهد.
این فرآیند در مورد تمام containerها یکی است و به این ترتیب به ازای هر نرم افزار مختلف، شاهد روش نصب متفاوتی نخواهیم بود.
- اجرای دستور docker stop نیز سبب خاتمه‌ی تمام این‌ها می‌شود.


در این تصویر مقایسه‌ای را بین مراحل متداول یافتن، دریافت، نصب و اجرای برنامه‌ها را در دو حالت متداول و همچنین استفاده‌ی از docker، مشاهده می‌کنید.

همچنین نکته‌ی جالبی که در مورد docker وجود دارد این است که اگر به task manager ویندوز مراجعه کنیم:


تمام پروسه‌هایی که با job id مساوی 172 در اینجا اجرا شده‌اند، متعلق به docker بوده و آن‌ها دقیقا مانند یک پروسه‌ی معمولی سیستم عامل جاری، در کنار سایر پروسه‌های موجود، اجرا می‌شوند. بنابراین برنامه‌ای که از طریق docker اجرا می‌شود، هیچ تفاوتی با اجرای متداول آن بر روی سیستم عامل، از طریق روش مراجعه‌ی مستقیم به فایل exe مرتبط و اجرای مستقیم آن ندارد. همانطور که پیش‌تر نیز عنوان شد، containerها در مورد نرم افزارها هستند و نه مجازی سازی و یک container در حال اجرا، حاوی تعدادی برنامه‌ی در حال اجرای بر روی سیستم عامل جاری، در کنار سایر برنامه‌های آن می‌باشد.
البته containers به همراه ایزوله سازی‌های بسیاری اجرا می‌شوند. برای مثال به روز رسانی یک کتابخانه‌ی ثالث بر روی سیستم عامل، سبب از کار افتادن برنامه‌ی اجرای شده‌ی توسط یک container نمی‌شود.


در قسمت بعد، نحوه‌ی نصب Docker را بر روی ویندوز، بررسی می‌کنیم.
نظرات مطالب
کار با Docker بر روی ویندوز - قسمت پنجم - ایجاد Imageهای سفارشی
Docker در اصل یک برنامه‌ی لینوکسی هست که بعدها به ویندوز و Mac هم منتقل شد و containers آن بر فراز هسته‌ی لینوکس اجرا می‌شوند (در سایر سیستم عامل‌ها یک Linux virtual machine برای آن تهیه شده). ویندوز یک سیستم عامل هست که تشکیل شده از کتابخانه‌ها و برنامه‌های کاربردی که بر فراز یک هسته اجرا می‌شود. در قسمت سوم، این مورد بیشتر بحث شده: «چرا حجم Image مخصوص .NET Core نگارش nanoserver آن حدود 800 مگابایت است؟»
نظرات مطالب
مقدمه‌ای بر داکر، قسمت سوم
من دستور زیر را اجرا میکنم 
docker build -f Dockerfile -t alikhll/testasp1 . 
Step 5/7 : RUN npm i
 ---> [Warning] The requested image's platform (linux/amd64) does not match the detected host platform (windows/amd64) and no specific platform was requested
 ---> Running in dc37481d5781

added 48 packages, and audited 49 packages in 6s

found 0 vulnerabilities
npm notice
npm notice New minor version of npm available! 8.5.5 -> 8.7.0
npm notice Changelog: <https://github.com/npm/cli/releases/tag/v8.7.0>
npm notice Run `npm install -g npm@8.7.0` to update!
npm notice
The command '/bin/sh -c npm i' returned a non-zero code: 4294967295: failed to shutdown container: container dc37481d5781615dbf01c129f2322f06fcac8851a8a4078a6273438a8427254a encountered an error during hcsshim::System::waitBackground: failure in a Windows system call: The virtual machine or container with the specified identifier is not running. (0xc0370110): subsequent terminate failed container dc37481d5781615dbf01c129f2322f06fcac8851a8a4078a6273438a8427254a encountered an error during hcsshim::System::waitBackground: failure in a Windows system call: The virtual machine or container with the specified identifier is not running. (0xc0370110)
کلا هر دستور npm که اجرا میکنم خطای بالا رو میده
توی پوشه اش کامند npm i  اجرا میکنم درست کار میکنه و پکیچ‌ها نصب میشه ولی داخل محیط داکر خیر
از docker desktop و ویندوز 10 استفاده میکنم
پکیج نود هم از داکر دانلود و نصب کردم
نظرات مطالب
کار با Docker بر روی ویندوز - قسمت دوم - نصب Docker
بله. فعال سازی Hyper-V سبب از کار افتادن VirtualBox می‌شود و این دو با هم سازگار نیستند (البته با VMWare مشکلی نیست؛ شخصا این مورد را آزمایش کردم). یک نگارش قدیمی‌تر از Docker برای ویندوز، به نام docker toolbox هم وجود دارد که برای اجرای Linux Containers از خود VirtualBox استفاده می‌کند. این روش مشکلات زیر را به همراه دارد:
- docker toolbox یک پروژه‌ی خاتمه یافته و منسوخ شده‌است و مطلقا ویژگی‌های جدید docker را به همراه ندارد.
- فقط و فقط قابلیت اجرای Linux Containers را دارد. برای اجرای Windows Containers تنها راه حل موجود، روشی است که در مطلب جاری بحث شده‌است؛ یعنی استفاده از برنامه‌های Docker For Windows به همراه Hyper-V.
بنابراین اگر نیاز به کار با Docker For Windows و همچنین Virtual Box را دارید، باید به صورت زیر عمل کنید:
الف) نیاز به اجرای Virtual Box است؛ Hyper-V را توسط اجرای دستور زیر با دسترسی ادمین، غیرفعال کنید:
bcdedit /set hypervisorlaunchtype off
ب) نیاز به اجرای Docker for Windows است؛ Hyper-V را توسط اجرای دستور زیر با دسترسی ادمین، فعال کنید:
bcdedit /set hypervisorlaunchtype auto
هر دو دستور، نیاز به ری‌استارت کردن سیستم را هم دارند؛ چون Hyper-V پیش از فعال شدن کرنل ویندوز شروع به کار می‌کند. Hyper-V ویندوز اصطلاحا  Type 1 hyper-visor است و بر روی سخت افزار هاست اجرا می‌شود. اما Virtual Box یا VMWare متفاوت بوده و Type 2 hosted hyper-visor هستند که بر روی OS اجرا می‌شوند.
مطالب
راه اندازی دیتابیس postgresql در برنامه‌های ASP.NET Core – قسمت 2

در قسمت قبل به معرفی postgresql پرداختیم; در این قسمت قصد ایجاد و راه اندازی یک api با استفاده از دیتابیس postgresql و استفاده از تکنولوژی‌های آن را با استفاده از docker داریم.


ابتدا با استفاده از دستور زیر یک پروژه‌ی جدید asp.net core را ایجاد کنید:

dotnet new webapi --minimal -o YourDirectoryPath:\YourFolderName

سپس فایل docker-compose.yaml را به روت پروژه اضافه کنید که شامل کانفیگ‌های زیر میباشد: 

version: '3.1'

services:

  db:
    image: postgres
    container_name: db
    restart: always
    environment:
      POSTGRES_PASSWORD: postgres
      POSTGRES_USERNAME: postgres
      POSTGRES_DB: BloggingDb
    ports:
        - "5432:5432"
    volumes:
      - postgres_data:/data/db

  adminer:
    image: adminer
    restart: always
    ports:
      - 8080:8080
  
  pgadmin4:
    image: dpage/pgadmin4
    restart: always
    environment:
      PGADMIN_DEFAULT_EMAIL: pgadmin4@pgadmin.org
      PGADMIN_DEFAULT_PASSWORD: admin
      PGADMIN_CONFIG_SERVER_MODE: 'False'
    ports:
      - 5050:80
    volumes:
      - pgadmin:/var/lib/pgadmin
    depends_on:
      - db

volumes:
  postgres_data:
  pgadmin:

سپس با اجرای دستور زیر در روت پروژه، سرویس‌ها را راه اندازی کنید: 

docker compose up -d


معرفی سرویس‌های استفاده شده در تنظیمات فایل بالا: 

سرویس db

نمونه ایمیج اصلی، volume، تنظیمات connection string در آن استفاده شده است.

سرویس adminer :

https://hub.docker.com/_/adminer /

Adminer - Database management in a single PHP file

یک برنامه تحت وب مدیریت پایگاه داده ساده میباشد که ویژگی‌ها MySql را در کنار سرعت و امنیت ارائه میدهد و در آدرس http://localhost:8080 / اجرا خواهد شد.

سرویس pgadmin4 :

pgAdmin - PostgreSQL Tools

dpage/pgadmin4 - Docker Image | Docker Hub

در حال حاضر این برنامه محبوب‌ترین برنامه مدیریت پایگاه داده میباشد که ویژگی‌های پیشرفته‌ای را نیز پوشش میدهد و در آدرس http://localhost:5050 / اجرا خواهد شد. 


اکنون نوبت نوشتن کد‌ها میباشد. 

- تنظیم connection string در فایل appsettings.json:

"ConnectionStrings": {
    "BloggingContext": "Username=postgres;Password=postgres;Server=localhost;Database=BloggingDb”
}

- و همینطور پکیج‌های زیر را به برنامه خود رفرنس دهید: 

dotnet add package Npgsql.EntityFrameworkCore.PostgreSQL
dotnet add package Microsoft.EntityFrameworkCore
dotnet add package Microsoft.EntityFrameworkCore.Design

- مدل‌های برنامه را در مسیر /Models ایجاد کنید: 

namespace NpgsqlAPI.Models;

public class Post
{
    public int PostId { get; set; }
    public string Title { get; set; } = null!;
    public string Content { get; set; } = null!;

    public int BlogId { get; set; }
    public Blog Blog { get; set; } = null!;
}

namespace NpgsqlAPI.Models;

public class Blog
{
    public int BlogId { get; set; }
    public string? Url { get; set; }

    public List<Post>? Posts { get; set; }
}

- سپس BloggingContext را در مسیر /Data ایجاد کنید:

using Microsoft.EntityFrameworkCore;
using NpgsqlAPI.Models;

namespace NpgsqlAPI.Data;

public class BloggingContext : DbContext
{
    public BloggingContext(DbContextOptions<BloggingContext> options)
        : base(options)
    {
    }
    public DbSet<Blog> Blogs => Set<Blog>();
    public DbSet<Post


- سپس اینترفیس IBlogServices را در مسیر  /Servicec/Blogs ایجاد کنید: 

using NpgsqlAPI.Models;

namespace NpgsqlAPI.Services.Blogs;
public interface IBlogServices
{
    Task<IEnumerable<Blog>> GetList();
    Task<Blog?> Get(uint id);
    Task<uint> Add(Blog obj);
    Task AddRange(Blog[] obj);
    Task Update(Blog obj);
    Task UpdateRange(Blog[] obj);
    Task Remove(uint id);
}


-  و سپس پیاده سازی آن را در فایل BlogEFServices و در کنار اینترفیس آن قرار دهید: 

using Microsoft.EntityFrameworkCore;
using NpgsqlAPI.Data;
using NpgsqlAPI.Models;

namespace NpgsqlAPI.Services.Blogs;
public sealed class BlogEFServices : IBlogServices
{
    private readonly BloggingContext _context;
    public BlogEFServices(BloggingContext context)
    {
        _context = context;
    }

    public async Task<uint> Add(Blog obj)
    {
        await _context.Blogs.AddAsync(obj);
        return (uint)await SaveChangesAsync();
    }

    public async Task AddRange(Blog[] obj)
    {
        await _context.Blogs.AddRangeAsync(obj);
        await SaveChangesAsync();
    }

    public async Task<Blog?> Get(uint id)
    {
        return await _context.Blogs.FirstOrDefaultAsync(x=>x.BlogId == id);
    }

    public async Task<IEnumerable<Blog>> GetList()
    {
       return await _context.Blogs.ToListAsync();
    }

    public async Task Remove(uint id)
    {
        var entity = await Get(id);
        _context.Blogs.Remove(entity!);
        await SaveChangesAsync();
    }

    public async Task Update(Blog obj)
    {
        _context.Blogs.Update(obj);
        await SaveChangesAsync();
    }

    public async Task UpdateRange(Blog[] obj)
    {
        _context.Blogs.UpdateRange(obj);
        await SaveChangesAsync();
    }

    private async Task<int> SaveChangesAsync()
    {
        return await _context.SaveChangesAsync();
    }
}


- اکنون  endpoint‌های api را در فایل program.cs ایجاد کنید:

using System.Data;
using Microsoft.EntityFrameworkCore;
using Npgsql;
using NpgsqlAPI.Services.Blogs;
using NpgsqlAPI.Data;
using NpgsqlAPI.Models;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

string connectionString = builder.Configuration.GetConnectionString("BloggingContext")!;

builder.Services.AddDbContext<BloggingContext>(options =>
        options.UseNpgsql(connectionString));

builder.Services.AddTransient<IDbConnection>(_ => new NpgsqlConnection(connectionString));

// builder.Services.AddScoped<IBlogServices, BlogDapperServices>();
// builder.Services.AddScoped<IBlogServices, BlogEFRawQueryServices>();
builder.Services.AddScoped<IBlogServices, BlogEFServices>();

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.MapGet("/blogs", async (IBlogServices service) => await service.GetList())
.WithName("GetBlogs")
.WithOpenApi();

app.MapGet("/blogs/{id}", async (IBlogServices service, uint id) => await service.Get(id))
.WithName("GetBlog")
.WithOpenApi();

app.MapPost("/blogs", async (IBlogServices service, Blog blog) => await service.Add(blog))
.WithName("AddBlog")
.WithOpenApi();

app.MapDelete("/blogs/{id}", async (IBlogServices service, uint id) => await service.Remove(id))
.WithName("RemoveBlog")
.WithOpenApi();

app.MapPut("/blogs", async (IBlogServices service, Blog blog) => await service.Update(blog))
.WithName("UpdateBlog")
.WithOpenApi();

app.MapPut("/blogs/Bulk", async (IBlogServices service, Blog[] blogs) =>
 await service.UpdateRange(blogs))
.WithName("UpdateBulkBlog")
.WithOpenApi();

app.MapPost("/blogs/Bulk", async (IBlogServices service, Blog[] blogs) =>
 await service.AddRange(blogs))
.WithName("AddBulkBlog")
.WithOpenApi();

app.Run();

تمامی کد‌های برنامه تا به اینجا نوشته شده‌اند. اکنون migration را پس از اطمینان از اجرا بودن داکر اجرا کنید 

dotnet ef migrations add Init
dotnet ef database update

و برنامه را اجرا و تست کنید. 


کد‌های کامل این مطلب

مطالب
معرفی Bit Platform
پلتفرم Bit یک پروژه تماما Open Source در GitHub میباشد که هدف آن تسهیل توسعه نرم افزار با کیفیت و پرفرمنس بالا بر بستر ASP.NET Core و زبان #C است که با آن بتوان فقط با یکبار کدنویسی و با کمک استانداردهای وب همچون HTML / CSS و Web Assembly ، خروجی‌هایی چون Android / iOS / Windows را با دسترسی کامل به امکانات سیستم‌عامل به همراه برنامه‌های تحت وب SPA و PWA (با یا بدون Pre-Rendering) گرفت.
پلتفرم Bit تا به اینجا از دو قسمت Bit Blazor Components (شامل بیش از ۳۰ کمپوننت کاربردی، کم حجم و High Performance مانند Tree / Multi Select / Data Grid / Date Picker / Color Picker و...) به همراه Bit Project Templates (قالب پروژه‌های حاوی امکانات پر استفاده) تشکیل شده است.
برخی مواردی که در رابطه با آنها صحبت شد، ممکن است برای شما آشنا نباشند، بنابراین قبل از بررسی مفصل‌‌تر Bit Platform، نگاهی به آن می‌اندازیم:


وب اسمبلی چیست؟

برای درک بهتر وب اسمبلی ابتدا باید بدانیم این تکنولوژی اصلا از کجا آمده و هدف آن چیست؟
میدانیم که مرورگر‌ها پروایدر صفحات وب هستند و ما برای اینکه بتوانیم اپلیکیشنی که ساختیم را در محیط وب برای کاربران به اشتراک بگذاریم باید از این مرورگر‌ها و زبان ارتباطی آن‌ها پیروی کنیم. این زبان‌های ارتباطی مشخصا سه چیز است: HTML CSS JavaScript
اما آیا راهی هست که بتوان بجای JavaScript از زبان‌های دیگر هم در سمت مرورگر استفاده کرد؟
وب اسمبلی یا همان WASM، آمده تا به ما اجازه دهد از هر زبانی که خروجی به Web Assembly دارد، برای تعاملات UI استفاده کنیم. یعنی با زبان هایی مثل #C / C++ / C و... میتوان کدی نوشت که مرورگر آن را اجرا کند. این یک تحول بزرگ است که امروزه تمامی مرورگرها (به جز مرورگرهایی که از دور خارج شده اند) از آن پشتیانی می‌کنند چرا که Web Assembly به یکی از اجزای استاندارد وب تبدیل شده است.
اطلاعات بیشتر در رابطه با وب اسمبلی را میتوانید از این مقاله بخوانید. 


تعریف SPA و PWA:
SPA: Single Page Application
PWA: Progressive Web Application
در گذشته برای رندر کردن صفحات وب با عوض شدن URL یا درخواست کاربر برای دریافت اطلاعات جدید از سرور و نمایش آن ، صفحه مرورگر ملزم به رفرش شدن مجدد و از سر گیری کل فرایند تولید HTML میشد. طبیعتا این تکرار برای کاربر هنگام استفاده از اپلیکیشن خیلی خوشایند نبود چرا که هربار میبایست زمانی بیشتر صرف تولید مجدد صفحات را منتظر میماند. اما در مقابل آن Single Page Application (SPA)‌‌ها این پروسه را تغیر داد.
در رویکرد SPA، کل CSS , HTML و JS ای که برای اجرای هر صفحه ای از اپلیکیشن نیاز هست در همان لود اولیه برنامه توسط مرورگر دانلود خواهد شد. با این روش هنگام تغییر URL صفحات مرورگر دیگر نیازی به لود دوباره اسکریپت‌ها ندارد. همچنین وقتی قرار است اطلاعات جدیدی از سرور آپدیت و نمایش داده شود این درخواست بصورت یک دستور Ajax به سرور ارسال شده و سرور با فرمت JSON اطلاعات درخواست شده را پاسخ میدهد. در نهایت مرورگر نیز اطلاعات برگشتی از سرور را مجدد جای گذاری میکند و کل این روند بدون هیچگونه رفرش دوباره صفحه انجام میشود.
در نتیجه‌ی این امر، کاربر تجربه خوشایند‌تری هنگام کار کردن با SPA‌ها خواهد داشت. اما همانقدر که این تجربه در طول زمان استفاده از برنامه بهبود یافت، لود اولیه اپلیکیشن را کند‌تر کرد، چرا که اپلیکیشن میبایست همه کدهای مورد نیاز خود برای صفحاتش را در همان ابتدا دانلود کند.(در ادامه با قابلیت Pre-Rendering این اشکال را تا حدود زیادی رفع میکنیم)
با استفاده از PWA میتوانید وبسایت‌های SPA را بصورت یک برنامه نصبی و تمام صفحه، با آیکن مجزایی همانند اپلیکیشن‌های دیگر در موبایل و دسکتاپ داشته باشید. همچنین وقتی از PWA استفاده میکنیم برنامه وب میتواند به صورت آفلاین نیز کار کند.
البته حتی در برنامه‌هایی که لازم نیست آفلاین کار کنند، در صورت قطعی ارتباط کاربر با شبکه، به جای دیدن دایناسور معروف، اینکه برنامه در هر حالتی باز شود و به صورتی کاربر پسند و قطعی نت به وی اعلام شود ایده خیلی بهتری است (": 


قابلیت Pre-Rendering:
هدف Pre-Rendering بهبود گشت گذار کاربر در سایت است. شیوه کارکرد آن به این صورت است که وقتی کاربر وارد وبسایت میشود، سرور در همان ابتدای کار و جلوتر از اتمام دانلود اسمبلی‌ها، فایلی حاوی HTML ، CSS‌های صفحه ای که کاربر درخواست کرده را در سمت سرور می‌سازد و به مرورگر ارسال میکند. در همین حین، اسمبلی‌ها نیز توسط مرورگر دانلود می‌شوند و برنامه از محتوای صرف خارج شده و حالت تعاملی می‌گیرد. اصطلاحا به این قابلیت Server-Side Rendering(SSR) نیز میگویند. در این حالت کاربر زودتر محتوایی از برنامه را میبیند و تجربه بهتری خواهد داشت. این امر در بررسی Search Engine‌‌ها و سئو وبسایت نیز تاثیر بسزایی دارد. 


نگاهی به Blazor:
تا اینجا هر آنچه که نیاز بود برای درک بهتر از Blazor بدانیم را فهمیدیم، اما خود Blazor چیست؟ در یک توضیح کوتاه، فریمورکی ارائه شده توسط مایکروسافت میباشد برای پیاده‌سازی UI و منطق برنامه‌ها شامل امکانات Routing ، Binding و...
بلیزر در انواع مختلفی ارائه شده که هرکدام کاربرد مشخصی دارد:

Blazor Server
در بلیزر سرور پردازش‌ها جهت تعامل UI درون سرور اجرا خواهد شد. برای مثال وقتی کاربر روی دکمه ای کلیک میکند و آن دکمه مقداری عددی را افزایش می‌دهد که از قضا متن یک Label به آن عدد وابسته است، رویداد کلیک شدن این دکمه توسط SignalR WebSocket به سرور ارسال شده و سرور تغیر متن Label را روی همان وب سوکت به کلاینت ارسال می‌کند.
با توجه به این که تعامل کاربر با صفحات برنامه، بسته به میزان کندی اینترنت کاربر، ممکن است کند شود و همچنین Blazor Server قابلیت PWA شدن ندارد و علاوه بر این بار پردازش زیادی روی سرور می‌اندازد (بسته به پیچیدگی پروژه) و در نهایت ممکن است در آن از Component هایی استفاده کنیم که چون در حالت Blazor Server پردازش سمت سرور بوده، متوجه حجم دانلود بالای آنها نشویم و کمی بعدتر که با Blazor Hybrid می‌خواهیم خروجی Android / iOS بگیریم متوجه حجم بالای آنها شویم، استفاده از Blazor Server را در Production توصیه نمی‌کنیم، ولی این حالت برای Debugging بهترین تجربه را ارائه می‌دهد، بالاخص با امکان Hot Reload و دیدن آنی تغییرات C# / HTML / CSS در ظاهر و رفتار برنامه موقع کدنویسی.

Blazor WebAssembly
در بلیزر وب اسمبلی منطق مثال قبلی که با C# .NET نوشته شده است، روی مرورگر و با کمک Web Assembly اجرا می‌شود و نیازی به ارتباط جاری با سرور توسط SignalR نیست. این باعث میشود که با بلیزر وب اسمبلی بتوان اپلیکیشن‌های PWA نیز نوشت.
یک برنامه Blazor Web Assembly می‌تواند چیزی در حدود دو الی سه مگ حجم داشته باشد که در دنیای امروزه حجم بالایی به حساب نمیاید، با این حال با کمک Pre Rendering و CDN می‌توان تجربه کاربر را تا حدود زیادی بهبود داد.
برای مثال سایت Component‌های Bit Platform جزو معدود دموهای Component‌های Blazor است که در حالت Blazor Web Assembly ارائه می‌شود و شما می‌توانید با باز کردن آن، تجربه حدودی کاربرانتان را در حین استفاده از Blazor Web Assembly ببینید. به علاوه، در dotnet 7 سرعت عملکرد Blazor Web Assembly بهبود قابل توجهی پیدا کرده است.

Blazor Hybrid
از Blazor Hybrid زمانی استفاده می‌کنیم که بخواهیم برنامه‌های موبایل را برای Android , iOS و برنامه‌های Desktop را برای ویندوز، با کمک HTML , CSS توسعه دهیم. نکته اصلی در Blazor Hybrid این است که اگر چه از Web View برای نمایش HTML / CSS استفاده شده، اما منطق سمت کلاینت برنامه که با C# .NET توسعه داده شده است، بیرون Web View و به صورت Native اجرا می‌شود که ضمن داشتن Performance بالا، به تمامی امکانات سیستم عامل دسترسی دارد. علاوه بر دسترسی به کل امکانات Android / iOS Sdk در همان C# .NET ، عمده کتابخانه‌های مطرح مانند Firebase، با ابزار Binding Library ارائه شده توسط مایکروسافت، دارای Wrapper قابل استفاده در C# .NET هستند و ساختن Wrapper برای هر کتابخانه Objective-C ، Kotlin، Java، Swift با این ابزار فراهم است.

اگر شما در حال حاضر فقط #HTML , CSS , C بدانید، اکنون با بلیزر میتوانید هر اپلیکیشنی که بخواهید توسعه دهید. از وبسایت‌های SPA گرفته تا اپ‌های موبایل Android ، IOS و برنامه‌های دسکتاپی برای Windows , Mac و بزودی نیز برای Linux
سری آموزش بلیزر را میتوانید از این لینک‌ها (1 ، 2) دنبال کنید. 


معرفی پکیج Bit Blazor UI:
پکیج Bit Blazor UI مجموعه ای از کامپوننت‌های مرسومی است که بر پایه بلیزر نوشته شده و به ما این امکان را میدهد تا المان‌های پیچیده ای مثل Date Picker , Grid , Color Picker , File Upload , DropDown و بسیاری از المان‌های دیگر را با شکلی بهینه، بدون نیاز به اینکه خودمان بخواهیم برای هر یک از اینها از نو کدنویسی کنیم، آن را در اختیار داشته باشیم.
عمده مشکل کامپوننت‌های ارائه شده برای بلیزر حجم نسبتا بالای آن است که باعث میشد بیشتر در مصارفی از قبیل ایجاد Admin Panel کارایی داشته باشد. اما این موضوع به خوبی در Bit Blazor UI مدیریت شده و در حال حاضر با بیش از 30 کامپوننت با حجم بسیار پایینی، چیزی حدود 200 کیلوبایت قابل نصب است. از لحاظ حجمی نسبت به رقبای خود برتری منحصر به فردی دارد که باعث میشود به راحتی حتی در اپلیکیشن‌های موبایل هم قابل استفاده باشد و کماکان پرفرمنس خوبی ارائه دهد.
این کامپوننت‌ها با ظاهر Fluent پیاده سازی شده و میتوانید لیست کامپوننت‌های بلیزر Bit را از این لینک ببینید. 


معرفی Bit TodoTemplate:
قبل از اینکه به معرفی Bit TodoTemplate بپردازیم باید بدانیم که اصلا پروژه‌های Template چه هستند. در واقع وقتی شما Visual Studio را باز میکنید و روی گزینه Create New Project کلیک میکنید با لیستی از پروژه‌های تمپلیت روبرو میشوید که هرکدام چهارچوب خاصی را با اهدافی متفاوت در اختیارتان قرار میدهند.
Bit Platform هم Project Template ای با نام TodoTemplate توسعه داده که میتوانید پروژه‌های خودتان را از روی آن بسازید، اما چه امکاناتی به ما میدهد؟
در یک جمله، هر آنچه تا به اینجا توضیح داده شد بصورت یکجا در TodoTemplate وجود دارد.
در واقع TodoTemplate چهارچوبی را فراهم کرده تا شما تنها با دانستن همین مفاهیمی که در این مقاله خواندید، از همان ابتدا امکاناتی چون صفحات SignUp، SignIn یا Email Confirmation و... را داشته باشید و در نهایت بتوانید تمامی خروجی‌های قابل تصور را بگیرید.
اما چگونه؟
در TodoTemplate همه این قابلیت‌ها تنها درون یک فایل و با کمترین تغیر ممکن نوع خروجی کدی که نوشته اید را مشخص میکند. این تنظیمات به شکل زیر است :
<BlazorMode> ... </BlazorMode>
<WebAppDeploymentType> ... </WebAppDeploymentType>
شما میتوانید با تنظیم <BlazorMode> بین انواع hosting model‌های بلیزر سوییچ کنید. مثلا برای زمانی که در محیط development هستید نوع بلیزر را Blazor Server قرار دهید تا از قابلیت‌های debugging بهتری برخوردار باشید ، وقتی میخواهید وبسایت تکمیل شده تان را بصورت SPA / PWA پابلیش کنید نوع بلیزر را به Blazor WebAssembly و برای پابلیش‌های Android ، IOS ، Windows ، Mac نوع بلیزر را به Blazor Hybrid تغیر دهید.
به علاوه، شما تنها با تغیر <WebAppDeploymentType> قادر خواهید بود بین SPA (پیش فرض)، SSR و PWA سوئیچ کنید.
قابلیت‌های TodoTemplate در اینجا به پایان نمیرسد و بخشی دیگر از این قابلیت‌ها به شرح زیر است :
  • وجود سیستم Exception handling در سرور و کلاینت (این موضوع به گونه ای بر اساس Best Practice‌‌ها پیاده سازی شده که اپلیکیشن را از بروز هر خطایی که بخواهد موجب Crash کردن برنامه شود ایزوله کرده)
  • وجود سیستم User Authentication بر اساس JWT که شما در همان ابتدا که از این تمپلیت پروژه جدیدی میسازید صفحات SignIn ، SignUp را خواهید داشت.
  • پکیج Bit Blazor UI که بالاتر درمورد آن صحبت کرده ایم از همان ابتدا در TodoTemplate نصب و تنظیم شده تا بتوانید به راحتی صفحات جدید با استفاده از آن بسازید.
  • کانفیگ استاندارد Swagger در سمت سرور.
  • ارسال ایمیل در روند SignUp.
  • وجود خاصیت AutoInject برای ساده‌سازی تزریق وابستگی ها.
  • و بسیاری موراد دیگر که در داکیومنت‌های پروژه میتوانید آنهارا ببینید.
با استفاده از TodoTemplate پروژه ای با نام Todo ساخته شده که میتوانید چندین مدل از خروجی‌های این پروژه را در لینکهای پایین ببینید و پرفرمنس آن را بررسی کنید.
توجه داشته باشید هدف TodoTemplate ارائه ساختار Clean Architecture نبوده ، بلکه هدف ارائه بیشترین امکانات با ساده‌ترین حالت کدنویسی ممکن بوده که قابل استفاده برای همگان باشد و شما میتوانید از هر پترنی که میخواهید براحتی در آن استفاده کنید.
پلتفرم Bit یک تیم توسعه کاملا فعال تشکیل داده که بطور مداوم در حال بررسی و آنالیز خطاهای احتمالی ، ایشو‌های ثبت شده و افزودن قابلیت‌های جدید میباشد که شما به محض استفاده از این محصولات میتوانید در صورت بروز هر اشکال فنی برای آن ایشو ثبت کنید تا تیم مربوطه آن را بررسی و در دستور کار قرار دهد. در ادامه پلتفرم Bit قصد دارد بزودی تمپلیت جدیدی با نام Admin Panel Template با امکاناتی مناسب برای Admin Panel مثل Dashboard و Chart و... با تمرکز بر Clean Architecture نیز ارائه کند. چیزی که مشخص است اوپن سورس بودن تقریبا %100 کارها میباشد از جلسات و گزارشات کاری گرفته تا جزئیات کارهایی که انجام میشود و مسیری که در آینده این پروژه طی خواهد کرد.
میتوانید اطلاعات بیشتر و مرحله به مرحله برای شروع استفاده از این ابزار‌ها را در منابعی که معرفی میشود دنبال کنید.

منابع:

مشارکت در پروژه:
  • شما میتوانید این پروژه را در گیتهاب مشاهده کنید.
  • برای اشکالات یا قابلیت هایی که میخواهید برطرف شود Issue ثبت کنید.
  • پروژه را Fork کنید و Star دهید.
  • ایشوهایی که وجود دارد را برطرف کنید و Pull Request ارسال کنید.
  • برای در جریان بودن از روند توسعه در جلسات برنامه ریزی (Planning Meeting) و گزارشات هفتگی (Standup Meeting ) که همه اینها در Microsoft Teams برگزار میشود شرکت کنید.