چرا Docker برای دیپلوی؟
بدون کانتینر، دیپلوی یعنی نصب دستی نسخهی درست زبان، کتابخانهها و تنظیمات روی هر سرور؛ و اختلاف کوچک میان سرورها باگهایی میسازد که پیدا کردنشان روزها طول میکشد. در Docker همان تصویری که در تست اجرا شد، بدون تغییر به تولید میرود.
- قابلتکرار: محیط لپتاپ، استیجینگ و تولید یکسان است.
- برگشت سریع: اگر نسخهی جدید مشکل داشت، برچسب تصویر قبلی را دوباره اجرا میکنید.
- جداسازی: چند برنامه با نسخههای متفاوت روی یک سرور بدون تداخل اجرا میشوند.
نوشتن Dockerfile چندمرحلهای
در روش چندمرحلهای، مرحلهی اول ابزارهای ساخت (کامپایلر، devDependencies) را دارد و مرحلهی آخر فقط خروجی نهایی را. نمونه برای یک برنامهی Node.js:
# ---- build stage ----
FROM node:20-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# ---- runtime stage ----
FROM node:20-alpine
WORKDIR /app
ENV NODE_ENV=production
COPY package*.json ./
RUN npm ci --omit=dev && npm cache clean --force
COPY --from=build /app/dist ./dist
USER node
EXPOSE 3000
CMD ["node", "dist/server.js"]- ابتدا فقط
package*.jsonرا کپی و نصب میکنیم تا لایهی وابستگیها تا وقتی آن فایلها عوض نشدهاند از کش استفاده شود و ساخت سریع بماند. USER nodeبرنامه را با کاربر غیر root اجرا میکند؛ اگر نفوذی رخ دهد، خسارت محدودتر است.- فایل
.dockerignoreبسازید وnode_modules،.gitو فایلهای.envرا در آن بنویسید تا وارد تصویر نشوند.
جزئیات همهی دستورها در مرجع Dockerfile آمده است.
تعریف کل پشته با docker compose
برنامهی واقعی معمولاً چند بخش دارد: وباپ، پایگاهداده و کش. فایل compose همه را در یک مکان و با یک دستور بالا میآورد:
services:
app:
image: registry.example.com/shop-api:1.4.2
restart: unless-stopped
env_file: .env
ports: ["127.0.0.1:3000:3000"]
depends_on:
db: { condition: service_healthy }
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost:3000/health"]
interval: 15s
retries: 3
db:
image: postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_PASSWORD_FILE: /run/secrets/db_pass
volumes: [dbdata:/var/lib/postgresql/data]
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
cache:
image: redis:7-alpine
restart: unless-stopped
volumes:
dbdata:چند نکته: پورت برنامه فقط روی 127.0.0.1 منتشر شده تا از بیرون مستقیم در دسترس نباشد و ترافیک از Nginx عبور کند؛ پایگاهداده و Redis اصلاً پورت منتشر نمیکنند و فقط در شبکهی داخلی compose دیده میشوند. برچسب نسخهی مشخص (1.4.2) بهجای latest برگشت را قابلپیشبینی میکند. الگوهای استفاده از Redis را در کش کردن هوشمند با Redis ببینید.

