بررسی عمیق با ابزار «dive» برای پیدا کردن حجم اضافی (A Deep Dive with ‘dive’ to Find the Bloat)
نکات کلیدی
- یک ایمیج Docker فقط یک فایل یکپارچه نیست، بلکه مجموعهای از لایههای تغییرناپذیر است که هر لایه نشاندهنده تغییرات ایجادشده توسط یک دستور در Dockerfile است.
- ایمیجهای بزرگ Docker در پروژههای هوش مصنوعی، عمدتاً بهدلیل نصب کتابخانههای حجیم AI و اجزای سنگین سیستمعامل پایه دچار تورم میشوند.
- با ترکیب docker history برای مشاهده اندازه لایهها و ابزار dive برای بررسی تعاملی محتوای آنها، میتوان منابع دقیق ایجاد حجم اضافی را شناسایی کرد.
- شناسایی دقیق منابع تورم با این ابزارهای تشخیصی، امکان تصمیمگیری آگاهانه برای کاهش هدفمند اندازه ایمیج و افزایش بهرهوری را فراهم میکند.
- تشخیص مؤثر ایمیج، نهتنها وابستگیهای پایتون، بلکه نصب بستههای سیستمعامل پایه و فایلهای کپیشده از کانتکست بیلد را نیز بررسی میکند.
مقدمه
دو دلیل عالی برای استفاده از Docker image در یک پروژه هوش مصنوعی وجود دارد: اول اینکه کار میکند و مدل شما را بهدرستی اجرا میکند، و دوم اینکه میتوان آن را مهندسیشده ساخت؛ یعنی کمحجم باشد، سریع بیلد شود و بهصورت کارآمد دیپلوی گردد. ممکن است در نگاه اول این دو دلیل نامرتبط به نظر برسند، مثل یک موتور قدرتمند و یک شاسی ظریف. اما من چنین فکر نمیکنم. به نظر من، ایمیجی که خوب ساخته شده باشد، احتمال بیشتری دارد که در دنیای پرچالش مهندسی نرمافزار و هوش مصنوعی، پایدار کار کند و بهخوبی مقیاس بگیرد.
هدف این مقاله این است که ایمیجهای Docker ما را از جعبههای سیاه، بزرگ و غیرشفاف، به چیزی شفافتر و مهندسیشدهتر تبدیل کند. چرا باید به این موضوع اهمیت بدهیم؟ چون در دنیای هوش مصنوعی، جایی که سرعت تکرار حرف اول را میزند و هزینههای کلاد میتوانند سرسامآور باشند، یک ایمیج ۵ گیگابایتی که زمان زیادی برای بیلد و دیپلوی میخواهد، فقط یک مزاحمت نیست؛ بلکه مانعی برای پیشرفت و عاملی برای افزایش هزینههای استقرار است.
قبل از بهینهسازی، باید تشخیص بدهیم. باید به کارآگاهان ایمیج Docker تبدیل شویم و به تمام گوشهوکنار ایمیجها سرک بکشیم تا بفهمیم این کانتینرهای دیجیتال چگونه ساخته شدهاند؛ لایهبهلایه آنها را بررسی کنیم و دقیقاً مشخص کنیم حجم اضافی، ناکارآمدی و زبالههای دیجیتال واقعاً کجا پنهان شدهاند.
«چرا بهینهسازی؟» برای ایمیجهای Docker در هوش مصنوعی
انسانها ابزارساز هستند و Docker ابزاری فوقالعاده برای بستهبندی و دیپلوی ساختههای هوش مصنوعی ماست. اما مثل هر ابزار دیگری، کارایی آن به نحوه استفاده ما بستگی دارد. یک ایمیج Docker بهینهنشده در جریان کاری هوش مصنوعی میتواند مشکلات متعددی ایجاد کند.
چرخههای توسعه کندتر
ایمیج دمو ساده ما برای یک طبقهبند BERT (Bidirectional Encoder Representations from Transformers) که بهزودی آن را کالبدشکافی میکنیم، حجمی برابر با ۲.54GB داشت و حدود ۵۶ ثانیه طول کشید تا روی یک سیستم مدرن بیلد شود. حالا یک سرویس واقعی در محیط تولید را تصور کنید که وابستگیهای بیشتری دارد، شاید کتابخانههای سفارشی بزرگتر و دادههای جانبی گستردهتری را هم در خود بستهبندی کرده باشد. آن بیلد ۵۶ ثانیهای برای یک مثال اسباببازی، بهراحتی میتواند به چند دقیقه یا حتی دهها دقیقه برای یک ایمیج تولیدی تبدیل شود. حالا این زمان را در تعداد اعضای یک تیم ضرب کنید، جایی که هر توسعهدهنده چندین بار در روز بیلد انجام میدهد. این فقط از دست رفتن چند ثانیه نیست، بلکه ضربهای واقعی به سرعت تکرار و تمرکز توسعهدهندگان است.
پایپلاینهای CI/CD ناکارآمد
هر بار push و pull کردن آن ایمیج ۲.54GB از طریق سیستم یکپارچهسازی و استقرار مداوم، زمان و پهنای باند مصرف میکند. شاید ۲.54GB برای یک دیپلوی کمتکرار قابلقبول باشد، اما سیستمهای تولیدی معمولاً بهروزرسانیهای مکرر دارند؛ برای بازآموزی مدلها، بهروزرسانی کتابخانهها یا ارائه قابلیتهای جدید. اگر ایمیج تولیدی شما به 5GB، 10GB یا حتی بیشتر برسد (که چندان هم غیرمعمول نیست)، این عملیات CI/CD به گلوگاههای جدی تبدیل میشوند، انتشار نسخهها را به تأخیر میاندازند و منابع بیشتری مصرف میکنند.
هزینههای بالاتر کلاد
ذخیره ایمیجهای چندگیگابایتی در رجیستریهای کانتینر رایگان نیست، بهخصوص زمانی که چندین نسخه از پروژههای مختلف را مدیریت میکنید. کوچککردن ایمیج ۲.54GB ما بلافاصله باعث صرفهجویی در هزینه ذخیرهسازی میشود. مهمتر از آن، این تلاش برای بهرهوری با اهداف مدرن پایداری همراستا است. با کاهش حجم دادهای که هنگام push، pull و مقیاسدهی منتقل میشود، مصرف انرژی و ردپای کربنی زیرساخت کلاد کاهش مییابد. ساخت یک ایمیج Docker سبک فقط یک بهینهسازی فنی یا مالی نیست؛ بلکه گامی واقعی در جهت ایجاد سیستمهای هوش مصنوعی مسئولانهتر و «سبزتر» است.
وضعیت کمتر «کثیف»
ایمیج لاغرتر ذاتاً امنتر است. یک ایمیج Docker متورم، بهطور طبیعی چیزی فراتر از اپلیکیشن شما را در خود دارد؛ اغلب شامل مجموعهای کامل از ابزارهای سیستمعامل، شِلها، مدیران بسته (مثل apt و pip) و کتابخانههایی است که الزاماً موردنیاز نیستند. هر کدام از این اجزا یک مسیر بالقوه برای حمله محسوب میشود. اگر یک آسیبپذیری در curl، bash یا هرکدام از صدها ابزار دیگر کشف شود و آن ابزار در ایمیج شما وجود داشته باشد، استقرار شما آسیبپذیر خواهد بود. با کوچکسازی تهاجمی محتوای کانتینر، در سطح فایلسیستم اصل حداقل دسترسی را پیادهسازی میکنیم، سطح حمله را بهشدت کاهش میدهیم و ابزارهای کمتری برای سوءاستفاده مهاجم باقی میگذاریم. این تلاش برای رسیدن به یک وضعیت «تمیز»، بهینهسازی را از یک تنظیم عملکردی ساده به یک بهترینروش بنیادین امنیتی تبدیل میکند.
هدف فقط کوچککردن نیست، بلکه سریعتر، کارآمدتر و در نهایت مقاومتر کردن کل چرخه توسعه و استقرار هوش مصنوعی است. اصل «کوچکسازی» آنقدر در عملیات مدرن کلاد بنیادی است که دقیقاً به همین دلیل، هایپراسکیلرهایی مثل AWS، Microsoft Azure و Google Cloud روی ساخت و ترویج توزیعهای لینوکس سبک خودشان، مثل Bottlerocket OS و CBL-Mariner، سرمایهگذاری میکنند. آنها میدانند که در مقیاس بزرگ، هر مگابایت صرفهجوییشده و هر میلیثانیه کاهشیافته در انتقال و راهاندازی ایمیج، به بهبودهای چشمگیر در هزینه، عملکرد و امنیت منجر میشود. با بهینهسازی ایمیجهای هوش مصنوعی خود، ما همان منطق امتحانشدهای را به کار میگیریم که زیرساختهای کلاد در مقیاس جهانی را تغذیه میکند.
نمونه آزمایش ما: طبقهبند ساده BERT
بیایید «بیمار» جلسه تشخیص امروز را معرفی کنیم. این یک اپلیکیشن ساده طبقهبندی متن است که از مدل محبوب bert-base-uncased در Hugging Face Transformers استفاده میکند.
این راهنما همراه با یک مخزن در GitHub است که ایمیج «naive_image» ما را نمایش میدهد.
مواد اولیه سادهاند:
فایل requirements.txt (در مسیر naive_image/ پروژه)

