اتصال Docker Compose به پایگاه داده‌های مختلف (MySQL, PostgreSQL, MongoDB): نمونه کد عملی

فهرست مطالب

اتصال Docker Compose به پایگاه داده‌های مختلف (MySQL, PostgreSQL, MongoDB): نمونه کد عملی

در دنیای پرشتاب توسعه نرم‌افزار مدرن، مدیریت محیط‌های توسعه محلی که شامل چندین سرویس مجزا مانند یک سرور وب، یک بک‌اند API و یک یا چند پایگاه داده هستند، می‌تواند چالش‌برانگیز باشد. اینجاست که Docker Compose به عنوان یک ابزار قدرتمند و ضروری برای توسعه‌دهندگان به میدان می‌آید. Docker Compose به شما امکان می‌دهد تا یک برنامه چندکانتینری را با استفاده از یک فایل YAML واحد تعریف و اجرا کنید. این قابلیت نه تنها پیچیدگی را کاهش می‌دهد بلکه تضمین می‌کند که محیط توسعه شما همواره با محیط تولید سازگار باشد، از بروز خطاهای “در ماشین من کار می‌کند!” جلوگیری می‌کند و فرآیند onboarding توسعه‌دهندگان جدید را تسریع می‌بخشد.

هدف اصلی این مقاله، ارائه یک راهنمای جامع و عملی برای اتصال انواع مختلف پایگاه‌های داده – از جمله رابطه‌ای مانند MySQL و PostgreSQL، و NoSQL مانند MongoDB – به برنامه‌های کاربردی شما با استفاده از Docker Compose است. ما با بررسی مفاهیم اساسی Docker Compose آغاز می‌کنیم، سپس به سراغ مثال‌های کد عملی برای هر نوع پایگاه داده می‌رویم و در نهایت، به نکات پیشرفته و بهترین روش‌ها برای بهینه‌سازی و حل مشکلات احتمالی خواهیم پرداخت.

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

درک Docker Compose برای توسعه‌دهندگان: ابزاری کلیدی برای ارکستراسیون محلی

برای توسعه‌دهندگان مدرن، Docker Compose دیگر تنها یک ابزار کمکی نیست، بلکه به ستون فقرات فرآیند توسعه تبدیل شده است. این ابزار بخشی از اکوسیستم Docker است و به طور خاص برای تعریف و اجرای برنامه‌های چندکانتینری طراحی شده است. در هسته خود، Docker Compose از یک فایل YAML با نام پیش‌فرض docker-compose.yml استفاده می‌کند تا تمام سرویس‌های مورد نیاز یک برنامه، نحوه پیکربندی آن‌ها، و چگونگی تعاملشان با یکدیگر را مشخص کند.

Docker Compose چیست و چرا از آن استفاده کنیم؟

تصور کنید یک برنامه وب دارید که از یک بک‌اند نوشته شده با Node.js، یک فرانت‌اند React، یک پایگاه داده PostgreSQL و یک کش Redis استفاده می‌کند. بدون Docker Compose، شما باید هر یک از این سرویس‌ها را به صورت جداگانه در کانتینرهای Docker اجرا کنید، پورت‌ها را مدیریت کنید، شبکه‌سازی بین آن‌ها را پیکربندی کنید و اطمینان حاصل کنید که هر سرویس با متغیرهای محیطی صحیح راه‌اندازی شود. این فرآیند می‌تواند بسیار خسته‌کننده، مستعد خطا و زمان‌بر باشد. Docker Compose این پیچیدگی را با ارائه یک راهکار Declarative برای تعریف کل پشته برنامه، از بین می‌برد.

مزایای کلیدی استفاده از Docker Compose:

  • ساده‌سازی ارکستراسیون کانتینرها: به جای اجرای دستورات طولانی docker run برای هر سرویس، تنها با یک دستور docker-compose up می‌توانید تمام سرویس‌های برنامه خود را راه‌اندازی کنید.
  • سازگاری محیطی: Docker Compose تضمین می‌کند که محیط توسعه محلی شما دقیقاً مشابه محیط‌های staging و production باشد. این امر به کاهش خطاهای ناشی از تفاوت‌های محیطی کمک می‌کند.
  • ایزوله‌سازی سرویس‌ها: هر سرویس در کانتینر ایزوله خود اجرا می‌شود، به این معنی که وابستگی‌های نرم‌افزاری یک سرویس با دیگری تداخل نخواهد داشت.
  • پایداری و قابلیت تکرار: فایل docker-compose.yml قابل نسخه‌گذاری (version-controlled) است و به راحتی می‌تواند بین اعضای تیم به اشتراک گذاشته شود، که منجر به محیط‌های توسعه یکسان برای همه می‌شود.
  • توسعه سریع‌تر: با یک محیط از پیش پیکربندی شده، توسعه‌دهندگان می‌توانند زمان کمتری را صرف تنظیم محیط و زمان بیشتری را صرف نوشتن کد کنند.
  • مدیریت آسان وابستگی‌ها: به راحتی می‌توانید وابستگی‌های بین سرویس‌ها را تعریف کنید، مانند اینکه پایگاه داده باید قبل از سرور بک‌اند راه‌اندازی شود.

اجزای اصلی فایل docker-compose.yml

یک فایل docker-compose.yml معمولاً از سه بخش اصلی تشکیل شده است:

Services (سرویس‌ها)

این بخش اصلی جایی است که شما هر کانتینر را به عنوان یک “سرویس” تعریف می‌کنید. برای هر سرویس، شما مشخص می‌کنید که از چه تصویری (image) استفاده کند، چه پورت‌هایی را مپ (map) کند، چه متغیرهای محیطی داشته باشد، چه ولوم‌هایی (volumes) را متصل کند و سایر تنظیمات مربوط به آن کانتینر. هر سرویس در واقع یک کانتینر مجزا است که می‌تواند به صورت مستقل اجرا شود یا با سایر سرویس‌ها تعامل داشته باشد.


services:
  web:
    image: nginx:latest
    ports:
      - "80:80"
  app:
    build: .
    environment:
      DATABASE_URL: postgres://user:password@db:5432/mydb
  db:
    image: postgres:14

Networks (شبکه‌ها)

Docker Compose به طور پیش‌فرض یک شبکه بریج (bridge network) برای تمام سرویس‌های تعریف شده در فایل docker-compose.yml ایجاد می‌کند. این به سرویس‌ها اجازه می‌دهد تا با استفاده از نام سرویس (مانند db یا app) به یکدیگر متصل شوند. با این حال، شما می‌توانید شبکه‌های سفارشی خود را برای کنترل دقیق‌تر بر ارتباطات بین کانتینرها تعریف کنید. این قابلیت به ویژه برای جداسازی ترافیک یا اتصال سرویس‌ها به شبکه‌های موجود مفید است.


networks:
  app_network:
    driver: bridge

Volumes (ولوم‌ها)

ولوم‌ها برای پایداری داده‌ها (data persistence) حیاتی هستند. کانتینرهای Docker به طور پیش‌فرض بی‌حالت (stateless) هستند، به این معنی که هر داده‌ای که در داخل کانتینر ایجاد شود، پس از حذف کانتینر از بین می‌رود. ولوم‌ها به شما امکان می‌دهند تا دایرکتوری‌ها را از سیستم فایل میزبان (host machine) به داخل کانتینر مپ کنید، به این ترتیب داده‌ها حتی پس از حذف و ایجاد مجدد کانتینر نیز حفظ می‌شوند. این امر برای پایگاه‌های داده که نیاز به ذخیره دائمی اطلاعات دارند، ضروری است.


volumes:
  db_data:
    driver: local

با درک این اجزای اساسی، شما آماده‌اید تا به سراغ راه‌اندازی محیط‌های توسعه با پایگاه‌های داده مختلف با استفاده از Docker Compose بروید. این ساختار قدرتمند، نه تنها پیچیدگی را کاهش می‌دهد، بلکه انعطاف‌پذیری بی‌نظیری را برای مدیریت پروژه‌های توسعه شما فراهم می‌آورد.

مقدمات و پیش‌نیازها: آماده‌سازی برای کار با Docker Compose

