2025, Sep 29 07:31
Docker में Python सर्विस को Postgres से जोड़ना: बिल्ड‑टाइम बनाम रनटाइम का भरोसेमंद तरीका
Docker में Python सर्विस और Postgres कनेक्शन की बिल्ड‑टाइम DNS समस्या का हल जानें: host नेटवर्क, हेल्थचेक, प्रकाशित पोर्ट और अलग कनेक्शन स्ट्रिंग से सेटअप.
Docker के अंदर Python सर्विस को Postgres से जोड़ना सामान्य-सी बात होनी चाहिए, लेकिन यह समझना कि आपका कोड वास्तव में कहाँ चल रहा है, आसानी से भ्रम पैदा कर सकता है। अक्सर लक्षण psycopg2 में DNS समस्या जैसे लगते हैं: सर्विस का नाम रनटाइम पर चलता है, मगर इमेज बिल्ड और टेस्ट के दौरान फेल हो जाता है। नीचे इस विफलता के पैटर्न का संक्षिप्त विवरण और बिना अनुमान लगाए काम करने वाला उपाय दिया गया है।
न्यूनतम विफल सेटअप
एप्लिकेशन कोड सर्विस नाम को होस्ट मानकर सीधे Postgres से कनेक्ट करता है। जब कंटेनर Compose नेटवर्क के भीतर चलता है, तब यह संरचना ठीक काम करती है।
import psycopg2
class PgSession:
    def __init__(self, db_label, login_user):
        self.db_label = db_label
        self.login_user = login_user
        self.link = None
    def __enter__(self):
        self.link = psycopg2.connect(
            user=self.login_user,
            database=self.db_label,
            password="data",
            host="db",
            port=5432
        )
        return self.link
    def __exit__(self, exc_type, exc_val, exc_tb):
        self.link.close()
त्रुटि संदेश DNS रेज़ोल्यूशन के बारे में साफ बताता है। psycopg2 होस्टनेम को पते में बदल नहीं पा रहा, जबकि Compose का सर्विस नाम सही दिख रहा है।
psycopg2.OperationalError: could not translate host name "db" to address: Name or service not known
असल में क्या हो रहा है
"db" नाम का रेज़ोल्यूशन केवल उस Docker नेटवर्क के अंदर काम करता है जिसे Compose आपकी चल रही सर्विसेज़ के लिए बनाता है। जब आप इमेज बिल्ड के दौरान इंटीग्रेशन टेस्ट चलाते हैं, तो वह बिल्ड चरण Compose नेटवर्क से जुड़ा नहीं होता। जब तक आप बिल्ड की नेटवर्किंग मोड को स्पष्ट रूप से नहीं बदलते, उसे "db" सर्विस नाम दिखाई नहीं देगा। चलते कंटेनर से आप सर्विस नाम को सफलतापूर्वक ping कर सकते हैं, जिससे यह पक्का होता है कि रनटाइम पर नेटवर्क और DNS की व्यवस्था ठीक है। विफलता बिल्ड कॉन्टेक्स्ट में होती है, कंटेनर उठने के बाद नहीं।
एंड‑टू‑एंड काम करने वाला समाधान
दो समन्वित बदलाव समस्या को सुलझाते हैं। पहले, यह सुनिश्चित करें कि डेटाबेस कंटेनर दूसरे इमेज के बनने से पहले स्वस्थ (healthy) हो। दूसरे, इमेज बिल्ड को host नेटवर्क से जोड़ें ताकि इंटीग्रेशन टेस्ट आंतरिक Compose DNS नाम की बजाय होस्ट‑मैप किए गए पोर्ट पर चलें। इसके साथ, दो अलग कनेक्शन स्ट्रिंग उपयोग करें: बिल्ड के दौरान चलने वाले टेस्ट के लिए (host नेटवर्क, localhost और प्रकाशित पोर्ट के साथ) एक, और रनटाइम पर चलने वाले ऐप के लिए (आंतरिक DNS नाम और कंटेनर पोर्ट के साथ) दूसरा।
अपडेटेड docker-compose: पहले से बने एक्सटर्नल नेटवर्क, हेल्थचेक, और dataseeder बिल्ड के लिए host नेटवर्क सहित:
networks:
  backend:
    name: value_tracker_backend
    external: true
services:
  db:
    build:
      context: ./sql
      dockerfile: db.dockerfile
    environment:
      POSTGRES_PASSWORD: postgres
      POSTGRES_DB: EntitiesAndValues
    ports:
      - "5431:5432"
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres -d EntitiesAndValues"]
      interval: 10s
      retries: 5
      start_period: 30s
      timeout: 10s
    networks:
      - backend
  dataseeder:
    build:
      context: .
      dockerfile: dataseeder.dockerfile
      network: host
    depends_on:
      db:
        condition: service_healthy
        restart: true
    networks:
      - backend