یک Dockerfile «مسئلهدار»
این فایل ایمیج bert-classifier-naive ما را میسازد. ایمیج کار میکند، اما عمداً چند اشتباه رایج در آن باقی گذاشتهایم تا مسیر تشخیص آموزندهتر شود.

وقتی این ایمیج را بیلد میکنیم، یک ایمیج ۲.54GB ایجاد میشود.
حالا بیایید درِ آن را باز کنیم.
جعبهابزار تشخیص: لایهبهلایه کالبدشکافی
به یک ایمیج Docker نه بهعنوان یک توده یکپارچه، بلکه بهعنوان پشتهای از ورقهای شفاف فکر کنید که هر کدام نماینده یک تغییر یا اضافهشدن هستند. ابزارهای ما کمک میکنند این ورقها را بررسی کنیم.
نگاه اول
این دستور وزنکشی سریع شماست.
خروجی فوراً نشان میدهد ایمیج bert-classifier-naive حجمی معادل ۲.54GB دارد؛ نشانهای واضح از اینکه جای بهبود وجود دارد.
لاگ دستورات
اگر docker image ls اندازه نهایی ایمیج را نشان میدهد، docker history این عدد را میشکند. این دستور تمام دستورات Dockerfile را فهرست میکند و دقیقاً نشان میدهد هر مرحله چه مقدار به حجم اضافه کرده است.

