چطور داکر را با پایتون به کار ببریم؟
                مقاله زیر، درسنامهای مقدماتی درباره کانتینرهای داکر است. پس از خواندن این مقاله، خواهید توانست بر سیستم خود از داکر استفاده کنید. ما علاوه بر پایتون، کانتینرهای Nginx و Redis را نیز اجرا خواهیم کرد. در این مثالها، فرض بر این است که با مفاهیم اولیه این فناوریها آشنایی دارید. در این مثالها از shell به وفور استفاده خواهیم کرد، پس همین الان terminal سیستم خود را باز کنید!
دوره پیشنهادی: دوره آموزش Docker
# داکر چیست؟
داکر یک ابزار متنباز است که فرآیند استقرار (deployment) یک برنامه در داخل یک کانتینر نرمافزاری را خودکار میکند. آسانترین راه برای درک ایده داکر، مقایسه آن با کانتینرهای ارسالی استانداردیست که مبادله میشوند.
در زمانهای قدیم، شرکتهای مبادلات کالا با چالشهای زیر روبهرو بودند:
- چگونگی انتقال کالاهای متفاوت (ناسازگار با هم) در کنار هم (مانند محصولات غذایی و مواد شیمیایی، یا آجر و شیشه).
 - چگونگی حملونقل بستههایی با اندازههای متفاوت توسط یک وسیله معین
 
بعد از معرفی کانتینرها به جهان، امکان گذاشتن آجرها روی شیشه، و ذخیره مواد شیمیایی کنار محصولات غذایی پدید آمد. محمولههایی با اندازههای متفاوت داخل کانتینرهای استاندارد قرار داده شده و توسط یک وسیله بارگذاری یا ترخیص میشدند.
بیایید به بحث توسعه نرمافزار در پایتون و استفاده از داکر برگردیم.
زمانی که یک برنامه را توسعه میدهید، نیاز دارید به همراه کد برنامه، سایر وابستگیهای پروژه مانند کتابخانهها، وبسرور، پایگاههای داده و... را نیز فراهم کنید. ممکن است به شرایطی برخورد کنید که در آن، این برنامه در کامپیوتر شما بهخوبی کار میکند، اما روی سرورهای اولیه، سا سیستم سایر توسعهدهندهها یا ارزیابهای کیفیت (QA)، حتی شروع نمیشود.
این چالش میتواند با منزوی کردن برنامه و مستقل کردن آن از سیستم، حل شود.
# داکر چه تفاوتی با مجازیسازی دارد؟
به طور رایج، ماشینهای مجازی برای جلوگیری از این رفتار ساخته شدهبودند. مشکل اصلی این ماشینهای مجازی یا VMها این است که وجود یک "سیستمعامل اضافه" بر روی سیستمعامل میزبان، سبب میشود چندین گیگابایت به حجم پروژه اضافه شود. در اکثر مواقع، سرور شما میزبان چندین VM است که حجم بسیار بیشتری را اشغال خواهند کرد. و علاوه بر این، در حال حاضر، اکثر سرویسهای رایانش ابری (cloud) برای حجم اضافه از شما هزینهای دریافت خواهند کرد. یکی از معایب بزرگ دیگر VMها، بالا آمدن بسیار آهسته آنها است.
داکر تمام مشکلات زیر را با به اشتراکگذاری هسته سیستمعامل میان تمام کانتینرهایی که پردازشهای مستقلی از سیستمعامل به حساب میآیند، به آسانی رفع میکند.
در نظر داشته باشید که داکر، اولین یا تنها بستر کانتینربندی نیست؛ اگرچه در حال حاضر، بزرگترین و قدرتترین بستر موجود در بازار به حساب میآید.

مقاله پیشنهادی: چرا مردم نرمافزارهای open source میسازند؟
# چرا به داکر نیاز داریم؟
لیست کوتاهی از مزایای داکر شامل موارد زیر میشود:
- فرآیند توسعه سریعتر
 - کپسولسازی (encapsulation) کارآمد برنامهها
 - داشتن رفتار مشابه در سیستم محلی، حالت توسعهدهنده و سرورهای اولیه و نهایی
 - قابلیت بررسی آسان و واضح
 - گسترشپذیری آسان
 
