ویرگول
ورودثبت نام
شراره شادالو
شراره شادالو
شراره شادالو
شراره شادالو
خواندن ۴ دقیقه·۷ روز پیش

Frontend Deployment, Revisited (Part 1 — Build & Docker)

در فرانت‌اند، تمرکز اغلب روی کد، کامپوننت و فریم‌ورک‌ هاست، اما مسیر واقعی پروژه از نوشتن کد تا اجرا شدن در دست کاربر بسیار گسترده‌تره. این مجموعه نشون می‌ده که تصمیم‌های فنی در مراحل build، deploy، runtime و observability چطور روی پرفورمنس، پایداری و هزینه‌ی نگه‌داری سیستم اثر می‌گذارن. هدف این چهار بخش، دادن دیدی end-to-end است؛ در قسمت اول، تمرکز روی Build و Docker است و یاد می‌گیریم چگونه مرحله‌ی build را قابل پیش‌بینی و قابل اعتماد کنیم تا پایه‌ی معماری پروژه محکم شود.

(Part 1 — Build & Docker)

سال‌هاست که در فرانت‌اند درباره‌ی Performance، معماری و تجربه‌ی کاربری صحبت می‌کنیم،اما یک لایه‌ی مهم معمولاً دست‌کم گرفته می‌شه: فرآیندی که کد را به خروجی قابل اجرا تبدیل می‌کند.

فرانت‌اند، برخلاف تصور رایج، صرفاً مجموعه‌ای از کامپوننت‌ها یا stateها نیست.سیستمیه که در نهایت باید build بشه، منتقل بشه و اجرا بشه.

نقطه‌ی شروع تمام این زنجیره، build هستش ...


در این مرحله، دپندنسی ها ریزالو می‌شن، کد به خروجی قابل اجرا تبدیل می‌شه و بسیاری از تصمیم‌های معماری عملاً غیرقابل بازگشت می‌شن.

بعد از این مرحله:

  • فریم‌ورک صرفاً یک جزئیات پیاده‌سازیه

  • رفتار runtime تابع خروجی تولیدشده است

  • و هر تفاوتی در build، می‌تونه به تفاوت در رفتار نهایی منجر بشه

به همین دلیل، build یک مرحله‌ی اجرایی ساده نیست؛بلکه بخشی از معماری سیستم محسوب می‌شه.

یکی از مسائل نادیده گرفته‌شده در پروژه‌ها این است که build اغلب وابسته به محیط یا همون environment افراد مختلف است؛ نسخه‌ی Node، سیستم‌عامل یا حتی تنظیمات جزئی package manager می‌تواند باعث شود یک سورس‌کد واحد در شرایط مختلف خروجی‌های متفاوت تولید کند. در چنین شرایطی، صحبت از ثبات، قابلیت پیش‌بینی یا حتی دیباگ مؤثر عملاً بی‌معنا می‌شود.

Docker دقیقاً چه مسئله‌ای رو حل می‌کنه؟

جواب : کنترل فرآیند و تکرارپذیری

Docker در فرانت‌اند وظیفه‌اش کنترل محیط build است.با Docker می‌تونیم:

  • محیط build را ثابت کنیم

  • نسخه‌ی runtime و وابستگی‌ها را تثبیت کنیم

  • خروجی قابل اعتماد تولید کنیم، مستقل از ماشین یا شخص اجراکننده

یک مثال واقعی:
پروژه React شما روی سیستم local بدون مشکل build می‌شه، اما در CI یا staging شکست می‌خوره. مشکل معمولاً تفاوت محیط‌هاست. Docker این تفاوت را حذف می‌کنه و خروجی یکسان تولید می‌کنه.

فرض کنید پروژه‌ای با React داریم. ساختار پروژه:

my-app/ ├─ src/ │ ├─ App.jsx │ └─ components/ ├─ package.json └─ Dockerfile

یک Dockerfile ساده‌ی multi-stage می‌تونه این شکلی باشه:

# Stage 1: Build FROM node:20 AS build WORKDIR /app COPY package.json package-lock.json ./ RUN npm ci COPY . . RUN npm run build # Stage 2: Serve FROM nginx:alpine COPY --from=build /app/build /usr/share/nginx/html EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]

توضیح کوتاه:

  • مرحله اول، کد ما رو build می‌کنه و تمام وابستگی‌ها رو resolve می‌کنه

  • مرحله دوم، خروجی static build شده رو داخل Nginx می‌ذاره تا سرو بشه

