"मेरी मशीन पर चलता है" अक्सर इसका मतलब है कि लैपटॉप पर उत्पादन जैसा प्रोसेस ग्राफ़ नहीं चल रहा। पोस्टग्रेस का अलग संस्करण। रेडिस ही नहीं। एपीआई डेटाबेस के कनेक्शन स्वीकार करने से पहले ही स्टार्ट हो जाती है। शेल प्रोफ़ाइल में एनव वेरिएबल जिन्हें सीआई कभी नहीं देखता।

डॉकर कंपोज़ स्थानीय वातावरण को कुबेरनेट्स या ईसीएस जैसा एक जैसा नहीं बना देगा। लेकिन इतना क़रीब ज़रूर ला सकता है कि स्टार्टअप क्रम, कॉन्फ़िग और निर्भरताओं की हेल्थ हर डेवलपर मशीन और ज़्यादातर सीआई जॉब पर एक जैसी रहे। "काफ़ी क़रीब" ही लक्ष्य है।


"काफ़ी क़रीब" का मतलब

लैपटॉप पर प्रोडक्शन रेप्लिका, सर्विस मेश या मल्टी-एजेड नेटवर्क की ज़रूरत नहीं। ज़रूरत है:

१. वही सेवा सेट जिसे आप सच में चलाते हैं (एपीआई, डेटाबेस, कैश, वर्कर, शायद मेल कैचर)। २. स्टेटफुल निर्भरताओं के वही मेजर संस्करण (अगर प्रोड में पोस्टग्रेस १६ है तो लोकल भी १६)। ३. कॉन्फ़िग उसी तरीके से इंजेक्ट (एनव फ़ाइलें / कंपोज़ का environment, शेल के जादुई एक्सपोर्ट नहीं)। ४. रेडीनेस का इंतज़ार करने वाला स्टार्टअप, सिर्फ़ कंटेनर स्टार्ट नहीं। ५. पुनः आरंभ पर टिकने वाला डेटा जब चाहिए, और साफ़ वाइप जब नहीं चाहिए।

ये पाँच बातें हों तो "केवल स्टेजिंग में टूटता है" वाले ज़्यादातर बग पहले दिन दिख जाते हैं।


एक व्यावहारिक कंपोज़ स्केच

न्यूनतम एपीआई + पोस्टग्रेस + रेडिस स्टैक। इमेज टैग प्रोड से मिलाएँ।

# docker-compose.yml
services:
  api:
    build:
      context: .
      dockerfile: Dockerfile
      target: development   # multi-stage: keep a thin prod stage for real deploys
    ports:
      - "8080:8080"
    env_file:
      - .env.example
      - .env.local          # gitignored; overrides example
    environment:
      DATABASE_URL: postgres://app:app@db:5432/app
      REDIS_URL: redis://redis:6379/0
      APP_ENV: local
    volumes:
      - ./src:/app/src:cached
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_healthy
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
      interval: 10s
      timeout: 3s
      retries: 5
      start_period: 20s

  db:
    image: postgres:16.4-alpine
    environment:
      POSTGRES_USER: app
      POSTGRES_PASSWORD: app
      POSTGRES_DB: app
    ports:
      - "5432:5432"        # optional: host tools / GUI clients
    volumes:
      - pgdata:/var/lib/postgresql/data
      - ./db/init:/docker-entrypoint-initdb.d:ro
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U app -d app"]
      interval: 5s
      timeout: 3s
      retries: 10

  redis:
    image: redis:7.2-alpine
    ports:
      - "6379:6379"
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      timeout: 2s
      retries: 10

volumes:
  pgdata:

चलाएँ:

docker compose up --build
# or detached:
docker compose up -d --build

कंपोज़ प्रोजेक्ट नाम डिफ़ॉल्ट में डायरेक्टरी नाम होता है। अगर एक मशीन पर कई चेकआउट हों तो फ़ाइल के ऊपर name: या COMPOSE_PROJECT_NAME से पिन करें।


सेवाएँ: असली ग्राफ़ मॉडल करें, हर रेप्लिका नहीं

