همانطور که میدانید داکر از جمله اصلیترینِ ابزارها برای مدیریت و اجرای کانتینرها است. اما یکی از اجزای کلیدی خود داکر، فایل Dockerfile است که به تعریف نحوه ساخت یک ایمیج (Image) در محیط Docker کمک میکند. در این مطلب از وبلاگ همروش ابتدا قصد داریم با Dockerfile و ساختار آن آشنا شویم، سپس بهترین رویکردها و ترفندهایی را که به نوشتن Dockerfile بهتر منتهی میشود بپردازیم.
آشنایی با Dockerfile
Dockerfile یک فایل متنی ساده است و شامل دستوراتی میشود که برای ساخت ایمیج در Docker استفاده میشود. این دستورات به Docker میگویند که چگونه یک محیط اجرایی مشخص را از صفر ایجاد کند. هر Dockerfile معمولاً از موارد زیر تشکیل میشود:
- مشخص کردن پایه: با استفاده از دستور
FROM
مشخص میشود که ایمیج پایهای که باید استفاده شود چیست. این ایمیج میتواند یک سیستم عامل ساده یا یک ایمیج پیچیدهتر با نرمافزارهای از پیش نصب شده باشد. - اضافه کردن فایلها و دایرکتوریها: با دستور
COPY
یاADD
فایلها و دایرکتوریها به ایمیج اضافه میشوند. - اجرای دستورات: با دستور
RUN
دستورات مختلفی اجرا میشوند، مانند نصب نرمافزارها و بستههای مورد نیاز. - تنظیم متغیرهای محیطی: با استفاده از دستور
ENV
متغیرهای محیطی تنظیم میشوند. - مشخص کردن نقطه ورودی: با دستور
ENTRYPOINT
یاCMD
مشخص میشود که چه برنامهای باید هنگام اجرای کانتینر اجرا شود. - باز کردن پورتها: با دستور
EXPOSE
پورتهایی که کانتینر برای ارتباطات خارجی استفاده میکند تعریف میشوند.
در زیر نمونهای از سینتکس این فایل را مشاهده خواهید کرد:
FROM node:18-alpine
WORKDIR /app
COPY . .
RUN yarn install --production
CMD ["node", "src/index.js"]
EXPOSE 3000
بهترین روشها برای نوشتن Dockerfile
هر چند که مستندات داکر مکان بسیار مناسبی برای یادگیری شیوه نوشتن درست فایلهای Dockerfile است، اما جدای از روشهای استاندارد، ترفندها و روشهایی وجود دارد که به بهینهتر شدن Dockerfile کمک میکند. در ادامه سعی میکنیم تا برخی از اصلیترین این موارد را بررسی کنیم.
استفاده از ایمیج پایه مناسب
از ایمیجهای رسمی و مورد تایید Docker استفاده کنید تا از امنیت و کیفیت آنها مطمئن شوید. همچنین ترجیحاً از ایمیجهای پایه کوچک مانند alpine
استفاده کنید تا حجم نهایی ایمیج کاهش یابد. استفاده از ایمیجهای پایه سبک در Dockerfile به معنای انتخاب سیستمعاملها و توزیعهایی است که اندازه کوچکتری دارند و بهینهتر هستند. این کار میتواند مزایای زیادی به همراه داشته باشد. برای مثال، میتوان از توزیع لینوکس Alpine به عنوان ایمیج پایه استفاده کرد. Alpine یک توزیع بسیار کوچک و سبک لینوکس است که برای ساختن ایمیجهای Docker بسیار مناسب است.
اغلب ایمیجهای رسمی یک نسخه کوچک شده از ایمیج اصلی را در داکرهاب خود قرار میدهند. وجود پسوند Alpine در کنار نام ایمیجها بیانگر کوچک و سبک بودن آنهاست.
بهرهگیری از کش Docker
دستورات را طوری سازماندهی کنید که لایههای کوچکتر و کمتری ایجاد شوند. دستورات پر تغییر (مانند نصب پکیجها) را در لایههای انتهایی قرار دهید تا از کش لایههای قبلی بهره ببرید. بهرهگیری از کش Docker میتواند به طور چشمگیری زمان ساخت ایمیجهای Docker را کاهش دهد. داکر از کش برای ذخیرهسازی لایههایی که در مراحل قبلی ساخته شدهاند استفاده میکند، به طوری که در صورت عدم تغییر محتوای آنها، نیاز به ساخت مجدد آنها نیست. برای بهینهسازی در بهرهگیری از کش، میتوان دستورات را به گونهای ترتیب داد که بخشهای ثابتتر در ابتدا قرار گیرند.
حفظ امنیت
اطلاعات حساس مانند کلیدهای API و رمز عبورها را در Dockerfile ذخیره نکنید. همواره از آخرین نسخههای بستهها و نرمافزارها استفاده کنید و آنها را بهروز نگه دارید. همچنین بهتر است از اجرای کانتینرها با کاربر root خودداری کنید و یک کاربر Non-Root را برای اینکار مشخص کنید.
کاهش حجم ایمیجها
پس از نصب بستهها، فایلهای موقت و کش را حذف کنید تا حجم نهایی ایمیج کاهش یابد. از تکنیکهای ساخت چند مرحلهای (multi-stage builds) استفاده کنید تا فقط فایلهای ضروری در ایمیج نهایی قرار گیرند. Multi-stage Builds در داکر به یک تکنیک ساخت ایمیج اشاره دارد که در آن از چندین مرحله (استیج) برای ساخت یک ایمیج نهایی استفاده میشود. این تکنیک به شما اجازه میدهد تا فایلهای موقت و ابزارهای غیرضروری که فقط در مراحل ساخت و توسعه نیاز است، را در مرحله نهایی (مرحله اجرا) حذف کرده و حجم ایمیج خود را کاهش دهید.
بهینهسازی دستورات
برای اینکه بهصورت عملی نکات گفته شده را درک کنیم در ادامه بهصورت جداگانه چندین تکنیک برای بهینهتر شدن Dockerfile را به همراه نمونه کدهای آن بررسی میکنیم:
کاهش تعداد لایهها:
در Docker، هر دستور RUN
, COPY
, ADD
, FROM
یک لایه جدید ایجاد میکند. ترکیب چندین دستور RUN
در یک دستور واحد میتواند به کاهش تعداد لایهها کمک کند. به عنوان مثال:
RUN apt-get update && apt-get install -y \
package1 \
package2 \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
استفاده از کش موثر:
دستورات COPY
و ADD
را به گونهای ترتیب دهید که فایلهایی که کمتر تغییر میکنند زودتر کپی شوند تا Docker بتواند از کش استفاده کند. به عنوان مثال، فایلهای وابستگیها مانند requirements.txt
را قبل از سایر فایلها کپی کنید.
COPY requirements.txt /app/
RUN pip install --no-cache-dir -r /app/requirements.txt
COPY . /app/
پاکسازی پس از نصب:
بستههای موقت و فایلهای نصبی را پس از نصب پاک کنید تا حجم نهایی ایمیج کاهش یابد. به عنوان مثال:
RUN apt-get update && apt-get install -y package \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
استفاده از ساخت چند مرحلهای یا (Multi-stage builds):
برای کاهش اندازه نهایی ایمیج، از ساخت چند مرحلهای استفاده کنید تا فقط فایلهای نهایی و مورد نیازِ ایمیج نهایی اضافه شوند.
FROM golang:1.16 AS builder
WORKDIR /src
COPY . .
RUN go build -o myapp
FROM alpine:latest
COPY --from=builder /src/myapp /app/
CMD ["/app/myapp"]
استفاده Build Arguments:
استفاده از ARG
برای تعریف Build Arguments میتواند به تنظیم بهتر و انعطافپذیری بیشتر کمک کند.
ARG BASE_IMAGE=python:3.9-slim
FROM ${BASE_IMAGE}
در پایان
با رعایت بهترین شیوههای نگارش Dockerfile میتوانید ایمیجهای کارآمد، بهینه و امنتری ایجاد کنید. در این مطلب بهصورت خلاصه در ارتباط با بهترین تکنیکها برای نوشتن Dockerfileهای بهتر صحبت کردیم. اما مطمئنا روشهای بیشتری برای این موضوع وجود دارد که باید با جستجو و کسب تجربه آنها را پیدا کنید.