قبل از اینکه به جزئیات اتصال Docker Compose به پایگاه‌های داده بپردازیم، لازم است مطمئن شویم که تمام ابزارهای لازم را نصب کرده‌اید و با دستورات اولیه آشنا هستید. این بخش به شما کمک می‌کند تا محیط خود را برای شروع کار آماده کنید.

نصب Docker Desktop / Docker Engine

اولین و مهم‌ترین پیش‌نیاز، نصب Docker Desktop (برای Windows و macOS) یا Docker Engine (برای Linux) است. Docker Desktop شامل Docker Engine، Docker CLI، Docker Compose و Kubernetes (اختیاری) می‌شود و یک تجربه کاربری یکپارچه را فراهم می‌کند. می‌توانید آن را از وب‌سایت رسمی Docker دانلود و نصب کنید.

  • برای Windows و macOS: به وب‌سایت Docker Desktop مراجعه کرده و بسته نصبی مربوطه را دانلود و اجرا کنید.
  • برای Linux: دستورالعمل‌های نصب Docker Engine را برای توزیع خاص لینوکس خود در مستندات Docker دنبال کنید.

پس از نصب، می‌توانید با اجرای دستورات زیر در ترمینال، از صحت نصب اطمینان حاصل کنید:


docker --version
docker-compose --version

این دستورات باید نسخه‌های نصب شده Docker و Docker Compose را نمایش دهند.

آشنایی با دستورات پایه Docker

اگرچه Docker Compose بسیاری از دستورات پیچیده Docker را پنهان می‌کند، اما آشنایی با چند دستور پایه Docker می‌تواند در اشکال‌زدایی و درک بهتر عملکرد آن مفید باشد:

  • docker run [OPTIONS] IMAGE [COMMAND] [ARG...]: برای اجرای یک کانتینر از یک تصویر.
  • docker build [OPTIONS] PATH | URL | -: برای ساخت یک تصویر Docker از یک Dockerfile.
  • docker ps: نمایش کانتینرهای در حال اجرا.
  • docker images: نمایش تصاویر موجود.
  • docker stop [CONTAINER_ID]: توقف یک کانتینر.
  • docker rm [CONTAINER_ID]: حذف یک کانتینر.
  • docker rmi [IMAGE_ID]: حذف یک تصویر.
  • docker logs [CONTAINER_ID]: نمایش لاگ‌های یک کانتینر.
  • docker exec -it [CONTAINER_ID] bash: اجرای یک دستور در داخل کانتینر (مانند ورود به shell کانتینر).

دستورات پایه Docker Compose

دستورات اصلی Docker Compose که بیشتر از آن‌ها استفاده خواهید کرد:

  • docker-compose up: ساخت، ایجاد و راه‌اندازی تمام سرویس‌های تعریف شده در فایل docker-compose.yml. (با -d برای اجرای در پس‌زمینه)
  • docker-compose down: توقف و حذف تمام سرویس‌ها، شبکه‌ها و ولوم‌های تعریف شده در فایل docker-compose.yml.
  • docker-compose ps: نمایش وضعیت سرویس‌های Docker Compose.
  • docker-compose logs [SERVICE_NAME]: نمایش لاگ‌های یک سرویس خاص.
  • docker-compose exec [SERVICE_NAME] COMMAND: اجرای یک دستور در داخل یک سرویس (کانتینر).
  • docker-compose build [SERVICE_NAME]: ساخت یا بازسازی تصاویر برای سرویس‌های مشخص شده.

درک حالت‌های شبکه در Docker Compose

به طور پیش‌فرض، Docker Compose یک شبکه بریج سفارشی برای برنامه شما ایجاد می‌کند. این شبکه به کانتینرها اجازه می‌دهد تا با نام سرویس به یکدیگر متصل شوند. به عنوان مثال، اگر یک سرویس به نام db دارید، سرویس‌های دیگر می‌توانند با استفاده از نام هاست db به آن متصل شوند. این قابلیت بسیار مهم است زیرا شما را از نیاز به دانستن آدرس‌های IP کانتینرها بی‌نیاز می‌کند.

شما می‌توانید شبکه‌های سفارشی خود را نیز تعریف کنید و سرویس‌ها را به آن‌ها متصل کنید تا جداسازی شبکه یا اتصال به شبکه‌های موجود را مدیریت کنید.

متغیرهای محیطی و فایل .env

متغیرهای محیطی برای پیکربندی پویا کانتینرها بسیار مهم هستند، به خصوص برای اطلاعات حساسی مانند رمز عبور پایگاه داده. Docker Compose از چندین روش برای مدیریت متغیرهای محیطی پشتیبانی می‌کند:

  • مستقیم در docker-compose.yml: با استفاده از کلید environment برای هر سرویس.
  • فایل .env: Docker Compose به طور خودکار فایل .env را در همان دایرکتوری docker-compose.yml بارگذاری می‌کند. این روش برای اطلاعات حساس و متغیرهای مختص به محیط توسعه محلی توصیه می‌شود و نباید به سیستم کنترل نسخه (مانند Git) اضافه شود.
  • env_file: می‌توانید یک فایل متغیر محیطی مجزا را با کلید env_file برای یک سرویس مشخص کنید.

استفاده از فایل .env برای رمز عبورها و اطلاعات حساس بهترین روش است، زیرا آن را از فایل پیکربندی اصلی جدا می‌کند و از commit شدن ناخواسته به مخازن عمومی جلوگیری می‌کند. به عنوان مثال، شما می‌توانید یک فایل .env به این شکل داشته باشید:


MYSQL_ROOT_PASSWORD=mysecretpassword
POSTGRES_USER=myuser
POSTGRES_PASSWORD=mypassword

و سپس در docker-compose.yml به آن متغیرها ارجاع دهید:


services:
  db:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}

با آماده‌سازی این پیش‌نیازها و درک مفاهیم پایه، شما اکنون کاملاً آماده‌اید تا به سراغ مثال‌های عملی اتصال Docker Compose به پایگاه‌های داده مختلف برویم.

اتصال به MySQL با Docker Compose: گام به گام

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

انتخاب تصویر MySQL

اولین گام انتخاب تصویر مناسب MySQL از Docker Hub است. شما می‌توانید از تگ‌های مختلفی مانند mysql:latest برای آخرین نسخه پایدار، یا تگ‌های نسخه‌ای خاص مانند mysql:8.0 یا mysql:5.7 استفاده کنید. استفاده از یک تگ نسخه‌ای خاص توصیه می‌شود تا از تغییرات ناخواسته در نسخه‌های جدید جلوگیری کنید و سازگاری محیطی را تضمین کنید.

متغیرهای محیطی ضروری برای MySQL

برای پیکربندی یک کانتینر MySQL، چندین متغیر محیطی کلیدی وجود دارد که باید آن‌ها را تنظیم کنید. این متغیرها توسط اسکریپت ورودی (entrypoint script) تصویر MySQL برای مقداردهی اولیه پایگاه داده استفاده می‌شوند:

  • MYSQL_ROOT_PASSWORD: اجباری. رمز عبور برای کاربر روت (root) MySQL. این رمز عبور باید تنظیم شود تا سرور MySQL به درستی راه‌اندازی شود.
  • MYSQL_DATABASE: (اختیاری) نام پایگاه داده‌ای که می‌خواهید هنگام راه‌اندازی کانتینر ایجاد شود.
  • MYSQL_USER: (اختیاری) نام کاربری که می‌خواهید هنگام راه‌اندازی کانتینر ایجاد شود. این کاربر به پایگاه داده‌ای که با MYSQL_DATABASE مشخص شده، دسترسی خواهد داشت.
  • MYSQL_PASSWORD: (اختیاری) رمز عبور برای کاربری که با MYSQL_USER مشخص شده.

توصیه می‌شود رمز عبورها را در یک فایل .env نگهداری کنید و به آن‌ها در docker-compose.yml ارجاع دهید.

مپ کردن ولوم برای پایداری داده

برای اطمینان از اینکه داده‌های پایگاه داده شما پس از توقف و راه‌اندازی مجدد کانتینر MySQL حفظ می‌شوند، باید یک ولوم برای دایرکتوری ذخیره‌سازی داده‌های MySQL مپ کنید. دایرکتوری پیش‌فرض برای داده‌های MySQL در داخل کانتینر /var/lib/mysql است.


