یک توسعهدهنده جونیور حجم یک ایمیج داکر پایتون را از ۱.۲ گیگابایت به ۸۵ مگابایت کاهش داد و زمان خط لوله CI را از ۱۱ دقیقه به ۹۰ ثانیه رساند. ایمیجهای کوچکتر سریعتر دانلود میشوند، هزینه ذخیرهسازی کمتری دارند و آسیبپذیریهای امنیتی کمتری را نمایش میدهند.
چرا اندازه ایمیج اهمیت دارد
هر بار که یک کانتینر pull میشود، ریجستری (registry) کل ایمیج را استریم میکند. یک لایه ۸۵ مگابایتی در عرض چند ثانیه فرود میآید؛ اما یک لایه ۱.۲ گیگابایتی در یک شبکه معمولی میتواند دقایق طول بکشد. عوامل ساخت (build agents) نیز باید ایمیج کامل را آپلود و کش کنند که باعث افزایش زمان CI و هزینههای ذخیرهسازی ابری میشود. هر بسته اضافی یک آسیبپذیری بالقوه است، بنابراین کاهش حجم پایه، سطح حمله (attack surface) را کوچکتر میکند.
حجم اضافی از کجا میآید
- ابزارهای ساخت مانند gcc اگر در همان مرحلهای که اپلیکیشن اجرا میشود نصب شوند، در ایمیج نهایی باقی میمانند.
- کشهای مدیریت بسته (مانند کشهای
aptیاpip) روی دیسک میمانند و بهطور پیشفرض هرگز پاکسازی نمیشوند. - هر دستور
RUNیک لایه خواندنی-فقط (read-only) جدید ایجاد میکند؛ فایلهای تکراری در لایههای مختلف روی هم انباشته میشوند. - ایمیجهای پایه بزرگ مانند
ubuntu:latestهمراه با یک سیستمعامل کامل عرضه میشوند که بسیار فراتر از نیاز یک محیط اجرای مینیمال پایتون است.
فرآیند کاهش حجم در سه مرحله
| مرحله | ایمیج پایه | رویکرد ساخت | حجم نهایی |
|---|---|---|---|
| ۱ | پایتون استاندارد (کامل) | تک مرحلهای، با تمام ابزارها | ۱.۱۸ گیگابایت |
| ۲ | python:slim |
چند مرحلهای: builder با gcc، مرحله نهایی فقط بستههای کامپایلشده را کپی میکند | ۲۱۰ مگابایت |
| ۳ | python:alpine |
چند مرحلهای روی Alpine Linux که خودش بسیار کوچک است | ۸۵ مگابایت |
مرحله ۱ – خط پایه
با شروع از ایمیج پیشفرض پایتون، توسعهدهنده با یک آرتیفکت ۱.۱۸ گیگابایتی مواجه شد. این ایمیج شامل تمام پشته Debian، هدرهای توسعه (development headers) و کش pip بود.
مرحله ۲ – نسخه slim با یک builder
تغییر به python:slim ردپای سیستمعامل را کاهش داد، اما ابزارهای ساخت همچنان باقی ماندند. اضافه کردن یک مرحله builder اجازه داد تا gcc، make و سایر وابستگیهای زمان کامپایل، نصب و کامپایل شوند و سپس حذف گردند. مرحله نهایی از COPY --from=builder استفاده کرد تا فقط wheelهای کامپایلشده و فایلهای زمان اجرا (runtime) را فراخوانی کند و حجم را به ۲۱۰ مگابایت کاهش داد.
مرحله ۳ – پیروزی Alpine
Alpine Linux بر پایه musl libc و busybox ساخته شده است. تکرار الگوی چند مرحلهای روی Alpine منجر به تولید یک ایمیج ۸۵ مگابایتی شد که ۹۳٪ نسبت به نسخه اصلی کاهش حجم داشت. توسعهدهنده خاطرنشان کرد که Kubernetes ایمیج را در عرض چند ثانیه pull کرد و کار CI در ۹۰ ثانیه به پایان رسید.
تأثیر در دنیای واقعی
- کاهش هزینههای ذخیرهسازی – فضای ذخیرهسازی ریجستری کاهش مییابد.
- بهبود امنیت – بستههای کمتر به معنای CVEهای کمتر برای پیگیری است. ایمیج Alpine فقط شامل محیط اجرای پایتون و کد اپلیکیشن است.
- CI سریعتر – زمان اجرای خط لوله از ۱۱ دقیقه به ۹۰ ثانیه کاهش یافت.
نکات کاربردی برای کاهش حجم Dockerfile شما
- از تگهای
:latestخودداری کنید؛ نسخههای:slimیا:alpineرا که با نیازهای شما مطابقت دارند انتخاب کنید. - از ساختهای چند مرحلهای (multi-stage builds) استفاده کنید: یک مرحله اختصاصی builder برای کامپایل، و یک مرحله runtime که فقط آرتیفکتهای مورد نیاز شما را دریافت میکند.
- به صورت انتخابی کپی کنید:
COPY --from=builder /path/to/installed /path/in/final. - دستورات را طوری ترتیببندی کنید که نصب وابستگیها قبل از کپی کردن کد منبع انجام شود؛ این کار باعث حداکثر شدن کش لایهها میشود.
- کشها را بهطور صریح پاک کنید، مثلاً:
rm -rf /var/lib/apt/lists/* ~/.cache/pip.
اگر از Alpine استفاده میکنید، زمانی که اپلیکیشن شما با مشکلات حل نام (DNS resolution) مواجه شد، بسته nss را اضافه کنید. هنگام نصب با pip، مسیر /root/.local/bin را به PATH اضافه کنید تا اسکریپتهای نصبشده در سطح محلی در زمان اجرا پیدا شوند.
هشدارها
musl libc در Alpine ممکن است با binary wheelهایی که برای glibc کامپایل شدهاند تداخل داشته باشد و باعث خطاهای زمان اجرا شود. در این موارد، wheelها را داخل builder مربوط به Alpine دوباره بسازید یا به یک پایه slim بازگردید. اضافه کردن بسته nss هزینه کمی برای تضمین قابلیت اطمینان DNS است.
چه چیزهایی را در مرحله بعد بررسی کنیم
- ایمیجهای موجود خود را برای یافتن لایههای بزرگی که میتوانند کاندیدای بازنویسی چند مرحلهای باشند، اسکن کنید.
- لاگهای CI را برای زمانهای آپلود و دانلود مانیتور کنید تا حجم اضافی ایمیج را شناسایی کنید.
- گزارشهای آسیبپذیری توزیع پایه انتخابی خود را زیر نظر داشته باشید.
درس واضح است: یک Dockerfile منضبط، یک پایه سبک و یک مرحله builder میتواند حجم یک ایمیج پایتون را بیش از یک مرتبه بزرگی کاهش دهد و بدون فدا کردن عملکرد، مزایای ملموس سرعت و هزینه را به ارمغان بیاورد.
