پایین نگه داشتن حجم داکر imageها یکی از چالشهای مهم در داکر است و زمانی که حجم image بالا میرود نگه داری و استفاده از آن نیز مشکل تر خواهد بو و از طرفی نوشتن چند داکرفایل برای یک پروژه هم میتونه پیچیدگی رو بیشتر کنه.
میدونید که هر دستوری که در داکرفایل اجرا میشود یک لایه به image اضافه میکند و از این رو باید جوری دستورها را بنویسیم که image نهایی بهینه باشد. یکی از کارهایی که به بهینه کردن image کمک میکنه استفاده از داکرفایلهای چندسطحی (multi-stage) است.
تا ورژنهای قبلی داکر که قابلیت داکرهای چندسطحی (multi stage) رو نداشت مجبور میشدیم از builder pattern استفاده کنیم، یا به زبون ساده بخواهیم بگیم یعنی اینکه برای حالت Develop و Production داکر فایلهای جداگانه بنویسیم و هر کدام را جدا اجرا کنیم.
اما...
مولتی استیج یعنی اینکه در یک داکرفایل چندین بار دستور FROM را داشته باشیم. هر FROM از یک Base image استفاده میکند و یک stage جدید را میسازد. در multi stage این قابلیت را داریم که از یک استیج محتوایی را به استیج دیگر کپی کنیم و این بدان معنا است که میتوانیم از آنچه که نیاز نداریم صرفنظر کنیم
FROM golang:1.7.3 WORKDIR /go/src/github.com/alexellis/href-counter/ RUN go get -d -v golang.org/x/net/html COPY app.go . RUN CGO ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app . FROM alpine:latest RUN apk - -no-ache add ca-certificates WORKDIR /root/ COPY - -from=0 /g/src/github.com/alexellis/href-counter/app . CMD ["./app"]
در داکر فایل بالا میبینید که دو دستور FROM داریم و از Base image های golang و alpine استفاده کردیم با این اوصاف ما فقط یک داکر فایل داریم و با یک دستور آنرا اجرا میکنیم
docker build -t example/example .
در این داکر فایل دستور FROM دوم یک نسخه از آخرین ورژن alpine را به عنوان Base image قرار میدهد و دستور COPY --from=0 محتویات آدرس داده شده را از درون مسیر
/g/src/github.com/alexellis/href-counter/app
از stage نخست به آدرس / در stage دوم کپی میکند.
احتمالا این سوال برایتان پیش آمده که from=0-- به چه معناست! stage ها به طور پیش فرض هیچ اسمی ندارند و برای اینکه به یک stage اشاره کنیم، از عدد شمارندهی هر کدام از آنها استفاده میکنیم. این عدد به طور پیش فرض به هر stage داده میشود و توجه کنید که این شمارنده از صفر شروع میشود و برای هر استیج یک واحد افزایش پیدا میکند.
اما اگر بخواهیم به یک stage نام بدهیم با استفاده از عبارت AS اینکار را انجام میدهیم.
بعد از اینکار برای اشاره به یک stage دیگر نیازی به استفاده از عدد نیست و میتوان از طریق نام داده شده به آن دسترسی داشت.
به طور مثال:
FROM golang:1.7.3 AS golang_builder WORKDIR /go/src/github.com/alexellis/href-counter/ RUN go get -d -v golang.org/x/net/html COPY app.go . RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app . FROM alpine:latest RUN apk --no-cache add ca-certificates WORKDIR /root/ COPY --from=golang_builder /go/src/github.com/alexellis/href-counter/app . CMD ["./app"]
حتما، ممکنه که یک داکر فایل چند stage داشته باشه و ما نخواهیم که همهی آنها را اجرا کنیم در این شرایط میتونیم در دستور build مشخص کنیم که قصد اجرای کدام بخش از داکر فایل را داریم.
docker build --target golang_builder example/example .
یعنی در دستور build با استفاده از آپشن target ، نام stageی که قصد اجرای آن را داریم، ذکر میکنیم.
گاهی اوقات برای دیباگ یک بخش از داکر فایل ممکنه که بخواهید فقط یک استیج رو اجرا کنید، با این روش به آسونی این کار ممکن میشه.
- همیشه برای کپی کردن از یک stage نیاز نیست که اون stage از قبل در داکر فایل ما موجود باشه! شما میتونید نامی که به عنوان ورودی به from-- میدید خودش یک Image به صورت لوکال و یا روی داکرهاب باشه.
مثلا میتونید بنویسید:
COPY --from=nginx:latest /etc/nginx.conf /nginx.conf
دستور بالا ابتدا چک میکند که اگر ایمیج nginx:lates موجود بود، کانفیگهای موجود در آدرس /etc/nginx.conf را به آدرس nginx.conf/ کپی میکند و اگر موجود نبود ابتدا nginx:latest رو دریافت میکنه و سپس عمل کپی را انجام میدهد.
- استفاده از multi-stage در جهت بهینهتر شدن imageها خیلی مفیده اما این موضوع رو هم فراموش نکنید که استفاده از این شیوه وحی مُنزل نیست و ممکنه بعضی از همکارانتون با این سبک نوشتن آشنایی نداشته باشند و یا جاهایی باعث پیچیده تر شدن قضيه بشه. پس قبل از استفاده شرایط رو بررسی کنید.
در ضمن، یک مثال از داکر فایلهای چند سطحی رو میتونید تو این پست مشاهده کنید.