volumes:
  mysql_data:
    driver: local

و سپس در تعریف سرویس:


volumes:
  - mysql_data:/var/lib/mysql

مپ کردن پورت

به طور پیش‌فرض، MySQL بر روی پورت 3306 گوش می‌دهد. اگر می‌خواهید از سیستم میزبان (host machine) خود یا از کانتینرهای دیگر خارج از شبکه Docker Compose به MySQL متصل شوید، باید پورت کانتینر را به پورت سیستم میزبان مپ کنید:


ports:
  - "3306:3306"

این خط به این معنی است که پورت 3306 روی سیستم میزبان به پورت 3306 داخل کانتینر مپ می‌شود. اگر پورت 3306 روی سیستم میزبان شما اشغال است، می‌توانید از پورت دیگری استفاده کنید، مثلاً "3307:3306".

نمونه docker-compose.yml برای MySQL

در اینجا یک نمونه کامل از فایل docker-compose.yml برای راه‌اندازی یک سرویس MySQL به همراه یک فایل .env آورده شده است:

فایل .env (در کنار docker-compose.yml):


# .env file
MYSQL_ROOT_PASSWORD=my_strong_root_password
MYSQL_DATABASE=mydatabase
MYSQL_USER=myuser
MYSQL_PASSWORD=my_user_password

فایل docker-compose.yml:


# docker-compose.yml
version: '3.8'

services:
  db:
    image: mysql:8.0
    container_name: mysql_db_container
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: ${MYSQL_DATABASE}
      MYSQL_USER: ${MYSQL_USER}
      MYSQL_PASSWORD: ${MYSQL_PASSWORD}
    ports:
      - "3306:3306" # Map host port 3306 to container port 3306
    volumes:
      - mysql_data:/var/lib/mysql # Persistent storage for database data
      # Optional: Mount a directory of SQL scripts for initial setup
      # - ./db_init:/docker-entrypoint-initdb.d
    healthcheck: # Health check to ensure the database is truly ready
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "${MYSQL_USER}", "-p${MYSQL_PASSWORD}"]
      interval: 10s
      timeout: 5s
      retries: 5
    restart: always # Always restart the container if it stops

  app: # Example application service that connects to MySQL
    build: . # Or use a specific image, e.g., image: my_node_app:latest
    container_name: my_app_container
    ports:
      - "8000:8000"
    environment:
      DB_HOST: db # Use the service name as the hostname
      DB_USER: ${MYSQL_USER}
      DB_PASSWORD: ${MYSQL_PASSWORD}
      DB_NAME: ${MYSQL_DATABASE}
      DB_PORT: 3306
    depends_on:
      db:
        condition: service_healthy # Ensure 'db' service is healthy before starting 'app'
    # Optional: Mount application code for live reloading during development
    # - ./app:/usr/src/app
    # working_dir: /usr/src/app
    # command: npm start # Example command for a Node.js app

volumes:
  mysql_data: # Define the named volume

برای سرویس app، باید یک Dockerfile در همان دایرکتوری docker-compose.yml داشته باشید، یا از یک تصویر از پیش ساخته شده استفاده کنید. متغیرهای محیطی DB_HOST، DB_USER، DB_PASSWORD و DB_NAME به برنامه شما کمک می‌کنند تا به پایگاه داده MySQL متصل شود. نکته مهم اینجا استفاده از db به عنوان DB_HOST است که نام سرویس پایگاه داده در Docker Compose است و Docker به طور خودکار آن را به IP داخلی کانتینر MySQL ترجمه می‌کند.

مثال اتصال برنامه (Python/Node.js/PHP)

نحوه اتصال برنامه شما به MySQL در داخل کانتینر app تقریباً شبیه اتصال به یک سرور MySQL استاندارد است، با این تفاوت که به جای localhost یا یک آدرس IP، از نام سرویس db به عنوان هاست استفاده می‌کنید.

پایتون (مثال با SQLAlchemy):


# Python example using SQLAlchemy
from sqlalchemy import create_engine
import os

DB_USER = os.getenv('DB_USER')
DB_PASSWORD = os.getenv('DB_PASSWORD')
DB_HOST = os.getenv('DB_HOST') # Will be 'db'
DB_NAME = os.getenv('DB_NAME')
DB_PORT = os.getenv('DB_PORT')

DATABASE_URL = f"mysql+mysqlconnector://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_NAME}"
engine = create_engine(DATABASE_URL)

try:
    with engine.connect() as connection:
        result = connection.execute("SELECT 1")
        print("Successfully connected to MySQL!", result.scalar())
except Exception as e:
    print(f"Error connecting to MySQL: {e}")

Node.js (مثال با `mysql2`):


// Node.js example using mysql2
const mysql = require('mysql2/promise');
require('dotenv').config(); // If using dotenv for local .env files outside Docker Compose

async function connectToMySQL() {
  try {
    const connection = await mysql.createConnection({
      host: process.env.DB_HOST, // Will be 'db'
      user: process.env.DB_USER,
      password: process.env.DB_PASSWORD,
      database: process.env.DB_NAME,
      port: process.env.DB_PORT,
    });
    console.log('Successfully connected to MySQL!');
    const [rows, fields] = await connection.execute('SELECT 1 + 1 AS solution');
    console.log('The solution is: ', rows[0].solution);
    await connection.end();
  } catch (error) {
    console.error('Error connecting to MySQL:', error);
  }
}

connectToMySQL();

PHP (مثال با PDO):


// PHP example using PDO
<?php

$host = getenv('DB_HOST') ?: 'db';
$db   = getenv('DB_NAME') ?: 'mydatabase';
$user = getenv('DB_USER') ?: 'myuser';
$pass = getenv('DB_PASSWORD') ?: 'my_user_password';
$port = getenv('DB_PORT') ?: '3306';
$charset = 'utf8mb4';

$dsn = "mysql:host=$host;dbname=$db;port=$port;charset=$charset";
$options = [
    PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    PDO::ATTR_EMULATE_PREPARES   => false,
];

try {
    $pdo = new PDO($dsn, $user, $pass, $options);
    echo "Successfully connected to MySQL!\n";
    $stmt = $pdo->query('SELECT 1 + 1');
    $result = $stmt->fetchColumn();
    echo "Result: " . $result . "\n";
} catch (\PDOException $e) {
    throw new \PDOException($e->getMessage(), (int)$e->getCode());
}

?>

healthcheck و depends_on

  • healthcheck: این یک ویژگی بسیار مهم است که به Docker Compose اجازه می‌دهد تا وضعیت سلامت یک سرویس را به طور فعال بررسی کند. در مثال MySQL، از mysqladmin ping استفاده کردیم تا مطمئن شویم نه تنها کانتینر MySQL در حال اجراست، بلکه سرور MySQL داخل آن نیز آماده پذیرش اتصالات است. این کار به جلوگیری از خطاهای “پایگاه داده در دسترس نیست” در سرویس‌های وابسته کمک می‌کند.
  • depends_on: این ویژگی یک وابستگی ساده را بین سرویس‌ها تعریف می‌کند. به عنوان مثال، depends_on: db به Docker Compose می‌گوید که سرویس db را قبل از سرویس app راه‌اندازی کند. با اضافه کردن condition: service_healthy، شما این وابستگی را قوی‌تر می‌کنید و تضمین می‌کنید که سرویس app تنها زمانی راه‌اندازی می‌شود که سرویس db به طور کامل سالم و آماده باشد.

با این پیکربندی، شما یک محیط توسعه MySQL پایدار، قابل تکرار و مقاوم در برابر خطا خواهید داشت که به راحتی می‌توانید آن را در پروژه‌های خود به کار ببرید.

اتصال به PostgreSQL با Docker Compose: رویکردی مشابه

PostgreSQL یک سیستم مدیریت پایگاه داده شیء-رابطه‌ای پیشرفته و متن‌باز است که به خاطر قابلیت اطمینان، پایداری ویژگی‌ها و عملکردش مشهور است. راه‌اندازی PostgreSQL با Docker Compose بسیار شبیه به MySQL است و از همان مفاهیم اصلی برای پیکربندی و پایداری داده‌ها استفاده می‌کند. در این بخش، به طور خاص به پیکربندی PostgreSQL می‌پردازیم.

