Docker Compose Extends: استفاده مجدد و ارث‌بری از پیکربندی‌ها

فهرست مطالب

Docker Compose Extends: استفاده مجدد و ارث‌بری از پیکربندی‌ها

در دنیای توسعه نرم‌افزار مدرن، داکر کامپوز (Docker Compose) به ابزاری جدایی‌ناپذیر برای تعریف و اجرای برنامه‌های چند کانتینری تبدیل شده است. این ابزار به توسعه‌دهندگان و تیم‌های DevOps امکان می‌دهد تا تمامی سرویس‌های مورد نیاز یک برنامه را در یک فایل YAML واحد پیکربندی کرده و تنها با یک دستور آن‌ها را راه‌اندازی کنند. اما با افزایش پیچیدگی پروژه‌ها و نیاز به مدیریت پیکربندی‌های مختلف برای محیط‌های توسعه، تست و تولید، چالش‌های جدیدی پدیدار می‌شود: چگونه می‌توان از تکرار کد جلوگیری کرد و پیکربندی‌ها را به صورت کارآمدتر مدیریت نمود؟ اینجاست که قابلیت extends در داکر کامپوز به نجات ما می‌آید.

extends یک ویژگی قدرتمند در داکر کامپوز است که به شما اجازه می‌دهد تا یک سرویس را از یک فایل کامپوز دیگر یا حتی از یک سرویس دیگر در همان فایل گسترش دهید. این قابلیت شبیه به وراثت در برنامه‌نویسی شی‌گرا عمل می‌کند؛ شما می‌توانید یک پیکربندی پایه (base configuration) تعریف کنید و سپس پیکربندی‌های خاص‌تر را بر اساس آن بسازید، تنها با اضافه کردن یا تغییر دادن آنچه که نیاز است. این نه تنها باعث کاهش تکرار کد می‌شود، بلکه مدیریت و نگهداری پیکربندی‌ها را نیز بسیار ساده‌تر و خطاپذیری را کمتر می‌کند.

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

چرا به Docker Compose Extends نیاز داریم؟ چالش‌های تکرار پیکربندی

پیش از غواصی در جزئیات فنی extends، لازم است درک کنیم که این ویژگی چه مشکلاتی را حل می‌کند. در پروژه‌های واقعی، معمولاً با سناریوهای زیر مواجه می‌شویم:

  • پیکربندی‌های محیطی مختلف: یک برنامه ممکن است در محیط توسعه نیاز به پورت‌های خاص، متغیرهای محیطی متفاوت یا نسخه‌های داکر ایمیج خاصی داشته باشد، در حالی که در محیط تولید به پورت‌های دیگر، متغیرهای حساس و ایمیج‌های بهینه‌شده نیاز دارد. مدیریت چندین فایل docker-compose.yaml مجزا برای هر محیط می‌تواند به سرعت منجر به تکرار بخش‌های عمده‌ای از پیکربندی شود.
  • سرویس‌های مشترک بین پروژه‌ها: بسیاری از پروژه‌ها از سرویس‌های پایه‌ای مشابهی مانند دیتابیس (PostgreSQL, MySQL)، کشینگ (Redis) یا صف پیام (RabbitMQ) استفاده می‌کنند. اگر هر پروژه پیکربندی این سرویس‌ها را از ابتدا بنویسد، زمان زیادی هدر رفته و نگهداری آن‌ها دشوار می‌شود.
  • تقسیم مسئولیت‌ها در تیم‌های بزرگ: در تیم‌های بزرگ، ممکن است مسئولیت پیکربندی سرویس‌های پایه بر عهده یک تیم (مثلاً تیم پلتفرم) باشد، در حالی که تیم‌های توسعه‌دهنده برنامه تنها نیاز به استفاده و افزودن جزئیات مربوط به سرویس‌های خود را دارند. extends این جداسازی مسئولیت‌ها را تسهیل می‌کند.
  • کاهش خطاهای انسانی: کپی-پیست کردن پیکربندی‌ها نه تنها خسته‌کننده است، بلکه به طور بالقوه منجر به خطاهای انسانی می‌شود که شناسایی و رفع آن‌ها می‌تواند پرهزینه باشد. با متمرکز کردن پیکربندی‌های پایه، احتمال بروز این خطاها کاهش می‌یابد.

