توی این درس یاد میگیری کانتینرها چطور با هم و با دنیای بیرون حرف میزنند: داکر برای هر کانتینر یک شبکهی مجازی میسازد. سه نوع اصلی را میشناسی: bridge (پیشفرض)، host و none، و یاد میگیری چطور با docker network create یک شبکهی سفارشی بسازی، کانتینرها را به آن وصل کنی (docker run --network) و ایزولهسازی بین شبکهها را ببینی. دستورهای اصلی: docker network create، docker network ls، docker network inspect و docker network connect.
تشبیه: ساختمان و واحدهای آن
Section titled “تشبیه: ساختمان و واحدهای آن”شبکهی داکر مثل راهروهای یک ساختمان است. کانتینرهایی که در یک راهرو (یک شبکه) باشند، به هم دسترسی دارند؛ کانتینرهای راهروهای مختلف نمیتوانند همدیگر را ببینند، مگر اینکه یک نفر در هر دو راهرو باشد. و بیرون ساختمان (اینترنت و میزبان) هم فقط از «دروازههایی» که تو باز میکنی (-p) در دسترس است.
سه درایور اصلی
Section titled “سه درایور اصلی”مثالهای عملی
Section titled “مثالهای عملی”مثال ۱: شبکههای موجود (docker network ls)
Section titled “مثال ۱: شبکههای موجود (docker network ls)”docker network ls --format 'table {{.Name}}\t{{.Driver}}\t{{.Scope}}' | grep -E '^(NAME|bridge|host|none)'NAME DRIVER SCOPEbridge bridge localhost host localnone null localداکر همیشه سه شبکهی آماده دارد: bridge (پیشفرض)، host و none. هر کانتینری که با docker run (بدون --network) بسازی روی شبکهی bridge پیشفرض میرود. (اگر پروژههای Compose دیگری روی سیستم داشته باشی، شبکههایشان هم در docker network ls دیده میشود؛ ما فقط سه شبکهی پیشفرض را فیلتر کردیم.)
مثال ۲: شبکهی پیشفرض و IP کانتینرها
Section titled “مثال ۲: شبکهی پیشفرض و IP کانتینرها”docker run -d --name lx-n1 alpine sleep 300 >/dev/nulldocker run -d --name lx-n2 alpine sleep 300 >/dev/nulldocker inspect lx-n1 lx-n2 --format '{{.Name}}: {{.NetworkSettings.Networks.bridge.IPAddress}}'IP2=$(docker inspect lx-n2 --format '{{.NetworkSettings.Networks.bridge.IPAddress}}')docker exec lx-n1 ping -c 1 -W 2 $IP2 | sed -n '1p;3p'/lx-n1: 172.17.0.4/lx-n2: 172.17.0.5PING 172.17.0.5 (172.17.0.5): 56 data bytesهر کانتینر یک آدرس IP خصوصی گرفت و با IP میتوانند به هم برسند. ولی با اسم چطور؟
docker exec lx-n1 ping -c 1 -W 2 lx-n2 2>&1 | head -2ping: bad address 'lx-n2'روی شبکهی bridge پیشفرض، resolve کردن اسم کانتینر کار نمیکند (bad address). دلیل و راهحلش در مثال بعد و درس بعدی است.
کانتینرها روی شبکهی bridge پیشفرض میتوانند همدیگر را با اسم پیدا کنند؟
DNS داخلی داکر فقط روی شبکههای سفارشی کار میکند.
مثال ۳: شبکهی سفارشی (docker network create)
Section titled “مثال ۳: شبکهی سفارشی (docker network create)”docker rm -f lx-n1 lx-n2 >/dev/nulldocker network create lx-net-adocker network ls --filter name=lx-net-a --format 'table {{.Name}}\t{{.Driver}}'docker run -d --name lx-n1 --network lx-net-a alpine sleep 300 >/dev/nulldocker run -d --name lx-n2 --network lx-net-a alpine sleep 300 >/dev/nulldocker exec lx-n1 ping -c 1 -W 2 lx-n2 | sed -n '1p;3p'5ec798beb39079e92543c9b8cd87da1ba7e3ad9c2985adbd1c01b6bfd42120f7NAME DRIVERlx-net-a bridgePING lx-n2 (172.20.0.3): 56 data bytesحالا دو کانتینر در شبکهی lx-net-a هستند و ping lx-n2 با اسم کار کرد. چون شبکهی سفارشی یک DNS داخلی دارد که اسم کانتینرها را به IP تبدیل میکند (درس بعد). docker network create بهصورت پیشفرض یک شبکهی bridge میسازد.
مثال ۴: ایزولهسازی بین شبکهها
Section titled “مثال ۴: ایزولهسازی بین شبکهها”docker network create lx-net-b >/dev/nulldocker run -d --name lx-n3 --network lx-net-b alpine sleep 300 >/dev/nullecho "از lx-n1 (شبکه a) به lx-n3 (شبکه b):"docker exec lx-n1 ping -c 1 -W 2 lx-n3 2>&1 | head -1IP3=$(docker inspect lx-n3 --format '{{(index .NetworkSettings.Networks "lx-net-b").IPAddress}}')docker exec lx-n1 ping -c 1 -W 2 $IP3 2>&1 | grep -E 'packet loss'از lx-n1 (شبکه a) به lx-n3 (شبکه b):ping: bad address 'lx-n3'1 packets transmitted, 0 packets received, 100% packet lossکانتینرها در دو شبکهی مختلف نه با اسم و نه با IP همدیگر را نمیبینند (بستهها ۱۰۰٪ گم میشوند). این ایزولهسازی پیشفرض است و پایهی معماری امن.
مثال ۵: اتصال یک کانتینر به دو شبکه (docker network connect)
Section titled “مثال ۵: اتصال یک کانتینر به دو شبکه (docker network connect)”docker network connect lx-net-b lx-n1docker inspect lx-n1 --format '{{range $k, $v := .NetworkSettings.Networks}}{{$k}} {{end}}'docker exec lx-n1 ping -c 1 -W 2 lx-n3 | sed -n '1p;3p'lx-net-a lx-net-bPING lx-n3 (172.21.0.2): 56 data bytesحالا lx-n1 در هر دو شبکه است و پلی بینشان شده؛ lx-n3 را با اسم میبیند. این همان الگوی «API در frontend و backend» از سناریو است: API هر دو را میبیند، ولی وباپ و دیتابیس همدیگر را نه.
مثال ۶: host و none
Section titled “مثال ۶: host و none”echo "none:"; docker run --rm --network none alpine sh -c 'ip -o addr | awk "{print \$2, \$4}"; ping -c 1 -W 2 8.8.8.8 2>&1 | tail -1'echo "host (روی Docker Desktop یعنی شبکهی ماشین مجازی داکر):"; docker run --rm --network host alpine sh -c 'ip link show docker0 >/dev/null 2>&1 && echo "رابط docker0 (مال میزبان) را میبینم"; echo "تعداد رابطهای شبکه: $(ip -o link | wc -l)"'none:lo 127.0.0.1/8lo ::1/128ping: sendto: Network unreachablehost (روی Docker Desktop یعنی شبکهی ماشین مجازی داکر):رابط docker0 (مال میزبان) را میبینمتعداد رابطهای شبکه: 32با none فقط loopback (lo) هست و هیچچیز بیرون نمیرود. با host کانتینر مستقیم از شبکهی میزبان استفاده میکند (رابطهایی مثل docker0 که مال خود میزبان است در دسترسش هستند؛ در حالت عادی هیچکدام از اینها را نمیدید) و دیگر -p معنی ندارد. نکته: روی مک و ویندوز «میزبان» یعنی همان VM لینوکسی داکر، نه خود سیستم تو؛ پس --network host برای دسترسی به localhost مک جواب نمیدهد. حالت host عملاً برای لینوکس است.
پشت پرده
Section titled “پشت پرده”شبکهی bridge روی لینوکس با یک پل مجازی (virtual bridge، مثلاً docker0 یا br-...) و جفتهای veth ساخته میشود: هر کانتینر یک سر کابل مجازی دارد که داخل network namespace خودش است و سر دیگر به پل وصل است. خروج به اینترنت از طریق NAT (قانونهای فایروال) انجام میشود؛ ورود از بیرون فقط به پورتهایی که با -p منتشر کردهای.
ببین یک شبکه چه ساختاری دارد:
docker network inspect lx-net-a --format 'driver: {{.Driver}}subnet: {{(index .IPAM.Config 0).Subnet}}gateway: {{(index .IPAM.Config 0).Gateway}}containers: {{range .Containers}}{{.Name}} {{end}}'driver: bridgesubnet: 172.20.0.0/16gateway: 172.20.0.1containers: lx-n2 lx-n1هر شبکه یک subnet (بازهی IP) و یک gateway دارد؛ و هر شبکهی سفارشی بازهی جدا میگیرد، به همین دلیل ایزولهاند.
جدولهای مرجع
Section titled “جدولهای مرجع”| درایور | ایزولاسیون | کاربرد |
|---|---|---|
bridge |
شبکهی خصوصی با NAT | پیشفرض و بیشترین استفاده |
host |
هیچ (شبکهی میزبان) | کارایی بالا، لینوکس |
none |
کامل (فقط loopback) | کار ایزوله و بدون شبکه |
overlay |
چند میزبان (Swarm) | خوشههای چندسرور |
| دستور | کار |
|---|---|
docker network ls |
فهرست شبکهها |
docker network create N |
ساخت شبکهی bridge سفارشی |
docker network inspect N |
جزئیات (subnet، کانتینرها) |
docker run --network N IMG |
اجرا روی یک شبکه |
docker network connect N C |
وصل کردن کانتینر در حال اجرا به شبکه |
docker network disconnect N C |
جدا کردن |
docker network rm N |
حذف (باید خالی باشد) |
اشتباهات رایج
Section titled “اشتباهات رایج”۱) انتظار resolve اسم روی bridge پیشفرض
Section titled “۱) انتظار resolve اسم روی bridge پیشفرض”مثال ۲: bad address. راهحل: شبکهی سفارشی بساز و کانتینرها را به آن وصل کن.
۲) حذف شبکهی در حال استفاده
Section titled “۲) حذف شبکهی در حال استفاده”docker network rm lx-net-aError response from daemon: error while removing network: network lx-net-a has active endpoints (name:"lx-n2" id:"ee3221d6ac63", name:"lx-n1" id:"cd3b20e4006b")exit status 1تا کانتینری به شبکه وصل است حذف نمیشود (active endpoints). راهحل: اول کانتینرها را حذف یا disconnect کن.
۳) اسم شبکهی تکراری
Section titled “۳) اسم شبکهی تکراری”docker network create lx-net-bError response from daemon: network with name lx-net-b already existsاسم شبکه یکتاست (network with name ... already exists). راهحل: اسم دیگر یا حذف قبلی.
۴) فکر کردن «کانتینرها به هم میرسند چون روی یک میزباناند»
Section titled “۴) فکر کردن «کانتینرها به هم میرسند چون روی یک میزباناند»”مثال ۴: نه؛ شبکههای مختلف ایزولهاند. راهحل: همه را در یک شبکه بگذار یا docker network connect.
۵) استفاده از --network host روی مک/ویندوز برای دسترسی به localhost
Section titled “۵) استفاده از --network host روی مک/ویندوز برای دسترسی به localhost”فایده ندارد (میزبان = VM داکر). راهحل: -p یا host.docker.internal.
یک شبکه به اسم lx-net-a را فهرست کن و subnet اش را با docker network inspect بخوان.
دیدن جواب
docker network inspect lx-net-a --format '{{(index .IPAM.Config 0).Subnet}}'172.20.0.0/16تمرین اصلی: دو کانتینر را در یک شبکهی سفارشی به هم وصل کن. شبکهی lx-net-c را بساز، دو کانتینر alpine (lx-n4 و lx-n5) در آن بگذار و از یکی به دیگری با اسم ping بزن.
دیدن جواب
docker network create lx-net-c >/dev/nulldocker run -d --name lx-n4 --network lx-net-c alpine sleep 120 >/dev/nulldocker run -d --name lx-n5 --network lx-net-c alpine sleep 120 >/dev/nulldocker exec lx-n4 ping -c 1 -W 2 lx-n5 | sed -n '1p;3p'docker rm -f lx-n4 lx-n5 >/dev/null; docker network rm lx-net-cPING lx-n5 (172.22.0.3): 56 data bytes
lx-net-cسناریوی درس را بساز: شبکههای lx-frontend و lx-backend؛ کانتینر web فقط در frontend، db فقط در backend و api در هر دو. ثابت کن web میتواند api را ببیند ولی نمیتواند db را ببیند، و api میتواند db را ببیند.
دیدن جواب
docker network create lx-frontend >/dev/null; docker network create lx-backend >/dev/nulldocker run -d --name web --network lx-frontend alpine sleep 120 >/dev/nulldocker run -d --name db --network lx-backend alpine sleep 120 >/dev/nulldocker run -d --name api --network lx-frontend alpine sleep 120 >/dev/nulldocker network connect lx-backend apiecho "web -> api: $(docker exec web ping -c 1 -W 2 api >/dev/null 2>&1 && echo OK || echo FAIL)"echo "web -> db: $(docker exec web ping -c 1 -W 2 db >/dev/null 2>&1 && echo OK || echo FAIL)"echo "api -> db: $(docker exec api ping -c 1 -W 2 db >/dev/null 2>&1 && echo OK || echo FAIL)"docker rm -f web db api >/dev/null; docker network rm lx-frontend lx-backend >/dev/nullweb -> api: OKweb -> db: FAILapi -> db: OKآزمونک
Section titled “آزمونک”کانتینری که بدون --network اجرا شود، روی کدام شبکه میرود؟
شبکهی bridge پیشفرض بدون DNS اسم.
کدام درایور کانتینر را کاملاً بیشبکه میکند؟
فقط loopback میماند.
چرا کانتینرهای دو شبکهی سفارشی مختلف همدیگر را نمیبینند؟
برای ارتباط، یکی را به هر دو شبکه وصل کن.
docker network connect چه میکند؟
کانتینر میتواند عضو چند شبکه باشد.
روی مک --network host به چه شبکهای وصل میکند؟
میزبان کانتینر همان VM است.
جمعبندی
Section titled “جمعبندی”- سه شبکهی آماده:
bridge(پیشفرض)،host،none؛ هرdocker runمعمولاً رویbridgeپیشفرض میرود. - شبکهی
bridgeپیشفرض DNS اسم ندارد؛ شبکهی سفارشی (docker network create) دارد. - کانتینر روی شبکهی سفارشی با
--network Nاجرا میشود؛ شبکههای مختلف ایزولهاند. docker network connectیک کانتینر را به چند شبکه میبرد (الگوی پل بین frontend و backend).hostبرای کارایی روی لینوکس؛noneبرای ایزولاسیون کامل.
| دستور | کاری که میکند |
|---|---|
docker network ls | فهرست شبکهها |
docker network create NAME | ساخت شبکهی سفارشی |
docker network inspect NAME | جزئیات شبکه |
docker run --network NAME IMG | اجرا روی یک شبکه |
docker network connect NAME CONTAINER | وصل کردن کانتینر |
docker network disconnect NAME CONTAINER | جدا کردن |
docker network rm NAME | حذف شبکه (باید خالی باشد) |
docker run --network none IMG | بدون شبکه |
docker run --network host IMG | شبکهی میزبان |