تنظیمات، رمزها و دادهی ماندگار
اصل دوازدهعاملی: تنظیمات از محیط میآید، نه از کد. فایل .env را فقط روی سرور نگه دارید و هیچوقت در Git یا تصویر قرار ندهید. برای رمزهای حساستر از Docker secrets یا مدیر رمز ابری استفاده کنید و در برنامه از مسیر فایل بخوانید.
کانتینر موقتی است؛ هر بار که دوباره ساخته شود، فایلهای داخلش از بین میرود. هر چیزی که باید بماند (فایل پایگاهداده، آپلودها) را روی volume بگذارید و از آن پشتیبان بگیرید. یک پشتیبان که هرگز بازیابیاش را آزمایش نکردهاید، پشتیبان حساب نمیشود.
# nightly logical backup of the Postgres volume's data
docker compose exec -T db pg_dump -U postgres shop | gzip > /backups/shop-$(date +%F).sql.gzدیپلوی روی سرور و بهروزرسانی
جریان ساده و قابلاعتماد برای یک سرور:
- در CI تصویر را با برچسب نسخه میسازید و به رجیستری (Docker Hub، GitHub Container Registry یا رجیستری خصوصی) میفرستید.
- روی سرور برچسب جدید را در
docker-compose.ymlمیگذارید (یا متغیر نسخه را عوض میکنید). docker compose pullسپسdocker compose up -dفقط سرویسهای تغییرکرده را دوباره میسازد.- health check را بررسی کنید و اگر خراب بود، برچسب قبلی را برگردانید و دوباره
up -dبزنید.
docker compose pull app
docker compose up -d app
docker compose ps # STATUS should say "healthy"
docker compose logs -f --tail=50 app
docker image prune -f # reclaim disk from old layersبا یک سرویس، در لحظهی جایگزینی چند ثانیه قطعی ممکن است. اگر قطعی صفر لازم دارید، دو نمونه از برنامه را پشت Nginx اجرا و یکییکی بهروزرسانی کنید، یا از یک ابزار ارکستراسیون استفاده کنید. دیپلوی خودکار با CI را هم میتوانید با تستهای اجراشده در تست خودکار در لاراول ترکیب کنید تا فقط نسخهی سالم منتشر شود.
چکلیست تولید و نظارت
- تصویر پایهی کوچک (alpine یا slim) با برچسب نسخه، نه
latest. - اجرای برنامه با کاربر غیر root و فایلسیستم فقطخواندنی در صورت امکان.
restart: unless-stoppedو health check برای هر سرویس.- محدودیت حافظه و CPU برای کانتینرها تا یک سرویس کل سرور را نبلعد.
- چرخش لاگ فعال (
max-sizeوmax-fileدر درایور json-file) تا دیسک پر نشود. - پشتیبان خودکار و تست بازیابی دورهای.
بعد از دیپلوی، بدون پایش نمیتوانید بفهمید چیزی خراب شده است؛ برای لاگ، متریک و هشدار به مانیتورینگ و لاگگیری حرفهای سر بزنید. اگر میخواهید راهاندازی زیرساخت و دیپلوی را به یک تیم باتجربه بسپارید، خدمات وب تتا تیم این کار را هم پوشش میدهد.
پرسشهای متداول
Docker و docker compose چه فرقی دارند؟
Docker یک کانتینر را میسازد و اجرا میکند. docker compose چند کانتینر مرتبط، شبکه و volumeهایشان را در یک فایل YAML تعریف میکند و با یک دستور بالا میآورد؛ برای برنامههای چندبخشی ایدهآل است.
آیا برای یک سایت کوچک هم Docker لازم است؟
الزامی نیست، ولی دیپلوی را قابلتکرار و برگشتپذیر میکند و راهاندازی مجدد روی سرور جدید را ساده میکند. برای پروژهای که قرار است رشد کند، هزینهی یادگیری آن معمولاً میارزد.
دادههای پایگاهداده در Docker از بین میروند؟
فقط اگر روی volume نباشند. با تعریف volume نامگذاریشده، داده با حذف و ساخت دوبارهی کانتینر باقی میماند. با این حال حتماً پشتیبان جدا هم بگیرید.
چطور حجم تصویر Docker را کم کنم؟
از تصویر پایهی alpine یا slim، ساخت چندمرحلهای، نصب فقط وابستگیهای تولید و فایل .dockerignore استفاده کنید. معمولاً حجم از چند صد مگابایت به چند ده مگابایت میرسد.

دیدگاهها
هنوز دیدگاهی ثبت نشده است. اولین نفر باشید.