انتخاب تصویر PostgreSQL

مانند MySQL، اولین قدم انتخاب یک تصویر مناسب PostgreSQL از Docker Hub است. شما می‌توانید از postgres:latest برای آخرین نسخه پایدار یا از تگ‌های نسخه‌ای خاص مانند postgres:14، postgres:13 و غیره استفاده کنید. باز هم، استفاده از یک تگ نسخه‌ای خاص برای حفظ سازگاری محیطی توصیه می‌شود.

متغیرهای محیطی ضروری برای PostgreSQL

تصویر رسمی PostgreSQL نیز از متغیرهای محیطی برای پیکربندی اولیه استفاده می‌کند:

  • POSTGRES_DB: (اختیاری) نام پایگاه داده‌ای که هنگام راه‌اندازی کانتینر ایجاد می‌شود. اگر این متغیر تنظیم نشود، یک پایگاه داده با همان نام کاربری ایجاد می‌شود.
  • POSTGRES_USER: (اختیاری) نام کاربری که می‌خواهید ایجاد شود. اگر تنظیم نشود، نام کاربری پیش‌فرض postgres استفاده می‌شود. این کاربر دسترسی کامل به پایگاه داده‌ای که با POSTGRES_DB مشخص شده است، خواهد داشت.
  • POSTGRES_PASSWORD: اجباری. رمز عبور برای کاربری که با POSTGRES_USER (یا کاربر پیش‌فرض postgres) مشخص شده است. این متغیر باید تنظیم شود.
  • POSTGRES_HOST_AUTH_METHOD: (اختیاری) روش احراز هویت را برای اتصالات اولیه تعیین می‌کند. برای محیط‌های توسعه، گاهی اوقات trust استفاده می‌شود، اما برای امنیت بهتر، به آن دست نزنید و رمز عبور را استفاده کنید.

مانند MySQL، توصیه می‌شود رمز عبورها را در یک فایل .env ذخیره کنید.

مپ کردن ولوم برای پایداری داده

برای حفظ داده‌های PostgreSQL، باید یک ولوم برای دایرکتوری ذخیره‌سازی داده‌ها مپ کنید. دایرکتوری پیش‌فرض برای داده‌های PostgreSQL در داخل کانتینر /var/lib/postgresql/data است.


volumes:
  postgres_data:
    driver: local

و در تعریف سرویس:


volumes:
  - postgres_data:/var/lib/postgresql/data

مپ کردن پورت

PostgreSQL به طور پیش‌فرض بر روی پورت 5432 گوش می‌دهد. برای دسترسی به آن از سیستم میزبان، پورت را مپ کنید:


ports:
  - "5432:5432"

نمونه docker-compose.yml برای PostgreSQL

در اینجا یک نمونه کامل از فایل docker-compose.yml برای راه‌اندازی یک سرویس PostgreSQL به همراه یک فایل .env آورده شده است:

فایل .env (در کنار docker-compose.yml):


# .env file
POSTGRES_DB=mydb
POSTGRES_USER=myuser
POSTGRES_PASSWORD=my_strong_password

فایل docker-compose.yml:


# docker-compose.yml
version: '3.8'

services:
  db:
    image: postgres:14 # Using a specific version for stability
    container_name: postgres_db_container
    environment:
      POSTGRES_DB: ${POSTGRES_DB}
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      PGTZ: Asia/Tehran # Optional: Set timezone for PostgreSQL
    ports:
      - "5432:5432" # Map host port 5432 to container port 5432
    volumes:
      - postgres_data:/var/lib/postgresql/data # Persistent storage
      # Optional: Mount a directory of SQL scripts for initial setup
      # - ./db_init:/docker-entrypoint-initdb.d
    healthcheck: # Health check to ensure the database is truly ready
      test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
      interval: 10s
      timeout: 5s
      retries: 5
    restart: always

  app: # Example application service that connects to PostgreSQL
    build: . # Or use a specific image, e.g., image: my_go_app:latest
    container_name: my_go_app_container
    ports:
      - "8080:8080"
    environment:
      DATABASE_URL: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}?sslmode=disable
    depends_on:
      db:
        condition: service_healthy # Wait for db to be healthy
    # Mount application code for live reloading during development
    # - ./app:/usr/src/app
    # working_dir: /usr/src/app
    # command: go run main.go

volumes:
  postgres_data:

مثال اتصال برنامه (Go/Python/Node.js)

اتصال برنامه به PostgreSQL نیز با استفاده از نام سرویس db به عنوان هاست انجام می‌شود. رشته اتصال (connection string) کمی متفاوت است، اما مفهوم یکسان است.

Go (مثال با `database/sql` و `pq`):


// Go example
package main

import (
    "database/sql"
    "fmt"
    "log"
    "os"

    _ "github.com/lib/pq" // PostgreSQL driver
)

func main() {
    connStr := os.Getenv("DATABASE_URL")
    if connStr == "" {
        connStr = "postgres://myuser:my_strong_password@db:5432/mydb?sslmode=disable"
    }

    db, err := sql.Open("postgres", connStr)
    if err != nil {
        log.Fatalf("Error opening database: %q\n", err)
    }
    defer db.Close()

    err = db.Ping()
    if err != nil {
        log.Fatalf("Error connecting to the database: %q\n", err)
    }

    fmt.Println("Successfully connected to PostgreSQL!")

    var version string
    db.QueryRow("SELECT version()").Scan(&version)
    fmt.Println("PostgreSQL Version:", version)
}

پایتون (مثال با Psycopg2):


# Python example using Psycopg2
import psycopg2
import os

DB_HOST = os.getenv('DB_HOST', 'db')
DB_USER = os.getenv('DB_USER')
DB_PASSWORD = os.getenv('DB_PASSWORD')
DB_NAME = os.getenv('DB_NAME')
DB_PORT = os.getenv('DB_PORT', '5432')

try:
    conn = psycopg2.connect(
        host=DB_HOST,
        database=DB_NAME,
        user=DB_USER,
        password=DB_PASSWORD,
        port=DB_PORT
    )
    cur = conn.cursor()
    cur.execute('SELECT version()')
    db_version = cur.fetchone()
    print("Successfully connected to PostgreSQL!")
    print(f"PostgreSQL version: {db_version[0]}")
    cur.close()
    conn.close()
except Exception as e:
    print(f"Error connecting to PostgreSQL: {e}")

Node.js (مثال با `pg`):


// Node.js example using pg
const { Client } = require('pg');
require('dotenv').config();

async function connectToPostgreSQL() {
  const client = new Client({
    host: process.env.DB_HOST || 'db',
    port: process.env.DB_PORT || 5432,
    user: process.env.DB_USER,
    password: process.env.DB_PASSWORD,
    database: process.env.DB_NAME,
  });

  try {
    await client.connect();
    console.log('Successfully connected to PostgreSQL!');
    const res = await client.query('SELECT current_database()');
    console.log('Current database:', res.rows[0].current_database);
    await client.end();
  } catch (err) {
    console.error('Error connecting to PostgreSQL:', err);
  }
}

connectToPostgreSQL();

متغیر محیطی PGTZ

یکی از متغیرهای محیطی مفید برای PostgreSQL، PGTZ است که به شما امکان می‌دهد منطقه زمانی (timezone) سرور PostgreSQL را تنظیم کنید. این می‌تواند برای هماهنگی زمان‌بندی داده‌ها با منطقه زمانی برنامه شما مفید باشد و از مشکلات مربوط به نمایش زمان جلوگیری کند. در مثال بالا، آن را روی Asia/Tehran تنظیم کردیم.

با استفاده از این الگو، می‌توانید به سرعت و به طور قابل اعتماد یک محیط توسعه با PostgreSQL راه‌اندازی کنید که تمام مزایای Docker Compose را به همراه دارد.

اتصال به MongoDB با Docker Compose: انعطاف‌پذیری NoSQL

MongoDB یک پایگاه داده NoSQL محبوب است که از ساختار داده مبتنی بر سند (Document-Oriented) استفاده می‌کند و به خاطر انعطاف‌پذیری، مقیاس‌پذیری و عملکرد بالا در مدیریت حجم زیادی از داده‌های ساختارنیافته یا نیمه‌ساختاریافته شناخته شده است. راه‌اندازی MongoDB با Docker Compose نیز فرآیند مشابهی دارد، اما تفاوت‌های کوچکی در پیکربندی و نحوه مدیریت کاربران وجود دارد.

