Docker چیست؟ آموزش کامل Docker و Docker Compose با پروژه عملی هوش مصنوعی
در این آموزش Docker را از صفر یاد میگیرید و با مفاهیم Image، Container، Volume، Network و Docker Compose آشنا میشوید. در پایان یک API هوش مصنوعی واقعی با FastAPI، درواره و Redis را داخل کانتینر اجرا میکنیم.
اگر برنامه روی کامپیوتر شما اجرا میشود اما روی سیستم همکار، سرور آزمایشی یا محیط Production کار نمیکند، معمولاً تفاوت سیستمعامل، نسخه زبان برنامهنویسی، کتابخانهها یا تنظیمات محیطی عامل مشکل است.
Docker برای حل همین مسئله ساخته شده است. با Docker میتوانید برنامه و وابستگیهای آن را داخل یک محیط استاندارد به نام Container قرار دهید و همان محیط را روی لپتاپ، سرور، سرویس CI/CD یا زیرساخت ابری اجرا کنید.
در این آموزش، Docker و Docker Compose را از صفر یاد میگیریم و در پایان یک پروژه کاربردی میسازیم: API هوش مصنوعی با Python و FastAPI که به API درواره متصل است و برای کاهش درخواستهای تکراری از Redis Cache استفاده میکند.
Docker چیست؟
Docker پلتفرمی برای ساخت، توزیع و اجرای برنامهها داخل Container است. Container محیطی جداشده و قابلانتقال برای اجرای یک برنامه محسوب میشود و کد، Runtime، کتابخانهها و تنظیمات موردنیاز آن را در کنار هم قرار میدهد.
بر اساس معرفی رسمی Docker، Docker به توسعهدهنده اجازه میدهد برنامه را از زیرساخت جدا کند و همان برنامه را با روش استانداردی در محیطهای مختلف اجرا کند.
به زبان ساده، Docker کمک میکند این جمله کمتر شنیده شود:
روی سیستم من که درست کار میکرد!
وقتی برنامه داخل Docker Image ساخته شود، تمام اعضای تیم میتوانند همان Image را اجرا کنند و محیطی نزدیک به یکدیگر داشته باشند.
Container چیست؟
Container یک فرایند جداشده است که برنامه را همراه با وابستگیهای موردنیاز اجرا میکند.
یک Container معمولاً شامل این موارد است:
- کد برنامه
- نسخه مشخصی از زبان برنامهنویسی
- کتابخانههای موردنیاز
- ابزارهای سیستمی ضروری
- تنظیمات اجرای برنامه
- فرمان شروع برنامه
Container معمولاً از Kernel سیستمعامل میزبان استفاده میکند و به همین دلیل نسبت به ماشین مجازی سبکتر است. بااینحال، Container و ماشین مجازی دقیقاً یک کاربرد ندارند و انتخاب میان آنها به معماری و سطح جداسازی موردنیاز بستگی دارد.
تفاوت Container و ماشین مجازی
ماشین مجازی یا Virtual Machine معمولاً یک سیستمعامل کامل را همراه با Kernel خودش اجرا میکند. Container بیشتر اجزای سطح سیستمعامل میزبان را به اشتراک میگذارد و محیط اجرای برنامه را جدا میکند.
ویژگیهای معمول Container:
- شروع سریعتر
- مصرف کمتر منابع
- اندازه کوچکتر
- ساخت و حذف آسان
- مناسب برای توسعه، تست و استقرار مداوم
- قابلانتقال میان محیطهای سازگار
ویژگیهای معمول ماشین مجازی:
- سیستمعامل مستقل
- جداسازی عمیقتر
- مصرف منابع بیشتر
- زمان راهاندازی طولانیتر
- مناسب برای اجرای سیستمعامل یا بار کاری کاملاً مستقل
Docker همیشه جایگزین ماشین مجازی نیست؛ اما برای بستهبندی و اجرای بسیاری از برنامههای وب، APIها، Workerها و سرویسهای هوش مصنوعی انتخاب مناسبی است.
مفاهیم اصلی Docker
برای یادگیری Docker باید چند اصطلاح کلیدی را بشناسید.
Docker Image چیست؟
Docker Image یک بسته فقطخواندنی و نسخهبندیشده است که دستورالعملها و فایلهای لازم برای اجرای برنامه را در خود دارد.
Image را میتوان مانند یک قالب آماده در نظر گرفت. برای مثال، Image پروژه ما شامل Python، وابستگیهای FastAPI و کد برنامه خواهد بود.
Docker Container چیست؟
Container نمونه در حال اجرای یک Image است.
از یک Image میتوانید چند Container مستقل ایجاد کنید:
Docker Image
├── Container 1
├── Container 2
└── Container 3
اگر Image را مانند کلاس در برنامهنویسی در نظر بگیریم، Container تقریباً شبیه نمونهای ساختهشده از آن کلاس است.
Dockerfile چیست؟
Dockerfile یک فایل متنی شامل دستورالعملهای ساخت Image است.
در Dockerfile مشخص میکنیم:
- Image پایه چه باشد
- مسیر کاری کجا باشد
- چه فایلهایی کپی شوند
- چه وابستگیهایی نصب شوند
- برنامه با چه دستوری اجرا شود
Docker Registry چیست؟
Registry محلی برای ذخیره و توزیع Docker Imageها است. Docker Hub یکی از Registryهای شناختهشده محسوب میشود، اما سازمانها میتوانند Registry خصوصی نیز داشته باشند.
Docker Volume چیست؟
Volume فضای ذخیرهسازی پایدار برای Container است. اطلاعات ذخیرهشده در لایه داخلی Container ممکن است با حذف Container از بین بروند؛ اما دادههای Volume مستقل از چرخه عمر Container باقی میمانند.
طبق مستندات رسمی Docker Volume، Volumeها توسط Docker مدیریت میشوند و برای نگهداری دادهای که باید بعد از حذف Container نیز باقی بماند مناسباند.
Docker Network چیست؟
Network امکان ارتباط کنترلشده Containerها با یکدیگر را فراهم میکند. در Docker Compose، سرویسهای یک پروژه بهصورت پیشفرض میتوانند از طریق نام سرویس یکدیگر را پیدا کنند.
برای مثال، برنامه ما بهجای آدرس IP متغیر Redis از نام سرویس redis استفاده میکند:
redis://redis:6379/0
طبق مستندات Network در Docker Compose، Compose بهصورت پیشفرض یک Network برای برنامه ایجاد میکند و سرویسها در آن با نام سرویس قابل شناسایی هستند.
تفاوت Image و Container
این تفاوت را میتوان با یک مثال ساده توضیح داد:
- Image مانند فایل نصب یا قالب آماده برنامه است.
- Container نسخه در حال اجرای آن Image است.
- Dockerfile دستور ساخت Image را تعریف میکند.
- Registry محل نگهداری Image است.
دستور زیر یک Image میسازد:
docker build -t darvareh-ai-api:1.0 .
دستور زیر از آن Image یک Container اجرا میکند:
docker run --name darvareh-api darvareh-ai-api:1.0
Docker Compose چیست؟
Docker Compose ابزاری برای تعریف و اجرای برنامههای چندکانتینری است. تنظیمات سرویسها، Networkها، Volumeها و متغیرهای موردنیاز در یک فایل YAML نوشته میشوند.
در نسخههای جدید Docker، فرمان Compose معمولاً به این شکل اجرا میشود:
docker compose
فرم قدیمیتر آن چنین بود:
docker-compose
در این مقاله از ساختار جدید docker compose استفاده میکنیم.
بر اساس مستندات رسمی Docker Compose، Compose میتواند تمام اجزای یک برنامه چندکانتینری را در یک فایل تعریف و با یک فرمان اجرا کند.
برای نمونه، پروژه ما دو سرویس خواهد داشت:
- سرویس
appبرای اجرای FastAPI - سرویس
redisبرای Cache پاسخها
سپس هر دو سرویس با این فرمان اجرا میشوند:
docker compose up
تفاوت Dockerfile و compose.yaml
Dockerfile توضیح میدهد Image برنامه چگونه ساخته شود.
فایل compose.yaml توضیح میدهد Containerهای برنامه چگونه اجرا و به یکدیگر متصل شوند.
Dockerfile معمولاً شامل این موارد است:
- Image پایه
- نصب وابستگیها
- کپی کد
- تنظیم کاربر
- فرمان اجرای برنامه
فایل Compose معمولاً شامل این موارد است:
- فهرست سرویسها
- Image یا مسیر Build
- Portها
- Volumeها
- Networkها
- متغیرهای محیطی
- وابستگی سرویسها
- Healthcheck
- سیاست Restart
نصب Docker
نصب در ویندوز
در ویندوز معمولاً Docker Desktop نصب میشود. نسخه مناسب سیستم خود را از صفحه رسمی دریافت Docker انتخاب کنید.
Docker Desktop در بسیاری از سیستمهای ویندوز از WSL 2 استفاده میکند. پس از نصب ممکن است لازم باشد سیستم را Restart کنید.
نصب در macOS
برای macOS نیز Docker Desktop در نسخههای متناسب با پردازندههای Apple Silicon و Intel ارائه میشود. هنگام دانلود، معماری درست دستگاه خود را انتخاب کنید.
نصب در Linux
در توزیعهای Linux میتوانید Docker Engine و افزونه Docker Compose را مطابق راهنمای رسمی همان توزیع نصب کنید. بهدلیل تغییر نسخهها و مخازن، بهتر است فرمان نصب را مستقیماً از مستندات رسمی Docker دریافت کنید.
بررسی نصب
بعد از نصب، ترمینال را باز کنید:
docker --version
نسخه Compose را بررسی کنید:
docker compose version
سپس یک Container آزمایشی اجرا کنید:
docker run --rm hello-world
اگر پیام آزمایشی Docker نمایش داده شود، نصب و ارتباط Docker CLI با Docker Engine درست است.
معماری پروژه عملی
در این پروژه یک API با FastAPI میسازیم که پیام کاربر را دریافت میکند و به یکی از مدلهای هوش مصنوعی درواره میفرستد.
Redis نیز پاسخ درخواستهای تکراری را برای مدت محدودی نگه میدارد تا:
- تعداد درخواستهای تکراری کاهش یابد
- زمان پاسخ برای درخواست مشابه کمتر شود
- هزینه فراخوانی غیرضروری مدل کاهش پیدا کند
معماری پروژه:
Client
│
▼
FastAPI Container
│
├── بررسی Redis Cache
│
├── پاسخ موجود ← Redis Container
│
└── پاسخ موجود نیست
│
▼
Darvareh AI API
ساختار فایلها:
darvareh-docker-ai-api/
├── app.py
├── requirements.txt
├── Dockerfile
├── compose.yaml
├── .dockerignore
├── .gitignore
├── .env.example
└── README.md
مرحله اول: ساخت پوشه پروژه
mkdir darvareh-docker-ai-api
cd darvareh-docker-ai-api
مرحله دوم: دریافت کلید API درواره
برای اجرای پروژه به دو مقدار نیاز دارید:
- کلید API درواره
- شناسه مدل موردنظر
در درواره ثبتنام و کلید API ایجاد کنید. سپس شناسه مدل مناسب پروژه را از صفحه مدلهای درواره بردارید.
در این آموزش از مقادیر نمونه زیر استفاده میکنیم:
DARVAREH_API_KEY=YOUR_DARVAREH_API_KEY
DARVAREH_MODEL_ID=YOUR_MODEL_ID
مرحله سوم: ساخت requirements.txt
فایل requirements.txt را ایجاد کنید:
fastapi>=0.115,<1.0
uvicorn[standard]>=0.34,<1.0
openai>=1.68,<3.0
redis>=5.2,<7.0
python-dotenv>=1.1,<2.0
pydantic>=2.10,<3.0
محدودکردن بازه نسخه وابستگیها احتمال نصب نسخهای با تغییرات ناسازگار را کاهش میدهد. برای محیط Production بهتر است پس از تست، نسخههای دقیق را در فایل Lock یا requirements نهایی ثبت کنید.
مرحله چهارم: ساخت فایل متغیرهای محیطی
فایل .env.example:
DARVAREH_API_KEY=YOUR_DARVAREH_API_KEY
DARVAREH_MODEL_ID=YOUR_MODEL_ID
CACHE_TTL_SECONDS=600
حالا یک نسخه با نام .env بسازید.
در Linux و macOS:
cp .env.example .env
در PowerShell:
Copy-Item .env.example .env
مقادیر واقعی را فقط در فایل .env قرار دهید.
متغیر REDIS_URL را داخل فایل .env قرار نمیدهیم؛ زیرا آن را در فایل Compose با نام سرویس داخلی Redis تنظیم خواهیم کرد.
مرحله پنجم: ساخت برنامه FastAPI
فایل app.py:
import hashlib
import json
import os
from contextlib import asynccontextmanager
import redis
from fastapi import FastAPI, HTTPException, Request
from openai import OpenAI
from pydantic import BaseModel, Field
class ChatRequest(BaseModel):
message: str = Field(min_length=1, max_length=8000)
temperature: float = Field(default=0.3, ge=0, le=2)
max_tokens: int = Field(default=800, ge=1, le=2000)
def create_cache_key(
model: str,
message: str,
temperature: float,
max_tokens: int,
) -> str:
payload = {
"model": model,
"message": message.strip(),
"temperature": temperature,
"max_tokens": max_tokens,
}
serialized = json.dumps(
payload,
sort_keys=True,
ensure_ascii=False,
)
digest = hashlib.sha256(serialized.encode("utf-8")).hexdigest()
return f"chat:{digest}"
@asynccontextmanager
async def lifespan(app: FastAPI):
api_key = os.getenv("DARVAREH_API_KEY")
model_id = os.getenv("DARVAREH_MODEL_ID")
redis_url = os.getenv("REDIS_URL", "redis://redis:6379/0")
cache_ttl = int(os.getenv("CACHE_TTL_SECONDS", "600"))
if not api_key:
raise RuntimeError("DARVAREH_API_KEY is not configured")
if not model_id:
raise RuntimeError("DARVAREH_MODEL_ID is not configured")
app.state.model_id = model_id
app.state.cache_ttl = cache_ttl
app.state.ai_client = OpenAI(
api_key=api_key,
base_url="https://api.darvareh.ir/v1",
)
app.state.redis_client = redis.Redis.from_url(
redis_url,
decode_responses=True,
socket_connect_timeout=2,
socket_timeout=2,
)
yield
app.state.ai_client.close()
app.state.redis_client.close()
app = FastAPI(
title="Darvareh Docker AI API",
version="1.0.0",
lifespan=lifespan,
)
@app.get("/health")
def health(request: Request):
redis_status = "unavailable"
try:
if request.app.state.redis_client.ping():
redis_status = "ok"
except redis.RedisError:
pass
return {
"status": "ok",
"redis": redis_status,
}
@app.post("/chat")
def chat(payload: ChatRequest, request: Request):
model_id = request.app.state.model_id
cache_key = create_cache_key(
model=model_id,
message=payload.message,
temperature=payload.temperature,
max_tokens=payload.max_tokens,
)
try:
cached_answer = request.app.state.redis_client.get(cache_key)
except redis.RedisError:
cached_answer = None
if cached_answer:
return {
"answer": cached_answer,
"model": model_id,
"cached": True,
}
try:
response = request.app.state.ai_client.chat.completions.create(
model=model_id,
messages=[
{
"role": "system",
"content": (
"You are a helpful Persian assistant. "
"Answer clearly and accurately in Persian."
),
},
{
"role": "user",
"content": payload.message,
},
],
temperature=payload.temperature,
max_tokens=payload.max_tokens,
)
answer = response.choices[0].message.content
if not answer:
raise HTTPException(
status_code=502,
detail="مدل پاسخ قابل استفادهای برنگرداند.",
)
try:
request.app.state.redis_client.setex(
cache_key,
request.app.state.cache_ttl,
answer,
)
except redis.RedisError:
pass
return {
"answer": answer,
"model": model_id,
"cached": False,
}
except HTTPException:
raise
except Exception:
raise HTTPException(
status_code=502,
detail="دریافت پاسخ از سرویس هوش مصنوعی با مشکل مواجه شد.",
)
آدرس پایه API درواره:
https://api.darvareh.ir/v1
Endpoint نهایی Chat Completions:
https://api.darvareh.ir/v1/chat/completions
کتابخانه مورد استفاده مسیر chat/completions را به Base URL اضافه میکند.
در این نمونه، از Cache بهصورت Best Effort استفاده شده است؛ یعنی اگر Redis موقتاً در دسترس نباشد، درخواست همچنان میتواند مستقیماً به مدل ارسال شود.
مرحله ششم: ساخت Dockerfile
فایل Dockerfile را بدون پسوند ایجاد کنید:
FROM python:3.12-slim AS builder
ENV PIP_DISABLE_PIP_VERSION_CHECK=1 \
PIP_NO_CACHE_DIR=1
WORKDIR /build
COPY requirements.txt .
RUN pip wheel \
--no-cache-dir \
--wheel-dir /wheels \
-r requirements.txt
FROM python:3.12-slim
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
PIP_DISABLE_PIP_VERSION_CHECK=1
WORKDIR /app
RUN groupadd --system appgroup \
&& useradd \
--system \
--gid appgroup \
--create-home \
appuser
COPY --from=builder /wheels /wheels
COPY requirements.txt .
RUN pip install \
--no-cache-dir \
--no-index \
--find-links=/wheels \
-r requirements.txt \
&& rm -rf /wheels
COPY --chown=appuser:appgroup app.py .
USER appuser
EXPOSE 8000
HEALTHCHECK \
--interval=30s \
--timeout=3s \
--start-period=10s \
--retries=3 \
CMD python -c \
"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=2)"
CMD [
"uvicorn",
"app:app",
"--host",
"0.0.0.0",
"--port",
"8000",
"--workers",
"1"
]
توضیح دستورهای Dockerfile
FROM مشخص میکند Image بر اساس چه Image دیگری ساخته شود:
FROM python:3.12-slim
WORKDIR پوشه کاری داخل Image را تعیین میکند:
WORKDIR /app
COPY فایل را از Build Context به Image منتقل میکند:
COPY app.py .
RUN دستوری را هنگام ساخت Image اجرا میکند:
RUN pip install -r requirements.txt
USER کاربر اجرای برنامه را تعیین میکند:
USER appuser
اجرای برنامه با کاربر غیر Root برای بسیاری از برنامهها انتخاب مناسبتری است.
EXPOSE پورت مورد انتظار برنامه را مستند میکند:
EXPOSE 8000
HEALTHCHECK وضعیت سلامت Container را بررسی میکند.
CMD فرمان پیشفرض اجرای Container را مشخص میکند.
چرا Dockerfile چندمرحلهای ساختهایم؟
Dockerfile پروژه دارای دو Stage است:
builderبرای ساخت Wheel وابستگیها- Stage نهایی برای اجرای برنامه
این روش باعث میشود ابزارها و فایلهای موقتی مرحله Build وارد Image نهایی نشوند. نتیجه معمولاً Image مرتبتر و قابلکنترلتری است.
استفاده از Multi-stage Build برای همه پروژهها الزامی نیست، اما در پروژههای واقعی میتواند ساختار Build و Runtime را بهتر از یکدیگر جدا کند.
مرحله هفتم: ساخت .dockerignore
فایل .dockerignore تعیین میکند چه فایلهایی وارد Build Context نشوند:
.git
.gitignore
.env
.env.*
!.env.example
.venv
venv
__pycache__
*.pyc
*.pyo
.pytest_cache
.mypy_cache
.ruff_cache
.idea
.vscode
dist
build
*.log
README.md
compose.yaml
فایل .dockerignore با .gitignore تفاوت دارد:
.gitignoreاز ثبت فایل در Git جلوگیری میکند..dockerignoreاز ارسال فایل به Docker Build Context جلوگیری میکند.
وجود .env در هر دو فایل مهم است؛ زیرا نباید کلید API وارد Git یا Docker Image شود.
مرحله هشتم: ساخت .gitignore
فایل .gitignore:
.env
.env.*
!.env.example
.venv/
venv/
__pycache__/
*.py[cod]
.pytest_cache/
.mypy_cache/
.ruff_cache/
.idea/
.vscode/
dist/
build/
*.log
مرحله نهم: ساخت compose.yaml
فایل compose.yaml:
name: darvareh-docker-ai-api
services:
app:
build:
context: .
dockerfile: Dockerfile
env_file:
- .env
environment:
REDIS_URL: redis://redis:6379/0
ports:
- "127.0.0.1:8000:8000"
depends_on:
redis:
condition: service_healthy
restart: unless-stopped
init: true
read_only: true
tmpfs:
- /tmp
redis:
image: redis:7-alpine
command:
- redis-server
- --appendonly
- "yes"
volumes:
- redis_data:/data
healthcheck:
test:
- CMD
- redis-cli
- ping
interval: 10s
timeout: 3s
retries: 5
start_period: 5s
restart: unless-stopped
volumes:
redis_data:
بررسی تنظیمات Compose
قبل از اجرا، ساختار فایل را اعتبارسنجی کنید:
docker compose config
این دستور فایل Compose را پردازش میکند و خطاهای ساختاری یا متغیرهای ناقص را نشان میدهد.
توجه کنید که خروجی docker compose config ممکن است مقادیر متغیرهای پردازششده را نمایش دهد. خروجی آن را بدون بررسی در Issue، Log عمومی یا پیام گروهی منتشر نکنید.
ساخت و اجرای پروژه
ساخت Image و اجرای سرویسها:
docker compose up --build
برای اجرا در پسزمینه:
docker compose up --build -d
طبق راهنمای دستور docker compose up، این فرمان سرویسهای تعریفشده را میسازد، ایجاد میکند و اجرا میکند.
مشاهده وضعیت:
docker compose ps
نمونه سرویسهای مورد انتظار:
app
redis
آزمایش API
بررسی Health Endpoint:
curl http://127.0.0.1:8000/health
پاسخ مورد انتظار:
{
"status": "ok",
"redis": "ok"
}
ارسال درخواست به مدل:
curl -X POST http://127.0.0.1:8000/chat \
-H "Content-Type: application/json" \
-d '{
"message": "Docker را برای یک برنامهنویس مبتدی توضیح بده",
"temperature": 0.3,
"max_tokens": 600
}'
پاسخ اول احتمالاً شامل این مقدار است:
{
"answer": "پاسخ مدل...",
"model": "YOUR_MODEL_ID",
"cached": false
}
اگر همان درخواست را دوباره و پیش از پایان TTL ارسال کنید:
{
"answer": "پاسخ مدل...",
"model": "YOUR_MODEL_ID",
"cached": true
}
مقدار cached: true نشان میدهد پاسخ از Redis خوانده شده است.
در محیط واقعی، استفاده از Cache باید متناسب با نوع اطلاعات انجام شود. برای پرسشهایی که پاسخ آنها باید کاملاً تازه باشد یا حاوی داده شخصیاند، سیاست Cache باید جداگانه طراحی شود.
اجرای درخواست در PowerShell
$body = @{
message = "Docker Compose چیست؟"
temperature = 0.3
max_tokens = 600
} | ConvertTo-Json
Invoke-RestMethod `
-Method Post `
-Uri "http://127.0.0.1:8000/chat" `
-ContentType "application/json" `
-Body $body
مشاهده Logهای Container
نمایش Log تمام سرویسها:
docker compose logs
دنبالکردن زنده Logها:
docker compose logs -f
مشاهده Log فقط برای برنامه:
docker compose logs -f app
نمایش ۱۰۰ خط آخر:
docker compose logs --tail=100 app
برای خروج از حالت دنبالکردن Log میتوانید Ctrl+C را بزنید. اگر سرویسها با -d اجرا شده باشند، این کار Containerها را متوقف نمیکند.
ورود به Container
اجرای Shell داخل Container برنامه:
docker compose exec app sh
مشاهده فایلهای داخل آن:
ls -la
خروج:
exit
اجرای یک فرمان بدون ورود تعاملی:
docker compose exec app python --version
اجرای Redis CLI:
docker compose exec redis redis-cli
داخل Redis CLI:
PING
DBSIZE
SCAN 0
خروج:
QUIT
در محیط Production از فرمانهای سنگین مانند دریافت یکجای تمام کلیدهای Redis روی پایگاه داده بزرگ استفاده نکنید.
توقف و حذف Containerها
توقف سرویسها:
docker compose stop
اجرای دوباره:
docker compose start
توقف و حذف Containerها و Network پروژه:
docker compose down
Volume با دستور بالا حذف نمیشود؛ بنابراین Cache یا داده پایدار Redis باقی میماند.
حذف Containerها به همراه Volumeها:
docker compose down -v
گزینه -v دادههای Volume پروژه را حذف میکند. پیش از اجرای آن مطمئن شوید اطلاعات موردنیاز یا داده Production داخل Volume وجود ندارد.
مدیریت Docker Image
مشاهده Imageها:
docker image ls
ساخت Image با Tag مشخص:
docker build -t darvareh-ai-api:1.0.0 .
اجرای مستقیم بدون Compose:
docker run \
--rm \
--name darvareh-ai-api \
--env-file .env \
-e REDIS_URL="" \
-p 127.0.0.1:8000:8000 \
darvareh-ai-api:1.0.0
پروژه فعلی برای Cache به Redis طراحی شده است، اما در نبود Redis نیز میتواند درخواست مدل را پردازش کند. در استفاده مستقیم، خطای اتصال Redis توسط برنامه نادیده گرفته میشود.
مشاهده تاریخچه لایههای Image:
docker history darvareh-ai-api:1.0.0
مشاهده اطلاعات کامل Image:
docker image inspect darvareh-ai-api:1.0.0
Docker Volume در پروژه چگونه کار میکند؟
در فایل Compose این بخش را داریم:
volumes:
- redis_data:/data
و در انتهای فایل:
volumes:
redis_data:
Docker یک Named Volume میسازد و آن را به مسیر /data در Container مربوط به Redis متصل میکند.
مشاهده Volumeها:
docker volume ls
بررسی Volume پروژه:
docker volume inspect darvareh-docker-ai-api_redis_data
نام دقیق Volume ممکن است بر اساس نام پروژه Compose متفاوت باشد.
حتی اگر Container مربوط به Redis حذف و دوباره ساخته شود، Volume میتواند باقی بماند. به همین دلیل نباید پاککردن Container را معادل پاککردن داده بدانید.
Docker Network در پروژه چگونه کار میکند؟
Compose برای سرویسهای پروژه یک Network پیشفرض میسازد.
سرویس app میتواند با این آدرس به Redis دسترسی داشته باشد:
redis://redis:6379/0
در این آدرس:
- بخش اول
redisنام پروتکل است. - بخش دوم
redisنام سرویس در Compose است. - عدد
6379پورت داخلی Redis است. - عدد
0شماره Database منطقی Redis است.
نباید از localhost برای اتصال app به Redis استفاده کنیم؛ زیرا داخل Container برنامه، localhost به همان Container برنامه اشاره میکند، نه Container Redis.
تفاوت پورت داخلی و پورت میزبان
در Compose نوشتهایم:
ports:
- "127.0.0.1:8000:8000"
ساختار کلی:
HOST_IP:HOST_PORT:CONTAINER_PORT
پورت داخلی FastAPI برابر 8000 است و همان پورت روی آدرس محلی سیستم میزبان در دسترس قرار میگیرد.
اتصال به 127.0.0.1 باعث میشود سرویس در این نمونه فقط از همان سیستم میزبان قابل دسترسی باشد. اگر قصد انتشار عمومی دارید، معمولاً یک Reverse Proxy مانند Nginx در مقابل برنامه قرار میگیرد و TLS، Domain و محدودیتهای دسترسی در آن تنظیم میشوند.
Redis را در بخش ports منتشر نکردهایم، زیرا فقط برنامه داخل Network پروژه باید به آن دسترسی داشته باشد.
Healthcheck چیست؟
Healthcheck بررسی میکند که یک Container فقط در حال اجراست یا واقعاً توان پاسخگویی دارد.
ممکن است Process اجرا شده باشد اما برنامه هنوز آماده پذیرش درخواست نباشد. Healthcheck این تفاوت را مشخص میکند.
در Compose برای Redis نوشتهایم:
healthcheck:
test:
- CMD
- redis-cli
- ping
برنامه نیز با این شرط منتظر سالمشدن Redis میماند:
depends_on:
redis:
condition: service_healthy
وجود depends_on بدون Healthcheck لزوماً به معنای آمادهبودن کامل سرویس وابسته نیست. راهنمای رسمی ترتیب شروع Compose نیز برای مدیریت آمادگی سرویسها از Healthcheck و شرط service_healthy استفاده میکند.
بهروزرسانی کد و بازسازی Container
اگر app.py را تغییر دادید:
docker compose up -d --build app
مشاهده Log:
docker compose logs -f app
اگر فقط فایل Compose تغییر کرده باشد:
docker compose up -d
Compose وضعیت جدید را با سرویسهای موجود تطبیق میدهد و بخشهای لازم را دوباره ایجاد میکند.
استفاده از Bind Mount در محیط توسعه
برای اینکه تغییر فایل app.py بدون Build مجدد داخل Container دیده شود، میتوانید یک فایل توسعه مانند compose.dev.yaml بسازید:
services:
app:
volumes:
- ./app.py:/app/app.py:ro
command:
- uvicorn
- app:app
- --host
- 0.0.0.0
- --port
- "8000"
- --reload
اجرا:
docker compose \
-f compose.yaml \
-f compose.dev.yaml \
up --build
این تنظیم برای توسعه محلی مناسب است. در Production بهتر است کد داخل Image قرار بگیرد و از --reload استفاده نشود.
متغیر محیطی و Secret چه تفاوتی دارند؟
در پروژه نمونه، متغیرها از فایل .env خوانده میشوند. این روش برای توسعه محلی ساده است، اما فایل .env نباید وارد Git یا Image شود.
برای بررسی:
git check-ignore -v .env
بررسی فایلهای Build Context:
docker build --no-cache -t darvareh-ai-api:test .
کلید API را با ARG یا ENV ثابت داخل Dockerfile قرار ندهید:
ENV DARVAREH_API_KEY=کلید-واقعی
این کار مناسب نیست، زیرا مقدار ممکن است وارد لایهها، تاریخچه Build یا Image شود.
برای محیط Production از قابلیت مدیریت Secret پلتفرم استقرار یا Secret Manager استفاده کنید. همچنین دسترسی هر Secret را فقط به سرویس نیازمند آن محدود کنید.
اگر کلید API افشا شد، حذف آن از فایل بهتنهایی کافی نیست؛ کلید را در پنل مربوط باطل یا تعویض کنید.
کاهش اندازه Docker Image
برای کوچکتر و سریعترشدن Image:
- از Base Image متناسب با نیاز استفاده کنید.
- فایل
.dockerignoreکامل داشته باشید. - Cache، فایلهای Git و محیط مجازی را کپی نکنید.
- وابستگیهای غیرضروری نصب نکنید.
- Build و Runtime را در Stageهای جدا انجام دهید.
- فایلهای موقتی Package Manager را نگه ندارید.
- ترتیب دستورها را برای استفاده بهتر از Build Cache تنظیم کنید.
برای مثال، ابتدا requirements.txt را کپی کردهایم و بعد از نصب وابستگیها، app.py را کپی میکنیم. در نتیجه تغییر کد الزاماً Cache لایه نصب وابستگیها را بیاعتبار نمیکند.
ترتیب لایهها و Build Cache
هر دستور مهم در Dockerfile یک لایه ایجاد میکند. اگر یک لایه تغییر کند، لایههای بعد از آن معمولاً دوباره ساخته میشوند.
ترتیب مناسب:
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY app.py .
ترتیب کمتر بهینه:
COPY . .
RUN pip install -r requirements.txt
در حالت دوم، تغییر کوچک در فایل کد میتواند باعث اجرای دوباره نصب تمام وابستگیها شود.
برای ساخت بدون Cache:
docker compose build --no-cache
این فرمان برای عیبیابی مفید است، اما در استفاده روزمره زمان Build را افزایش میدهد.
Tag مناسب برای Image
از Tagهایی مانند latest بهتنهایی برای انتشارهای مهم استفاده نکنید. Tag نسخهدار امکان ردیابی و Rollback دقیقتر را فراهم میکند:
docker build -t darvareh-ai-api:1.0.0 .
docker build -t darvareh-ai-api:1.0.1 .
میتوانید شناسه Commit را نیز وارد Tag کنید:
docker build -t darvareh-ai-api:a1b2c3d .
در CI/CD مقدار شناسه معمولاً بهصورت خودکار از Git دریافت میشود.
اجرای چند نمونه از سرویس
برای آزمایش محلی میتوانید تعداد Containerهای سرویس app را افزایش دهید:
docker compose up -d --scale app=3
اما اگر در Compose برای سرویس app یک پورت ثابت میزبان تعریف شده باشد، چند Container نمیتوانند همزمان همان پورت را تصاحب کنند. برای Scale واقعی باید Load Balancer یا Reverse Proxy در مقابل نمونهها قرار گیرد و تنظیم Port نیز متناسب با آن طراحی شود.
Docker Compose برای محیط تکسرور و توسعه بسیار کاربردی است، اما Scale چندسروری معمولاً به ابزارها و معماری دیگری نیاز دارد.
تفاوت restart و rebuild
Restart فقط Container موجود را دوباره اجرا میکند:
docker compose restart app
اگر کد داخل Image تغییر کرده باشد، Restart تغییر جدید را وارد Image نمیکند. ابتدا باید Image بازسازی شود:
docker compose up -d --build app
قاعده ساده:
- تغییر تنظیم Runtime: ممکن است Recreate کافی باشد.
- تغییر کد کپیشده داخل Image: نیازمند Build جدید است.
- توقف موقت Process: Restart کافی است.
دستورات کاربردی Docker
مشاهده Containerهای در حال اجرا
docker ps
مشاهده تمام Containerها
docker ps -a
مشاهده Imageها
docker image ls
مشاهده Volumeها
docker volume ls
مشاهده Networkها
docker network ls
مشاهده مصرف منابع
docker stats
بررسی جزئیات Container
docker inspect CONTAINER_NAME
مشاهده Processهای Container
docker top CONTAINER_NAME
توقف Container
docker stop CONTAINER_NAME
شروع دوباره Container
docker start CONTAINER_NAME
حذف Container متوقفشده
docker rm CONTAINER_NAME
حذف Image مشخص
docker image rm IMAGE_NAME:TAG
پیش از حذف Image یا Volume مطمئن شوید به آن نیاز ندارید و Container وابستهای وجود ندارد.
خطاهای رایج Docker و راهحل آنها
خطای Cannot connect to the Docker daemon
نمونه:
Cannot connect to the Docker daemon
موارد زیر را بررسی کنید:
- Docker Desktop یا Docker Engine اجرا شده باشد.
- کاربر اجازه ارتباط با Docker daemon داشته باشد.
- Docker Context درست انتخاب شده باشد.
- سرویس Docker متوقف نشده باشد.
بررسی Context:
docker context ls
بررسی اطلاعات Engine:
docker info
خطای Port is already allocated
نمونه:
port is already allocated
یک برنامه دیگر از پورت 8000 استفاده میکند.
در Compose پورت میزبان را تغییر دهید:
ports:
- "127.0.0.1:8080:8000"
حالا API از این آدرس در دسترس است:
http://127.0.0.1:8080
خطای ModuleNotFoundError
احتمالاً کتابخانه موردنیاز داخل requirements.txt نیست یا Image بعد از تغییر وابستگیها بازسازی نشده است.
docker compose build --no-cache app
docker compose up -d app
برنامه نمیتواند به Redis متصل شود
داخل Container برنامه از localhost استفاده نکنید. آدرس صحیح بر اساس نام سرویس است:
redis://redis:6379/0
وضعیت Redis:
docker compose ps redis
Log آن:
docker compose logs redis
آزمایش:
docker compose exec redis redis-cli ping
پاسخ مورد انتظار:
PONG
متغیر محیطی در دسترس نیست
بررسی کنید فایل .env در مسیر اجرای Compose وجود داشته باشد:
ls -la
در PowerShell:
Get-ChildItem -Force
مشاهده نام متغیرهای داخل Container بدون نمایش مقدارهای محرمانه:
docker compose exec app python -c \
"import os; print('DARVAREH_API_KEY' in os.environ)"
پاسخ باید True باشد.
تغییر کد اعمال نمیشود
اگر کد داخل Image کپی شده باشد، آن را بازسازی کنید:
docker compose up -d --build app
سپس Container و Image استفادهشده را بررسی کنید:
docker compose ps
docker compose images
Container بلافاصله متوقف میشود
Log را مشاهده کنید:
docker compose logs --tail=100 app
پیکربندی نهایی Compose را بررسی کنید:
docker compose config
فرمان اجرای Image را نیز ببینید:
docker image inspect darvareh-docker-ai-api-app
اشتباهات رایج هنگام استفاده از Docker
قرار دادن کلید API داخل Dockerfile
Secret را داخل Dockerfile یا Image ذخیره نکنید. آن را هنگام اجرای Container از محیط امن دریافت کنید.
استفاده دائمی از latest
Tag نسخهدار امکان تشخیص Image و بازگشت به نسخه قبلی را آسانتر میکند.
انتشار مستقیم Port پایگاه داده
اگر فقط سرویس داخلی به Redis یا Database نیاز دارد، Port آن را روی اینترنت یا حتی میزبان منتشر نکنید.
اجرای غیرضروری برنامه با Root
در صورت امکان، داخل Image کاربر محدود بسازید و برنامه را با آن اجرا کنید.
نگهداری داده داخل لایه Container
برای داده پایدار از Volume یا سرویس ذخیرهسازی مناسب استفاده کنید.
ساخت Image بسیار بزرگ
فایلهای غیرضروری، Cacheها، Datasetها، وزن مدلها و محیط مجازی محلی را داخل Image کپی نکنید.
استفاده از Docker بهجای Backup
Volume باعث پایداری داده میشود، اما جایگزین Backup نیست. برای اطلاعات مهم باید برنامه پشتیبانگیری و بازیابی جداگانه داشته باشید.
استفاده از docker compose down -v بدون بررسی
گزینه -v میتواند Volumeهای پروژه و داده داخل آنها را حذف کند. قبل از اجرا نام و اهمیت Volumeها را بررسی کنید.
چکلیست آمادهسازی پروژه Docker
پیش از Build:
- Dockerfile وجود دارد.
.dockerignoreساخته شده است..envوارد Build Context نمیشود.- وابستگیها مشخصاند.
- کلید API داخل کد نیست.
- Health Endpoint وجود دارد.
پس از Build:
- Image بدون خطا ساخته میشود.
- Container با کاربر غیر Root اجرا میشود.
- Healthcheck موفق است.
- Logها اطلاعات محرمانه ندارند.
- Portهای غیرضروری منتشر نشدهاند.
- API به مدل درواره متصل میشود.
- Redis از داخل برنامه قابل دسترسی است.
- Cache در صورت قطع Redis مانع عملکرد اصلی نمیشود.
پیش از انتشار:
- نسخه Image مشخص است.
- تنظیمات Production جدا شدهاند.
- Secretها از سامانه امن دریافت میشوند.
- TLS و Reverse Proxy تنظیم شدهاند.
- احراز هویت API فعال است.
- محدودیت نرخ درخواست در نظر گرفته شده است.
- Timeout و Retry کنترلشدهاند.
- Backup دادههای پایدار تعریف شده است.
- مانیتورینگ و مشاهده Logها برقرار است.
- مسیر Rollback آزمایش شده است.
پرسشهای متداول
Docker برای چه کسانی مناسب است؟
Docker برای توسعهدهندگان Backend، Frontend، DevOps، Data Engineering، Machine Learning و تیمهایی مناسب است که میخواهند برنامه را در محیطهای مختلف بهصورت سازگار اجرا کنند.
آیا برای یادگیری Docker باید Linux بلد باشیم؟
دانستن مفاهیم پایه Linux و خط فرمان مفید است، اما برای شروع الزامی نیست. بسیاری از دستورات Docker در ویندوز، macOS و Linux یکساناند.
آیا Docker رایگان است؟
Docker Engine متنباز است، اما بعضی محصولات و قابلیتهای تجاری Docker ممکن است شرایط یا پلنهای متفاوتی داشته باشند. برای تصمیم سازمانی، شرایط جاری محصول موردنظر را در وبسایت رسمی بررسی کنید.
آیا Docker برای اجرای مدل هوش مصنوعی مناسب است؟
بله، بهخصوص برای بستهبندی API مدل، Worker، پردازش داده و سرویسهای جانبی. بااینحال، اجرای مدلهای بزرگ محلی ممکن است به GPU، Driver، Runtime سازگار و فضای ذخیرهسازی زیادی نیاز داشته باشد.
در پروژه این مقاله، مدل روی سرور محلی اجرا نمیشود؛ برنامه از طریق API درواره به مدل انتخابی متصل میشود. بنابراین Image برنامه کوچکتر است و به وزن مدل محلی نیاز ندارد.
آیا باید وزن مدلها را داخل Docker Image قرار دهیم؟
معمولاً وزن مدلهای بزرگ را مستقیم داخل Image عمومی قرار نمیدهند. این فایلها میتوانند بسیار حجیم باشند و فرایند Build، Push و Pull را کند کنند. روش مناسب به زیرساخت، مجوز مدل و شیوه استقرار بستگی دارد.
تفاوت docker run و docker compose up چیست؟
docker run معمولاً برای اجرای مستقیم یک Container استفاده میشود. docker compose up مجموعهای از سرویسها، Networkها و Volumeهای تعریفشده در فایل Compose را مدیریت میکند.
آیا حذف Container باعث حذف Image میشود؟
خیر. Container و Image منابع جداگانهاند. حذف Container معمولاً Image آن را حذف نمیکند.
آیا حذف Container باعث حذف Volume میشود؟
معمولاً Named Volume با حذف Container باقی میماند، مگر آنکه صریحاً دستور حذف Volume اجرا شود.
چرا داخل Container نمیتوانم با localhost به Redis وصل شوم؟
زیرا localhost داخل Container برنامه به خود همان Container اشاره میکند. برای اتصال به سرویس دیگر در Compose باید از نام سرویس، مانند redis، استفاده کنید.
آیا Docker Compose برای Production مناسب است؟
Docker Compose برای توسعه، آزمایش و استقرارهای تکسرور بسیار کاربردی است. برای Production باید محدودیتها، Availability، Backup، مدیریت Secret، بهروزرسانی، Rollback و Scale پروژه را جداگانه بررسی کنید.
آیا Docker امنیت برنامه را تضمین میکند؟
خیر. Docker ابزار جداسازی و بستهبندی برنامه است، اما امنیت نهایی به Imageها، تنظیمات دسترسی، Secretها، Network، سیستم میزبان، بهروزرسانیها و معماری برنامه نیز وابسته است.
آیا میتوان API درواره را از داخل Docker فراخوانی کرد؟
بله. کافی است Container به اینترنت دسترسی داشته باشد و Base URL و کلید API بهدرستی تنظیم شده باشند:
client = OpenAI(
api_key=os.getenv("DARVAREH_API_KEY"),
base_url="https://api.darvareh.ir/v1",
)
جمعبندی
Docker روشی استاندارد برای بستهبندی و اجرای برنامهها فراهم میکند. با Docker میتوانید کد، Runtime و وابستگیهای برنامه را داخل Image قرار دهید و از روی آن Containerهای قابلتکرار بسازید.
در این آموزش با مفاهیم زیر آشنا شدیم:
- Docker Image
- Docker Container
- Dockerfile
- Docker Compose
- Volume
- Network
- Port Mapping
- Healthcheck
- Build Cache
- Environment Variable
- اجرای چند سرویس کنار یکدیگر
همچنین یک پروژه عملی ساختیم که شامل FastAPI، Redis و اتصال به API هوش مصنوعی درواره بود. این پروژه میتواند پایهای برای ساخت دستیار سازمانی، تولیدکننده محتوا، ابزار خلاصهسازی، سرویس پردازش متن یا قابلیت هوش مصنوعی داخل نرمافزار شما باشد.
برای شروع، در درواره ثبتنام کنید، کلید API بگیرید و شناسه مدل مناسب پروژه را از صفحه مدلهای درواره انتخاب کنید.
منابع تکمیلی
- Docker چیست؟ مستندات رسمی Docker
- راهنمای دریافت و نصب Docker
- مستندات Docker Compose
- راهنمای سریع Docker Compose
- مرجع Compose Specification
- راهنمای Docker Volume
- مدیریت Network در Docker Compose
- مدیریت ترتیب شروع سرویسها
مقالات مرتبط
- ساخت Dockerfile و Docker Compose با هوش مصنوعی
- آموزش Microservices با Python، FastAPI و Docker
- آموزش Redis برای Cache و Queue در FastAPI
- آموزش Nginx، Reverse Proxy و Load Balancing برای FastAPI
- ساخت API هوش مصنوعی آماده Production
- اتصال API هوش مصنوعی به اپلیکیشن
- آموزش هوش مصنوعی با Python و API
- آموزش CI/CD و GitHub Actions با هوش مصنوعی
- آموزش Cloudflare Workers و ساخت API هوش مصنوعی Serverless
برای مطالعه شرایط استفاده و محدودیتهای مسئولیت، صفحه «سلب مسئولیت» را مشاهده کنید.