Docker Compose networking: default network اور service DNS
Compose کا default project bridge، service name سے DNS، host mode کا درست استعمال، projects میں مشترک network، اور UFW کو bypass کرنے والی published port سمجھیں۔
آپ کی ایپ شروع ہونے سے پہلے Compose کیا بناتا ہے
Docker Compose کی نیٹ ورکنگ ایک اصول سے شروع ہوتی ہے: docker compose up پروجیکٹ کے لیے ایک نجی نیٹ ورک بناتا ہے، ہر سروس کو اس سے منسلک کرتا ہے، اور ان سروسز کو سروس کے نام کے ذریعے ایک دوسرے تک پہنچنے دیتا ہے۔ اس کے لیے آپ کو ایک بھی networks: لائن لکھنے کی ضرورت نہیں ہوتی۔ Compose نیٹ ورکنگ سے متعلق زیادہ تر الجھن اس لیے پیدا ہوتی ہے کہ لوگوں کو معلوم نہیں ہوتا کہ ڈیفالٹ نیٹ ورک پہلے ہی موجود ہوتا ہے۔
یہ ایک چھوٹی فائل ہے۔ اسے shop نامی ڈائریکٹری میں compose.yaml کے نام سے محفوظ کریں۔
services:
web:
image: nginx:1.27
ports:
- "8080:80"
db:
image: postgres:16
environment:
POSTGRES_PASSWORD: exampleاسے شروع کریں اور دیکھیں کہ Docker نے کیا بنایا ہے:
docker compose up -d
docker network lsاب فہرست میں shop_default نامی نیٹ ورک موجود ہے۔ Compose اسے <project>_default نام دیتا ہے، اور پروجیکٹ کا نام بطور ڈیفالٹ ڈائریکٹری کے lowercase نام پر مشتمل ہوتا ہے۔ اسے docker compose -p myproject up -d کے ذریعے یا فائل میں top-level name: myproject کے ذریعے تبدیل کریں۔ اس کا driver bridge ہے، جو host کے اندر ایک virtual switch ہوتا ہے۔ ہر container کو ایک private subnet پر ایک address ملتا ہے، اور باہر جانے والا traffic راستے میں host کے address میں translate ہو جاتا ہے۔
docker compose down اس نیٹ ورک کو دوبارہ حذف کرتا ہے۔ اسی لیے کسی پرانے پروجیکٹ کا stale container نیٹ ورک کو برقرار رکھ سکتا ہے: Docker error while removing network: network shop_default has active endpoints کے ساتھ انکار کرتا ہے، اور حل یہ ہے کہ اس container کو stop یا remove کریں جو اب بھی اس سے منسلک ہے۔
اگر Compose آپ کے لیے نیا ہے تو پہلے Compose فائل کی ساخت اور lifecycle commands پڑھ لینا مفید ہوگا، کیونکہ ذیل کی تمام معلومات یہ فرض کرتی ہیں کہ آپ پروجیکٹ کو شروع اور روک سکتے ہیں۔
سروس کے نام سے DNS وہ حصہ ہے جسے مبتدی نظرانداز کر دیتے ہیں
ہر صارف کی بنائی ہوئی network پر، Docker ایک embedded DNS server چلاتا ہے جسے ہر container 127.0.0.11 پر دیکھتا ہے۔ یہ service names کو موجودہ container addresses میں resolve کرتا ہے۔ اس لیے web database تک hostname db اور port 5432 کے ذریعے، بغیر کسی configuration کے، پہنچ جاتا ہے۔
docker compose exec web getent hosts dbیہ 172.18.0.2 db جیسی ایک سطر دکھاتا ہے۔ اگر کچھ بھی ظاہر نہ ہو تو دونوں services ایک ہی network پر نہیں ہیں۔
تقریباً ہر شخص ایک بار جو غلطی کرتا ہے، وہ application config میں localhost استعمال کرنا ہے۔ کسی container کے اندر localhost اسی container کو ظاہر کرتا ہے، host یا دوسری service کو نہیں۔ Postgres clients اسے واضح طور پر رپورٹ کرتے ہیں:
could not connect to server: Connection refused
Is the server running on host "localhost" (127.0.0.1) and accepting
TCP connections on port 5432?Connection string postgresql://postgres:example@db:5432/postgres ہونی چاہیے۔ اس میں host کا حصہ service name ہے۔
بعد میں وقت بچانے والی دو باتیں اہم ہیں۔ Names اس وقت چلنے والی چیز سے resolve ہوتے ہیں، اس لیے docker compose up -d --scale web=3 ایک نام کے ساتھ تین addresses دیتا ہے، جبکہ ایسا client جو DNS کو ہمیشہ cache کرتا رہے، خود کو ایک dead container سے وابستہ کر دے گا۔ اس کے علاوہ، وہ legacy bridge network جو بغیر --network کے سادہ docker run استعمال کرنے پر بنتی ہے، اس میں name resolution بالکل نہیں ہوتی۔ اسی لیے 2016 میں container links کے بارے میں دی گئی ہدایات آپ کے موجودہ مشاہدے سے مطابقت نہیں رکھتیں۔
دو سروسز کو مربوط کرنے کے لیے ports: درکار نہیں
ports:، container port کو host پر publish کرتا ہے۔ یہ Docker کے باہر سے آنے والی network traffic کے لیے ہے۔ اس کا service-to-service traffic سے کوئی تعلق نہیں، کیونکہ project network پر port range کے پورے حصے میں یہ پہلے ہی کام کرتی ہے۔
اس لیے بہت سے لوگ اپنی database service میں جو ports: - "5432:5432" شامل کرتے ہیں، وہ بے فائدہ ہونے کے ساتھ حقیقی نقصان بھی پہنچاتا ہے: یہ server کے public interface پر Postgres کو ظاہر کر دیتا ہے۔ اسے حذف کریں۔ اگر migration کے لیے اسے اپنے laptop سے قابل رسائی بنانا ہو، تو اسے "127.0.0.1:5432:5432" کے ذریعے loopback سے bind کریں اور SSH tunnel کے ذریعے رسائی حاصل کریں۔ listening socket، published port، اور firewall rule کے درمیان فرق Linux پر ports اور listening services کیسے کام کرتی ہیں میں بیان کیا گیا ہے۔
Compose کے تحت expose: صرف documentation ہے۔ یہ کوئی چیز کھولتا نہیں، کیونکہ ایک ہی network پر موجود containers کے درمیان کچھ بھی بند نہیں تھا۔
host network_mode کب درست ہے، اور اس کی قیمت کیا ہے
Host mode، container کا اپنا network namespace ختم کر دیتا ہے اور process کو host کے interfaces براہِ راست استعمال کرنے دیتا ہے۔
services:
probe:
image: alpine:3.20
network_mode: host
command: sleep infinityاسے استعمال کرنے کی حقیقی وجوہات موجود ہیں۔ اگر کسی process کو local network پر broadcast یا multicast traffic دیکھنا ہو، مثلاً media server یا home automation hub کے لیے device discovery، تو bridge کے پیچھے موجود container اسے نہیں دیکھ سکتا، کیونکہ bridge یہ traffic container تک forward نہیں کرتا۔ ایسا monitoring agent جو host کے interface counters پڑھتا ہو، اسے host کے interfaces درکار ہوتے ہیں۔ اس کے علاوہ address translation کا ایک مرحلہ بھی ختم ہو جاتا ہے، جو packet rates زیادہ ہونے پر اہم ہو سکتا ہے۔
اس کے نقصانات مخصوص ہیں۔
ports: کام کرنا بند کر دیتا ہے۔ Docker خبردار کرتا ہے کہ host network mode استعمال کرنے پر published ports نظرانداز کر دیے جاتے ہیں، اور container وہی ports bind کرتا ہے جنہیں اس کا process bind کرے۔ اگر host mode کے دو containers port 8080 استعمال کرنا چاہیں تو ان میں تصادم ہو گا، اور دوسرا container bind: address already in use کے ساتھ بند ہو جائے گا۔
Service name کے ذریعے name resolution دونوں سمتوں میں ختم ہو جاتی ہے۔ Container project network پر موجود نہیں ہوتا، اس لیے وہ db کو resolve نہیں کر سکتا، اور دوسری services بھی اسے resolve نہیں کر سکتیں۔ وہ ان services تک صرف host پر published ports کے ذریعے پہنچتا ہے، جو عموماً 127.0.0.1 پر ہوتی ہیں۔
Isolation ختم ہو جاتی ہے۔ اگر host mode container کے اندر کوئی process 0.0.0.0 کو bind کرے، تو وہ آپ کے server کے ہر interface پر، بشمول public interface، اسی طرح listening کرے گا جیسے apt سے install کیا گیا کوئی package کرتا ہے۔ اس کا ایک فائدہ بھی ہے: یہ traffic معمول کے input path سے گزرتا ہے، اس لیے UFW rules اس پر لاگو ہوتی ہیں۔ Published ports کے لیے ایسا نہیں ہوتا۔
Host mode، Linux Docker Engine کی feature ہے۔ Docker Desktop اسے صرف version 4.34 سے support کرتا ہے، اور اسے پہلے enable کرنا ضروری ہے۔ مزید یہ کہ containers host IP addresses کو bind نہیں کر سکتے، اور صرف TCP اور UDP handle کیے جاتے ہیں۔ اگر آپ کی آدھی team Linux servers اور آدھی Docker Desktop استعمال کرتی ہے، تو توقع رکھیں کہ ایک ہی file مختلف طریقے سے کام کرے گی۔
Host mode اس وقت استعمال کریں جب آپ کو host کے interfaces درکار ہوں۔ Connection problem حل کرنے کے لیے اسے استعمال نہ کریں، کیونکہ عموماً یہ ایک مسئلے کی جگہ زیادہ مشکل مسئلہ پیدا کر دیتا ہے۔
دو Compose پروجیکٹس کو بیرونی نیٹ ورک سے مربوط کریں
ایک پروجیکٹ کا بنایا ہوا نیٹ ورک دوسرے پروجیکٹ کو نظر نہیں آتا۔ اسی لیے proxy/compose.yaml میں موجود reverse proxy اسی server پر ہونے کے باوجود app/compose.yaml میں موجود app تک نہیں پہنچ سکتا۔ حل یہ ہے کہ ایسا network استعمال کیا جائے جس کی ملکیت کسی بھی پروجیکٹ کے پاس نہ ہو۔
اسے ایک بار دستی طور پر بنائیں:
docker network create edgeپھر ہر پروجیکٹ میں اسے external قرار دیں۔ Proxy کا حصہ:
services:
proxy:
image: traefik:v3.1
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
networks:
- edge
networks:
edge:
name: edge
external: trueApplication کا حصہ:
services:
app:
image: nginx:1.27
networks:
- edge
- internal
db:
image: postgres:16
environment:
POSTGRES_PASSWORD: example
networks:
- internal
networks:
edge:
name: edge
external: true
internal:external: true Compose کو موجودہ network سے منسلک ہونے کا حکم دیتا ہے، نیا network بنانے کا نہیں، اور docker compose down پر اسے برقرار رکھتا ہے۔ الگ name: key بظاہر معمولی لگتی ہے، لیکن اہم ہے: اس کے بغیر Compose بالکل edge نام کا network تلاش کرتا ہے۔ اس key کے ذریعے آپ اپنی file میں network کا ایک نام اور host پر دوسرا نام رکھ سکتے ہیں۔
اگر network موجود نہ ہو تو Compose شروع ہونے سے انکار کر دیتا ہے اور بتاتا ہے کہ network کو external قرار دیا گیا تھا، لیکن وہ نہیں ملا۔ اسے پہلے بنائیں۔
غور کریں کہ application file internal کے ساتھ کیا کرتی ہے۔ Database صرف اسی پروجیکٹ کے مقامی network پر موجود ہے، اس لیے proxy اس تک نہیں پہنچ سکتا اور صرف app ہی اس تک رسائی رکھتا ہے۔ کسی network کے تحت internal: true شامل کرنے سے یہ ایک قدم آگے جاتا ہے اور اس کا بیرونی دنیا تک route مکمل طور پر ختم کر دیتا ہے۔ Database کے لیے یہ ایک اچھا default ہے، لیکن اسے مقرر کرنے سے پہلے ایک اہم قیمت سمجھ لیں: internal network پر موجود container کچھ بھی download نہیں کر سکتا۔ اس لیے startup کے وقت apt-get update یا pip install چلانے والا entrypoint hang ہو جائے گا اور پھر timeout کے ساتھ ناکام ہو جائے گا۔
Routing rules اور certificates کے ساتھ مکمل عملی setup کے لیے ایک Traefik instance کے پیچھے متعدد apps چلانا دیکھیں۔
شائع کیے گئے ports، UFW کو bypass کرتے ہیں
یہ Compose networking کا وہ حصہ ہے جو security incident کا سبب بن سکتا ہے۔ آپ ایک port شائع کرتے ہیں، چیک کرتے ہیں کہ UFW فعال ہے اور SSH کے علاوہ ہر چیز کو deny کر رہا ہے، لیکن service پھر بھی internet سے قابل رسائی رہتی ہے۔
sudo ufw status
curl http://203.0.113.10:8080UFW بتاتا ہے کہ port blocked ہے۔ curl پھر بھی page واپس کرتا ہے۔ کچھ بھی خراب نہیں ہے۔ Docker اپنے address translation اور forwarding rules براہ راست iptables میں لکھتا ہے۔ Published container port پر آنے والی traffic کو host تک پہنچانے کے بجائے container کو forward کیا جاتا ہے۔ اس لیے یہ اس chain سے نہیں گزرتی جسے UFW مقامی طور پر مطلوب traffic کے لیے manage کرتا ہے۔ Docker کے rules بھی UFW سے پہلے match ہوتے ہیں۔
مختصر حل یہ ہے کہ port صرف اس جگہ publish کریں جہاں اس کی ضرورت ہو:
ports:
- "127.0.0.1:8080:80"اس سے host side کو loopback سے bind کیا جاتا ہے۔ یوں port خود server سے اور SSH tunnel کے ذریعے قابل رسائی رہتا ہے، لیکن کسی اور جگہ سے نہیں۔ Public entry point کو ایسے reverse proxy کے پیچھے رکھیں جو 80 اور 443 کو دانستہ طور پر publish کرے۔ مکمل وضاحت، جس میں ان حالات کے لیے DOCKER-USER chain بھی شامل ہے جہاں published port کو filter کرنا ضروری ہو، یہ وضاحت کہ Docker UFW کو براہ راست کیسے bypass کرتا ہے اور اسے کیسے درست کیا جائے میں موجود ہے۔
چار کمانڈز میں مسئلے کی تشخیص کیسے کریں
سب سے پہلے معلوم کریں کہ ہر container حقیقت میں کس network پر ہے:
docker network inspect shop_defaultContainers بلاک ہر منسلک container کو اس کے address کے ساتھ دکھاتا ہے۔ جو service اس فہرست میں موجود نہ ہو، وہ کسی مختلف network پر ہے، host mode میں ہے، یا چل نہیں رہی۔
اسی network سے منسلک ایک عارضی container سے name resolution کی جانچ کریں۔ اس طرح آپ کو اپنی images کے اندر کسی tooling کی ضرورت نہیں پڑے گی:
docker run --rm --network shop_default busybox nslookup db
docker run --rm --network shop_default busybox nc -zv db 5432nslookup کا ناکام ہونا name resolution یا network membership کے مسئلے کی نشاندہی کرتا ہے۔ اگر nslookup کامیاب ہو لیکن nc ناکام ہو، تو service چل رہی ہے، مگر اس port پر listening نہیں کر رہی، یا اپنے container کے اندر 127.0.0.1 پر listening کر رہی ہے، 0.0.0.0 پر نہیں۔ یہ development servers میں عام ہے۔ اس کا حل Docker میں نہیں بلکہ application کے bind address میں ہے۔
ایک اور خرابی بھی ہے جو Docker bug معلوم ہوتی ہے۔ اگر containers ایک دوسرے سے رابطہ کر سکتے ہوں، مگر آپ کے office یا VPN network کی کسی machine تک نہیں پہنچ سکتے، تو غالباً Docker subnet اس network کے ساتھ overlap کر رہا ہے۔ Docker بطور default 172.17.0.0/16 سے اوپر کے addresses مختص کرتا ہے۔ pool کو /etc/docker/daemon.json میں منتقل کریں:
{
"default-address-pools": [
{ "base": "10.200.0.0/16", "size": 24 }
]
}اس کے بعد sudo systemctl restart docker چلائیں اور متاثرہ networks دوبارہ بنائیں، کیونکہ موجودہ network وہی subnet برقرار رکھتا ہے جس کے ساتھ اسے بنایا گیا تھا۔
FAQ
میرے containers ایک دوسرے تک service name کے ذریعے کیوں نہیں پہنچ سکتے؟
وہ ایک ہی network پر نہیں ہیں۔ Compose ہر service کو خودکار طور پر <project>_default پر رکھتا ہے، لیکن جیسے ہی آپ کسی service میں networks: کی فہرست شامل کرتے ہیں، وہ فہرست اس service کے لیے networks کا مکمل مجموعہ بن جاتی ہے اور default مزید خودکار طور پر شامل نہیں رہتا۔ docker network inspect <network> چلائیں اور تصدیق کریں کہ دونوں containers Containers block میں موجود ہیں۔ یہ بھی جانچیں کہ کسی service میں network_mode: host استعمال نہ ہو، کیونکہ host mode container کسی Docker network پر نہیں ہوتا اور service names resolve نہیں کر سکتا۔
کیا ایک service کو دوسری service تک پہنچنے کے لیے ports publish کرنا ضروری ہے؟
نہیں۔ Compose network پر موجود ہر container کا ہر port اسی network کے دوسرے containers کے لیے قابل رسائی ہوتا ہے۔ ports: صرف container کو Docker سے باہر کے network traffic کے لیے expose کرنے کے لیے ہوتا ہے، جبکہ expose: دستاویزی مقصد کے لیے ہے۔ Database port publish کرنا ایک عام اور نقصان دہ عادت ہے، کیونکہ اس سے database آپ کے server کے public interface پر دستیاب ہو جاتا ہے۔
bridge اور host networking میں کیا فرق ہے؟
Bridge container کو virtual switch پر اپنا network namespace اور address دیتا ہے۔ اس میں containers کے درمیان نام خودکار طور پر resolve ہوتے ہیں اور باہر جانے والے traffic کا ترجمہ کیا جاتا ہے۔ Host container کو براہ راست host کا network stack دیتا ہے۔ اس صورت میں الگ address نہیں ہوتا، service name کے ذریعے resolution نہیں ہوتی، port publishing کی ضرورت نہیں ہوتی، اور host کے دیگر listeners سے isolation نہیں رہتی۔ Bridge default انتخاب ہے اور عموماً درست بھی، سوائے اس کے کہ process کو host کے interfaces درکار ہوں۔
میں دو مختلف Compose files کے containers کو کیسے connect کروں؟
docker network create edge کے ذریعے shared network بنائیں۔ پھر دونوں files میں اسے external: true کے ذریعے declare کریں اور ان services کو اس network سے attach کریں جنہیں باہمی رابطے کی ضرورت ہے۔ Compose اسے نہ create کرے گا اور نہ delete۔ اگر create کرنے کا مرحلہ چھوڑ دیں تو Compose start ہونے سے انکار کر دے گا اور network کو declared external but not found کے طور پر رپورٹ کرے گا۔
UFW port کو block کرتا ہے، پھر بھی میرا container internet سے کیوں قابل رسائی ہے؟
کیونکہ published port کو Docker کی طرف سے iptables میں شامل کی جانے والی forwarding rules handle کرتی ہیں۔ یہ rules UFW کی rules سے پہلے match ہوتی ہیں، اور forwarded traffic اس chain سے گزرتا ہی نہیں جسے UFW filter کرتا ہے۔ "127.0.0.1:8080:80" کے ذریعے host side کو loopback پر bind کریں، اور public services کو ports 80 اور 443 پر reverse proxy کے پیچھے رکھیں۔