انتخاب تصویر MongoDB

مانند سایر پایگاه‌های داده، از Docker Hub تصویر مناسب MongoDB را انتخاب کنید. می‌توانید از mongo:latest برای آخرین نسخه یا تگ‌های نسخه‌ای خاص مانند mongo:5.0، mongo:6.0 و غیره استفاده کنید. برای محیط‌های توسعه، mongo:latest معمولاً کافی است، اما برای حفظ سازگاری در یک تیم، استفاده از یک نسخه ثابت توصیه می‌شود.

متغیرهای محیطی ضروری برای MongoDB

تصویر رسمی MongoDB برای پیکربندی اولیه از متغیرهای محیطی زیر استفاده می‌کند:

  • MONGO_INITDB_ROOT_USERNAME: (اختیاری) نام کاربری برای کاربر روت اولیه. اگر این متغیر تنظیم شود، باید MONGO_INITDB_ROOT_PASSWORD نیز تنظیم شود. این کاربر دسترسی کامل به تمام پایگاه‌های داده را خواهد داشت.
  • MONGO_INITDB_ROOT_PASSWORD: (اختیاری) رمز عبور برای کاربر روت اولیه.
  • MONGO_INITDB_DATABASE: (اختیاری) نام پایگاه داده‌ای که در هنگام راه‌اندازی برای کاربر root ایجاد می‌شود و به کاربر root دسترسی پیش‌فرض به آن داده می‌شود.
  • MONGO_INITDB_USERNAME و MONGO_INITDB_PASSWORD: (اختیاری) برای ایجاد یک کاربر عادی با دسترسی محدود به یک پایگاه داده خاص (مشخص شده توسط MONGO_INITDB_DATABASE). اینها جایگزین MONGO_INITDB_ROOT_USERNAME/PASSWORD نیستند، بلکه یک کاربر اضافی ایجاد می‌کنند.

برای شروع سریع در محیط توسعه، معمولاً از MONGO_INITDB_ROOT_USERNAME و MONGO_INITDB_ROOT_PASSWORD استفاده می‌شود.

مپ کردن ولوم برای پایداری داده

برای پایداری داده‌های MongoDB، باید یک ولوم برای دایرکتوری داده‌های آن مپ کنید. دایرکتوری پیش‌فرض در داخل کانتینر /data/db است.


volumes:
  mongodb_data:
    driver: local

و در تعریف سرویس:


volumes:
  - mongodb_data:/data/db

مپ کردن پورت

MongoDB به طور پیش‌فرض بر روی پورت 27017 گوش می‌دهد. برای دسترسی از سیستم میزبان، پورت را مپ کنید:


ports:
  - "27017:27017"

نمونه docker-compose.yml برای MongoDB

در اینجا یک نمونه کامل از فایل docker-compose.yml برای راه‌اندازی یک سرویس MongoDB به همراه یک فایل .env آورده شده است:

فایل .env (در کنار docker-compose.yml):


# .env file
MONGO_INITDB_ROOT_USERNAME=mongoadmin
MONGO_INITDB_ROOT_PASSWORD=my_strong_mongo_password
MONGO_DB=mydatabase

فایل docker-compose.yml:


# docker-compose.yml
version: '3.8'

services:
  mongodb:
    image: mongo:5.0 # Using a specific version
    container_name: mongo_db_container
    environment:
      MONGO_INITDB_ROOT_USERNAME: ${MONGO_INITDB_ROOT_USERNAME}
      MONGO_INITDB_ROOT_PASSWORD: ${MONGO_INITDB_ROOT_PASSWORD}
      MONGO_INITDB_DATABASE: ${MONGO_DB} # This will be the auth database
    ports:
      - "27017:27017" # Map host port 27017 to container port 27017
    volumes:
      - mongodb_data:/data/db # Persistent storage for database data
    healthcheck: # Health check for MongoDB
      test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"]
      interval: 10s
      timeout: 5s
      retries: 5
    restart: always

  app: # Example application service that connects to MongoDB
    build: . # Or use a specific image, e.g., image: my_express_app:latest
    container_name: my_express_app_container
    ports:
      - "3000:3000"
    environment:
      MONGODB_URI: mongodb://${MONGO_INITDB_ROOT_USERNAME}:${MONGO_INITDB_ROOT_PASSWORD}@mongodb:27017/${MONGO_DB}?authSource=admin
    depends_on:
      mongodb:
        condition: service_healthy # Wait for MongoDB to be healthy
    # Mount application code for live reloading during development
    # - ./app:/usr/src/app
    # working_dir: /usr/src/app
    # command: npm start

volumes:
  mongodb_data:

در رشته اتصال MONGODB_URI، از mongodb به عنوان هاست استفاده شده است که نام سرویس در Docker Compose است. همچنین authSource=admin مشخص می‌کند که احراز هویت باید در پایگاه داده admin انجام شود، جایی که کاربر روت (mongoadmin در این مثال) تعریف شده است.

مثال اتصال برنامه (Python/Node.js/Java)

اتصال به MongoDB از برنامه شما نیز با استفاده از نام سرویس mongodb به عنوان هاست انجام می‌شود.

پایتون (مثال با PyMongo):


# Python example using PyMongo
from pymongo import MongoClient
import os

MONGO_URI = os.getenv('MONGODB_URI') # e.g., from .env

try:
    client = MongoClient(MONGO_URI)
    db = client.get_database(os.getenv('MONGO_DB')) # Get the specified database
    
    # Optional: Test connection by listing collections
    print("Successfully connected to MongoDB!")
    print("Collections in database:", db.list_collection_names())

    # Example: insert a document
    # collection = db.mycollection
    # result = collection.insert_one({"name": "Test Document", "value": 1})
    # print(f"Inserted document with ID: {result.inserted_id}")

    client.close()
except Exception as e:
    print(f"Error connecting to MongoDB: {e}")

Node.js (مثال با `mongoose`):


// Node.js example using Mongoose
const mongoose = require('mongoose');
require('dotenv').config();

async function connectToMongoDB() {
  const mongoURI = process.env.MONGODB_URI;

  try {
    await mongoose.connect(mongoURI, {
      useNewUrlParser: true,
      useUnifiedTopology: true,
    });
    console.log('Successfully connected to MongoDB with Mongoose!');
    // Perform database operations here
  } catch (err) {
    console.error('Error connecting to MongoDB:', err);
  } finally {
    // await mongoose.disconnect(); // Disconnect after operations if needed
  }
}

connectToMongoDB();

جاوا (مثال با MongoDB Java Driver):


// Java example using MongoDB Java Driver
import com.mongodb.ConnectionString;
import com.mongodb.MongoClientSettings;
import com.mongodb.ServerApi;
import com.mongodb.ServerApiVersion;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoDatabase;
import org.bson.Document;

public class MongoDBConnection {
    public static void main(String[] args) {
        // Read from environment variable or provide default
        String connectionString = System.getenv("MONGODB_URI");
        if (connectionString == null || connectionString.isEmpty()) {
            connectionString = "mongodb://mongoadmin:my_strong_mongo_password@mongodb:27017/mydatabase?authSource=admin";
        }

        ServerApi serverApi = ServerApi.builder()
                .version(ServerApiVersion.V1)
                .build();

        MongoClientSettings settings = MongoClientSettings.builder()
                .applyConnectionString(new ConnectionString(connectionString))
                .serverApi(serverApi)
                .build();

        try (MongoClient mongoClient = MongoClients.create(settings)) {
            try {
                // Ping the deployment to confirm successful connection
                MongoDatabase database = mongoClient.getDatabase("admin"); // Or your target database
                database.runCommand(new Document("ping", 1));
                System.out.println("Successfully connected to MongoDB!");

                // Example: list database names
                System.out.println("Available databases:");
                mongoClient.listDatabaseNames().forEach(System.out::println);

            } catch (Exception e) {
                System.err.println("Error connecting to MongoDB: " + e.getMessage());
            }
        }
    }
}

پیاده‌سازی Replica Set برای MongoDB (پیشرفته‌تر)