+ فرآیند توسعه سریعتر
نیازی به نصب برنامههای میانجی مانند PostgreSQL، Redis یا Elasticsearch روی سیستم شما نیست، بلکه میتوانید آنها را در کانتینرها اجرا کنید. همچنین، داکر به شما این امکان را میدهد که نسخههای مختلف از یک برنامه را به طور همزمان اجرا کنید. برای مثال، فرض کنید احتیاج دارید تا دادههایی را به طور دستی از یک نسخه قدیمی از Postgres به نسخه جدیدتر انتقال دهید. این شرایط ممکن است در معماریهای مایکروسرویس زمانی اتفاق بیفتد که بخواهید مایکروسرویسی جدید با نسخهای جدید از نرمافزار میانجی ایجاد کنید.
نگهداری دو نسخه متفاوت از یک برنامه روی یک سیستمعامل میزبان میتواند بسیار پیچیده باشد. در اینجاست که داکر یک راهحل فوقالعاده به شمار میآید، زیرا به شما محیطهایی مجزا برای برنامهها و میانجیها اختصاص میدهد.
به علاوه، میتوانید نسخههای مختلفی از زبانهای برنامهنویسی را در کانتینرهای داکر مدیریت کنید؛ برای مثال، پایتون 3.7 و 3.9.
+ کپسولسازی کارآمد برنامهها
شما قادر خواهید بود پروژه خود را در یک جزء ارائه کنید! اکثر زبانهای برنامهنویسی، فریمورکها و سیستمعاملها، مدیر پکیج (package manager) مختص به خود دارند، و حتی اگر برنامه شما قادر به بستهبندی شدن با مدیر پکیج مختص به خود باشد، ایجاد یک درگاه دیگر در آن برای سایر سیستمها، دشوار خواهد بود.
داکر یک فرمت تصویری واحد برای توزیع برنامههای شما میان سیستمهای میزبان و سرویسهای رایانش ابری ارائه میدهد. با داکر، قادر خواهید بود که برنامه خود را به همراه تمام وابستگیهای آن (که در یک فایل تصویری لحاظ شدهاند)، آماده برای اجرا، ارائه کنید.
کانتینرها با استقرار و ارائه برنامههای پکیجشده در پایتون، به تیمهای توسعهدهنده کمک کرده و سبب افزایش سرعت و راحتی فرآیند توسعه میشوند.
+ داشتن رفتار مشابه در سیستم محلی، حالت توسعهدهنده، سرورهای اولیه و نهایی
داکر نمیتواند یکپارچگی میان حالت توسعه/مقدماتی/نهایی را 100% تضمین کند، زیرا پای عامل انسانی همیشه در میان است؛ اما احتمال بروز خطا به علت وجود نسخههای متفاوت از سیستمعامل، وابستگیها و... را تقریبا به صفر میرساند.
اگر از روشی مناسب برای ساخت تصاویر داکر استفاده شود، برنامههای شما مبنای تصویر، نسخه سیستمعامل و وابستگیهای یکسانی خواهند داشت.
+ قابلیت بررسی آسان و واضح
در اینجا روشی یکپارچه برای خواندن تمامی فایلهای گزارش از همه کانتینرهای در حال اجرا وجود دارد. نیازی نیست که مسیرهایی که برنامه و وابستگیهایش گزارشات را ذخیره میکنند، به خاطر بسپارید و ..هایی شخصی برای مدیریت آنها بنویسید.
میتوانید یک درایور اضافه برای گزارشات ترکیب کرده و تمام فایلهای گزارش را یکجا بررسی کنید.
+ گسترشپذیری آسان
برنامهای که به درستی بستهبندی شده باشد، بیشتر 12 معیار را خواهد داشت. داکر بهعلت شیوه طراحی، شما را وادار به رعایت مقررات اصلی خود، مانند پیکربندی با متغیرهای محیطی، برقراری ارتباط از طریق درگاههای TCP/UDP و... میکند. در نتیجه اگر این مقررات را به درستی رعایت کنید، برنامهشما نه فقط در داکر، بلکه در سایر محیطها نیز گسترشپذیر خواهد بود.
+ بسترهای پشتیبان
بستر اصلی داکر، لینوکس است، زیرا داکر دارای ویژگیهاییست که توسط هسته لینوکس ارائه میشوند؛ مانند اضافه کردن داکر به اوبنتو (Ubuntu) برای پروژههای پایتون شما. با این حال، میتوانید داکر را در سیستمعاملهای ویندوز یا مک نیز اجرا کنید. در این صورت، تنها تفاوت این خواهد بود که داکر در یک ماشین مجازی کوچک کپسولبندی خواهد شد. در حال حاضر، داکر برای ویندوز و مک به حد بالایی از کارایی رسیده است و مانند یک برنامه محلی برای آنها به حسابمی میآید.
مطلب پیشنهادی: دوره آموزش Nginx
# نصب داکر
میتوانید راهنمای نصب داکر را اینجا مطالعه کنید.
اگر روی لینوکس کار میکنید، نیاز دارید تمام دستورات زیر را به عنوان root اضافه کرده، یا نامکاربری خود را به گروه داکر اضافه کنید و مجددا وارد شوید:
sudo usermod -aG docker $(whoami)
+ واژهشناسی
Container – یک نمونه (instance) در حال اجرا که نرمافزار موردنیاز را کپسولبندی میکند. کانتینرها همیشه از تصاویر تشکیل میشوند. کانتینر میتواند برای ارتباط با خارج یا سایر کانتینرها، از درگاه یا والیومها استفاده کند. آنها میتوانند به آسانی متوقف یا حذف شده و در زمان کوتاهی مجددا ایجاد شوند. کانتینرها حالتی (state) را در خود نگه نمیدارند.
Image – المان پایه هر کانتینر. زمانی که یک تصویر ایجاد میکنید، هر مرحله ذخیره شده و بعدا قابل بازیابی خواهد بود (مدل Copy On Write). ساخت تصویر، بسته به نوع آن، ممکن است طول بکشد؛ اما از طرف دیگر، کانتینرها میتوانند در لحظه از یک تصویر ساخته شوند. زمانی که با پایتون برنامهنویسی میکنید، تصاویر داکر میتواند به کاهش دشواریهای پیکربندی کمک کنند.
Port - به معنی یک درگاه TCP/UDP است. به زبان ساده، فرض کنیم درگاهها در محیط خارجی قابل دسترسی (از سیستمعامل میزبان)، یا به سایر کانتینرها متصل هستند (در این حالت فقط توسط این کانتینرها قابل دسترسی هستند و از محیط خارجی نمیتوان به آنها دسترسی یافت).
Volume – میتواند به عنوان یک پوشه مشترک تعریف شود. والیومها با ایجاد یک کانتینر تعریف میشوند. آنها برای نگهداری دادهها ایجاد شده و مستقل از طول حیات کانتینر هستند.
Registry – سروری که تصاویر داکر را نگهداری میکند. میتوان آن را با گیتهاب مقایسه کرد – همانند گیتهاب، میتوانید یک تصویر را از آن بیرون بکشید (pull) تا بر روی سیستم خود با آن کار کنید، یا تصاویری که در محیط سیستم خود ساختهاید را بر روی آن قرار دهید (push).
داکرهاب (Docker Hub) – یک بایگانی به همراه رابط کاربری وب که توسط کمپانی داکر ارائه شده است. در این بایگانی تصاویر مختلفی با نرمافزارهای مختلف ذخیره شدهاند. داکرهاب منبع "رسمی" تصاویر داکریست که توسط تیم داکر یا با همکاری شرکتهای سازنده نرمافزار ایجاد شدهاند (البته لزوما به این معنا نیست که این تصاویرِ "اصلی" متعلق به شرکتهای رسمی نرمافزاری هستند). در کنار تصاویر رسمی لیستی از آسیبپذیریهای احتمالی آنها وجود دارد. این اطلاعات در اختیار هر فردی که در سایت ثبتنام کرده باشد قرار میگیرد. در این وبسایت دو نوع حساب رایگان و پولی وجود دارد. میتوان به ازای هر حساب، یک تصویر شخصی و هزاران تصاویر عمومی را رایگان دریافت کرد. داکراِستور (Docker Store) نیز سرویسی مشابه داکرهاب است؛ این وبسایت مانند یک فروشگاه آنلاین دارای امتیازبندی، بررسی و تحلیل تصاویر و... است. نظر شخصی من این است که این وبسایت بیشتر جنبه تجاری دارد، در نتیجه داکرهاب را ترجیح میدهم.
مطلب پیشنهادی: دوره آموزش لینوکس
# مثال 1: Hello World!
اکنون زمان آن رسیده که اولین کانتینر خود را اجرا کنید:
docker run ubuntu /bin/echo 'Hello world'
خروجی کنسول:
Unable to find image 'ubuntu:latest' locally  
latest: Pulling from library/ubuntu  
6b98dfc16071: Pull complete  
4001a1209541: Pull complete  
6319fc68c576: Pull complete  
b24603670dc3: Pull complete  
97f170c87c6f: Pull complete  
Digest:sha256:5f4bdc3467537cbbe563e80db2c3ec95d548a9145d64453b06939c4592d67b6d  
Status: Downloaded newer image for ubuntu:latest  
Hello world  
docker run دستوریست که کانتینر را اجرا میکند.
ubuntu تصویریست که اجرا میکنید، مانند تصویر سیستمعامل ubuntu. زمانی که یک تصویر را مشخص میکنید، داکر در ابتدا در هاست داکر شما به دنبال آن تصویر میگردد. اگر این تصویر به صورت محلی وجود نداشت، از بایگانی تصویر عمومی – داکرهاب – برداشته میشود.
/bin/echo ‘Hello world’ دستوریست که در کانتینر جدید اجرا میشود. این کانتینر تنها عبارت “Hello World” را چاپ کرده و سپس متوقف میشود.
حال بیابید داخل یک کانتینر، یک shell تعاملی ایجاد کنیم:
docker run -i -t --rm ubuntu /bin/bash
-t یک ترمینال جدید داخل کانتینر ایجاد میکند.
-i به شما اجازه میدهد با استفاده از ورودی استاندارد کانتینر (STDIN) یک ارتباط تعاملی برقرار کنید.
–rm به صورت خودکار، با اتمام پردازش کانتینر را حذف میکند. کانتینرها به طور عادی حذف نمیشوند. این کانتینر تا زمان باز بودن shell وجود دارد و با بستن آن حذف میشود (مانند کار با SSH با سرور از راه دور).
اگر میخواهید بعد از بستن shell کانتینر همچنان اجرا شود، باید آن را به پسزمینه ببرید (دیمِن کنید):
docker run --name daemon -d ubuntu /bin/sh -c "while true; do echo hello world; sleep 1; done"  
–name daemon یک نام دیمِن به کانتینر جدید اختصاص میدهد. اگر اسمی مشخص نکنید، داکر به طور خودکار به کانتینر اسمی اختصاص خواهد داد.
-d کانتینر را در پس زمینه اجرا میکند (دیمِن میکند).
بیایید ببینیم در حال حاضر چه کانتینرهایی داریم:
docker ps -a
خروجی کنسول:
CONTAINER ID  IMAGE   COMMAND                 CREATED             STATUS                         PORTS  NAMES  
1fc8cee64ec2  ubuntu  "/bin/sh -c 'while..."  32 seconds ago      Up 30 seconds                         daemon  
c006f1a02edf  ubuntu  "/bin/echo 'Hello ..."  About a minute ago  Exited (0) About a minute ago         gifted_nobel  
docker ps دستوریست که کانتینرها را لیست میکند.
-a تمامی کانتینرها را نمایش میدهد (بدون این نشانه ps فقط کانتینرهای در حال اجرا را لیست میکند).
ps به ما نشان میدهد که در حال حاضر دو کانتینر داریم:
- gifted_nobel (این اسم به طور خودکار انتخاب شده و در سیستم شما متفاوت خواهد بود) که اولین کانتینریست که ایجاد کردیم و عبارت "Hello World" را چاپ کرد.
 - Daemon که کانتینر سومیست که ایجاد کردیم و به عنوان دیمن در پسزمینه اجرا میشود.
 