उस रिक्वेस्ट पथ के रोल लिखें जो प्रोड में मायने रखते हैं। लोकल पर एक एपीआई कंटेनर काफ़ी है भले प्रोड में आठ रेप्लिका हों। एक पोस्टग्रेस काफ़ी। एक रेडिस काफ़ी।

आमतौर पर कंपोज़ में रखें:

  • ऐप प्रोसेस (या प्रोसेस: एपीआई + वर्कर + शेड्यूलर अगर अलग बाइनरी हों)।
  • मुख्य डेटास्टोर।
  • कैश / क्यू ब्रोकर जिसे कोड सच में कॉल करता है।
  • वे साइडकार जिन पर शुद्धता निर्भर है (ओपनटेलीमेट्री कलेक्टर स्टब, एस३ जैसी एपीआई के लिए मिनआईओ, एसएमटीपी के लिए मेलपिट)।

आमतौर पर रखें:

  • पूरा सर्विस मेश।
  • प्रोड लोड बैलेंसर।
  • ऑब्ज़र्वेबिलिटी बैकएंड जिन्हें फ़ीचर काम में कभी क्वेरी नहीं करते (बाद में वैकल्पिक ओवरराइड फ़ाइल से जोड़ें)।

वैकल्पिक टूलिंग दूसरी फ़ाइल में रखें ताकि डिफ़ॉल्ट स्टैक तेज़ रहे:

docker compose -f docker-compose.yml -f docker-compose.tools.yml up

वॉल्यूम: कोड हॉट-रिलोड बनाम टिकाऊ डेटा

दो अलग काम:

वॉल्यूम प्रकार उपयोग क्या compose down के बाद बचता है?
बाइंड माउंट (./src:/app/src) बिना रीबिल्ड कोड संपादन लागू नहीं (होस्ट फ़ाइलें)
नेम्ड वॉल्यूम (pgdata:) डेटाबेस फ़ाइलें, कैश हाँ (down -v तक)
एनॉनिमस वॉल्यूम शायद ही जानबूझकर आसानी से खो जाता है

बाइंड माउंट स्रोत कोड के लिए, node_modules या संकलित आउटपुट के लिए नहीं अगर कंटेनर ओएस होस्ट से अलग हो। होस्ट का node_modules लिनक्स कंटेनर में माउंट करना टूटे नेटिव मॉड्यूल का क्लासिक स्रोत है।

नेम्ड वॉल्यूम पोस्टग्रेस/माईएसक्यूएल/मोंगो डेटा के लिए। बिना इनके हर down + रिक्रिएट लोकल स्टेट मिटा देता है। इनके साथ स्कीमा ड्रिफ्ट जमा होता है, इसलिए वाइप पथ दस्तावेज़ित रखें:

docker compose down -v   # removes named volumes declared in this project

/docker-entrypoint-initdb.d के इनिट स्क्रिप्ट केवल पहली बार खाली डेटा डायरेक्टरी पर चलते हैं। इनिट एसक्यूएल बदलने से मौजूदा वॉल्यूम पर वे दोबारा नहीं चलते। माइग्रेशन ऐप में रखें (या वन-शॉट migrate सेवा में), सिर्फ़ इनिट स्क्रिप्ट में नहीं।


एनवायरनमेंट: लोकल, सीआई और प्रोड आकार की एक कहानी

बुरा पैटर्न: सीक्रेट और यूआरएल सिर्फ़ आपके शेल में, रीडमी कहता है "ये बारह वेरिएबल एक्सपोर्ट करें", और आधी टीम के पास पुराने मान।

बेहतर पैटर्न:

१. .env.example कमिटेड: कुंजियाँ, डमी मान, आवश्यक बनाम वैकल्पिक की टिप्पणियाँ। २. .env.local (या .env) गिटइग्नोर्ड: असली लोकल ओवरराइड। ३. कंपोज़ environment: उन मानों के लिए जो नेटवर्क टोपोलॉजी से तय हैं (db होस्टनेम, आंतरिक यूआरएल)। ४. कभी नहीं कंपोज़ फ़ाइलों में प्रोडक्शन क्रेडेंशियल कमिट करें।

