در دنیای برنامههای کانتینری، اندازه تصویر تنها یک عدد نیست؛ بلکه معیاری است که مستقیماً بر سرعت استقرار، هزینههای ذخیرهسازی و سطح حمله تأثیر میگذارد. برای توسعهدهندگان متوسط تا پیشرفته، گذر از ساختهای تکمرحلهای به چندمرحلهای یکی از حیاتیترین بهینهسازیها در جعبهابزار مهندسی DevOps است. این پست وبلاگ مکانیکها، مزایا و پیادهسازی عملی ساختهای چندمرحلهای داکر را بررسی میکند.
مشکل ساختهای تکمرحلهای
به طور سنتی، ساخت یک تصویر داکر شامل ترکیب محیط ساخت و محیط اجرا در یک لایه واحد بود. یک برنامه معمولی Go یا Java را در نظر بگیرید. برای کامپایل کد منبع، به کامپایلرهایی مانند gcc یا javac، ابزارهای ساخت مانند make و gradle و احتمالاً کتابخانههای توسعه نیاز دارید. این ابزارها وزن قابل توجهی به تصویر نهایی اضافه میکنند.
حتی اگر این وابستگیها را قبل از اجرای برنامه حذف کنید، لایهها در تاریخچه تصویر باقی میمانند. این منجر به تصاویر حجمی میشود که زمان بیشتری برای ارسال و دریافت از رجیستریها، زمان راهاندازی کندتر و سطح حمله بزرگتری برای آسیبپذیریها نیاز دارند. ساختهای چندمرحلهای با اجازه دادن به استفاده از چندین دستور FROM در Dockerfile، کپی کردن خروجیها از یک مرحله به مرحله دیگر و حذف اتلافهای غیرضروری، این مشکل را حل میکنند.
ساختهای چندمرحلهای چگونه کار میکنند
ساختهای چندمرحلهای فرآیند ساخت را به مراحل متمایز تقسیم میکنند. هر دستور FROM یک مرحله جدید را آغاز میکند. میتوانید به مراحل نام بدهید تا ارجاع به آنها آسانتر شود. مفهوم کلیدی این است که تنها مرحله نهایی به عنوان تصویر واقعی که به رجیستری شما ارسال میشود، در نظر گرفته میشود.
بیایید یک مثال عملی با یک برنامه ساده Go را بررسی کنیم. هدف تولید یک باینری استاتیک کوچک است که در یک تصویر پایه Alpine Linux مینیمال اجرا شود.
مثال: بهینهسازی یک برنامه Go
# مرحله ۱: محیط ساخت
FROM golang:1.21-alpine AS builder
# نصب ابزارهای ساخت ضروری
RUN apk add --no-cache git
# تنظیم دایرکتوری کاری
WORKDIR /app
# کپی فایلهای go.mod و go.sum برای کشگذاری وابستگیها
COPY go.mod go.sum ./
RUN go mod download
# کپی کد منبع
COPY . .
# ساخت برنامه
RUN go build -o myapp .
# مرحله ۲: محیط اجرا
FROM alpine:3.18 AS final
# نصب گواهیهای ca برای درخواستهای HTTPS
RUN apk --no-cache add ca-certificates
# ایجاد یک کاربر غیرریشه برای امنیت
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
# کپی باینری از مرحله builder
COPY --from=builder /app/myapp /usr/local/bin/myapp
# تنظیم مالکیت برای کاربر غیرریشه
RUN chown appuser:appgroup /usr/local/bin/myapp
# تغییر به کاربر غیرریشه
USER appuser
# نمایش پورت (در صورت کاربرد)
EXPOSE 8080
# اجرای برنامه
CMD ["myapp"]
در این مثال، مرحله builder از یک تصویر Go کامل برای کامپایل کد استفاده میکند. مرحله final از یک تصویر Alpine مینیمال استفاده میکند، فقط باینری کامپایل شده را کپی میکند و تمام ابزارهای ساخت و کد منبع را دور میریزد. تصویر حاصل به طور چشمگیری کوچکتر است—اغلب کمتر از ۱۰ مگابایت در مقایسه با چندین صد مگابایت.
بهترین شیوهها و تکنیکهای پیشرفته
برچسبگذاری مراحل
همیشه مراحل خود را با استفاده از AS name برچسبگذاری کنید. این کار Dockerfile شما را خواناتر میکند و به شما اجازه میدهد مراحل خاصی را در طول فرآیند کپی با استفاده از --from=name ارجاع دهید.
بهینهسازی کشگذاری
دستورهایی که به ندرت تغییر میکنند (مانند نصب وابستگیها) را در اوایل فرآیند ساخت قرار دهید. این کار از کشگذاری لایههای داکر بهره میبرد و بازسازیها را زمانی که کد منبع را تغییر میدهید اما وابستگیها را خیر، سریعتر میکند.
ملاحظات امنیتی
با استفاده از یک تصویر نهایی مینیمال (مانند Alpine یا Distroless)، تعداد بستههای نصب شده و در نتیجه تعداد آسیبپذیریهای بالقوه را کاهش میدهید. علاوه بر این، همیشه برنامه خود را به عنوان یک کاربر غیرریشه اجرا کنید تا تأثیر فرار از کانتینر را محدود نمایید.
نتیجهگیری
ساختهای چندمرحلهای تنها یک راحتی نیستند؛ آنها برای کانتینرهای درجه تولید ضروری هستند. آنها به توسعهدهندگان امکان میدهند محیط ساخت را از محیط اجرا جدا کنند که منجر به تصاویر کوچکتر، سریعتر و امنتر میشود. با پذیرش این الگو، خطوط لوله CI/CD خود را هموار میکنید، هزینههای ذخیرهسازی ابری را کاهش میدهید و قابلیت اطمینان کلی زیرساخت خود را بهبود میبخشید. از امروز شروع به بازنویسی Dockerfileهای خود کنید تا از مزایای این ویژگی قدرتمند بهرهمند شوید.