टू‑स्टेज Dockerfile बिल्ड स्टेज के दौरान इंटीग्रेशन टेस्ट चलाता है और एक हल्का रिलीज़ स्टेज तैयार करता है:
FROM python:latest AS base
RUN apt-get update
COPY packages/ packages/
COPY tests/ tests/
COPY localtest.txt .
COPY DataSeeder.py .
RUN pip install -r localtest.txt
RUN pytest -v tests/
FROM base AS release
RUN apt-get update
COPY packages/ packages/
COPY release.txt .
COPY DataSeeder.py .
RUN pip install -r release.txt
CMD ["python","DataSeeder.py"]
जब बिल्ड host नेटवर्क से जुड़ा हो और DB पोर्ट 5431 पर प्रकाशित किया गया हो, तब टेस्ट localhost और प्रकाशित पोर्ट से कनेक्ट करते हैं। रनटाइम पर, ऐप सर्विस नाम और कंटेनर पोर्ट से कनेक्ट करता है। यही विभाजन दोनों चरणों को सफल बनाने की कुंजी है।
# टेस्ट के दौरान (बिल्ड स्टेज host नेटवर्क का उपयोग करता है)
import psycopg2
import pytest
class PgSessionWithHost:
    def __init__(self, db_label, login_user, db_host, db_port):
        self.db_label = db_label
        self.login_user = login_user
        self.db_host = db_host
        self.db_port = db_port
        self.link = None
    def __enter__(self):
        self.link = psycopg2.connect(
            user=self.login_user,
            database=self.db_label,
            password="data",
            host=self.db_host,
            port=self.db_port
        )
        return self.link
    def __exit__(self, exc_type, exc_val, exc_tb):
        self.link.close()
@pytest.fixture(scope="session")
def bootstrap():
    with PgSessionWithHost("EntitiesAndValues", "data_seeder", "localhost", 5431) as cn:
        svc = EntitiesValuesFunctions(cn)
        with svc.conn.cursor() as cur:
            cur.execute("TRUNCATE TABLE public.entities, public.entity_values;")
            cn.commit()
        yield svc
        with svc.conn.cursor() as cur:
            cur.execute("TRUNCATE TABLE public.entities, public.entity_values;")
            cn.commit()
# रनटाइम पर, Compose नेटवर्क के अंदर
if __name__ == "__main__":
    with PgSessionWithHost("EntitiesAndValues", "data_seeder", "db", 5432) as cn:
        svc = EntitiesValuesFunctions(cn)
        seeder = DataSeeder(svc)
        seeder.run()
यह क्यों मायने रखता है
बिल्ड‑टाइम और रनटाइम अलग‑अलग नेटवर्क कॉन्टेक्स्ट हैं। Compose का सर्विस नाम जैसा कि "db" केवल तब रेज़ॉल्व होता है जब कंटेनर चल रहे हों और आंतरिक नेटवर्क सक्रिय हो; इमेज बिल्ड के दौरान इसके रेज़ॉल्व होने की गारंटी नहीं है। बिल्ड को host नेटवर्क से संरेखित करने पर, शर्त यह है कि डेटाबेस पहले से चल रहा व स्वस्थ हो और उसका पोर्ट प्रकाशित हो, आप फाइनल इमेज बनने से पहले ही इंटीग्रेशन टेस्ट चला सकते हैं। वैकल्पिक रूप से, आप इंटीग्रेशन टेस्ट इमेज बिल्ड में न चलाकर पूरी प्रणाली के डिप्लॉय होने के बाद चला सकते हैं; तब परिवेश प्रोडक्शन‑जैसी टोपोलॉजी से मेल खाएगा। दोनों तरीके मान्य हैं; महत्वपूर्ण यह है कि हर चरण में आपका कोड किस नेटवर्क को लक्ष्य बना रहा है, यह स्पष्ट हो।
एक व्यावहारिक बात और: दस्तावेज़ीकरण के अनुसार बिल्ड के लिए कस्टम नेटवर्क बताना संभव होना चाहिए। इस सेटअप में कस्टम नेटवर्क अपेक्षा के अनुरूप काम नहीं कर पाए, जबकि host नेटवर्क मोड काम कर गया। यदि आपको भी ऐसा व्यवहार दिखे, तो बिल्ड को host नेटवर्क पर स्विच करना एक व्यवहारिक समाधान है।
मुख्य बातें
कनेक्शन रणनीति बनाते समय कंटेनर का जीवनचक्र और नेटवर्क कॉन्टेक्स्ट को प्राथमिकता दें। सुनिश्चित करें कि डेटाबेस स्वस्थ हो, तभी कोई उससे कनेक्ट करने की कोशिश करे। बिल्ड के दौरान चलने वाले टेस्ट के लिए host नेटवर्किंग और प्रकाशित पोर्ट का उपयोग करें, और रनटाइम पर आंतरिक सर्विस नाम व कंटेनर पोर्ट का। यदि सेवाओं को अलग‑अलग शुरू करना हो, तो एक बाहरी नेटवर्क उन्हें एक ही लॉजिकल ब्रिज पर रखता है और हर Compose रन के लिए अलग‑थलग नेटवर्क बनने से रोकता है।
यह लेख StackOverflow पर एक प्रश्न (लेखक: Rory Coble) और उसी लेखक Rory Coble के उत्तर पर आधारित है।