Docker Network Temelleri — bridge, host, none
Önceki bölümlerde container'ları nasıl oluşturacağımızı, image'ları nasıl yöneteceğimizi öğrendik. Ama şimdiye kadar hep tek başına çalışan container'larla uğraştık. Gerçek hayatta ise uygulamalar böyle çalışmaz. Bir web sitesi düşün — frontend var, backend API var, veritabanı var, belki bir de cache sistemi var. Bunların hepsinin birbiriyle konuşması lazım.
İşte bu derste tam bunu öğreneceğiz: Docker'daki container'lar birbirleriyle nasıl iletişim kurar? Dış dünyayla nasıl konuşur? Ya da nasıl tamamen izole edilir?
Önce Büyük Resmi Görelim
Bunu gerçek hayattan bir örnekle anlatayım. Bir ofis binası düşün. Her katta farklı departmanlar var. Aynı kattaki çalışanlar birbirleriyle doğrudan konuşabilir — kapıyı çalar, "şu dosyayı ver" der. Farklı katlardaki çalışanlar ise ya telefon santralinden geçer ya da asansörle iner çıkar. Ve binanın dışındaki insanlar? Onlar ancak binanın resepsiyonu üzerinden içeridekilere ulaşabilir.
Docker networking de tam bu mantıkla çalışır. Container'lar "odalar", network'ler "katlar", ve host makinenin network arayüzü de binanın "resepsiyonu" gibi düşünebilirsin. Şimdi bu yapıyı adım adım inceleyelim.
Docker'ı bilgisayarına kurduğun anda, Docker sana otomatik olarak üç tane network oluşturur. Bunu hemen görebilirsin:
docker network lsŞöyle bir çıktı alacaksın:
NETWORK ID NAME DRIVER SCOPE
abc123def456 bridge bridge local
def456ghi789 host host local
ghi789jkl012 none null localBu üç network'ün her biri farklı bir amaca hizmet eder. Birini "varsayılan apartman dairesi", birini "ev sahibinin evi", birini de "izole oda" olarak düşünebilirsin. Her birini detaylıca inceleyelim.
Bridge Network — Varsayılan İletişim Yolu
Bir container başlattığında ve hiçbir network belirtmediğinde, Docker onu otomatik olarak bridge network'üne bağlar. Bu, Docker'ın varsayılan network'ü. Hemen deneyelim:
docker run -d --name web1 nginx:alpine
docker run -d --name web2 nginx:alpineİki tane Nginx container'ı başlattık. Hiçbir --network parametresi vermedik, yani ikisi de varsayılan bridge network'üne bağlandı. Peki bunların IP adreslerini görmek istersek ne yaparız?
docker inspect web1 --format '{{.NetworkSettings.IPAddress}}'172.17.0.2docker inspect web2 --format '{{.NetworkSettings.IPAddress}}'172.17.0.3Güzel, her ikisinin de 172.17.0.x aralığında bir IP adresi var. Yani aynı "kattalar" diyebiliriz. Peki birbirlerine ulaşabilirler mi? Deneyelim:
docker exec web1 ping -c 2 172.17.0.3PING 172.17.0.3: 64 bytes from 172.17.0.3: seq=0 ttl=64 time=0.1msEvet, IP adresiyle birbirlerini bulabiliyorlar. Ama şimdi ilginç bir şey deneyelim — IP yerine isimle erişmeye çalışalım:
docker exec web1 ping -c 2 web2ping: bad address 'web2'Çalışmadı! Varsayılan bridge network'ünde container'lar birbirini isimle bulamaz. Sadece IP adresiyle iletişim kurabilirler. Bu çok önemli bir sınırlama çünkü IP adresleri her container restart'ında değişebilir. Yarın web2'yi yeniden başlattığında 172.17.0.5 alabilir ve artık 172.17.0.3'e yaptığın bağlantılar çalışmaz.
Peki bu sorunu nasıl çözeriz? İşte burada custom network devreye giriyor.
Custom Bridge Network — Doğru Yöntem
Kendi network'ümüzü oluşturduğumuzda, Docker bize otomatik DNS hizmeti verir. Yani container'lar birbirini isimle bulabilir. Hemen deneyelim:
docker network create mynetBu kadar — mynet adında yeni bir bridge network oluşturduk. Şimdi container'larımızı bu network'te başlatalım:
docker run -d --name web --network mynet nginx:alpine
docker run -d --name api --network mynet node:20-alpine sleep 3600Dikkat et, bu sefer --network mynet parametresini verdik. Şimdi isimle erişimi deneyelim:
docker exec api ping -c 2 webPING web (172.18.0.2): 64 bytes from 172.18.0.2: seq=0 ttl=64 time=0.1msÇalıştı! api container'ı, web container'ını ismiyle bulabildi. Peki bu neden önemli? Çünkü artık kodunda veritabanı bağlantısını şöyle yazabilirsin:
DATABASE_URL=postgres://db:5432/myappIP adresi yerine db yazdın. Container restart olsa bile, IP değişse bile, Docker'ın DNS sunucusu db ismini her zaman doğru IP'ye yönlendirecek. Bu, production ortamında hayat kurtaran bir özellik.
Şimdi iki network tipi arasındaki farkları bir tablo ile özetleyelim — ama asıl önemli olan şu: her zaman custom network kullan, varsayılan bridge'i hiç kullanma.
| Özellik | Varsayılan Bridge | Custom Bridge |
|---|---|---|
| İsimle erişim (DNS) | ❌ Yok | ✅ Var |
| Network izolasyonu | Herkes aynı yerde | Network bazlı izolasyon |
| Çalışırken bağla/kopar | ❌ | ✅ |
| Konfigürasyon esnekliği | Sınırlı | Tam kontrol |
Host Network — Doğrudan Bağlantı
Bridge network bir nevi "çevirmen" gibi çalışır — container'ın kendi IP'si var, host'un kendi IP'si var, aralarında NAT (Network Address Translation) yapılır. Ama bazen bu çevirmen katmanını istemezsin. Mesela çok yüksek performans gerektiren bir uygulaman var ve her milisaniye önemli.
İşte host network tam bunu sağlar. Container, host makinenin network'ünü doğrudan kullanır. Kendi ayrı IP'si olmaz — host'un IP'sidir:
docker run -d --name web --network host nginx:alpineDikkat ettiysen -p ile port mapping yapmadık. Çünkü gerek yok! Nginx doğrudan host'un 80 portunda dinliyor:
curl http://localhost:80Bu, Nginx'in default sayfasını döndürecek. Container'ın IP'sine bakarsan host'un IP'sini görürsün:
docker exec web hostname -i192.168.1.100Host networking kulağa harika gelse de ciddi dezavantajları var. İki container aynı portu kullanamaz — ikisi de 80 isterse çakışma olur. Ayrıca izolasyon sıfır, yani güvenlik riski daha yüksek. Ve önemli bir detay: macOS ve Windows'ta düzgün çalışmaz, çünkü Docker bu işletim sistemlerinde bir VM içinde çalışır.
O yüzden host networking'i sadece çok özel durumlar için kullan: yüksek performans gerektiren uygulamalar, network monitoring araçları, ya da host'un tüm trafiğini görmesi gereken özel yazılımlar.
None Network — Tamamen İzole
Bazen bir container'ın hiçbir ağ bağlantısının olmamasını istersin. Güvenilmeyen bir kodu çalıştırıyorsundur, ya da offline veri işleme yapıyorsundur. İşte none network tam bunu sağlar:
docker run -d --name isolated --network none alpine sleep 3600Bu container'ın dış dünyayla hiçbir bağlantısı yok. Deneyip görelim:
docker exec isolated ping -c 1 google.comping: bad address 'google.com'docker exec isolated ip addr1: lo: <LOOPBACK,UP> inet 127.0.0.1/8Sadece loopback (kendisiyle konuşma) arayüzü var. Başka hiçbir ağ bağlantısı yok. İnternet yok, diğer container'lara erişim yok — tamamen karanlık bir oda.
Docker'ın Network Mimarisini Anlamak
Şimdi bir adım geri çekilip büyük resme bakalım. Docker networking'in nasıl çalıştığını gerçekten anlamak için mimariye göz atalım:
┌─────────────────────────────────────────────────────────────┐
│ Host Machine │
│ │
│ ┌────────────── docker0 (bridge) ──────────────────┐ │
│ │ 172.17.0.0/16 │ │
│ │ │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │Container A│ │Container B│ │Container C│ │ │
│ │ │172.17.0.2 │ │172.17.0.3 │ │172.17.0.4 │ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ │ │
│ │ │ │
│ └──────────────────────┬────────────────────────────┘ │
│ │ NAT │
│ ┌──────────────────────▼────────────────────────────┐ │
│ │ eth0 (Host Network Interface) │ │
│ │ 192.168.1.100 │ │
│ └──────────────────────┬────────────────────────────┘ │
└──────────────────────────┼──────────────────────────────────┘
│
İnternetGördüğün gibi, Docker varsayılan olarak docker0 adında sanal bir bridge interface oluşturur. Her container bu bridge'e bağlanır ve 172.17.0.x aralığından bir IP alır. Dış dünyaya çıkmak için ise NAT (adres çevirisi) kullanılır — tıpkı evdeki router'ın yaptığı gibi.
Custom network oluşturduğunda ise Docker yeni bir bridge interface oluşturur (mesela br-5a2b3c4d) ve bu network'e bağlı container'lar kendi subnet'lerini alır (mesela 172.18.0.x).
Container DNS ve Service Discovery
Custom network'ün en güçlü özelliği, Docker'ın dahili DNS sunucusudur. Bu sunucu 127.0.0.11 adresinde çalışır ve container isimlerini otomatik olarak IP adreslerine çevrir.
Bunu bir senaryo üzerinde görelim. Diyelim ki bir uygulama geliştiriyorsun — PostgreSQL veritabanı, Redis cache ve bir API servisi var:
docker network create appnet
docker run -d --name postgres --network appnet -e POSTGRES_PASSWORD=secret postgres:16
docker run -d --name redis --network appnet redis:7
docker run -d --name api --network appnet node:20-alpine sleep 3600Şimdi api container'ından diğerlerine erişmeyi deneyelim:
docker exec api ping -c 1 postgresPING postgres (172.18.0.2): 56 data bytes
64 bytes from 172.18.0.2: seq=0 ttl=64 time=0.085 msdocker exec api ping -c 1 redisPING redis (172.18.0.3): 56 data bytes
64 bytes from 172.18.0.3: seq=0 ttl=64 time=0.092 msHer iki servis de ismiyle bulunabiliyor. DNS'in nasıl çalıştığını detaylı görmek istersen:
docker exec api nslookup postgresServer: 127.0.0.11
Address 1: 127.0.0.11:53
Name: postgres
Address 1: 172.18.0.2 postgres.appnetGördüğün gibi, DNS sunucusu 127.0.0.11 — bu Docker'ın dahili DNS'i. Her container oluşturulduğunda, Docker otomatik olarak o container'ın ismini DNS kaydı olarak ekler. Bu sayede sen kodunda postgres://postgres:5432 yazarsın ve Docker gerisini halleder.
Network Alias — Birden Fazla İsim Verme
Bazen bir container'a birden fazla isim vermek istersin. Mesela veritabanı container'ının adı postgres-v16-main ama uygulaman db olarak arıyor. İşte network alias tam bunu sağlar:
docker run -d --name postgres-v16-main \
--network appnet \
--network-alias db \
--network-alias database \
--network-alias pg \
-e POSTGRES_PASSWORD=secret \
postgres:16Şimdi bu container'a dört farklı isimle erişebilirsin:
docker exec api ping -c 1 postgres-v16-main # Orijinal isim ✓
docker exec api ping -c 1 db # Alias ✓
docker exec api ping -c 1 database # Alias ✓
docker exec api ping -c 1 pg # Alias ✓Hepsi aynı IP'ye çözümlenir. Bu özellik özellikle veritabanı migration'larında çok işe yarar — eski container'ı yenisiyle değiştirirken alias'ı aynı tutarsın ve uygulama kodunda hiçbir şeyi değiştirmezsin.
Round-Robin DNS — Basit Yük Dağılımı
Bir de şöyle ilginç bir senaryo var: aynı alias'ı birden fazla container'a verirsen ne olur?
docker network create loadnet
docker run -d --name web1 --network loadnet --network-alias web nginx:alpine
docker run -d --name web2 --network loadnet --network-alias web nginx:alpine
docker run -d --name web3 --network loadnet --network-alias web nginx:alpineÜç farklı Nginx container'ı, hepsi web alias'ına sahip. Şimdi DNS sorgusuna bakalım:
docker run --rm --network loadnet alpine nslookup webName: web
Address 1: 172.20.0.2
Address 2: 172.20.0.3
Address 3: 172.20.0.4Üç IP de dönüyor! Docker, web ismine yapılan her DNS sorgusunda bu üç IP'yi farklı sıralarda döndürür. Bu sayede istekler farklı container'lara dağılır — basit bir load balancing.
⚠️ Ama dikkatli ol: Round-robin DNS gerçek bir load balancer değildir. DNS cache'leme, sağlık kontrolü ve akıllı dağıtım yapmaz. Production ortamında Nginx, HAProxy veya Traefik gibi gerçek bir load balancer kullan.
Gerçek Dünya Senaryosu: Üç Katmanlı Uygulama
Şimdi öğrendiklerimizi birleştirip gerçek bir senaryo kuralım. Diyelim ki bir web uygulaması yapıyorsun: frontend (Nginx), backend API ve PostgreSQL veritabanı. Güvenlik açısından şunu istiyorsun: frontend veritabanına doğrudan erişemesin, her şey API üzerinden geçsin.
Bunu farklı network'ler oluşturarak sağlayabiliriz:
docker network create frontend-net
docker network create backend-netİki network oluşturduk. Şimdi container'ları yerleştirelim:
# Veritabanı — sadece backend network'te
docker run -d \
--name db \
--network backend-net \
-e POSTGRES_PASSWORD=secret \
-v pgdata:/var/lib/postgresql/data \
postgres:16
# API — hem frontend hem backend network'te (köprü rolü)
docker run -d \
--name api \
--network backend-net \
-e DATABASE_URL=postgres://postgres:secret@db:5432/postgres \
node:20-alpine sleep 3600
docker network connect frontend-net api
# Frontend — sadece frontend network'te
docker run -d \
--name web \
--network frontend-net \
-p 80:80 \
nginx:alpineDikkat ettiysen API container'ını önce backend-net'e bağladık, sonra docker network connect ile frontend-net'e de ekledik. Yani API hem frontend hem backend tarafını görebiliyor — köprü rolü oynuyor.
Sonuç olarak erişim matrisi şöyle oluştu:
frontend-net: web ←→ api (ikisi de bu network'te)
backend-net: api ←→ db (ikisi de bu network'te)
web → db: ❌ (farklı network'ler, erişim yok!)Frontend doğrudan veritabanına erişemez. Bu, güvenlik açısından çok önemli bir katman — saldırgan frontend container'ına girse bile veritabanına ulaşamaz.
Bu testi yaparak doğrulayabiliriz:
docker exec web ping -c 1 dbping: bad address 'db'Frontend, veritabanını göremiyor bile. Ama API'ye erişebilir:
docker exec web ping -c 1 apiPING api (172.19.0.3): 56 data bytes
64 bytes from 172.19.0.3: seq=0 ttl=64 time=0.1msVe API de veritabanına erişebilir:
docker exec api ping -c 1 dbPING db (172.20.0.2): 56 data bytes
64 bytes from 172.20.0.2: seq=0 ttl=64 time=0.085msİşte bu, Docker networking'in gücü. Farklı network'ler oluşturarak güvenlik katmanları inşa edebilirsin.
Network Yönetim Komutları
Şimdi günlük hayatta sıkça kullanacağın network yönetim komutlarına bakalım.
Yeni network oluşturmayı zaten gördük ama daha detaylı seçenekler de var. Mesela kendi subnet'ini belirlemek isteyebilirsin:
docker network create \
--subnet=10.10.0.0/16 \
--gateway=10.10.0.1 \
mynetYa da internet erişimini engelleyen bir internal network oluşturabilirsin:
docker network create --internal secure-netBu network'teki container'lar birbirleriyle konuşabilir ama internete çıkamaz. Veritabanı container'ları için ideal.
Bir network'ün detaylarını görmek için inspect komutunu kullanırsın:
docker network inspect mynetBu komut sana network'ün subnet'ini, gateway'ini ve bağlı container'ların listesini gösterir. Hangi container hangi IP'yi almış, hepsini burada görebilirsin.
Çalışan bir container'ı yeni bir network'e bağlamak veya mevcut bir network'ten koparmak da çok kolay:
# Çalışan container'ı yeni network'e ekle
docker network connect mynet mycontainer
# Network'ten kopar
docker network disconnect mynet mycontainerVe artık kullanmadığın network'leri temizlemek için:
docker network pruneBu komut, hiçbir container'a bağlı olmayan tüm custom network'leri siler. Varsayılan bridge, host ve none network'lerine dokunmaz.
Network Sorun Giderme
Docker networking'te sorun yaşadığında — ki başlarda sık yaşarsın — şu adımları takip et:
Önce container'ın hangi network'te olduğunu kontrol et:
docker inspect web --format '{{range $key, $val := .NetworkSettings.Networks}}{{$key}}: {{$val.IPAddress}}{{"\n"}}{{end}}'Sonra hedef container'a ping at:
docker exec web ping -c 3 apiEğer ping çalışmıyorsa, DNS çözümlemesini kontrol et:
docker exec web nslookup apiDNS çözümleniyor ama bağlantı kurulamıyorsa, portun dinlenip dinlenmediğini kontrol et:
docker exec web nc -zv api 3000Ve en güçlü debug aracı olarak nicolaka/netshoot image'ını kullanabilirsin — içinde curl, dig, tcpdump, iftop gibi tüm network araçları var:
docker run --rm -it --network mynet nicolaka/netshoot bashBu container'dan network'teki tüm servisleri test edebilir, paket yakalayabilir ve detaylı analiz yapabilirsin.
Sıkça Yapılan Hatalar
Başlangıçta herkesin düştüğü birkaç tuzak var, bunları burada paylaşayım ki sen düşme.
Hata 1: Varsayılan bridge'de isimle erişmeye çalışmak. docker run ile container başlattın, --network vermedin, sonra isimle ping atmaya çalışıyorsun — çalışmaz. Çözüm: Custom network oluştur, --network ile belirt.
Hata 2: Port çakışması. İki container'ı da -p 80:80 ile başlatmaya çalışıyorsun — ikincisi hata verir çünkü host'un 80 portu zaten dolu. Çözüm: Farklı host portları kullan (-p 80:80 ve -p 81:80).
Hata 3: Container'ın dış dünyaya erişememesi. Container içinden ping google.com çalışmıyor. Bu genellikle DNS problemi. Container'ın /etc/resolv.conf dosyasını kontrol et. Gerekirse Docker daemon'a DNS ayarı ekle:
# /etc/docker/daemon.json
{
"dns": ["8.8.8.8", "8.8.4.4"]
}sudo systemctl restart dockerHata 4: Container IP'lerini kodda hardcode etmek. "172.17.0.3" yazdın, container restart oldu, IP değişti, uygulama patladı. Her zaman container ismi veya alias kullan — Docker DNS'i gerisini halleder.
Bu Derste Ne Öğrendik?
Bu derste Docker networking'in temellerini öğrendik. Hızlıca özetleyelim:
Docker üç varsayılan network oluşturur: bridge (varsayılan), host (doğrudan bağlantı) ve none (izole).
Varsayılan bridge network'te DNS çözümleme yoktur — container'lar birbirini isimle bulamaz. Bu yüzden her zaman custom network kullan.
Custom bridge network'te Docker otomatik DNS sağlar — container'lar birbirini isimle bulur.
Network alias ile bir container'a birden fazla DNS ismi verebilirsin.
Farklı network'ler oluşturarak güvenlik katmanları inşa edebilirsin — frontend veritabanına doğrudan erişemesin.
docker network connectile çalışan container'ları yeni network'lere bağlayabilirsin.
Bir sonraki derste container'lar arası iletişimi daha derinlemesine inceleyeceğiz — DNS detayları, service discovery pattern'leri ve farklı network'lerdeki container'ların nasıl iletişim kuracağını göreceğiz.
AI Asistan
Sorularını yanıtlamaya hazır