۱۴۰۵ مهر ۶, دوشنبه
دلار آمریکا۲۴۳٬۴۱۵▲ ۳٫۵۸٪یورو۲۷۷٬۰۰۰▲ ۳٫۳۹٪درهم امارات۶۶٬۳۰۵▲ ۳٫۶۱٪سکه امامی۲۴۴٬۵۰۵٬۰۰۰▲ ۱٫۶۶٪طلای ۱۸ عیار (گرم)۲۴٬۳۱۹٬۰۰۰▲ ۱٫۷۵٪انس طلا۴٬۱۵۰ $▼ ۳٫۲۶٪تتر۲۴۴٬۴۵۶▲ ۳٫۲۴٪بیت‌کوین۸۲٬۶۵۳ $▼ ۲٫۴۱٪اتریوم۲٬۶۴۰ $▼ ۱٫۸۹٪سولانا۱۱۷٫۸۴ $▼ ۴٫۳۴٪اپل۳۴۱٫۰۷ $▲ ۱٫۵۳٪انویدیا۲۲۵٫۰۷ $▲ ۰٫۲۲٪مایکروسافت۵۱۶٫۱۷ $▲ ۳٫۶۶٪آلفابت (گوگل)۳۴۳٫۹۲ $▲ ۰٫۴۶٪تسلا۳۷۲٫۱۱ $▼ ۱٫۵۴٪شاخص نزدک۲۷٬۰۶۹ $▲ ۰٫۴۸٪
نرخ ارز

آموزش Docker برای توسعه‌دهندگان با مثال عملی از صفر

آموزش Docker از صفر برای برنامه‌نویسان: مفاهیم ایمیج و کانتینر، نوشتن Dockerfile چندمرحله‌ای، Docker Compose با PostgreSQL و بهترین روش‌ها با مثال Node.js.

دواپس و کلاد۵ دقیقه مطالعه
آموزش Docker برای توسعه‌دهندگان با مثال عملی از صفر
فهرست مطالب
  1. مفاهیم پایه: ایمیج، کانتینر و رجیستری
  2. نصب Docker و اولین کانتینر
  3. دستورهای ضروری Docker
  4. آموزش Docker با یک پروژه واقعی Node.js
  5. Docker Compose: چند سرویس با یک دستور
  6. Docker در حین توسعه: تغییر کد بدون build دوباره
  7. عیب‌یابی رایج
  8. بهترین روش‌ها برای ایمیج‌های حرفه‌ای
  9. قدم بعدی

آموزش Docker برای هر توسعه‌دهنده‌ای که با بک‌اند، دواپس یا حتی فرانت‌اند سروکار دارد، دیگر یک مهارت اختیاری نیست. Docker به شما اجازه می‌دهد اپلیکیشن را همراه همه وابستگی‌هایش در یک بسته استاندارد به نام کانتینر (Container) قرار دهید؛ بسته‌ای که روی لپ‌تاپ شما، سرور تست و محیط عملیاتی دقیقاً یکسان اجرا می‌شود.

در این آموزش یک API ساده Node.js را قدم‌به‌قدم کانتینری می‌کنیم و بعد با Docker Compose یک پایگاه داده PostgreSQL را هم کنارش بالا می‌آوریم.

مفاهیم پایه: ایمیج، کانتینر و رجیستری

پیش از نوشتن اولین خط، سه مفهوم را روشن کنیم:

  • ایمیج (Image): قالبی فقط‌خواندنی شامل سیستم‌فایل، کد و تنظیمات اجرا. ایمیج‌ها از لایه‌ها (Layers) ساخته می‌شوند و هر دستور در Dockerfile یک لایه می‌سازد.
  • کانتینر (Container): نمونه در حال اجرای یک ایمیج. می‌توانید از یک ایمیج چندین کانتینر مستقل بسازید.
  • رجیستری (Registry): مخزنی برای نگهداری و اشتراک ایمیج‌ها؛ مثل Docker Hub یا GitHub Container Registry.

اگر ایمیج را به «کلاس» تشبیه کنیم، کانتینر «شیء» ساخته‌شده از آن است.

نصب Docker و اولین کانتینر

در ویندوز و مک، Docker Desktop را از سایت رسمی Docker نصب کنید. در لینوکس، Docker Engine را طبق مستندات رسمی توزیع خود نصب کنید. سپس نصب را آزمایش کنید:

docker --version
docker run hello-world

دستور docker run اگر ایمیج را محلی نداشته باشد، آن را از رجیستری دانلود و سپس اجرا می‌کند. یک مثال جذاب‌تر، اجرای وب‌سرور Nginx است:

docker run -d -p 8080:80 --name web nginx:alpine

حالا http://localhost:8080 را باز کنید. فلگ -d کانتینر را در پس‌زمینه اجرا می‌کند و -p 8080:80 پورت ۸۰۸۰ سیستم شما را به پورت ۸۰ کانتینر وصل می‌کند.

دستورهای ضروری Docker