ویژگی extends به ما این امکان را می‌دهد که این چالش‌ها را به طور مؤثری برطرف کنیم. با تعریف پیکربندی‌های پایه و سپس ارث‌بری از آن‌ها، می‌توانیم یک منبع حقیقت واحد برای هر سرویس داشته باشیم و تنها تغییرات لازم را در هر محیط یا پروژه اعمال کنیم.

مبانی Docker Compose Extends: چگونه کار می‌کند؟

extends در داکر کامپوز به شما اجازه می‌دهد تا تعریف یک سرویس را از یک سرویس دیگر که در یک فایل کامپوز جداگانه (یا حتی در همان فایل) تعریف شده است، به ارث ببرید. وقتی از extends استفاده می‌کنید، کامپوز پیکربندی سرویس پایه را بارگذاری کرده و سپس تغییرات یا اضافات تعریف شده در سرویس گسترش‌یافته را بر روی آن اعمال می‌کند.

سینتکس پایه

سینتکس extends نسبتاً ساده است. شما باید دو چیز را مشخص کنید:

  1. مسیر فایل کامپوز پایه: با استفاده از کلید file (اختیاری اگر سرویس پایه در همان فایل باشد).
  2. نام سرویس پایه: با استفاده از کلید service.

به عنوان مثال، فرض کنید ما یک فایل base-compose.yaml برای تعریف یک سرویس پایگاه داده داریم:


# base-compose.yaml
version: '3.8'
services:
  db-base:
    image: postgres:13
    environment:
      POSTGRES_DB: mydb
      POSTGRES_USER: user
      POSTGRES_PASSWORD: password
    volumes:
      - db_data:/var/lib/postgresql/data
    networks:
      - app-net
volumes:
  db_data:
networks:
  app-net:

حالا، در فایل docker-compose.yaml پروژه خود، می‌توانیم این سرویس پایه را گسترش دهیم:


# docker-compose.yaml
version: '3.8'
services:
  app-db:
    extends:
      file: ./base-compose.yaml
      service: db-base
    ports:
      - "5432:5432" # اضافه کردن پورت برای دسترسی از هاست
    environment:
      # اورراید کردن یا افزودن متغیرهای محیطی
      POSTGRES_HOST_AUTH_METHOD: trust

در این مثال:

  • سرویس app-db پیکربندی خود را از سرویس db-base که در فایل base-compose.yaml تعریف شده، به ارث می‌برد.
  • تمامی تنظیمات image، environment (به جز آنچه اورراید شده)، volumes و networks از db-base به app-db منتقل می‌شوند.
  • app-db یک پورت جدید (5432:5432) اضافه می‌کند که در db-base وجود نداشت.
  • app-db متغیر محیطی POSTGRES_HOST_AUTH_METHOD را اضافه می‌کند. اگر متغیری با همین نام در db-base وجود داشت، مقدار جدید جایگزین مقدار قدیمی می‌شد.

قوانین ادغام (Merging Rules)

درک نحوه ادغام پیکربندی‌ها بسیار مهم است:

  • Mapping (دیکشنری‌ها/اشیاء): اگر هر دو سرویس یک کلید از نوع mapping (مانند environment، labels، ports و غیره) را تعریف کنند، مقادیر آن‌ها به صورت بازگشتی ادغام می‌شوند. به این معنی که کلیدهای جدید اضافه می‌شوند و کلیدهای موجود در سرویس گسترش‌یافته، مقادیر کلیدهای متناظر در سرویس پایه را رونویسی می‌کنند.
  • List (آرایه‌ها): برای لیست‌ها (مانند volumes، ports، expose، external_links، dns و cap_add/cap_drop)، لیست سرویس گسترش‌یافته به انتهای لیست سرویس پایه اضافه می‌شود. این بدان معناست که شما نمی‌توانید عناصر خاصی از لیست پایه را به طور مستقیم حذف کنید، بلکه می‌توانید موارد جدیدی را اضافه کنید. (نکته: برای ports، اگر پورتی در سرویس گسترش یافته با پورتی در سرویس پایه همپوشانی داشته باشد، پورت جدید جایگزین پورت پایه می‌شود).
  • Scalar (مقادیر ساده): برای کلیدهایی مانند image، command، entrypoint و container_name، مقدار تعریف شده در سرویس گسترش‌یافته به طور کامل مقدار سرویس پایه را رونویسی می‌کند.

