داکر برای برنامه‌نویس‌ها: قسمت دهم - best practices

مقدمه

قسمت قبل یک برنامه جنگو رو داکرایز کردیم. اما برای یک برنامه‌نویس کافی نیست که فقط ایمیج بسازه. بلکه باید یک سری نکات رو رعایت کنه تا به استانداردهای لازم برسه. طی دوقسمت قراره به برخی از این نکات بپردازیم و دلیلش رو هم ببینیم. به این نکات میگن best practice. قطعا برای برنامه‌نویس‌ها این مطالب به درد می‌خورن. مهندسین دواپس هم این مطالب رو مفید خواهند یافت.

برای ارتباط بهتر با این نوشته باید با کانتینرها، داکر و نحوه‌ی داکرایز کردن یک برنامه آشنایی داشته باشین. داشتن پیش‌زمینه در برنامه‌نویسی هم لازمه.


ساختار ایمیج و چگونگی بیلد آن

ابتدا بیاید ساختار یک ایمیج رو بررسی کنیم. ایمیج‌ها یک سری لایه read only هستند و وقتی ما در داکرفایل برخی کارها رو می‌کنیم یک لایه جدید ساخته میشه. مثلا داکرفایل قسمت قبل رو ببینید:


FROM python:3.7 
ENV PYTHONUNBUFFERED 1 
RUN mkdir /code 
WORKDIR /code 
COPY requirements.txt /code/ 
RUN pip install -r requirements.txt 
COPY . /code/ 
ENTRYPOINT [&quot/bin/bash&quot, &quotstart.sh&quot]

بعضی دستورها هستن که فایل سیستم رو تغییر میدن. مثلا وقتی COPY رو استفاده می‌کنم یه فایل از سیستم من می‌فرسته به ایمیج. در نتیجه فایل سیستم ایمیج تغییر می‌کنه. زمانی که چنین دستورهایی رو اجرا می‌کنیم داکر یک لایه جدید ایجاد می‌کنه. فایده‌‌ی این لایه لایه بودن چیه؟ در بخش بعد می‌بینیم.


استفاده از قابلیت بیلد کش (build cache)

طبق صحبتی که بخش قبل کردیم، با اجرای بعضی دستورها لایه‌هایی به ایمیج اضافه میشن. خوبی استفاده از لایه‌ها اینه که می‌تونیم از قابلیت کش استفاده کنیم. یعنی وقتی یک لایه قبلا ساخته شده و تغییری هم قرار نیست بکنه دوباره ساخته نمیشه و سریع میره مرحله بعد.

دو تا از دستورهایی که فایل رو از سیستم میزبان به ایمیج منتقل می‌کنن COPY و ADD هستن. COPY رو قبلا دیدیم؛ فایل رو کپی می‌کنه. ADD هم همین کار رو انجام میده با این تفاوت که می‌تونیم علاوه بر مسیر داخل سیستم میزبان بهش یک url بدیم تا اون رو دانلود کنه و بذاره داخل ایمیج. وقتی این دو تا دستور می‌خواد اجرا شه داکر میاد روی لایه فعلی تغییرات رو ایجاد کنه. تمامی لایه‌هایی که تو سیستم هستن و از روی لایه فعلی ساخته شدن توسط داکر بررسی میشن. به این لایه‌ها میگیم لایه‌های فرزند برای لایه‌ی فعلی.

بررسی لایه‌های فرزند در داکر برای استفاده از cache
بررسی لایه‌های فرزند در داکر برای استفاده از cache


داخل این لایه‌های فرزند چی رو بررسی می‌کنه؟ فایل‌هایی که در ایمیج هست رو نگاه می‌کنه. اگه تمام فایل‌ها در یک لایه فرزند وجود داشته باشن و تغییری نکرده باشن از همون لایه استفاده می‌کنه و لایه‌ی جدید نمی‌سازه. مثلا به داکرفایلی که بالا گذاشتم دوباره نگاه کنید. به جایی که می‌خوام requirements.txt رو کپی کنم توجه کنید. دارم یه دونه فایل به ایمیج کپی می‌کنم. دستور قبلیش هم ببینید. داکر به همه لایه‌هایی که بعد از دستور قبلی وجود دارن نگاه می‌کنه و توی اون‌ها فایل‌ها رو با فایل‌های لایه فعلی مقایسه می‌کنه. البته در مورد requirements.txt محتوای فایلی که داره کپی میشه رو می‌بینه. اگه یکیشون دقیقا همین محتویات رو داشته باشه، از همون استفاده می‌کنه و لایه جدیدی نمی‌سازه. به این شکل سرعت بیلد بالاتر میره (داخل پرانتز بگم که این مقایسه رو با هش کردن انجام میده و نمیاد دونه دونه اطلاعات داخل فایل رو ببینه).