برای محیط‌های تولید یا حتی توسعه که نیاز به آزمایش Failover یا تراکنش‌های چند سند دارید، ممکن است بخواهید یک Replica Set برای MongoDB راه‌اندازی کنید. این کار با Docker Compose کمی پیچیده‌تر است و نیاز به تعریف چندین سرویس MongoDB و پیکربندی آن‌ها برای تشکیل یک Replica Set دارد.

مثال زیر یک تنظیمات پایه از Replica Set سه عضوی را نشان می‌دهد:


# docker-compose.yml for a MongoDB Replica Set
version: '3.8'

services:
  mongo1:
    image: mongo:5.0
    container_name: mongo1
    command: ["mongod", "--replSet", "rs0", "--bind_ip_all"]
    ports:
      - "27017:27017"
    volumes:
      - mongo1_data:/data/db
    environment:
      MONGO_INITDB_ROOT_USERNAME: ${MONGO_INITDB_ROOT_USERNAME}
      MONGO_INITDB_ROOT_PASSWORD: ${MONGO_INITDB_ROOT_PASSWORD}
      MONGO_INITDB_DATABASE: ${MONGO_DB}
    healthcheck:
      test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"]
      interval: 10s
      timeout: 5s
      retries: 5
    restart: always

  mongo2:
    image: mongo:5.0
    container_name: mongo2
    command: ["mongod", "--replSet", "rs0", "--bind_ip_all"]
    ports:
      - "27018:27017"
    volumes:
      - mongo2_data:/data/db
    environment:
      MONGO_INITDB_ROOT_USERNAME: ${MONGO_INITDB_ROOT_USERNAME}
      MONGO_INITDB_ROOT_PASSWORD: ${MONGO_INITDB_ROOT_PASSWORD}
      MONGO_INITDB_DATABASE: ${MONGO_DB}
    healthcheck:
      test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"]
      interval: 10s
      timeout: 5s
      retries: 5
    restart: always

  mongo3:
    image: mongo:5.0
    container_name: mongo3
    command: ["mongod", "--replSet", "rs0", "--bind_ip_all"]
    ports:
      - "27019:27017"
    volumes:
      - mongo3_data:/data/db
    environment:
      MONGO_INITDB_ROOT_USERNAME: ${MONGO_INITDB_ROOT_USERNAME}
      MONGO_INITDB_ROOT_PASSWORD: ${MONGO_INITDB_ROOT_PASSWORD}
      MONGO_INITDB_DATABASE: ${MONGO_DB}
    healthcheck:
      test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"]
      interval: 10s
      timeout: 5s
      retries: 5
    restart: always

  mongo-init:
    image: mongo:5.0
    container_name: mongo_init_replica
    command: >
      mongosh --host mongo1:27017 --eval "rs.initiate({
        _id: 'rs0',
        members: [
          { _id: 0, host: 'mongo1:27017' },
          { _id: 1, host: 'mongo2:27017' },
          { _id: 2, host: 'mongo3:27017' }
        ]
      })"
    depends_on:
      mongo1: { condition: service_healthy }
      mongo2: { condition: service_healthy }
      mongo3: { condition: service_healthy }
    restart: "no" # This is a one-off command

volumes:
  mongo1_data:
  mongo2_data:
  mongo3_data:

پس از اجرای docker-compose up -d، باید با استفاده از docker-compose logs mongo-init بررسی کنید که فرآیند rs.initiate() با موفقیت اجرا شده باشد. سپس می‌توانید با رشته اتصال زیر به Replica Set متصل شوید:


MONGODB_URI=mongodb://${MONGO_INITDB_ROOT_USERNAME}:${MONGO_INITDB_ROOT_PASSWORD}@mongo1:27017,mongo2:27017,mongo3:27017/${MONGO_DB}?replicaSet=rs0&authSource=admin

این پیکربندی پیچیده‌تر، قدرت و انعطاف‌پذیری Docker Compose را برای سناریوهای پیشرفته‌تر نشان می‌دهد.

بهینه‌سازی و نکات پیشرفته در Docker Compose

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

مدیریت شبکه پیشرفته: شبکه‌های سفارشی و نام‌های مستعار

همانطور که قبلاً اشاره شد، Docker Compose به طور خودکار یک شبکه بریج پیش‌فرض ایجاد می‌کند. اما برای کنترل بیشتر و جداسازی بهتر، می‌توانید شبکه‌های سفارشی تعریف کنید.


version: '3.8'
services:
  web_app:
    image: my_app:latest
    networks:
      - app_tier # Connects to 'app_tier' network
  db:
    image: postgres:14
    networks:
      - db_tier # Connects to 'db_tier' network
      - app_tier # Also connects to 'app_tier' for web_app to reach it
    # network_aliases allows service 'app_tier' to reach 'db' via 'postgres_master'
    # aliases can be defined per network
    networks:
      app_tier:
        aliases:
          - postgres_master

networks:
  app_tier:
    driver: bridge
  db_tier:
    driver: bridge
    internal: true # Makes this network internal, containers on this network cannot directly talk to the outside world

در این مثال، سرویس web_app و db هر دو به app_tier متصل هستند تا بتوانند با هم ارتباط برقرار کنند. db همچنین به db_tier متصل است که با internal: true ایزوله شده است. این می‌تواند برای سرویس‌های مدیریتی که فقط باید از طریق یک واسط به دیتابیس متصل شوند، مفید باشد. همچنین، با aliases می‌توانید چندین نام برای یک سرویس در یک شبکه خاص تعریف کنید، که برای سوئیچ کردن بین master/replicaها یا هاست‌های مختلف دیتابیس بدون تغییر کد برنامه مفید است.

Health Checks: اطمینان از آمادگی واقعی سرویس‌ها

استفاده از depends_on فقط تضمین می‌کند که کانتینر وابسته راه‌اندازی شده است، نه اینکه سرویس داخل آن کاملاً آماده پذیرش درخواست‌ها باشد. Health Checks این مشکل را حل می‌کنند. با تعریف یک healthcheck، Docker Compose به طور مداوم وضعیت سلامت سرویس را بررسی می‌کند و تنها زمانی آن را “سالم” در نظر می‌گیرد که دستور test با موفقیت اجرا شود.


services:
  db:
    image: mysql:8.0
    # ... other configurations
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "myuser", "-pmy_strong_password"]
      interval: 5s # Check every 5 seconds
      timeout: 3s  # Wait up to 3 seconds for the command to execute
      retries: 10  # Retry 10 times before marking as unhealthy
      start_period: 30s # Give the container 30 seconds to start without checking
  • interval: هر چند وقت یکبار تست اجرا شود.
  • timeout: حداکثر زمان مجاز برای اجرای تست.
  • retries: تعداد تلاش‌های ناموفق قبل از علامت‌گذاری کانتینر به عنوان ناسالم.
  • start_period: یک دوره اولیه پس از شروع کانتینر که در آن Failures حساب نمی‌شوند. این برای سرویس‌هایی که زمان زیادی برای راه‌اندازی نیاز دارند مفید است.

مدیریت متغیرهای محیطی: `.env` و `env_file`

علاوه بر فایل .env در دایرکتوری ریشه پروژه، می‌توانید از env_file برای بارگذاری متغیرها از فایل‌های دیگر استفاده کنید. این برای جداسازی متغیرهای مربوط به یک سرویس خاص یا محیط‌های مختلف (مثلاً .env.development و .env.test) مفید است.


services:
  app:
    build: .
    env_file:
      - ./config/app.env
      - ./config/secrets.env
  db:
    image: postgres:14
    env_file:
      - ./config/db.env

محدودیت‌های منابع: مدیریت CPU و Memory

برای جلوگیری از مصرف بیش از حد منابع سیستم میزبان، به خصوص در محیط‌های توسعه با چندین سرویس، می‌توانید محدودیت‌هایی را برای CPU و حافظه کانتینرها تعیین کنید. این کار به پایداری محیط توسعه شما کمک می‌کند.


services:
  app:
    image: my_app:latest
    deploy:
      resources:
        limits:
          cpus: '0.5' # Limit to 0.5 of a CPU core
          memory: 512M # Limit to 512 MB of RAM
        reservations:
          cpus: '0.25' # Reserve 0.25 of a CPU core
          memory: 256M # Reserve 256 MB of RAM