این قوانین ادغام باعث می‌شود که extends هم برای افزودن پیکربندی‌ها و هم برای اورراید کردن آن‌ها بسیار انعطاف‌پذیر باشد.

سناریوهای پیشرفته استفاده از Docker Compose Extends

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

1. پیکربندی پایه مرکزی برای سرویس‌ها

فرض کنید در سازمان شما، سرویس‌های مشترکی مانند PostgreSQL، Redis، Nginx و غیره با پیکربندی‌های استاندارد خاصی استفاده می‌شوند. می‌توانید یک فایل کامپوز مرکزی (مثلاً common-services.yaml) ایجاد کنید که این سرویس‌های پایه را تعریف می‌کند.


# common-services.yaml
version: '3.8'
services:
  postgres-base:
    image: postgres:14-alpine
    environment:
      POSTGRES_USER: admin
      POSTGRES_PASSWORD: secretpassword
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U admin"]
      interval: 5s
      timeout: 5s
      retries: 5
    networks:
      - backend

  redis-base:
    image: redis:6-alpine
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      timeout: 3s
      retries: 5
    networks:
      - backend

  nginx-proxy-base:
    image: nginx:latest
    volumes:
      - ./nginx/conf.d:/etc/nginx/conf.d:ro
    networks:
      - frontend
      - backend
    depends_on:
      - some-app # placeholder, to be overridden
networks:
  backend:
    name: common_backend_net
  frontend:
    name: common_frontend_net

سپس، در فایل docker-compose.yaml هر پروژه، می‌توانید این سرویس‌ها را گسترش دهید و جزئیات خاص پروژه را اضافه کنید:


# project-a/docker-compose.yaml
version: '3.8'
services:
  db:
    extends:
      file: ../common-services.yaml # فرض بر این است که common-services در یک سطح بالاتر است
      service: postgres-base
    environment:
      POSTGRES_DB: project_a_db # اورراید نام دیتابیس
    volumes:
      - db_data_project_a:/var/lib/postgresql/data # افزودن ولوم اختصاصی
    ports:
      - "5432:5432" # تنها در محیط توسعه نیاز است

  cache:
    extends:
      file: ../common-services.yaml
      service: redis-base
    command: redis-server --appendonly yes # افزودن کامند خاص
    ports:
      - "6379:6379"

  web-app:
    build: .
    ports:
      - "8000:8000"
    environment:
      DATABASE_URL: postgres://admin:secretpassword@db:5432/project_a_db
    depends_on:
      - db
      - cache
    networks:
      - backend

volumes:
  db_data_project_a:

networks:
  backend:
    name: project_a_backend_net # نام شبکه در پروژه خاص

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

2. مدیریت پیکربندی‌های محیطی (توسعه، تست، تولید)

یکی از قوی‌ترین کاربردهای extends مدیریت آسان پیکربندی‌های خاص محیط است. به جای داشتن چندین فایل کامپوز کاملاً مجزا (مانند docker-compose.dev.yaml، docker-compose.prod.yaml)، می‌توانید یک فایل پایه داشته باشید و فایل‌های محیطی را برای اورراید کردن مقادیر خاص ایجاد کنید.


# docker-compose.base.yaml (پیکربندی پایه)
version: '3.8'
services:
  web:
    image: myapp:latest # در تولید این ایمیج استفاده شود
    build:
      context: .
      dockerfile: Dockerfile # در توسعه ممکن است نیاز به build داشته باشیم
    environment:
      APP_ENV: production
      DATABASE_HOST: production-db
    volumes:
      - app_data:/app/data
    networks:
      - app-net

  db:
    image: postgres:13-alpine
    environment:
      POSTGRES_DB: production_db
      POSTGRES_USER: prod_user
      POSTGRES_PASSWORD: prod_password
    volumes:
      - prod_db_data:/var/lib/postgresql/data
    networks:
      - app-net

networks:
  app-net:
volumes:
  app_data:
  prod_db_data:

# docker-compose.dev.yaml (اوررایدهای محیط توسعه)
version: '3.8'
services:
  web:
    extends:
      file: docker-compose.base.yaml
      service: web
    image: myapp:dev # استفاده از ایمیج توسعه
    build:
      context: . # حتما باید بیلد شود
    ports:
      - "80:80"
      - "443:443"
    environment:
      APP_ENV: development
      DATABASE_HOST: dev-db # اشاره به دیتابیس توسعه
    volumes:
      - ./src:/app/src:cached # ماونت کد منبع برای توسعه
    # build is now the default behavior because it is defined in dev, overriding image from base

  db:
    extends:
      file: docker-compose.base.yaml
      service: db
    image: postgres:13 # شاید در dev از یک نسخه کامل‌تر استفاده کنیم
    ports:
      - "5432:5432" # برای دسترسی مستقیم در توسعه
    environment:
      POSTGRES_DB: dev_db
      POSTGRES_USER: dev_user
      POSTGRES_PASSWORD: dev_password
    volumes:
      - dev_db_data:/var/lib/postgresql/data

volumes:
  dev_db_data: # ولوم جداگانه برای dev

برای اجرای محیط توسعه، کافیست دستور زیر را اجرا کنید:


docker compose -f docker-compose.base.yaml -f docker-compose.dev.yaml up

این روش استفاده از چندین فایل کامپوز (که extends را درون خود استفاده می‌کنند) بسیار قدرتمند است. فایل docker-compose.base.yaml حاوی تمام تنظیمات مشترک است و فایل docker-compose.dev.yaml (یا docker-compose.prod.yaml) تنها تغییرات و اضافات مخصوص آن محیط را شامل می‌شود. این رویکرد را می‌توان با استفاده از متغیرهای محیطی نیز ترکیب کرد تا فایل‌های کامپوز حتی منعطف‌تر شوند.

3. ارث‌بری زنجیره‌ای (Chaining Extends)

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


# docker-compose.tier0.yaml (پایه‌ترین پیکربندی)
version: '3.8'
services:
  app-base:
    image: ubuntu:latest
    command: /bin/bash -c "while true; do echo Hello from base; sleep 1; done"
    environment:
      BASE_VAR: "Hello"
    networks:
      - base-net

networks:
  base-net:

# docker-compose.tier1.yaml (گسترش از tier0)
version: '3.8'
services:
  web-app-base:
    extends:
      file: docker-compose.tier0.yaml
      service: app-base
    image: my-web-app:1.0 # اورراید image
    environment:
      TIER1_VAR: "World"
      BASE_VAR: "Goodbye" # اورراید BASE_VAR
    ports:
      - "8080:80"

  api-app-base:
    extends:
      file: docker-compose.tier0.yaml
      service: app-base
    image: my-api-app:1.0
    environment:
      TIER1_VAR: "API"
      BASE_VAR: "API Base"
    ports:
      - "8000:8000"

# docker-compose.project.yaml (گسترش از tier1)
version: '3.8'
services:
  project-web:
    extends:
      file: docker-compose.tier1.yaml
      service: web-app-base
    environment:
      PROJECT_VAR: "Project A Web"
      TIER1_VAR: "Project A Web Override" # اورراید TIER1_VAR
    volumes:
      - ./web-code:/app/code

  project-api:
    extends:
      file: docker-compose.tier1.yaml
      service: api-app-base
    environment:
      PROJECT_VAR: "Project A API"
    ports:
      - "8001:8000" # اورراید پورت
    volumes:
      - ./api-code:/app/api

در این سناریو:

  • app-base در tier0 پایه را تعریف می‌کند.
  • web-app-base و api-app-base در tier1 از app-base ارث می‌برند و تغییرات خاص خود را اعمال می‌کنند.
  • project-web و project-api در project از سرویس‌های tier1 ارث می‌برند و سفارشی‌سازی‌های نهایی را انجام می‌دهند.

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

4. استفاده از extends برای تنها بخشی از پیکربندی (Partial Extension)

در برخی موارد، ممکن است نیاز باشد که تنها یک بخش خاص از پیکربندی یک سرویس را به ارث ببرید. داکر کامپوز extends به صورت سرویس-محور کار می‌کند، اما شما می‌توانید با تعریف سرویس‌های کوچک و تخصصی در فایل پایه، این کار را شبیه‌سازی کنید.


# common-configs.yaml
version: '3.8'
services:
  default-logging:
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "5"

  default-network:
    networks:
      - app-network

networks:
  app-network:

# docker-compose.yaml
version: '3.8'
services:
  my-app:
    image: my-app:latest
    extends:
      file: common-configs.yaml
      service: default-logging # به ارث بردن تنظیمات لاگینگ
    ports:
      - "80:80"
    environment:
      APP_CONFIG: production
    networks:
      - app-network # نیاز است network را هم جداگانه تعریف کنیم

توجه داشته باشید که در مثال بالا، سرویس my-app از default-logging ارث می‌برد. اما این به این معنی نیست که تنها بخش logging را به ارث می‌برد. بلکه تمام پیکربندی default-logging (که در اینجا فقط logging است) به my-app اضافه می‌شود. اگر default-logging شامل image یا command بود، آن‌ها نیز به my-app اعمال می‌شدند. برای اعمال شبکه نیز می‌توانیم یا آن را مستقیماً در my-app اضافه کنیم یا یک سرویس دیگر در common-configs.yaml برای آن تعریف کنیم و با استفاده از دستور docker compose -f آن را با هم ادغام کنیم.

این مثال نشان می‌دهد که extends بر اساس سرویس‌ها عمل می‌کند، نه بخش‌های کوچک پیکربندی. برای ترکیب دقیق‌تر بخش‌های کوچک، استفاده از قابلیت چند فایل docker compose -f و متغیرهای محیطی می‌تواند گزینه‌های بهتری باشد.

مقایسه Extends با سایر روش‌های استفاده مجدد

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

1. متغیرهای محیطی (Environment Variables)

متغیرهای محیطی یکی از رایج‌ترین روش‌ها برای پویاسازی پیکربندی‌ها در داکر کامپوز هستند. شما می‌توانید مقادیر را در فایل .env تعریف کنید و سپس در فایل docker-compose.yaml از آن‌ها استفاده کنید.


# .env
POSTGRES_VERSION=13
APP_PORT=8080

# docker-compose.yaml
version: '3.8'
services:
  db:
    image: postgres:${POSTGRES_VERSION}
    environment:
      POSTGRES_DB: ${DB_NAME:-mydb} # مقدار پیش‌فرض در صورت عدم تعریف DB_NAME
    ports:
      - "${APP_PORT}:80"

مزایا:

  • سادگی: بسیار آسان برای استفاده و درک.
  • انعطاف‌پذیری: امکان تغییر مقادیر بدون تغییر فایل کامپوز.
  • پویاسازی: ایده‌آل برای مقادیر کوچک و متغیر (مانند پورت‌ها، نام دیتابیس، پسوردها).

معایب:

  • عدم قابلیت ارث‌بری ساختار: نمی‌تواند ساختارهای پیچیده YAML (مانند کلیدهای nested) را به ارث ببرد یا اضافه کند. فقط جایگزین مقادیر Scalar می‌شود.
  • پیچیدگی برای پیکربندی‌های بزرگ: برای تغییر بخش‌های بزرگ‌تر پیکربندی (مانند افزودن یک ولوم یا شبکه) نامناسب است.
  • خطاپذیری: اگر متغیری تعریف نشده باشد و مقدار پیش‌فرض نداشته باشد، ممکن است با خطا مواجه شوید.

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

2. انکرهای YAML (YAML Anchors)

انکرهای YAML به شما اجازه می‌دهند تا بخشی از یک فایل YAML را “لنگر” (anchor) کنید و سپس در جاهای دیگر همان فایل به آن “رجوع” (alias) کنید. این برای جلوگیری از تکرار در یک فایل مفید است.


# docker-compose.yaml
version: '3.8'
x-common-labels: &common-labels
  labels:
    env: development
    project: myapp

services:
  web:
    image: myapp:latest
    <<: *common-labels # استفاده از انکر
    ports:
      - "80:80"

  api:
    image: myapi:latest
    <<: *common-labels # استفاده از انکر
    ports:
      - "8000:8000"