خروجی چیزی شبیه به این خواهد بود:
از این تاریخچه، دو نکته فریاد میزنند. اول، لایه ۱.51GB مربوط به دستور pip install که بزرگترین سهم از اقدامات مستقیم ماست. بعد از آن، خود ایمیج پایه سهم قابلتوجهی دارد، بهطوری که یک لایه بهتنهایی 560MB است و apt-get install curl هم ۱۹.4MB دیگر اضافه میکند. این نمای تاریخی به ما میگوید کدام دستورات «سنگینوزن» هستند.
بررسی عمیق
و حالا ستاره نمایش تشخیصی ما: dive. Dive یک ابزار CLI متنباز برای بررسی ایمیج Docker، محتوای لایهها و کشف راههای کاهش اندازه ایمیج است.
سادهترین راه نصب dive استفاده از Homebrew است.
اجرای آن:
حالا بیایید با dive داخل ایمیج bert-classifier-naive قدم بزنیم:
پایه کار – لایههای ایمیج پایه
یکی از بزرگترین لایهها در پایین فهرست لایهها در سمت چپ را انتخاب کنید. مثلاً همان لایهای که docker history گفت 560MB است. در پنل سمت راست، ساختار فایلسیستم را میبینید. این شامل بخش عمدهای از ایمیج پایه python:3.10 (یک سیستمعامل کامل Debian)، کتابخانه استاندارد پایتون و موارد دیگر است. شبیه این است که یک خانه مبله بخرید، در حالی که فقط به یک اتاق خاص نیاز داشتید.
لایه apt-get install curl (19.4MB)
به این لایه بروید. در سمت راست، dive نشان میدهد curl و وابستگیهایش اضافه شدهاند. نکته مهم این است که اگر مسیر /var/lib/apt/lists/ را بررسی کنید، میبینید که پر از متادیتای بستههاست. چون این دادهها را در همان لایه پاک نکردهایم، با اینکه در زمان اجرا کاربردی ندارند، همچنان بخشی از حجم این لایه باقی ماندهاند. توجه کنید که dive حتی یک شاخص «Potential wasted space» هم دارد (پایین سمت چپ، در نمونه شما ۹.5MB بود) که معمولاً چنین مواردی را علامتگذاری میکند.
لایه pip install (رویداد اصلی)
این لایه را انتخاب کنید. اینجا جایی است که وابستگیهای خاص هوش مصنوعی ما وارد صحنه میشوند. مسیر /usr/local/lib/python3.10/site-packages/ را در سمت راست باز کنید. مقصرها را میبینید: دایرکتوریهای حجیم torch، transformers، numpy و دوستانشان. اینها «حجم اضافی» به معنای غیرضروریبودن نیستند (ما به این کتابخانهها نیاز داریم)، اما اندازه بسیار بزرگ آنها عاملی کلیدی است که باید مدیریت شود.
لایههای COPY
لایههای مربوط به
COPY naive_image/requirements.txt ./requirements.txt
COPY naive_image/app/ ./app/
COPY naive_image/sample_data/ ./sample_data/
در مورد ما کوچک هستند (بهترتیب ۳۲۰ بایت، ۱۲.۲ کیلوبایت و ۳۷۶ بایت). اما dive بهوضوح نشان میداد اگر فایل .dockerignore را فراموش کرده بودیم و ناخواسته کل تاریخچه .git، محیطهای مجازی محلی یا دیتاستهای بزرگ را کپی میکردیم چه فاجعهای رخ میداد. دستور COPY . . بدون یک .dockerignore دقیق، میتواند اسب تروای حجم اضافی باشد.
استفاده از dive مفهوم انتزاعی لایهها را به یک فایلسیستم ملموس و قابلکاوش تبدیل میکند. این ابزار به ما اجازه میدهد دقیقاً ببینیم هر دستور Dockerfile چه کاری انجام میدهد، چقدر فضا مصرف میکند و ناکارآمدیها کجا هستند.
بررسی مخزن کد
تمام کدها، از جمله Dockerfile مربوط به naive_image و فایلهای اپلیکیشن که امروز بررسی کردیم، در مخزن GitHub همراه مقاله در دسترس هستند.
این مخزن همچنین چندین دایرکتوری دیگر مثل slim_image، multi_stage_build، layered_image و distroless_image دارد که رویکردهای مختلفی برای ساخت یک کانتینر لاغرتر برای اپلیکیشن BERT ما را نشان میدهند. این یک محیط تمرینی عالی است تا مهارتهای تشخیصی جدیدتان را تمرین کنید. توصیه میکنیم ایمیجهای این Dockerfileها را خودتان بیلد کنید و با dive بررسیشان کنید تا دقیقاً ببینید ساختار، اندازه و ترکیب آنها چه تفاوتی با نقطه شروع ساده ما دارد. این بهترین راه برای تثبیت درک شما از این است که تغییرات Dockerfile چگونه در لایههای نهایی ایمیج بازتاب پیدا میکنند.
نوبت شماست که dive کنید
بررسی ما روی bert-classifier-naive نکات روشنگری داشت:
- حجم کل ایمیج ۲.54GB است.
- وابستگیهای پایتون مدل BERT (torch، transformers و غیره) بهتنهایی ۱.51GB فضا اشغال کردهاند.
- ایمیج پایه python:3.10 صدها مگابایت از اجزای سیستمعامل و کتابخانه استاندارد را اضافه میکند.
- حتی عملیاتهای کوچک، مثل نصب curl بدون پاکسازی کشهای مدیر بسته، وزن اضافی ایجاد میکنند (لایه ۱۹.4MB ما حدود ۹.5MB «فضای هدررفته» داشت).
اکنون نقشهای شفاف از محل تجمع گیگابایتها داریم. این تشخیص دقیق، زیربنای هر بهینهسازی مؤثر است. با ابزارهایی مثل dive، حالا شما هم مجهز هستید تا ایمیجهای خودتان را کالبدشکافی کنید و همین الگوها را شناسایی کنید. گامهای منطقی بعدی در مسیر بهینهسازی، بهطور طبیعی شامل بررسی انتخابهای پایه مثل ایمیج پایه و استفاده از تکنیکهایی برای جداسازی نیازهای زمان بیلد از الزامات زمان اجرا خواهد بود.