توجه داشته باشید که deploy یک کلید سطح بالا است که برای تنظیمات استقرار (مانند Swarm) استفاده می‌شود، اما می‌تواند برای تعریف محدودیت‌های منابع در Docker Compose نیز به کار رود.

Docker Compose Profiles

Profiles به شما امکان می‌دهند مجموعه‌های مختلفی از سرویس‌ها را در یک فایل docker-compose.yml تعریف کنید و تنها سرویس‌های خاصی را بر اساس نیاز راه‌اندازی کنید. این برای پروژه‌های بزرگ با وابستگی‌های زیاد که نیاز به راه‌اندازی همه چیز ندارید، بسیار مفید است.


version: '3.8'
services:
  app:
    build: .
    profiles: ["development", "testing"] # This service belongs to 'development' and 'testing' profiles
  db:
    image: postgres:14
    profiles: ["development", "production"]
  migrations:
    image: my_app_migrations:latest
    profiles: ["testing"]
    command: ["migrate"]
    depends_on:
      db:
        condition: service_healthy

volumes:
  db_data:

برای راه‌اندازی سرویس‌های پروفایل development:


docker-compose --profile development up -d

برای راه‌اندازی سرویس‌های پروفایل testing:


docker-compose --profile testing up -d

می‌توانید چندین پروفایل را همزمان فعال کنید: docker-compose --profile development --profile testing up -d.

Init Scripts برای پایگاه‌های داده

تصاویر رسمی پایگاه‌های داده (MySQL، PostgreSQL، MongoDB) اغلب از قابلیتی پشتیبانی می‌کنند که به شما امکان می‌دهد اسکریپت‌های SQL یا shell را در هنگام راه‌اندازی اولیه کانتینر اجرا کنید. این برای ایجاد جداول، وارد کردن داده‌های اولیه یا ایجاد کاربران اضافی در یک محیط توسعه بسیار مفید است.

  • MySQL و PostgreSQL: می‌توانید یک دایرکتوری را به /docker-entrypoint-initdb.d داخل کانتینر مپ کنید. هر فایل .sql، .sh، .sql.gz و غیره در این دایرکتوری در اولین راه‌اندازی کانتینر اجرا خواهد شد.
  • MongoDB: برای MongoDB، می‌توانید یک دایرکتوری را به /docker-entrypoint-initdb.d مپ کنید که اسکریپت‌های .js یا .sh را در هنگام راه‌اندازی اجرا می‌کند.

services:
  db:
    image: postgres:14
    volumes:
      - postgres_data:/var/lib/postgresql/data
      - ./db_init:/docker-entrypoint-initdb.d # Path to your SQL scripts

در دایرکتوری db_init (در کنار docker-compose.yml) می‌توانید فایل‌هایی مانند 01-schema.sql، 02-data.sql و … قرار دهید.

ملاحظات امنیتی (در محیط توسعه)

حتی در محیط توسعه، رعایت حداقل نکات امنیتی مهم است:

  • رمز عبورهای قوی: همیشه از رمز عبورهای قوی و تصادفی برای پایگاه‌های داده استفاده کنید، حتی اگر فقط برای توسعه باشد.
  • فایل .env: هرگز فایل .env حاوی اطلاعات حساس را به سیستم کنترل نسخه (مانند Git) commit نکنید. آن را به .gitignore اضافه کنید.
  • حداقل دسترسی: کاربران پایگاه داده‌ای که برای برنامه‌های خود ایجاد می‌کنید، باید حداقل دسترسی لازم را داشته باشند (Principle of Least Privilege).
  • پورت‌های در معرض دید: پورت‌های پایگاه داده را فقط در صورتی به سیستم میزبان مپ کنید که واقعاً نیاز به دسترسی مستقیم از ابزارهای خارجی (مانند ابزارهای GUI مدیریت پایگاه داده) دارید. در غیر این صورت، تنها کانتینرهای دیگر در شبکه Docker Compose به آن دسترسی خواهند داشت که امن‌تر است.

با به کارگیری این نکات پیشرفته، می‌توانید محیط‌های Docker Compose خود را بهینه کرده و تجربه توسعه خود را بهبود بخشید.

سناریوهای عملی و حل مشکلات رایج در Docker Compose

هنگام کار با Docker Compose و پایگاه‌های داده، ممکن است با چالش‌ها و خطاهایی مواجه شوید. این بخش به شما کمک می‌کند تا برخی از سناریوهای عملی رایج و نحوه عیب‌یابی مشکلات احتمالی را درک کنید.

خطاهای “Service not ready” یا “Connection Refused”

این یکی از رایج‌ترین مشکلاتی است که هنگام راه‌اندازی برنامه‌ها با پایگاه داده‌ها در Docker Compose با آن مواجه می‌شوید. این خطا معمولاً زمانی رخ می‌دهد که سرویس برنامه شما زودتر از پایگاه داده راه‌اندازی شده و تلاش می‌کند به آن متصل شود، در حالی که پایگاه داده هنوز کاملاً آماده پذیرش اتصالات نیست.

راه حل:

  1. استفاده از healthcheck: همانطور که قبلاً توضیح داده شد، healthcheck در تعریف سرویس پایگاه داده، بهترین راه برای اطمینان از آمادگی کامل آن است.
  2. استفاده از depends_on با condition: service_healthy: این تضمین می‌کند که سرویس برنامه شما تنها زمانی شروع به کار کند که پایگاه داده سالم و آماده باشد.
  3. مکانیزم‌های retry در برنامه: حتی با healthcheck و depends_on، ممکن است در شرایط بار بالا یا زمان‌های راه‌اندازی غیرمنتظره، خطاهایی رخ دهد. پیاده‌سازی یک مکانیزم retry با backoff (تأخیر فزاینده) در کد برنامه شما برای اتصال به پایگاه داده، یک practice قوی است.

# Example of depends_on with condition
services:
  app:
    depends_on:
      db:
        condition: service_healthy
  db:
    # ... healthcheck definition ...

تداخل پورت‌ها (Port Conflicts)

اگر پورت مپ شده در docker-compose.yml (مثلاً 3306:3306 برای MySQL) قبلاً توسط سرویس دیگری روی سیستم میزبان شما اشغال شده باشد، Docker Compose قادر به راه‌اندازی سرویس نخواهد بود.

راه حل:

  1. تغییر پورت میزبان: ساده‌ترین راه، تغییر پورت سمت میزبان است. به عنوان مثال، برای MySQL می‌توانید از "3307:3306" استفاده کنید.
  2. پیدا کردن سرویس اشغال‌کننده پورت: می‌توانید با استفاده از دستورات سیستم عامل خود (مانند netstat -tuln | grep 3306 در لینوکس یا netstat -ano | findstr :3306 در ویندوز) سرویسی که پورت را اشغال کرده را پیدا و متوقف کنید.

مشکلات پایداری داده (Volumes)

اگر داده‌های پایگاه داده شما پس از اجرای docker-compose down از بین می‌روند، احتمالاً ولوم‌ها به درستی پیکربندی نشده‌اند.

راه حل:

  1. اطمینان از تعریف ولوم نام‌گذاری شده: مطمئن شوید که یک ولوم نام‌گذاری شده در بخش volumes فایل docker-compose.yml تعریف کرده‌اید (مثلاً mysql_data:).
  2. مپ کردن صحیح ولوم: مطمئن شوید که این ولوم به دایرکتوری صحیح داده‌ها در داخل کانتینر مپ شده است (مثلاً - mysql_data:/var/lib/mysql).
  3. استفاده از docker-compose down -v: اگر می‌خواهید همه ولوم‌ها را نیز حذف کنید، باید از پرچم -v استفاده کنید. برای حفظ داده‌ها، فقط docker-compose down را اجرا کنید.
  4. بررسی مجوزها: گاهی اوقات، مشکل از مجوزهای دایرکتوری در سیستم میزبان است. Docker معمولاً این را به درستی مدیریت می‌کند، اما در برخی موارد ممکن است نیاز به بررسی داشته باشید.

مشکلات اتصال شبکه بین کانتینرها

اگر کانتینرها نمی‌توانند با یکدیگر ارتباط برقرار کنند (مثلاً برنامه نمی‌تواند به دیتابیس متصل شود)، ممکن است مشکل شبکه باشد.

