← Kursa Dön
📄 Text · 30 min

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      local

Bu üç 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.2
docker inspect web2 --format '{{.NetworkSettings.IPAddress}}'
172.17.0.3

Gü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.3
PING 172.17.0.3: 64 bytes from 172.17.0.3: seq=0 ttl=64 time=0.1ms

Evet, IP adresiyle birbirlerini bulabiliyorlar. Ama şimdi ilginç bir şey deneyelim — IP yerine isimle erişmeye çalışalım:

docker exec web1 ping -c 2 web2
ping: 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 mynet

Bu 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 3600

Dikkat et, bu sefer --network mynet parametresini verdik. Şimdi isimle erişimi deneyelim:

docker exec api ping -c 2 web
PING 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/myapp

IP 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.

ÖzellikVarsayılan BridgeCustom Bridge
İsimle erişim (DNS)❌ Yok✅ Var
Network izolasyonuHerkes aynı yerdeNetwork bazlı izolasyon
Çalışırken bağla/kopar
Konfigürasyon esnekliğiSı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:alpine

Dikkat ettiysen -p ile port mapping yapmadık. Çünkü gerek yok! Nginx doğrudan host'un 80 portunda dinliyor:

curl http://localhost:80

Bu, 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 -i
192.168.1.100

Host 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 3600

Bu container'ın dış dünyayla hiçbir bağlantısı yok. Deneyip görelim:

docker exec isolated ping -c 1 google.com
ping: bad address 'google.com'
docker exec isolated ip addr
1: lo: <LOOPBACK,UP> inet 127.0.0.1/8

Sadece 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                              │     │
│   └──────────────────────┬────────────────────────────┘     │
└──────────────────────────┼──────────────────────────────────┘
                           │
                      İnternet

Gö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 postgres
PING postgres (172.18.0.2): 56 data bytes
64 bytes from 172.18.0.2: seq=0 ttl=64 time=0.085 ms
docker exec api ping -c 1 redis
PING redis (172.18.0.3): 56 data bytes
64 bytes from 172.18.0.3: seq=0 ttl=64 time=0.092 ms

Her iki servis de ismiyle bulunabiliyor. DNS'in nasıl çalıştığını detaylı görmek istersen:

docker exec api nslookup postgres
Server:    127.0.0.11
Address 1: 127.0.0.11:53

Name:      postgres
Address 1: 172.18.0.2 postgres.appnet

Gö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 web
Name:      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:alpine

Dikkat 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 db
ping: bad address 'db'

Frontend, veritabanını göremiyor bile. Ama API'ye erişebilir:

docker exec web ping -c 1 api
PING api (172.19.0.3): 56 data bytes
64 bytes from 172.19.0.3: seq=0 ttl=64 time=0.1ms

Ve API de veritabanına erişebilir:

docker exec api ping -c 1 db
PING 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 \
    mynet

Ya da internet erişimini engelleyen bir internal network oluşturabilirsin:

docker network create --internal secure-net

Bu 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 mynet

Bu 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 mycontainer

Ve artık kullanmadığın network'leri temizlemek için:

docker network prune

Bu 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 api

Eğer ping çalışmıyorsa, DNS çözümlemesini kontrol et:

docker exec web nslookup api

DNS çözümleniyor ama bağlantı kurulamıyorsa, portun dinlenip dinlenmediğini kontrol et:

docker exec web nc -zv api 3000

Ve 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 bash

Bu 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 docker

Hata 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 connect ile ç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.