نکته:
در اینجا کانتینر دوم لیست نمیشود، زیرا گزینه –rm را فعال کرده بودیم؛ در نتیجه، این کانتینر پس از اجرا حذف شده است.
بیایید گزارشات را بررسی کرده و ببینم کانتینر دیمن چه کار میکند:
docker logs -f daemon
خروجی کنسول:
...
hello world  
hello world  
hello world  
docker logs گزارشات کانتینر را ارسال میکند.
-f برای دنبال کردن خروجی گزارشات استفاده میشود (مانند tail -f کار میکند).
حال، بیایید کانتینر دیمن را متوقف کنیم:
docker stop daemon  
و مطمئن بشیم که متوقف شده است:
docker ps -a
خروجی کنسول:
CONTAINER ID  IMAGE   COMMAND                 CREATED        STATUS                    PORTS  NAMES  
1fc8cee64ec2  ubuntu  "/bin/sh -c 'while..."  5 minutes ago  Up 3 seconds                     daemon  
c006f1a02edf  ubuntu  "/bin/echo 'Hello ..."  6 minutes ago  Exited (0) 7 minutes ago         gifted_nobel  
کانتینر متوقف شده است، میتوانیم دوباره شروع کنیم:
docker start daemon  
و مطمئن شویم که شروع شده است:
docker ps -a  
خروجی کنسول:
CONTAINER ID  IMAGE   COMMAND                 CREATED        STATUS                    PORTS  NAMES  
1fc8cee64ec2  ubuntu  "/bin/sh -c 'while..."  5 minutes ago  Up 3 seconds                     daemon  
c006f1a02edf  ubuntu  "/bin/echo 'Hello ..."  6 minutes ago  Exited (0) 7 minutes ago         gifted_nobel  
حالا، دوباره آن را متوقف کرده و تمامی کانتینرها را به طور دستی حذف میکنیم:
docker stop daemon  
docker rm <your first container name>  
docker rm daemon  
برای حذف همه کانتینرها از دستور زیر استفاده میکنیم:
docker rm -f $(docker ps -aq)  
- docker rm دستوریست که کانتینر را حذف میکند.
 - -f نشانهایست (برای rm) که در صورت در حال اجرا بودن کانتینر، آن را متوقف میکند. (حذف به اجبار)
 - -q نشانهایست (برای ps) که شناسه کانتینرها را چاپ میکند.
 