این یعنی تمام تصمیم‌های Build ما، از محیط گرفته تا وابستگی‌ها(دپندنسی ها)، قابل پیش‌بینی و یکسان باقی می‌مونن.

فلوچارت فرآیند Build با Docker

[Developer Code] │ ▼ [Install Dependencies] ---> (Resolve Node Modules) │ ▼ [Run Build Scripts] ---> (Transpile + Bundle + Minify) │ ▼ [Output: Static Files] │ ▼ [Docker Multi-Stage] │ ▼ [Stage 1: Build Environment] --> Compile Code │ ▼ [Stage 2: Runtime Environment] --> Serve Static Files │ ▼ [CI/CD / Staging / Production] --> Consistent & Reliable Output

این فلوچارت نشان می‌ده که فرانت‌اند از اولین خط کد تا خروجی پایدار روی سرور، نیازمند مرحله‌ی build کنترل‌شده است و Docker وظیفه‌ی تضمین این ثبات را داره.

Multi-Stage Build در فرانت‌اند

وقتی پروژه‌ی فرانت‌اند شما رشد می‌کنه، حجم وابستگی‌ها و فایل‌ها به سرعت زیاد می‌شه.اگر همه‌ی ابزارها، کامپایلرها و وابستگی‌های توسعه در خروجی نهایی باقی بمونن،

نتیجه: bundle بزرگ، اجرای سنگین و حتی ریسک‌های امنیتی بوجود میان.

اینجاست که Multi-Stage Build وارد بازی می‌شه.ایده‌ی ساده اما قدرتمندش اینه:

مراحل مختلف build را در کانتینرهای جداگانه اجرا کنیم و فقط خروجی مورد نیاز برای اجرا (runtime) را به مرحله‌ی نهایی منتقل کنیم.

به عبارت دیگر:

  • Stage 1: محیط کامل توسعه و build

  • Stage 2: محیط سبک runtime که فقط فایل‌های نهایی رو سرو می‌کنه

این کار باعث می‌شه خروجی کوچک، سبک، و قابل اعتماد باشه، و وابستگی‌های غیرضروری وارد سرور یا CDN نشه.

فرض کنید پروژه React داریم. Dockerfile معمولاً این شکلیه:

# Stage 1: Build FROM node:20 AS build WORKDIR /app COPY package.json package-lock.json ./ RUN npm ci COPY . . RUN npm run build # Stage 2: Serve FROM nginx:alpine COPY --from=build /app/build /usr/share/nginx/html EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]

توضیح مراحل:

  1. Stage 1 — Build

    • محیط Node کامل است

    • تمام وابستگی‌ها نصب می‌شوند

    • کد transpile و bundle می‌شود

    • خروجی نهایی در /app/build قرار می‌گیرد

  2. Stage 2 — Serve

    • محیط سبک Nginx

    • فقط فایل‌های static (HTML, JS, CSS) از Stage 1 منتقل می‌شوند

    • اپلیکیشن قابل اجرا روی سرور می‌شود

مزیت: فقط چیزی که نیاز داریم به محیط runtime می‌آید و حجم کانتینر کاهش می‌یابد

فلوچارت Multi-Stage Build

[Developer Code + Dependencies] │ ▼ [Stage 1: Build Container] - Install dependencies - Compile code - Bundle & minify │ ▼ [Build Output /app/build] │ ▼ [Stage 2: Runtime Container] - Lightweight environment (Nginx) - Copy only build output │ ▼ [Serve Static Files] --> Consistent & Optimized Delivery

چرا این مهمه؟

  • حجم کانتینر runtime کاهش پیدا می‌کنه

  • امنیت بالاتر، چون ابزارهای build وارد سرور نمی‌شن

  • قابلیت پیش‌بینی و تکرارپذیری افزایش پیدا می‌کنه

  • آماده‌سازی برای CI/CD ساده‌تر می‌شه

جمع‌بندی

  • Build تنها مرحله‌ی آماده‌سازی خروجی نیست؛ ستون معماری فرانت‌اند است

  • Docker ابزاری برای تثبیت تصمیم‌های فنی و قابل اعتماد کردن خروجی است

  • Multi-stage build نشان‌دهنده‌ی تفکر معماری و تفکیک مسئولیت‌هاست

در Part 2، سراغ deploy و CDN می‌ریم و یاد می‌گیریم چگونه خروجی build شده را بهینه، سریع و مطمئن در دسترس کاربران قرار دهیم.

دوست داشتین تو لینکدین با من در ارتباط باشین :)

ci cd
۱
۰
شراره شادالو
شراره شادالو
شاید از این پست‌ها خوشتان بیاید