دستور کاربرد
docker ps فهرست کانتینرهای در حال اجرا (-a برای همه)
docker logs -f web دنبال کردن لاگ‌های کانتینر
docker exec -it web sh باز کردن شل داخل کانتینر
docker stop web متوقف کردن کانتینر
docker rm web حذف کانتینر
docker image ls فهرست ایمیج‌های محلی
docker system prune پاک‌سازی منابع بلااستفاده

آموزش Docker با یک پروژه واقعی Node.js

یک پوشه به نام devna-api بسازید و فایل package.json را در آن قرار دهید:

{
  "name": "devna-api",
  "version": "1.0.0",
  "main": "server.js",
  "scripts": {
    "start": "node server.js"
  },
  "dependencies": {
    "express": "^5.1.0"
  }
}

سپس فایل server.js:

const express = require("express");

const app = express();
const port = process.env.PORT || 3000;

app.get("/", (req, res) => {
  res.json({ message: "Hello from inside a container!" });
});

app.get("/health", (req, res) => {
  res.send("ok");
});

app.listen(port, () => {
  console.log(`API listening on port ${port}`);
});

با npm install وابستگی‌ها را نصب کنید تا فایل package-lock.json ساخته شود.

نوشتن Dockerfile چندمرحله‌ای

Dockerfile دستورالعمل ساخت ایمیج است. ما از ساخت چندمرحله‌ای (Multi-stage Build) استفاده می‌کنیم: یک مرحله وابستگی‌ها را نصب می‌کند و مرحله نهایی فقط چیزهای لازم برای اجرا را برمی‌دارد.

# syntax=docker/dockerfile:1

# Stage 1: install production dependencies
FROM node:24-alpine AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --omit=dev

# Stage 2: minimal runtime image
FROM node:24-alpine
WORKDIR /app
ENV NODE_ENV=production
COPY --from=deps /app/node_modules ./node_modules
COPY . .
USER node
EXPOSE 3000
CMD ["node", "server.js"]

چند نکته مهم در این فایل:

  • ترتیب دستورها مهم است. اول فقط package.json و فایل قفل را کپی می‌کنیم. تا وقتی وابستگی‌ها عوض نشده‌اند، Docker لایه npm ci را از کش برمی‌دارد و ساخت‌های بعدی چند برابر سریع‌تر می‌شوند.
  • npm ci دقیقاً نسخه‌های فایل قفل را نصب می‌کند و برای ساخت‌های تکرارپذیر مناسب‌تر از npm install است.
  • USER node کانتینر را با کاربر غیر root اجرا می‌کند؛ یک اصل امنیتی ساده اما حیاتی.
  • شکل آرایه‌ای CMD باعث می‌شود Node.js سیگنال‌های توقف را مستقیم دریافت کند.

فایل .dockerignore را فراموش نکنید

بدون این فایل، node_modules محلی و حتی فایل‌های رمز شما وارد ایمیج می‌شوند:

node_modules
npm-debug.log
.git
.env
Dockerfile
compose.yaml

ساخت و اجرای ایمیج

docker build -t devna-api:1.0 .
docker run -d -p 3000:3000 --name api devna-api:1.0
curl http://localhost:3000

اگر پاسخ JSON را دیدید، اولین اپلیکیشن کانتینری شما آماده است. با docker image ls هم می‌توانید حجم ایمیج نهایی را ببینید.

Docker Compose: چند سرویس با یک دستور

اپلیکیشن‌های واقعی تنها نیستند؛ پایگاه داده، کش و صف پیام هم دارند. Docker Compose اجازه می‌دهد همه سرویس‌ها را در یک فایل YAML تعریف کنید. فایل compose.yaml را در ریشه پروژه بسازید:

services:
  api:
    build: .
    ports:
      - "3000:3000"
    environment:
      DATABASE_URL: postgres://devna:secret@db:5432/devna
    depends_on:
      db:
        condition: service_healthy

  db:
    image: postgres:17-alpine
    environment:
      POSTGRES_USER: devna
      POSTGRES_PASSWORD: secret
      POSTGRES_DB: devna
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U devna -d devna"]
      interval: 5s
      timeout: 3s
      retries: 5

volumes:
  pgdata:

در این فایل چند مفهوم کلیدی هست:

  • شبکه داخلی: سرویس‌ها با نامشان همدیگر را پیدا می‌کنند؛ برای همین در آدرس پایگاه داده به‌جای localhost نوشته‌ایم db.
  • Volume: داده‌های PostgreSQL در Volume نام‌دار pgdata ذخیره می‌شوند و با حذف کانتینر از بین نمی‌روند.
  • healthcheck و depends_on: سرویس api فقط وقتی بالا می‌آید که پایگاه داده واقعاً آماده پذیرش اتصال باشد.

حالا کل مجموعه را اجرا کنید:

docker compose up -d --build
docker compose ps
docker compose logs -f api
docker compose down        # stop and remove containers (volume is kept)

رمز عبور را در پروژه واقعی مستقیم در فایل Compose ننویسید. از فایل .env که در .gitignore قرار دارد یا از سازوکار secrets استفاده کنید.