مقاله پیشنهادی: دوره های آموزش پروژه محور و پیشرفته پایتون
# مثال 2: متغیرهای محیطی و والیومها
در این مثال، فایلهای اضافهای نیاز داریم که در مخزن گیتهاب من قابل دسترسی است. میتوانید این مخزن را clone کرده یا از طریق لینک این فایلها را دانلود کنید.
زمان این رسیده که کانتینری معنادارتر، مانند Nginx را ایجاد و اجرا کنیم.
مسیر اجرایی را به examples/nginx تغییر دهید:
docker run -d --name "test-nginx" -p 8080:80 -v $(pwd):/usr/share/nginx/html:ro nginx:latest  
اخطار: این دستور خیلی سنگین به نظر میرسد، اما این مثال برای متغیرهای محیطی و والیومهاست. در 99% مواقع کانتینرهای داکر را به طور دستی اجرا نکرده و به جای آن از سرویسهای ارکستراسیون (رایانش) استفاده خواهید کرد (درباره docker-compose در مثال 4 صحبت خواهیم کرد) یا به صورت دستی برنامهای خواهید نوشت که این کار را انجام دهد.
خروجی کنسول:
Unable to find image 'nginx:latest' locally  
latest: Pulling from library/nginx  
683abbb4ea60: Pull complete  
a470862432e2: Pull complete  
977375e58a31: Pull complete  
Digest: sha256:a65beb8c90a08b22a9ff6a219c2f363e16c477b6d610da28fe9cba37c2c3a2ac  
Status: Downloaded newer image for nginx:latest  
afa095a8b81960241ee92ecb9aa689f78d201cff2469895674cec2c2acdcc61c  
- -p درگاههای میزبان و کانتینر را متصل میکند (HOST PORT:CONTAINER PORT).
 - -v نصبکننده والیوم است (HOST DIRECTORY:CONTAINER DIRECTORY).
 
