آموزش داکر و مفاهیم اولیه برای برنامه نویسان

این نوشته تلاشی برای استفاده کاربردی از داکر است. مفاهیم و تعاریف مطرح شده دقیق نیستند. صرفا کاربرد و فهم سازوکار داکر مد نظر نگارنده بوده است. اگر برنامه نویس هستید و چیزی از داکر نمی دانید این نوشته نقطه شروع خوبی است. پیش نیاز این مقاله آشنایی با یک زبان برنامه نویسی یا یک فریم ورک است. من از node به عنوان مثال استفاده کردم (در صورتی که برنامه نویس دات نت هستم). حتی المقدور همه مطلب را کامل مطالعه کنید و بخش های node و mongo را رد نکنید، روند ایجاد فایل داکر برای برنامه ها و ابزارهای مختلف یکسان است.این نوشته پیشنیازی برای مجموعه مقالات آموزش میکروسرویس است.برای کار با داکر قبل از هر چیز مطمئن شوید داکر را نصب کرده اید. اگر سیستم عامل ویندوز دارید با نصب docker desktop میتوانید بدون مشکل از داکر استفاده کنید. در ویندوز بعد از نصب داکر فضایی شبیه به تصویر زیر خواهید دید.رنگ سبزِ پایین پنجره یعنی داکر روی سیستم شما فعال است. در سمت چپ صفحه لیستی از سه عنصر اساسی داکر میبینید.ImagesContainers/appVolumeداکر چیست و چگونه کار میکند؟اگر قبلا با دیسک های نوری یا همان سی دی کار کرده اید این مثال را به دقت بخوانید. Imageها در حقیقت مانند image های iso هستند که از سی دی تهیه میکردیم.( اگر با این image ها هم آشنایی ندارید، فرض کنید کل محتوای CD را در یک فایل Zip کنیم. این فایل زیپ شده را image میگویند.)حالا اگر فایل زیپ را در memory باز کنید و فایل اجرایی آن را اجرا کنید در حقیقت Container دارید.(یعنی همان Mount کردن فایل iso روی یک درایو مجازی)فرصت پرداختن به جزئیات داکر نیست اما برای استفاده عملی از داکر، لازم است توسعه دهنده حداقل هایی از طرز کار داکر بداند. به تصویر زیر دقت کنید.تصویر فوق مراحل داکرایز کردن و ایجاد کانتینر را نشان میدهد. با نصب داکر روی سیستم، بصورت بالقوه قابلیت ایجاد فایل image داکر و اجرایی کردن آن و تبدیل آن به container را دارید. روال به صورت تصویر فوق است. کافیست در فولدر build پروژه تان، فایل dockerfile را به همین نام ایجاد کنید. این فایل حاوی دستورات مرتبط با نصب و اجرای پروژه است. بطور مثال برای پروژه های Node شما برای debug دستور nodemon یا node و سپس run را مینویسید. (اگر دات نتی هستید اینکارها شبیه dotnet run است و اگر با فریم ورک های دیگر کار میکنید دستورات اجرای پروژه مد نظر است). پس در داکر فایل، مجموعه دستوراتی برای اجرای پروژه قرار دارد به نحوی که شما را از نصب ملزومات به شکل دستی بی نیاز کند. مثلا اگر برای node یا dotnet نیاز به runtime دارید ابتدای فایل داکر باید به کمک یک دستور، پکیج مورد نظر را از اینترنت دریافت کنید تا ایمیج همراه با ملزومات ساخته شود. مزیت اصلی داکر همین است که یک image بطور مستقل و بدون نیاز به هیچ برنامه ای اجرا میشود. مثلا یک برنامه java یا python از دوستتان گرفته اید بدون اینکه runtime جاوا یا پایتون داشته باشید این برنامه اجرا نخواهد شد و مجبورید پیش از استفاده از برنامه، کتابخانه ها یا sdk ی مورد نیاز (حتا با ورژن مشابه روی سیستم دوستتان!) نصب کنید. داکر کردن یک برنامه همه ی ملزومات را به آن میچسباند و بی نیاز از کارِ اضافه خواهید بود، به علاوه اینکه دردسر همخوانی ورژن مورد نیاز برنامه با ورژن نصب شده روی سیستم را ندارید. اکثر اوقات محل قرارگیری «داکرفایل» به جای فولدر build، کنار فایل های پروژه است. این بستگی به رفتار توسعه دهنده دارد. وقتی dockerfile را در فولدر فایل های پروژه قرار میدهیم باید چیزهای دیگری هم در این فایل بنویسیم، چون در این صورت این فایل به غیر از مسئولیت اجرا، مسئولیت build هم به عهده دارد. یعنی ما ابتدا باید پکیج های ابزار مورد نیاز برای build مثل sdk را در ابتدای فایل داکر از اینترنت فراخوانی کنیم تا build درست انجام شود و سپس دستورات اجرایی را بنویسیم.(فایل داکر یک فایل دستورالعمل است)نمونه یک «داکرفایل» برای یک پروژه node را میبینید(برای فریم ورک ها و زبان های دیگر مشابه همین دستورالعمل است): https://gist.github.com/375121a02f4a835358d96e368f29552b در خط اول ما node را که ابزار لازم برای بیلد و اجرای پروژه است دریافت کردیم.(اگر دات نت یا پایتونی باشید باید ابزار مربوط به همان فریم ورک را دریافت کنید)(سپس مسیری که در آن پروژه قرار میگیرد را مشخص کردیم.سپس فایل package.json را کپی میکنیم.در خط بعد پکیج ها را با npm iنصب میکنیم.در مرحله بعد سورس کد را کپی میکنیم.یک پورت به شکل اسمی expose میکنیم.دستور اجرای پروژه را مینویسیم. (پروژه با پورتی که node خودش expose کرده اجرا میشود)بر اساس این دستورات به کمک دستور docker build یک ایمیج از فولدر پروژه ما ساخته میشود. این ایمیج به محض اینکه با دستور docker run به container تبدیل شد اجرا میشود یعنی پورتی که از داخل برای localhost تعریف شده بود میتواند به بیرون expose شود و از بیرون هم صدا زده شود.یک image وقتی به container تبدیل میشود در حقیقت در یک بستر کوچک لینوکس پروژه شما اجرا میشود. شاید اگر با داکر کار نکرده باشید درک این موضوع سخت باشد. اینطور تصور کنید که هر ایمیجی که به container تبدیل میشود انگار یک لینوکس راه اندازی میشود و دنیای خودش را دارد یعنی شبکه ها، پورت ها و فهرست فایل ها و ... .فرض کنید پروژه ای برای دانلود و آپلود فایل ساخته ایم. فایل dockerfile را ساخته ایم. بر اساس این داکر فایل، با دستورات docker ابتدا از آن یک image ساخته و سپس با دستوراتی دیگر آن image را به container با خروجی 4200 تبدیل کرده ایم. یعنی بستر لینوکس مربوط به این پروژه را روشن و سپس خود پروژه را اجرا کرده ایم و گفته ایم localhost:4200 آن دسترسی به api های کد ما را میدهد. کانتینر به راحتی قابل توقف است. با نوشتن یک دستور کوچک میتوانیم آن را خاموش کنیم! و دیگر دسترسی به 4200 برقرار نیست. حتا میتوانیم آن را حذف کنیم و یا دوباره از روی ایمیج بسازیم. این یعنی ساختار فایل های لینوکس پاک شده و دوباره ساخته میشوند. مشکل اینجاست که چون پروژه ما در بستر همین ساختار فایل ایجاد شده، پس هر api در پروژه با همین ساختار فایل کار میکند. یعنی با فراخوانی api آپلود، فایل ها درون container آپلود میشوند. پس اگر روزی کانتینر متوقف و پاک شود و بخواهیم دوباره از ایمیج، کانتینر بسازیم فایل ها را از دست میدهیم. اینجا Volume مطرح میشود. یعنی ما یک فولدر خارج از container (محل فیزیکی روی سیستم) را به یک فولدر داخل container نسبت میدهیم. به همین راحتی. پس هر وقت container در حال اجرا بود و api آپلود یا دانلود فراخوانی شد ، فایل از/به داخل لینوکسِ کانتینر منتقل نمیشود. فایل به محلی فیزیکی و موجود روی کامپیوتری که این داکر روی آن نصب شده منتقل میشود.تصویر فوق همین موضوع را نشان میدهد. Docker host سیستمی است که ما داکر را روی آن نصب کردیم.(مثلا یک سیستم لینوکسی داریم و نه ویندوزی) ما روی این سیستم دو ایمیج داریم که به دو کانتینر تبدیل کرده ایم. حالا این کانتینرها به جای اینکه با file storage غیرمطمئن داخل خودشان کار کنند با فولدری فیزیکی و موجود از سیستم واقعی کار میکنند. به همین سادگی.چگونه از image ها استفاده کنیم؟همانطور که ما از پروژه های خود image تهیه میکنید در اینترنت ایمیج های رسمی و غیر رسمی از برنامه های مختلف وجود دارد. مثلا برای استفاده از sql server یا Mongo یا RabbitMq و ... نیازی به نصب مستقیم آن روی سیستم ندارید. مخزن داکر نسخه های رسمی از ایمیج های این برنامه ها را دارد و فقط کافیست آن را روی سیستم خودتان کپی یا pull کنید. مثلا برای استفاده از Mongo کافیست روی سیستم خودتان بنویسید:docker pull mongoبا این دستور ایمیج به سیستم شما منتقل میشود و میتوانید آن را به container تبدیل کنید و با پورتی که خودتان تعریف کرده اید در کدهایتان از آن استفاده کنید.با یک مثال این موضوع را نشان میدهیم.فرض کنید میخواهید Mongo را روی سیستمان اجرا و استفاده کنید.کافیست در گوگل سرچ کنید و از سایت docker ایمیج رسمی آن را دریافت کنیداطلاعات مربوط به مونگو و نصب آن و env variable ها و کانفیگ ها در این صفحه وجود دارد.مراحل نصب را شروع میکنیم :1. ابتدا می بینیم چه image هایی روی سیستم داریم :docker imagesروی سیستم من دو ایمیج وجود داشت. اگر تازه docker desktop را نصب کرده باشید ممکن است هیچ ایمیجی روی سیستم شما نباشد.2. در ترمینال دستور docker pull mongo را بنویسید تا ایمیج به سیستم شما منتقل شود.docker pull mongo3. حالا ایمیج را به container تبدیل میکنیم یا به تعبیری اجرا میکنیم:docker run -d -p 27017:27017 --name shopping-mongo mongoاز d- برای دیتچ کردن اجرا و اجرای پشت صحنه استفاده میشود.(یعنی بعد از اجرا منتظر نماند و کنسول روی اجرا قفل نشود)از p- برای مشخص کردن پورتی که به بیرون expose میکنیم استفاده میشود. پورت دیفالت 27017 در کانتینر برای مونگو استفاده میشود ما همین را اکسپوز میکنیم یعنی از بیرون با همین پورت به آن دسترسی داریم. ما پورت داخل کانتینر را به پورت لوکال کامپیوتر Expose کردیم.-p [docker host outside(local port)]:[container expose(image port)]از name-- برای تعیین نام برای کانتینری که از این ایمیج ساخته میشود استفاده میکنیم.در نهایت نام imageی که از روی آن کانتینر میسازیم را نوشتیم (mongo) 3. با دستور زیر چک میکنیم چه کانتینرهایی روی سیستم ما در حال اجرا هستند.docker psاین دستور ایمیج های کانتینر شده و در حال اجرا را نشان میدهد. و طبق تصویر فوق 21 ثانیه پیش شروع به کار کرده و پورت داخلی 27017 به پورت 27017خارجی مپ کرده است و نامش هم catalog-mongo است. همچنین شناسه آن b4b243f22fe6 است که در سیستم شما این شناسه متفاوت است.4. با دستور زیر میتوانیم لاگ این کانتینر را ببینیمdocker logs -f catalog-mongoو حتا با دستور زیر میتوانیم داخل ترمینال لینوکسی شویم که مونگو در بستر آن ایجاد شده و با دستورات ترمینال لینوکس در فولدرها بچرخیم یا پکیج نصب کنیم و ... .docker exec -it catalog-mongo /bin/bashداخل ترمینال لینوکس ، دستور mongo را اجرا میکنیم.حالا در ترمینال داخلی عبارت mongo را مینویسیم و میبینیم که شِل مونگو اجرا میشود و میتوانیم با دستور زیر از وجود دیتابیس ها مطمئن شویمبا فشردن Ctrl+C از محیط شل داخلی خارج میشویم و سپس با تایپ exit از محیط bash لینوکس خارج شوید.چون پورت را expose کرده ایم میتوانیم یک GUI مثل Compass یا studio3t نصب کنیم و به پورت 27017 متصل کنید و از دیتابیس استفاده کنیم.برای توقف و حذف این کانتینر از دستور docker stop b4b243f22fe6 و docker rm b4b243f22fe6 استفاده کنید. دقت کنید ابتدا باید کانتینر را stop و سپس حذف کنید.(میتوانید با کمک آپشن f یا force یکباره آن را حذف کنید) با اینکار کانینر را از دست میدهید(در اینجا برای مانگو یعنی دیتابیس هایتان را از دست میدهید) ولی ایمیج هنوز باقیست و میتوانید دوباره با دستور docker run ایمیج را به کانتینر تبدیل کنید.دقت کنید که ما برای mongo مثال زدیم ولی شما میتوانید هر اپلیکیشنی که در مخزن داکر به عنوان یک image وجود دارد را به سیستم خودتان منتقل کنید و از شر فرایندهای نصب خلاص شوید.دوباره به تصویر اول این مقاله یعنی محیط docker desktop برگردید. شما میتوانید کارهایی را که در ترمینال با دستورات docker انجام دادید در محیط ویندوزی برنامه هم انجام دهید.اگر دوست دارید یک پروژه واقعی را داکرایز کنید و از مفهوم volume و network در داکر سردر بیاورید این مقاله را ببینید. (این مقاله در دست تهیه است)