Docker در حین توسعه: تغییر کد بدون build دوباره

ساختن دوباره ایمیج بعد از هر تغییر کوچک در کد خسته‌کننده است. در محیط توسعه می‌توانید پوشه پروژه را با Bind Mount مستقیم به کانتینر وصل کنید تا تغییرات فوراً دیده شوند. Node.js از نسخه 18.11 به بعد فلگ --watch دارد که با تغییر فایل‌ها، برنامه را دوباره اجرا می‌کند:

docker run --rm -it -p 3000:3000   -v "$(pwd)":/app -v /app/node_modules   -w /app node:24-alpine   sh -c "npm install && node --watch server.js"

Volume دوم (/app/node_modules) باعث می‌شود پوشه node_modules داخل کانتینر با نسخه سیستم شما بازنویسی نشود؛ نکته‌ای مهم وقتی سیستم‌عامل شما با لینوکس کانتینر فرق دارد. Docker Compose هم برای همین کار قابلیت develop.watch دارد که همگام‌سازی فایل‌ها را خودکار انجام می‌دهد.

عیب‌یابی رایج

  • خطای «port is already allocated»: پورت روی سیستم شما اشغال است. پورت دیگری مثل -p 3001:3000 انتخاب کنید یا برنامه‌ای را که پورت را گرفته متوقف کنید.
  • کانتینر بلافاصله متوقف می‌شود: با docker logs <name> خروجی آخرین اجرا را ببینید. کانتینر تا وقتی زنده است که فرایند اصلی‌اش در حال اجرا باشد.
  • اپلیکیشن داخل کانتینر به localhost پایگاه داده وصل نمی‌شود: داخل کانتینر، localhost یعنی خود کانتینر. از نام سرویس در Compose استفاده کنید.
  • پر شدن دیسک: ایمیج‌ها و لایه‌های قدیمی جمع می‌شوند. با docker system df مصرف را ببینید و با docker system prune پاک‌سازی کنید.

بهترین روش‌ها برای ایمیج‌های حرفه‌ای

  • ایمیج پایه کوچک و مشخص انتخاب کنید. برچسب latest را در محیط عملیاتی به کار نبرید؛ نسخه را صریح بنویسید، مثل node:24-alpine.
  • در هر کانتینر فقط یک دغدغه. اپلیکیشن و پایگاه داده را در یک کانتینر نگذارید.
  • ایمیج را اسکن کنید. ابزارهایی مثل Docker Scout یا Trivy آسیب‌پذیری‌های شناخته‌شده را در لایه‌ها پیدا می‌کنند.
  • HEALTHCHECK و لاگ روی stdout. Docker و ابزارهای اورکستراسیون از همین‌ها برای پایش سلامت استفاده می‌کنند.
  • رازها را در ایمیج نگذارید. هر چیزی که در یک لایه کپی شود، حتی اگر بعداً پاک شود، در تاریخچه ایمیج باقی می‌ماند.

قدم بعدی

حالا ایمیجی دارید که هر جا اجرا شود، یکسان رفتار می‌کند. قدم بعدی خودکار کردن ساخت و انتشار آن است؛ در آموزش CI/CD با GitHub Actions همین ایمیج را در هر push به‌طور خودکار می‌سازیم و در رجیستری منتشر می‌کنیم. وقتی تعداد سرویس‌ها و سرورها زیاد شد، نوبت Kubernetes است؛ تازه‌های Kubernetes 1.37 را هم از دست ندهید.

پرسش‌های پرتکرار

فرق Docker با ماشین مجازی چیست؟

ماشین مجازی یک سیستم‌عامل کامل با هسته جداگانه اجرا می‌کند، اما کانتینرها هسته سیستم میزبان را به اشتراک می‌گذارند و فقط برنامه و وابستگی‌هایش را ایزوله می‌کنند. به همین دلیل کانتینرها سبک‌تر هستند و در چند ثانیه بالا می‌آیند.

آیا Docker روی ویندوز هم کار می‌کند؟

بله. با نصب Docker Desktop که روی ویندوز از WSL 2 استفاده می‌کند، می‌توانید کانتینرهای لینوکسی را اجرا کنید. در مک هم Docker Desktop یا ابزارهای جایگزین در دسترس‌اند.

داده‌های پایگاه داده بعد از حذف کانتینر از بین می‌رود؟

اگر از Volume استفاده نکنید، بله. داده‌های پایدار را همیشه در Volume نگه دارید تا با حذف یا بازسازی کانتینر از بین نروند.

بعد از یادگیری Docker چه چیزی یاد بگیرم؟

قدم منطقی بعدی، خودکارسازی ساخت ایمیج با CI/CD و سپس آشنایی با ابزارهای اورکستراسیون مثل Kubernetes است.

منابع

  1. Docker Docs – Get started
  2. Docker Docs – Multi-stage builds
  3. Docker Docs – Docker Compose

خطایی در این مطلب دیدید؟ به تحریریه گزارش دهید؛ اصلاحیه‌ها طبق اصول تحریریه ثبت می‌شوند.

مطالب مرتبط