مهم
دستورات run تنها آدرسهای قطعی را میپذیرند. در این مثال، از $(pwd) استفاده کردهایم تا آدرس قطعی مکان موردنظر خود را تنظیم کنیم.
اکنون آدرس 127.0.0.1:8080 را در مرورگر خود باز کنید.
میتوانید از آدرس /example/nginx/index.html (که والیوم آدرس /usr/share/nginx/html در داخل کانتینر است) نیز استفاده کنید.
بیایید اطلاعات کانتینر test-nginxرا دریافت کنیم:
docker inspect test-nginx
این دستور اطلاعات سیستمی درباره روند نصب داکر را ارائه میدهد. این اطلاعات شامل نسخه کِرنل، تعداد کانتینرها و تصویرها، درگاههای آماده، والیومهای نصبشده و... میباشند.
# مثال 3: نوشتن اولین داکرفایل شما
برای ساخت یک تصویر داکر، نیاز است تا یک داکرفایل بنویسید – یک فایل متنی ساده شامل دستورات و آرگومانها. در اینجا شرحی از دستوراتی که قرار است استفاده کنیم، میبینید:
- FROM – تصویر بِیس (پایه) را ایجاد میکند.
 - RUN – دستور را در کانتینر اجرا میکند.
 - ENV – متغیرهای محیطی را تنظیم میکند.
 - WORKDIR – آدرس محل کار را تنظیم میکند.
 - VOLUME – نقطه اتصالی برای والیوم ایجاد میکند.
 - CMD – کانتینر را قابل تغییر میکند.
 