कंपोज़ प्रोजेक्ट .env को यामल में वेरिएबल सबस्टिट्यूशन के लिए अपने आप लोड करता है (${POSTGRES_PASSWORD})। यह सेवा पर env_file: से अलग है, जो वेरिएबल कंटेनर के अंदर डालता है। दोनों मिलाना आम वजह है: "प्रोसेस में वेरिएबल खाली है पर होस्ट पर सेट है।"

# substitution in compose (host-side .env)
services:
  db:
    environment:
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-app}
# injection into the container
services:
  api:
    env_file:
      - .env.local

नेटवर्क के अंदर होस्टनेम सेवा नाम रखें (db, redis), localhost नहीं। कंटेनर के अंदर localhost वही कंटेनर है। होस्ट ब्राउज़र या मैक पर psql से localhost:5432 तभी चलता है जब ports: मैपिंग हो।


हेल्थचेक और depends_on: रेडी का इंतज़ार, स्टार्टेड का नहीं

बिना कंडीशन वाले depends_on केवल यह देखते हैं कि निर्भर कंटेनर स्टार्ट हुआ। पोस्टग्रेस अभी भी इनिट में हो सकता है। एपीआई connection refused पर क्रैश-लूप करेगी, कभी-कभी ठीक होकर रेस छिपा देगी।

कंपोज़ वी२ शैली:

depends_on:
  db:
    condition: service_healthy

यह तभी काम करता है जब db में असली healthcheck हो। सामान्य exit 0 की जगह डेटाबेस की अपनी रेडीनेस प्रोब चुनें (pg_isready, mysqladmin ping, रेडिस PING)।

एपीआई के लिए /health जो प्रोसेस अप जाँचे, ऑर्केस्ट्रेटर के लिए ठीक है। /ready जो डेटाबेस/रेडिस भी पिंग करे बेहतर है जब आप चाहें कि कंपोज़ (या बाद में कुबेरनेट्स) ट्रैफ़िक तब तक रोके जब तक निर्भरताएँ जवाब दें। हेल्थचेक कमांड इमेज में उपलब्ध हो (curl, wget, या डेव स्टेज में छोटा बाइनरी)।

start_period विफलताएँ गिनने से पहले बूट का समय देता है। बिना इसके धीमी जेवीएम और कोल्ड माइग्रेशन "अनहेल्दी" और फ्लेकी लगते हैं।


आम गलतियाँ (और सुधार)

१. कंटेनरों के अंदर localhost

लक्षण: होस्ट पर psql चलता है पर एपीआई पोस्टग्रेस तक नहीं पहुँचती।
सुधार: कंपोज़ नेटवर्क पर सेवा डीएनएस नाम; localhost होस्ट टूल के लिए रखें।

२. हेल्थचेक नहीं, सिर्फ़ depends_on

लक्षण: बीच-बीच में बूट फेल, धीमी मशीन और सीआई पर और बुरा।
सुधार: service_healthy + असली रेडीनेस कमांड।

३. लगभग सब कुछ बाइंड-माउंट

लक्षण: परमिशन त्रुटियाँ, गलत आर्क बाइनरी, गंदा होस्ट node_modules
सुधार: सिर्फ़ स्रोत माउंट करें; निर्भरताएँ इमेज में या कंटेनर मॉड्यूल वाले नेम्ड वॉल्यूम में इंस्टॉल करें।

४. रोज़ के कोडिंग के लिए प्रोड इमेज

लक्षण: एक पंक्ति बदलने पर २ मिनट का रीबिल्ड।
सुधार: मल्टी-स्टेज डॉकरफ़ाइल: development टारगेट (हॉट रिलोड, डीबग) और पतला production टारगेट सीआई/सीडी के लिए। कंपोज़ target: development बिल्ड करे; डिप्लॉय पाइपलाइन अंतिम स्टेज।

५. अलग-अलग संस्करण

लक्षण: लोकल पोस्टग्रेस १४ पर एसक्यूएल ठीक, प्रोड १६ पर फेल (या उलटा)।
सुधार: image: में प्रोड जैसा ही मेजर (आदर्श रूप से माइनर) पिन करें।

६. गिट में कमिटेड कंपोज़ सीक्रेट

लक्षण: पीआर इतिहास में क्रेडेंशियल लीक।
सुधार: गिट में प्लेसहोल्डर, असली मान गिटइग्नोर्ड एनव या सीक्रेट मैनेजर में; कभी लीक हुआ हो तो रोटेट करें।