راه حل:

  1. استفاده از نام سرویس به عنوان هاست: همیشه از نام سرویس (مثلاً db یا mongodb) به عنوان هاست برای اتصال بین کانتینرها استفاده کنید، نه localhost یا IP.
  2. بررسی شبکه‌ها: با docker network ls و docker network inspect [NETWORK_ID] می‌توانید شبکه‌های Docker و کانتینرهای متصل به آن‌ها را بررسی کنید.
  3. docker-compose logs [SERVICE_NAME]: لاگ‌های سرویس‌ها را بررسی کنید تا پیام‌های خطا مربوط به اتصال را پیدا کنید.
  4. docker-compose exec [SERVICE_NAME] ping [OTHER_SERVICE_NAME]: می‌توانید وارد یک کانتینر شوید و سعی کنید سرویس دیگر را پینگ کنید تا اتصال شبکه را بررسی کنید. (مثلاً docker-compose exec app ping db)

اشکال‌زدایی کانتینرها

وقتی چیزی درست کار نمی‌کند، باید بتوانید به داخل کانتینرها نگاه کنید.

  • docker-compose logs -f [SERVICE_NAME]: لاگ‌های یک سرویس را به صورت Real-time دنبال می‌کند.
  • docker-compose exec [SERVICE_NAME] bash (یا sh): به شما امکان می‌دهد وارد shell یک کانتینر در حال اجرا شوید. از آنجا می‌توانید دستوراتی مانند ping، netstat، ps aux را اجرا کنید و فایل‌های پیکربندی را بررسی کنید.
  • docker-compose restart [SERVICE_NAME]: برای راه‌اندازی مجدد یک سرویس خاص بدون توقف کل محیط.

استفاده از چندین پایگاه داده در یک پروژه

شما می‌توانید چندین نوع پایگاه داده را در یک فایل docker-compose.yml راه‌اندازی کنید و برنامه شما می‌تواند به هر یک از آن‌ها متصل شود. هر پایگاه داده به عنوان یک سرویس جداگانه تعریف می‌شود و برنامه شما از طریق نام سرویس به آن‌ها دسترسی پیدا می‌کند.


# docker-compose.yml with multiple databases
version: '3.8'

services:
  app:
    build: .
    ports:
      - "8000:8000"
    environment:
      MYSQL_HOST: mysql_db
      POSTGRES_HOST: pg_db
      MONGO_HOST: mongo_db
      # ... other database credentials ...
    depends_on:
      mysql_db:
        condition: service_healthy
      pg_db:
        condition: service_healthy
      mongo_db:
        condition: service_healthy

  mysql_db:
    image: mysql:8.0
    container_name: mysql_service
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: ${MYSQL_DATABASE}
    ports:
      - "3306:3306"
    volumes:
      - mysql_data:/var/lib/mysql
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 10s
      timeout: 5s
      retries: 5

  pg_db:
    image: postgres:14
    container_name: postgres_service
    environment:
      POSTGRES_DB: ${POSTGRES_DB}
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    ports:
      - "5432:5432"
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
      interval: 10s
      timeout: 5s
      retries: 5

  mongo_db:
    image: mongo:5.0
    container_name: mongo_service
    environment:
      MONGO_INITDB_ROOT_USERNAME: ${MONGO_INITDB_ROOT_USERNAME}
      MONGO_INITDB_ROOT_PASSWORD: ${MONGO_INITDB_ROOT_PASSWORD}
      MONGO_INITDB_DATABASE: ${MONGO_DB}
    ports:
      - "27017:27017"
    volumes:
      - mongodb_data:/data/db
    healthcheck:
      test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"]
      interval: 10s
      timeout: 5s
      retries: 5

volumes:
  mysql_data:
  postgres_data:
  mongodb_data:

در این سناریو، سرویس app می‌تواند از طریق mysql_db، pg_db و mongo_db به هر یک از پایگاه‌های داده متصل شود.

با درک این سناریوها و راه‌حل‌ها، می‌توانید با اطمینان بیشتری محیط‌های توسعه خود را با Docker Compose مدیریت کنید و به سرعت مشکلات را شناسایی و برطرف نمایید.

نتیجه‌گیری

در طول این مقاله، ما به طور عمیق به بررسی چگونگی استفاده از Docker Compose برای راه‌اندازی و مدیریت محیط‌های توسعه شامل پایگاه‌های داده مختلف پرداختیم. از پیکربندی MySQL و PostgreSQL رابطه‌ای گرفته تا MongoDB NoSQL، دیدیم که Docker Compose چگونه با استفاده از یک فایل YAML واحد، فرآیند ارکستراسیون چندین کانتینر را ساده‌سازی می‌کند.

مفاهیم کلیدی مانند Services، Volumes برای پایداری داده، و Networks برای ارتباطات بین کانتینری را پوشش دادیم. با نمونه کدهای عملی و تنظیمات دقیق متغیرهای محیطی، نشان دادیم که چگونه می‌توانید به سرعت و با اطمینان، پایگاه‌های داده مورد نیاز خود را در محیط‌های ایزوله Docker راه‌اندازی کنید.

همچنین، با پرداختن به نکات پیشرفته‌ای مانند Health Checks برای اطمینان از آمادگی واقعی سرویس‌ها، مدیریت بهینه متغیرهای محیطی، محدودیت‌های منابع، Docker Compose Profiles برای انعطاف‌پذیری بیشتر و اسکریپت‌های اولیه برای مقداردهی اولیه پایگاه‌های داده، به شما ابزارهایی برای ایجاد محیط‌های توسعه قدرتمندتر و مقاوم‌تر ارائه کردیم. در نهایت، با بحث درباره سناریوهای عملی و راه‌حل‌های رایج برای مشکلات مانند خطاهای اتصال و تداخل پورت‌ها، به شما در مسیر عیب‌یابی و رفع موانع کمک کردیم.

استفاده از Docker Compose در جریان کار توسعه، مزایای بی‌شماری از جمله سازگاری محیطی، تکرارپذیری، ایزوله‌سازی سرویس‌ها و توسعه سریع‌تر را به ارمغان می‌آورد. این ابزار نه تنها فرآیند راه‌اندازی را ساده می‌کند، بلکه اطمینان می‌دهد که تمام اعضای تیم بر روی یک محیط یکسان کار می‌کنند و از خطاهای ناشی از تفاوت‌های سیستمی جلوگیری می‌کند.

همانطور که پروژه‌های شما پیچیده‌تر می‌شوند، ارزش Docker Compose بیش از پیش نمایان خواهد شد. این مهارت نه تنها به شما کمک می‌کند تا کارایی خود را در توسعه محلی افزایش دهید، بلکه شما را برای درک بهتر سیستم‌های ارکستراسیون بزرگ‌تر مانند Docker Swarm یا Kubernetes در محیط‌های تولید آماده می‌سازد. با دانش و نمونه کدهای ارائه شده در این راهنما، اکنون شما مسلح به ابزاری قدرتمند هستید که می‌توانید آن را در هر پروژه توسعه‌ای به کار بگیرید.

“تسلط به برنامه‌نویسی پایتون با هوش مصنوعی: آموزش کدنویسی هوشمند با ChatGPT”

قیمت اصلی 2.290.000 ریال بود.قیمت فعلی 1.590.000 ریال است.

"تسلط به برنامه‌نویسی پایتون با هوش مصنوعی: آموزش کدنویسی هوشمند با ChatGPT"

"با شرکت در این دوره جامع و کاربردی، به راحتی مهارت‌های برنامه‌نویسی پایتون را از سطح مبتدی تا پیشرفته با کمک هوش مصنوعی ChatGPT بیاموزید. این دوره، با بیش از 6 ساعت محتوای آموزشی، شما را قادر می‌سازد تا به سرعت الگوریتم‌های پیچیده را درک کرده و اپلیکیشن‌های هوشمند ایجاد کنید. مناسب برای تمامی سطوح با زیرنویس فارسی حرفه‌ای و امکان دانلود و تماشای آنلاین."

ویژگی‌های کلیدی:

بدون نیاز به تجربه قبلی برنامه‌نویسی

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

۳۰ ٪ تخفیف ویژه برای دانشجویان و دانش آموزان