میتوانید برای اطلاعات بیشتر به منبع داکرفایل مراجعه کنید.
بیایید تصویری ایجاد کنیم که توسط curl محتویات یک وبسایت را دریافت کرده و در یک فایل متنی ذخیره میکند. ما نیاز داریم که آدرس وبسایت را توسط متغیر SITE_URL دریافت کنیم. نتیجه نهایی در آدرس موردنظر، به صورت یک والیوم ذخیره میشود.
فایل با نام Dockerfile را در آدرس examples/curl با محتویات زیر ذخیره میکنیم:
FROM ubuntu:latest  
RUN apt-get update   
    && apt-get install --no-install-recommends --no-install-suggests -y curl 
    && rm -rf /var/lib/apt/lists/*
ENV SITE_URL http://example.com/  
WORKDIR /data  
VOLUME /data  
CMD sh -c "curl -Lk $SITE_URL > /data/results"  
داکرفایل آماده است؛ زمان ساخت تصویر رسیده است.
به آدرس examples/curl بروید و دستور زیر را اجرا کنید تا تصویر ساخته شود:
docker build . -t test-curl
خروجی کنسول:
Sending build context to Docker daemon  3.584kB  
Step 1/6 : FROM ubuntu:latest  
 ---> 113a43faa138
Step 2/6 : RUN apt-get update     && apt-get install --no-install-recommends --no-install-suggests -y curl     && rm -rf /var/lib/apt/lists/*  
 ---> Running in ccc047efe3c7
Get:1 http://archive.ubuntu.com/ubuntu bionic InRelease [242 kB]  
Get:2 http://security.ubuntu.com/ubuntu bionic-security InRelease [83.2 kB]  
...
Removing intermediate container ccc047efe3c7  
 ---> 8d10d8dd4e2d
Step 3/6 : ENV SITE_URL http://example.com/  
 ---> Running in 7688364ef33f
Removing intermediate container 7688364ef33f  
 ---> c71f04bdf39d
Step 4/6 : WORKDIR /data  
Removing intermediate container 96b1b6817779  
 ---> 1ee38cca19a5
Step 5/6 : VOLUME /data  
 ---> Running in ce2c3f68dbbb
Removing intermediate container ce2c3f68dbbb  
 ---> f499e78756be
Step 6/6 : CMD sh -c "curl -Lk $SITE_URL > /data/results"  
 ---> Running in 834589c1ac03
Removing intermediate container 834589c1ac03  
 ---> 4b79e12b5c1d
Successfully built 4b79e12b5c1d  
Successfully tagged test-curl:latest  
- docker build تصویر جدیدی را به صورت محلی میسازد.
 - -t نشانهایست که یک برچسب اسمی به تصویر نسبت میدهد.
 
اکنون تصویر جدیدی داریم، و میتوانیم لیست تمامی تصاویر را مشاهده کنیم:
docker images
خروجی کنسول:
REPOSITORY  TAG     IMAGE ID      CREATED         SIZE  
test-curl   latest  5ebb2a65d771  37 minutes ago  180 MB  
nginx       latest  6b914bbcb89e  7 days ago      182 MB  
ubuntu      latest  0ef2e08ed3fa  8 days ago      130 MB  
میتوانیم از تصاویر، کانتینر ایجاد و اجرا کنیم. بیایید با پارامترهای متفاوتی امتحان کنیم:
docker run --rm -v $(pwd)/vol:/data/:rw test-curl  
برای دیدن نتایج ذخیره شده در فایل، دستور زیر را اجرا کنید:
cat ./vol/results
بیایید با فیسبوک امتحان کنیم:
docker run --rm -e SITE_URL=https://facebook.com/ -v $(pwd)/vol:/data/:rw test-curl  
برای دیدن نتایج ذخیره شده در فایل، دستور زیر را اجرا کنید:
cat ./vol/results
مطلب پیشنهادی: دوره آموزش پایتون (python)
# نکات موثر در ساخت imageهای داکر
- تنها محتوای موردنیاز خود را لحاظ کنید – از یک فایل .dockerignore استفاده کنید (مانند .gitignore در گیت).
 - از نصب پکیجهای غیرضروری خودداری کنید، زیرا حجم اضافی از دیسک شما را خواهند گرفت.
 - از cache استفاده کنید. فایلهایی که تغییرات زیادی دارند (مانند کد پروژه) را در انتهای داکرفایل قرار دهید – این کار سبب استفاده بهینه از حافظه cache داکر میشود.
 - مواضب والیومها باشید! باید بهخاطر بسپارید که چه اطلاعاتی در والیومها وجود دارند، زیرا والیومها دائمی هستند و با حذف کانتینر از بین نمیروند، و کانتینر بعدی از اطلاعات والیوم کانتینر قبلی استفاده خواهد کرد.
 - از متغیرهای محیطی (RUN, EXPOSE, VOLUME) استفاده کنید. این سبب انعطافپذیری بیشتر داکرفایلها خواهد شد.
 
# ایمیج alpine
بسیاری از تصاویر داکر (نسخههای تصاویر) بر روی لینوکس آلپاین ایجاد میشوند – توزیعی سبک که به شما اجازه میدهد حجم کلی تصاویر را کاهش دهید.
من پیشنهاد میکنم که برای سرویسهای طرف سوم، مانند Redis، Postgres و... از آلپاین استفاده کنید. برای تصاویر برنامههای خودتان از تصاویر مبتنی بر buildpack استفاده کنید – در این صورت اشکالیابی در داخل کانتینر آسانتر خواهد بود، و بسیاری از ابزارهای سیستمی موردنیاز شما در دسترس قرار خواهند گرفت.
فقط خودتان میتوانید تصمیم بگیرید که از کدام تصویر بِیس استفاده کنید، اما میتوانید با استفاده از یک تصویر بِیس برای تمامی تصاویر، حداکثر بهرهوری را داشته باشید؛ زیرا در این صورت استفاده از cache بهینهتر خواهد بود.
مطلب پیشنهادی: دوره آموزش ردیس
# مثال 4: اتصال میان کانتینرها
Docker compose یک نرمافزار سودمند CLI است که برای اتصال کانتینرها به یکدیگر استفاده میشود. میتوانید docker-compose را با pip ارسال کنید:
sudo pip install docker-compose
در این مثال، کانتینرهای Redis و Python را متصل میکنم:
version: '3.6'  
services:  
  app:
    build:
      context: ./app
    depends_on:
      - redis
    environment:
      - REDIS_HOST=redis
    ports:
      - "5000:5000"
  redis:
    image: redis:3.2-alpine
    volumes:
      - redis_data:/data
volumes:  
  redis_data:
به آدرس examples/compose رفته و دستور زیر را اجرا کنید:
docker-compose up
خروجی کنسول:
Building app  
Step 1/9 : FROM python:3.6.3  
3.6.3: Pulling from library/python  
f49cf87b52c1: Pull complete  
7b491c575b06: Pull complete  
b313b08bab3b: Pull complete  
51d6678c3f0e: Pull complete  
09f35bd58db2: Pull complete  
1bda3d37eead: Pull complete  
9f47966d4de2: Pull complete  
9fd775bfe531: Pull complete  
Digest: sha256:cdef88d8625cf50ca705b7abfe99e8eb33b889652a9389b017eb46a6d2f1aaf3  
Status: Downloaded newer image for python:3.6.3  
 ---> a8f7167de312
Step 2/9 : ENV BIND_PORT 5000  
 ---> Running in 3b6fe5ca226d
Removing intermediate container 3b6fe5ca226d  
 ---> 0b84340fa920
Step 3/9 : ENV REDIS_HOST localhost  
 ---> Running in a4f9a1d6f541
Removing intermediate container a4f9a1d6f541  
 ---> ebe63bf5959e
Step 4/9 : ENV REDIS_PORT 6379  
 ---> Running in fd06aa65fd33
Removing intermediate container fd06aa65fd33  
 ---> 2a581c31ff4f
Step 5/9 : COPY ./requirements.txt /requirements.txt  
 ---> 671093a12829
Step 6/9 : RUN pip install -r /requirements.txt  
 ---> Running in b8ea53bc6ba6
Collecting flask==1.0.2 (from -r /requirements.txt (line 1))  
  Downloading https://files.pythonhosted.org/packages/7f/e7/08578774ed4536d3242b14dacb4696386634607af824ea997202cd0edb4b/Flask-1.0.2-py2.py3-none-any.whl (91kB)
Collecting redis==2.10.6 (from -r /requirements.txt (line 2))  
  Downloading https://files.pythonhosted.org/packages/3b/f6/7a76333cf0b9251ecf49efff635015171843d9b977e4ffcf59f9c4428052/redis-2.10.6-py2.py3-none-any.whl (64kB)
Collecting click>=5.1 (from flask==1.0.2->-r /requirements.txt (line 1))  
  Downloading https://files.pythonhosted.org/packages/34/c1/8806f99713ddb993c5366c362b2f908f18269f8d792aff1abfd700775a77/click-6.7-py2.py3-none-any.whl (71kB)
Collecting Jinja2>=2.10 (from flask==1.0.2->-r /requirements.txt (line 1))  
  Downloading https://files.pythonhosted.org/packages/7f/ff/ae64bacdfc95f27a016a7bed8e8686763ba4d277a78ca76f32659220a731/Jinja2-2.10-py2.py3-none-any.whl (126kB)
Collecting itsdangerous>=0.24 (from flask==1.0.2->-r /requirements.txt (line 1))  
  Downloading https://files.pythonhosted.org/packages/dc/b4/a60bcdba945c00f6d608d8975131ab3f25b22f2bcfe1dab221165194b2d4/itsdangerous-0.24.tar.gz (46kB)
Collecting Werkzeug>=0.14 (from flask==1.0.2->-r /requirements.txt (line 1))  
  Downloading https://files.pythonhosted.org/packages/20/c4/12e3e56473e52375aa29c4764e70d1b8f3efa6682bef8d0aae04fe335243/Werkzeug-0.14.1-py2.py3-none-any.whl (322kB)
Collecting MarkupSafe>=0.23 (from Jinja2>=2.10->flask==1.0.2->-r /requirements.txt (line 1))  
  Downloading https://files.pythonhosted.org/packages/4d/de/32d741db316d8fdb7680822dd37001ef7a448255de9699ab4bfcbdf4172b/MarkupSafe-1.0.tar.gz
Building wheels for collected packages: itsdangerous, MarkupSafe  
  Running setup.py bdist_wheel for itsdangerous: started
  Running setup.py bdist_wheel for itsdangerous: finished with status 'done'
  Stored in directory: /root/.cache/pip/wheels/2c/4a/61/5599631c1554768c6290b08c02c72d7317910374ca602ff1e5
  Running setup.py bdist_wheel for MarkupSafe: started
  Running setup.py bdist_wheel for MarkupSafe: finished with status 'done'
  Stored in directory: /root/.cache/pip/wheels/33/56/20/ebe49a5c612fffe1c5a632146b16596f9e64676768661e4e46
Successfully built itsdangerous MarkupSafe  
Installing collected packages: click, MarkupSafe, Jinja2, itsdangerous, Werkzeug, flask, redis  
Successfully installed Jinja2-2.10 MarkupSafe-1.0 Werkzeug-0.14.1 click-6.7 flask-1.0.2 itsdangerous-0.24 redis-2.10.6  
You are using pip version 9.0.1, however version 10.0.1 is available.  
You should consider upgrading via the 'pip install --upgrade pip' command.  
Removing intermediate container b8ea53bc6ba6  
 ---> 3117d3927951
Step 7/9 : COPY ./app.py /app.py  
 ---> 84a82fa91773
Step 8/9 : EXPOSE $BIND_PORT  
 ---> Running in 8e259617b7b5
Removing intermediate container 8e259617b7b5  
 ---> 55f447f498dd
Step 9/9 : CMD [ "python", "/app.py" ]  
 ---> Running in 2ade293ecb25
Removing intermediate container 2ade293ecb25  
 ---> b85b4246e9f8
Successfully built b85b4246e9f8  
Successfully tagged compose_app:latest  
WARNING: Image for service app was built because it did not already exist. To rebuild this image you must use `docker-compose build` or `docker-compose up --build`.  
Creating compose_redis_1 ... done  
Creating compose_app_1   ... done  
Attaching to compose_redis_1, compose_app_1  
redis_1  | 1:C 08 Jul 18:12:21.851 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf  
redis_1  |                 _._  
redis_1  |            _.-``__ ''-._  
redis_1  |       _.-``    `.  `_.  ''-._           Redis 3.2.12 (00000000/0) 64 bit  
redis_1  |   .-`` .-```.  ```/    _.,_ ''-._  
redis_1  |  (    '      ,       .-`  | `,    )     Running in standalone mode  
redis_1  |  |`-._`-...-` __...-.``-._|'` _.-'|     Port: 6379  
redis_1  |  |    `-._   `._    /     _.-'    |     PID: 1  
redis_1  |   `-._    `-._  `-./  _.-'    _.-'  
redis_1  |  |`-._`-._    `-.__.-'    _.-'_.-'|  
redis_1  |  |    `-._`-._        _.-'_.-'    |           http://redis.io  
redis_1  |   `-._    `-._`-.__.-'_.-'    _.-'  
redis_1  |  |`-._`-._    `-.__.-'    _.-'_.-'|  
redis_1  |  |    `-._`-._        _.-'_.-'    |  
redis_1  |   `-._    `-._`-.__.-'_.-'    _.-'  
redis_1  |       `-._    `-.__.-'    _.-'  
redis_1  |           `-._        _.-'  
redis_1  |               `-.__.-'  
redis_1  |  
redis_1  | 1:M 08 Jul 18:12:21.852 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.  
redis_1  | 1:M 08 Jul 18:12:21.852 # Server started, Redis version 3.2.12  
redis_1  | 1:M 08 Jul 18:12:21.852 # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.  
redis_1  | 1:M 08 Jul 18:12:21.852 # WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis. To fix this issue run the command 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain the setting after a reboot. Redis must be restarted after THP is disabled.  
redis_1  | 1:M 08 Jul 18:12:21.852 * The server is now ready to accept connections on port 6379  
app_1    |  * Serving Flask app "app" (lazy loading)  
app_1    |  * Environment: production  
app_1    |    WARNING: Do not use the development server in a production environment.  
app_1    |    Use a production WSGI server instead.  
app_1    |  * Debug mode: on  
app_1    |  * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)  
app_1    |  * Restarting with stat  
app_1    |  * Debugger is active!  
app_1    |  * Debugger PIN: 170-528-240  
این مثال، سبب افزایش شمارنده view در Redis خواهد شد. آدرس 127.0.0.1:5000 را در مرورگر خود باز کرده و بررسی کنید.
نحوه استفاده از docker-compose خود نیاز به آموزشی جداگانه دارد. برای شروع، میتوانید با تصاویری از داکرهاب کار کنید. اگر خودتان میخواهید تصاویر را ایجاد کنید، توصیههای بالا را دنبال کنید. تنها چیزی که درباره docker-compose میتوانم بگویم این است که همیشه در فایل docker-compose.yml نام والیومها را دقیقا مشخص کنید (اگر والیوم دارید). این قانون ساده در زمان بازبینی والیومهایتان از مشکلات زیادی جلوگیری خواهد کرد.
version: '3.6'  
services:  
  ...
  redis:
    image: redis:3.2-alpine
    volumes:
      - redis_data:/data
volumes:  
  redis_data:
در اینجا، redis_data نامیست که در داخل فایل docker-compose.yml وجود خواهد داشت؛ نام واقعی والیوم، پیشوند نام پروژه + این نام خواهد بود.
برای دیدن والیومها، دستور زیر را اجرا کنید:
docker volume ls
خروجی کنسول:
DRIVER              VOLUME NAME  
local               apptest_redis_data  
در صورت عدم وجود نام والیوم، یک UUID جایگزین خواهد شد. این مثالی از سیستم من است:
DRIVER              VOLUME NAME  
local               ec1a5ac0a2106963c2129151b27cb032ea5bb7c4bd6fe94d9dd22d3e72b2a41b  
local               f3a664ce353ba24dd43d8f104871594de6024ed847054422bbdd362c5033fc4c  
local               f81a397776458e62022610f38a1bfe50dd388628e2badc3d3a2553bb08a5467f  
local               f84228acbf9c5c06da7be2197db37f2e3da34b7e8277942b10900f77f78c9e64  
local               f9958475a011982b4dc8d8d8209899474ea4ec2c27f68d1a430c94bcc1eb0227  
local               ff14e0e20d70aa57e62db0b813db08577703ff1405b2a90ec88f48eb4cdc7c19  
local               polls_pg_data  
local               polls_public_files  
local               polls_redis_data  
local               projectdev_pg_data  
local               projectdev_redis_data  
# روش داکر
داکر دارای قوانین و ملزوماتیست که به معماری سیستم شما بستگی دارد (برنامههایی که داخل کانتینرها قرار دارند). میتوانید این ملزومات را نادیده بگیرید یا راهحل جایگزینی پیدا کنید، اما در این صورت از همه مزایای داکر بهرهمند نخواهید شد. من شدیدا پیشنهاد میکنم که این موارد را رعایت کنید:
- هر برنامه = یک کانتینر
 - پردازش را در پسزمینه اجرا نکنید (از systemd، upstart یا سایر ابزارهای مشابه استفاده نکنید).
 - از SSH استفاده نکنید (در صورت نیاز به ورود به کانتینر، میتوانید از دستور docker exec استفاده کنید).
 - ازپیکربندی (یا عملیات) دستی داخل کانتینر خودداری کنید.
 
# نتیجهگیری
در جهت ارائه خلاصهای از این درسنامه، باید گفت در کنار IDEها و git، داکر امروزه به ابزاری ضروری برای توسعهدهندگان تبدیل شده است که فقط برای ارائه سرویسهای توسعه پایتون استفاده نمیشود. داکر ابزاری با محیط تولید آماده و ساختاری بالغ و غنی است.
داکر میتواند در تمام پروژهها با هر حجم و پیچیدگی استفاده شود. در ابتدا، میتوانید با compose و Swarm شروع کنید. با افزایش حجم پروژه، میتوانید به سرویسهای ابری مانند Amazon Container Services یا Kubernetes منتقل شوید.
همانند کانتینرهای استاندارد در حملونقل محمولهها، قراردادن کد شما در کانتینرهای داکر باعث ساخت سریعتر و بهینهتر فرآیندهای CI/CD خواهد شد. داکر فقط درباره پیشرفت تکنولوژی توسط عدهای از عاشقان آن نیست – بلکه جنبشی عظیم است که هماکنون در معماری کمپانیهای بزرگی مانند PayPal، Visa، Swisscom،General Electrics، Splunk و... استفاده میشود.
اگر مقاله بالا را دوست داشتید، پیشنهاد میکنیم به مطالب زیر هم سر بزنید:
معرفی 8 ابزار مهم پایتون برای ساخت api
جنگو در مقابل فلسک؟ کدامیک مناسب شماست؟