७. हर चीज़ के लिए एक विशाल कंपोज़

लक्षण: फ्रंटएंड ट्वीक के लिए ४ जीबी रैम।
सुधार: कोर पथ के लिए डिफ़ॉल्ट फ़ाइल; docker-compose.override.yml (लोकल ऑटो-मर्ज) या प्रोफ़ाइलर, अतिरिक्त वर्कर और ऑब्ज़र्वेबिलिटी के लिए स्पष्ट -f ओवरराइड।

८. सीआई में एग्ज़िट कोड नज़रअंदाज़

लक्षण: पाइपलाइन हरी, पर compose up की कोई सेवा क्रैश।
सुधार: docker compose up --wait (हेल्दी का इंतज़ार), या compose run --rm api pytest से टेस्ट ताकि कमांड स्टेटस जॉब स्टेटस बने।

docker compose up -d --build --wait
docker compose exec api npm test
docker compose down -v

व्यक्तिगत लोकल ट्वीक के लिए ओवरराइड फ़ाइल

साझा बेसलाइन के रूप में docker-compose.yml कमिट करें। डेवलपर पोर्ट टकराव, अतिरिक्त माउंट या आईडीई डीबग पोर्ट के लिए docker-compose.override.yml जोड़ सकते हैं (व्यक्तिगत हो तो गिटइग्नोर)। दोनों फ़ाइलें प्रोजेक्ट डायरेक्टरी में हों तो कंपोज़ अपने आप मर्ज करता है।

टीम-व्यापी वैकल्पिक स्टैक स्पष्ट रखें:

docker compose -f docker-compose.yml -f docker-compose.observability.yml up

प्रोड को लैपटॉप पर लाए बिना मिलान

चिंता लोकल कंपोज़ उत्पादन
प्रोसेस ग्राफ़ वही रोल वही रोल, ज़्यादा रेप्लिका
इमेज डेव टारगेट या वही रनटाइम बेस मल्टी-स्टेज अंतिम इमेज
कॉन्फ़िग एनव फ़ाइलें + कंपोज़ environment सीक्रेट मैनेजर / प्लेटफ़ॉर्म एनव
नेटवर्किंग कंपोज़ डीएनएस (db) सर्विस डिस्कवरी / मेश
रेडीनेस कंपोज़ हेल्थचेक कुबेरनेट्स प्रोब / एएलबी हेल्थ
डेटा नेम्ड वॉल्यूम, सीड स्क्रिप्ट प्रबंधित डेटाबेस, पाइपलाइन माइग्रेशन

जीत साझा अर्थ में है: वही एनव कुंजियाँ, वही निर्भरता क्रम, हेल्दी का वही मतलब। नीचे का प्लेटफ़ॉर्म अलग हो सकता है।


छोटा लोकल वर्कफ़्लो जो टिकता है

cp .env.example .env.local   # once
docker compose up --build    # daily
# run migrations if not automatic:
docker compose exec api npm run migrate
# tests against the stack:
docker compose exec api npm test
# clean slate:
docker compose down -v

यह क्रम रिपो रीडमी में पंद्रह पंक्तियों से कम में लिखें। अगर ऑनबोर्डिंग को विकी और जनजातीय चेकलिस्ट चाहिए, तो कंपोज़ फ़ाइल काफ़ी काम नहीं कर रही।


समापन

कंपोज़ प्रोडक्शन ऑर्केस्ट्रेटर का विकल्प नहीं। यह सबसे सस्ता तरीका है कि हर लैपटॉप वही सेवा ग्राफ़ उन्हीं रेडीनेस नियमों से चलाए। संस्करण पिन करें, कॉन्फ़िग साफ़-साफ़ इंजेक्ट करें, हेल्थचेक का इंतज़ार करें, वॉल्यूम जानबूझकर रखें।

ऐसा करें तो "मेरी मशीन पर चलता है" का मतलब बनता है "साझा लोकल स्टैक पर चलता है," जो नंगे प्रोसेस और सिस्टम पोस्टग्रेस से स्टेजिंग के काफ़ी क़रीब है।