مزایا:

  • کاهش تکرار: کد DRY (Don't Repeat Yourself) را در یک فایل YAML پیاده‌سازی می‌کند.
  • خوانایی: می‌تواند خوانایی فایل‌های کامپوز طولانی را بهبود بخشد.

معایب:

  • محدود به یک فایل: نمی‌توانید از انکرهای تعریف شده در یک فایل YAML در فایل YAML دیگری استفاده کنید. این بزرگترین محدودیت آن است.
  • عدم قابلیت اورراید آسان: اورراید کردن مقادیر جزئی از یک انکر می‌تواند پیچیده باشد.
  • محدودیت‌های ادغام: رفتارش در ادغام لیست‌ها یا دیکشنری‌ها ممکن است کمتر بصری باشد.

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

3. استفاده از چند فایل کامپوز (Multiple Compose Files)

این روش شامل استفاده از چندین فایل docker-compose.yaml است که با دستور docker compose -f file1.yaml -f file2.yaml up با هم ادغام می‌شوند. داکر کامپوز به طور هوشمندانه پیکربندی‌های سرویس‌ها را از همه فایل‌ها ادغام می‌کند. این روش مکمل extends است.


# docker-compose.base.yaml
version: '3.8'
services:
  web:
    image: myapp:latest
    networks:
      - app-net

  db:
    image: postgres:13
    networks:
      - app-net

networks:
  app-net:

# docker-compose.dev.yaml
version: '3.8'
services:
  web:
    build: .
    ports:
      - "80:80"
    volumes:
      - ./src:/app/src
    environment:
      APP_ENV: development

  db:
    ports:
      - "5432:5432"
    environment:
      POSTGRES_DB: dev_db

برای اجرای محیط توسعه:


docker compose -f docker-compose.base.yaml -f docker-compose.dev.yaml up

مزایا:

  • سازماندهی عالی: امکان جداسازی پیکربندی‌های پایه از پیکربندی‌های خاص محیط.
  • ادغام قدرتمند: داکر کامپوز به طور خودکار سرویس‌ها را با نام مشابه در فایل‌های مختلف ادغام می‌کند. این ادغام از قوانین مشابه extends پیروی می‌کند.
  • انعطاف‌پذیری: می‌توان فایل‌های مختلف را بسته به نیاز به صورت پویا ترکیب کرد.

معایب:

  • عدم وراثت سرویس به سرویس: این روش سرویس‌های کاملاً جداگانه را ادغام می‌کند. شما نمی‌توانید یک سرویس را در یک فایل از سرویس دیگری در همان فایل (یا فایل دیگر) که نامش یکسان نیست، به ارث ببرید. extends به شما اجازه می‌دهد که سرویس my-db را از base-db ارث ببرید.

نتیجه‌گیری: استفاده از چندین فایل کامپوز یک روش قدرتمند و بسیار توصیه شده برای مدیریت پیکربندی‌ها است. extends با این روش هم‌افزایی دارد. شما می‌توانید یک فایل پایه را با استفاده از extends به صورت داخلی ساختاربندی کنید و سپس از چندین فایل برای اوررایدهای محیطی استفاده کنید. این ترکیب بهترین هر دو جهان را ارائه می‌دهد.

به طور خلاصه، در حالی که متغیرهای محیطی برای مقادیر کوچک و متغیر و انکرهای YAML برای استفاده مجدد در یک فایل واحد مناسب هستند، extends و استفاده از چندین فایل کامپوز ابزارهای اصلی برای مدیریت استفاده مجدد و ارث‌بری پیکربندی‌های پیچیده‌تر در پروژه‌های داکر کامپوز هستند. extends برای ارث‌بری سرویس-به-سرویس و چندین فایل برای ترکیب پیکربندی‌های مختلف محیطی به کار می‌روند.

محدودیت‌ها و نکات عیب‌یابی در استفاده از Extends

با وجود قدرت بالای extends، آگاهی از محدودیت‌ها و چالش‌های احتمالی آن برای استفاده مؤثر و جلوگیری از سردرگمی ضروری است.

1. محدودیت‌های نام‌گذاری سرویس‌ها

هنگام استفاده از extends، نام سرویسی که گسترش داده می‌شود (سرویس "فرزند") می‌تواند با نام سرویس پایه (سرویس "والد") متفاوت باشد. این یک مزیت بزرگ است، زیرا شما می‌توانید یک سرویس پایه عمومی داشته باشید و سپس آن را در پروژه‌های مختلف با نام‌های خاص خود استفاده کنید. اما این می‌تواند باعث سردرگمی شود اگر به وضوح ندانید کدام سرویس از کجا ارث می‌برد.

نکته: همیشه سعی کنید نام‌گذاری سرویس‌ها را واضح نگه دارید و از کامنت‌ها برای توضیح سلسله مراتب ارث‌بری استفاده کنید.

2. عدم امکان حذف یا جایگزینی کامل عناصر لیست

همانطور که قبلاً ذکر شد، لیست‌ها (مانند volumes، ports، expose، networks در سطح سرویس) به صورت ادغام‌شده عمل می‌کنند؛ یعنی عناصر جدید به لیست پایه اضافه می‌شوند. شما نمی‌توانید به راحتی یک آیتم خاص را از لیست پایه حذف کنید یا آن را با یک لیست کاملاً جدید جایگزین کنید (مگر در موارد خاص مثل پورت‌ها که اگر دقیقا یکسان باشند، جایگزین می‌شوند). این می‌تواند یک محدودیت باشد اگر بخواهید پیکربندی لیست را به طور کامل در سرویس فرزند بازنویسی کنید.

راه‌حل (Workaround): اگر نیاز به جایگزینی کامل یک لیست دارید، باید تمام عناصر لیست جدید را در سرویس فرزند تعریف کنید و از یک لیست خالی در سرویس پایه استفاده کنید (یا آن را تعریف نکنید) اگر می‌خواهید کاملاً کنترل را به فرزند بدهید. این یک "hack" است و ایده‌آل نیست، اما برای سناریوهای خاص کارساز است. راه حل بهتر این است که از قابلیت چند فایل docker compose -f استفاده کنید که در آن یک سرویس در یک فایل کاملاً سرویس دیگری با همان نام را در فایل قبلی رونویسی می‌کند (به جز مواردی که قوانین ادغام apply می‌شوند).

3. پیچیدگی مسیردهی فایل (File Path Resolution)

هنگامی که از extends.file استفاده می‌کنید، مسیر فایل پایه (که سرویس از آن گسترش می‌یابد) می‌تواند نسبی یا مطلق باشد.

  • مسیرهای نسبی: همیشه نسبت به فایل کامپوزی که حاوی دستور extends است، حل می‌شوند. این رایج‌ترین و توصیه شده‌ترین روش است.
  • مسیرهای مطلق: ممکن است در محیط‌های مختلف به مشکلات مسیردهی منجر شوند، به خصوص اگر فایل‌های کامپوز در ساختارهای دایرکتوری متفاوتی قرار گیرند.

نکته: همیشه از مسیرهای نسبی استفاده کنید مگر اینکه دلیل محکمی برای استفاده از مسیرهای مطلق داشته باشید و از سازگاری آن‌ها در محیط‌های مختلف اطمینان داشته باشید.

4. مسائل مربوط به Build Context

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

نکته: در مورد build، بهتر است build context و Dockerfile را در همان فایل کامپوز (یا فایل محیطی) که واقعاً نیاز به ساخت ایمیج دارد، تعریف کنید و از ارث‌بری build context از طریق extends خودداری کنید یا حداقل با دقت آن را مدیریت کنید.

5. اشکال‌زدایی (Debugging) پیکربندی‌های extends شده

وقتی از چندین سطح extends استفاده می‌کنید، می‌تواند دشوار باشد که دقیقاً بفهمید پیکربندی نهایی یک سرویس چگونه خواهد بود. قوانین ادغام ممکن است همیشه واضح نباشند، به خصوص برای کاربران جدید.

نکته: برای اشکال‌زدایی، می‌توانید از دستور docker compose config استفاده کنید. این دستور خروجی نهایی و ادغام‌شده پیکربندی را نشان می‌دهد و به شما کمک می‌کند تا بررسی کنید که آیا extends به درستی عمل کرده است یا خیر.


docker compose -f docker-compose.project.yaml config

این دستور تمام فایل‌های کامپوز را (از جمله آن‌هایی که از طریق extends.file ارجاع داده شده‌اند) بارگذاری و ادغام می‌کند و پیکربندی نهایی را نمایش می‌دهد. این یک ابزار بسیار ارزشمند برای تأیید رفتار extends است.

6. عدم امکان Extends کردن کلیدهای سطح بالا (Top-Level Keys)

extends فقط می‌تواند برای سرویس‌ها (services) استفاده شود. شما نمی‌توانید کلیدهای سطح بالا مانند volumes، networks یا configs را از یک فایل دیگر به صورت مستقیم extends کنید. این کلیدها باید به صورت جداگانه در هر فایل کامپوز تعریف شوند یا با استفاده از قابلیت چندین فایل docker compose -f ادغام شوند.

نکته: برای اشتراک‌گذاری volumes و networks بین فایل‌ها، بهتر است از نام‌گذاری صریح و یکسان در تمام فایل‌ها استفاده کنید و داکر کامپوز به طور خودکار آن‌ها را با هم مرتبط می‌کند. برای شبکه‌ها، می‌توانید از شبکه‌های خارجی (external: true) برای ارجاع به شبکه‌هایی که از قبل وجود دارند استفاده کنید.

بهترین شیوه‌ها برای استفاده مؤثر از Docker Compose Extends

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

1. ساختاردهی منطقی فایل‌ها

  • فایل‌های پایه مجزا: پیکربندی‌های پایه (مثل دیتابیس، کش، وب‌سرور عمومی) را در فایل‌های کامپوز جداگانه نگهداری کنید. مثلاً docker-compose.db.yaml، docker-compose.redis.yaml یا یک common-services.yaml.
  • فایل‌های محیطی: از فایل‌های کامپوز جداگانه برای هر محیط (docker-compose.dev.yaml، docker-compose.prod.yaml) استفاده کنید که پیکربندی‌های پایه را گسترش داده و اورراید می‌کنند.
  • فایل پروژه اصلی: فایل docker-compose.yaml اصلی پروژه باید به عنوان نقطه ورود عمل کند و یا از extends به صورت داخلی استفاده کند یا با سایر فایل‌های محیطی ترکیب شود.

2. استفاده از نام‌گذاری واضح و مختصر

  • نام سرویس‌های پایه باید واضح باشد (مثلاً postgres-base، web-app-template).
  • نام سرویس‌های گسترش‌یافته باید نشان‌دهنده نقش آن‌ها در پروژه باشد (مثلاً db، web-app).

3. مستندسازی دقیق

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

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

متغیرهای محیطی و فایل .env هنوز هم ابزارهای قدرتمندی برای مدیریت مقادیر حساس یا متغیر هستند. extends را با .env ترکیب کنید: از extends برای ارث‌بری ساختارها و از .env برای پر کردن مقادیر متغیر استفاده کنید.

5. آزمایش و اعتبارسنجی مداوم

همانطور که قبلاً ذکر شد، از docker compose config به طور منظم برای بررسی پیکربندی نهایی استفاده کنید، به خصوص پس از ایجاد تغییرات بزرگ یا اضافه کردن سطوح جدید ارث‌بری. این کار به شما کمک می‌کند تا هرگونه مشکل ادغام یا اورراید ناخواسته را به سرعت شناسایی کنید.

6. اجتناب از ارث‌بری بیش از حد پیچیده

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

7. استفاده از شبکه‌های خارجی (External Networks)

برای شبکه‌ها، به جای تلاش برای ارث‌بری پیچیده از طریق extends (که برای کلیدهای سطح بالا کار نمی‌کند)، از شبکه‌های خارجی استفاده کنید. این به شما امکان می‌دهد شبکه‌ها را یک بار تعریف کرده و سپس در چندین فایل کامپوز به آن‌ها ارجاع دهید.


# docker-compose.yaml
version: '3.8'
services:
  my-app:
    image: my-app:latest
    networks:
      - my-shared-network # اتصال به شبکه مشترک

networks:
  my-shared-network:
    external: true # به یک شبکه که قبلاً ایجاد شده ارجاع می‌دهد

شما باید این شبکه را قبلاً با docker network create my-shared-network ایجاد کرده باشید.

نتیجه‌گیری: Extends، ابزاری قدرتمند برای مدیریت پیکربندی‌های داکر کامپوز

قابلیت extends در داکر کامپوز یک ویژگی ضروری برای هر تیم توسعه‌دهنده یا مهندس DevOps است که با پروژه‌های چند کانتینری در مقیاس بزرگ کار می‌کند. این ابزار با فراهم کردن مکانیزمی قدرتمند برای استفاده مجدد و ارث‌بری از پیکربندی‌های سرویس، به طور چشمگیری به کاهش تکرار کد، بهبود قابلیت نگهداری و افزایش خوانایی فایل‌های کامپوز کمک می‌کند.

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

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

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

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

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

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

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

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

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

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

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