یک دستور دیگه هم هست که برای کش کردن مهمه و در ارتباط با دو دستور بالا خیلی بهتر میشه. اون دستور RUN هستش. RUN موقع ساخت ایمیج دستوری رو داخلش اجرا می‌کنه. مثلا من در داکرفایل بالا pip install زدم. برای این دستور دیگه نمیاد تمام فایل‌های توی فایل سیستم رو بررسی کنه. بلکه فقط به دستوری که جلوش نوشته شده نگاه می‌کنه. مثل بند قبل میاد تمام فرزندان لایه قبلی رو نگاه می‌کنه و اگه بینشون لایه‌ای با همین دستور ساخته شده باشه از اون استفاده می‌کنه. حالا می‌تونیم ترکیب این با بند قبل رو داشته باشیم؛ تو داکرفایل وقتی کپی می‌کنم اغلب اوقات از کش استفاده میشه چون requirements.txt خیلی کم تغییر می‌کنه. در نتیجه RUN هم از کش استفاده می‌کنه چون لایه قبلیش تغییر نکرده و دستور جلوی RUN هم ثابته. اما اگر تمام سورس کد رو اول کپی می‌کردم (چون سورس کد دائما در حال تغییره) اون وقت برای COPY هر دفعه یک لایه جدید ساخته میشه و برای RUN هم دیگه کشی وجود نخواهد داشت چون لایه قبلیش جدیده. پس هر دفعه تمام اون پکیج‌های پایتون رو نصب می‌کنه که خیلی کار رو کند می‌کنه.


ساخت ایمیج‌ برای همه جا

وقتی یک ایمیج رو می‌سازیم باید دقت داشته باشیم که ممکنه به هر جایی بره و باید بیشترین انعطاف ممکن رو داشته باشه. منظورم اینه که نباید رفتار کدی که می‌نویسیم به شکل hard code شده داخلش قرار گرفته باشه و باید با خوندن یک فایل یا متغیر محیطی رفتارش تعیین بشه. برای مثال فرض کنید من یک برنامه نوشتم که قراره به برنامه‌ی یک همکار دیگه از طریق شبکه متصل بشه و بهش درخواست HTTP بده. این اصلا خوب نیست که آدرس، پورت و حتی HTTP یا HTTPS بودن این درخواست در برنامه‌ی من hard code بشه؛ چون در آینده شاید همه‌ی این‌ها تغییر کنه یا به محیط جدیدی بریم که نیازمند تغییر این مقادیر باشیم. در عوض مثلا این‌ها باید در یک متغیر محیطی گذاشته و متناسب با جایی که ایمیج در اون دیپلوی میشه تعیین بشن.


ایمیج‌های stateless

یکی از مهم‌ترین نکاتی که باید رعایت کنیم و از ویژگی‌های یک برنامه‌ی ۱۲ فاکتور این هست که ایمیج‌های ما داخل خودشون چیزی رو ذخیره نکنن. وظیفه‌ی نگهداری اطلاعات رو باید به ابزارهای خاصی که به این منظور نوشته شدن بسپاریم. به چنین سرویسی stateless میگیم چون هیچ state یا حالتی رو در خودش نگه نمی‌داره. مثلا برای نگهداری اطلاعات کاربران اون‌ها رو به دیتابیس MySQL بفرستیم که با ایمیج mysql اجرا شده. چنین ایمیجی وقتی اجرا بشه به راحتی می‌تونه scale بشه. اطلاعاتی ام که داخل ram یا فایل سیستم خود کانتینر قرار می‌گیرن برای cache استفاده میشن و نه برای ذخیره‌ی حالت سیستم.


جمع بندی

در این مطلب تعدادی از best practiceها یا نکات مهم در نوشتن برنامه و داکرفایل رو مرور کردیم. این موارد به ما کمک می‌کنن نرم‌افزارمون به شکل مناسب بیلد و دیپلوی بشه. امیدوارم این مطلب براتون مفید بوده باشه. در صورتی که سوال یا نظری دارید این پایین بفرمایید.

قسمت قبلی

قسمت بعدی