← Kursa Dön
📄 Text · 30 min

Container'lar Arası İletişim — DNS ve Linking

Bir önceki derste Docker networking'in temellerini öğrendik: bridge, host, none network'leri ve custom network'lerin avantajlarını gördük. Bu derste bir adım daha ileri gidiyoruz. Container'lar birbirini nasıl buluyor? Docker'ın DNS sistemi nasıl çalışıyor? Ve büyük projelerde servisler birbirini nasıl keşfediyor?

Şöyle düşün: bir şirketin dahili telefon rehberini bilirsin. Mehmet'i aramak istiyorsun ama dahili numarasını bilmiyorsun. Rehberden "Mehmet, Muhasebe" diye ararsın, sistem onu bulur. Mehmet masasını değiştirirse yeni numara alır ama sen hâlâ "Mehmet" diye ararsın — rehber otomatik güncellenir.

Docker'ın DNS sunucusu tam bu rehber gibi çalışır. Container'ların IP adresleri değişebilir ama isimleri sabit kalır. Docker DNS'i her zaman doğru IP'ye yönlendirir. Hadi bu mekanizmayı derinlemesine inceleyelim.


Docker DNS'i Nasıl Çalışır?

Hatırla, bir önceki derste önemli bir ayrım yapmıştık: varsayılan bridge network'te DNS yok, custom network'te var. Bu derste hep custom network üzerinden gideceğiz çünkü gerçek projelerde zaten custom network kullanacaksın.

Docker, her custom bridge network için 127.0.0.11 adresinde bir DNS sunucusu çalıştırır. Bu sunucu container oluşturulduğunda container ismini otomatik olarak DNS kaydına ekler. Bunu bir şema ile gösterelim:

┌──────────────────────────────────────────────────────┐
│                    Custom Network                     │
│                                                       │
│  ┌─────────┐                                         │
│  │   web   │─────────────┐                           │
│  │ .0.2    │             │                           │
│  └─────────┘             ▼                           │
│                 ┌──────────────────┐                 │
│                 │  Docker DNS       │                 │
│  ┌─────────┐   │  127.0.0.11:53    │                 │
│  │   api   │──→│                    │                 │
│  │ .0.3    │   │  "web" → .0.2     │                 │
│  └─────────┘   │  "api" → .0.3     │                 │
│                 │  "db"  → .0.4     │                 │
│  ┌─────────┐   │                    │                 │
│  │   db    │──→└──────────────────┘                 │
│  │ .0.4    │                                         │
│  └─────────┘                                         │
└──────────────────────────────────────────────────────┘

Her container, DNS sunucusuna kayıtlı. Bir container başka birini isimleriyle ararsa, DNS sunucusu doğru IP'yi döndürür. Hadi bunu adım adım kurup test edelim.

docker network create mynet

docker run -d --name web --network mynet nginx:alpine
docker run -d --name api --network mynet node:20-alpine sleep 3600
docker run -d --name db --network mynet -e POSTGRES_PASSWORD=secret postgres:16

Üç servisimiz hazır. Şimdi api container'ından diğerlerine erişelim:

docker exec api ping -c 2 web
PING web (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 2 db
PING db (172.18.0.4): 56 data bytes
64 bytes from 172.18.0.4: seq=0 ttl=64 time=0.092 ms

HTTP üzerinden de test edelim:

docker exec api wget -qO- http://web:80

Bu komut Nginx'in varsayılan HTML sayfasını döndürecek. web ismi Docker DNS tarafından çözümleniyor ve bağlantı kuruluyor.

Peki DNS çözümlemesinin detaylarını görmek istersek? nslookup komutu tam bunu yapar:

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

Name:      web
Address 1: 172.18.0.2 web.mynet

İlk satıra dikkat: DNS sunucusu 127.0.0.11 — bu Docker'ın dahili DNS'i. Container'ın /etc/resolv.conf dosyasına bakarsak bunu doğrulayabiliriz:

docker exec api cat /etc/resolv.conf
nameserver 127.0.0.11
options ndots:0

Docker, her container oluşturduğunda bu dosyayı otomatik yapılandırır ve kendi DNS sunucusunu nameserver olarak ayarlar.


IP Adresleri Neden Güvenilmez?

Bu noktada "Neden isim kullanıyoruz, IP adresi de çalışıyor" diye düşünebilirsin. Haklı bir soru. Şimdi IP kullanmanın neden tehlikeli olduğunu görelim:

docker inspect web --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'
172.18.0.2

Şimdi web container'ını yeniden başlatalım:

docker restart web

Ve IP'yi tekrar kontrol edelim:

docker inspect web --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'
172.18.0.5

Gördün mü? IP değişti! 172.18.0.2 idi, şimdi 172.18.0.5 oldu. Eğer uygulama kodunda IP'yi hardcode etmiş olsaydın, bağlantı kopardı. Ama isim kullanıyorsan sorun yok:

docker exec api ping -c 1 web

Docker DNS, web ismini her zaman güncel IP'ye çözümler. Container restart olsa da, IP değişse de, isim her zaman doğru yere işaret eder.

Bu yüzden altın kural şu: container'lar arasında asla IP adresi kullanma, her zaman isim kullan.

# ❌ Yanlış — IP değişebilir
DATABASE_URL=postgres://172.18.0.4:5432/myapp

# ✅ Doğru — isim her zaman çalışır
DATABASE_URL=postgres://db:5432/myapp

Network Alias ile Soyutlama

Bir önceki derste alias kavramına değinmiştik. Şimdi daha derinlemesine bakalım çünkü gerçek projelerde çok kullanacaksın.

Diyelim ki PostgreSQL container'ının adı postgres-v16-production. Ama uygulaman db olarak arıyor. Container adını değiştirmek istemiyorsun çünkü monitoring araçların bu isme bağlı. İşte alias tam bu durumda devreye girer:

docker run -d \
    --name postgres-v16-production \
    --network mynet \
    --network-alias db \
    --network-alias database \
    -e POSTGRES_PASSWORD=secret \
    postgres:16

Artık bu container'a dört farklı isimle erişebilirsin: postgres-v16-production (asıl isim), db (alias), database (alias). Hepsi aynı IP'ye çözümlenir.

Alias'ın en güçlü yanı soyutlama sağlaması. Yarın PostgreSQL 17'ye geçmek istediğinde şunu yaparsın:

  1. Yeni container'ı postgres-v17-production adıyla oluştur, aynı db alias'ını ver

  2. Eski container'ı durdur

  3. Uygulama kodunda hiçbir şey değiştirmene gerek yok — hâlâ db ismiyle erişiyor

Bu, microservice mimarisinde servis versiyonlamayı kolaylaştıran çok güçlü bir pattern.

Farklı Network'lerde Farklı Alias

Bir container birden fazla network'e bağlıysa, her network'te farklı alias kullanabilir:

docker network create frontend-net
docker network create backend-net

docker run -d --name myapi \
    --network frontend-net \
    --network-alias api \
    node:20-alpine sleep 3600

docker network connect --alias backend-api backend-net myapi

Artık frontend-net'teki container'lar bu servise api olarak, backend-net'tekiler ise backend-api olarak erişir. Aynı container, farklı perspektiflerden farklı isimlerle görünüyor.


DNS Çözümleme Sırası

Container içinde bir isim çözümlenirken Docker şu sırayı takip eder:

İlk olarak /etc/hosts dosyası kontrol edilir. Container'ın kendi hostname'i ve localhost burada tanımlıdır:

docker exec api cat /etc/hosts
127.0.0.1   localhost
172.18.0.3  abc123def456  api

Eğer aranan isim burada yoksa, Docker'ın dahili DNS sunucusu (127.0.0.11) sorgulanır. Container isimleri ve alias'lar burada çözümlenir.

Eğer dahili DNS'te de bulunamazsa (mesela google.com gibi dış bir domain), sorgu host'un DNS sunucusuna yönlendirilir.

Bu sıralamayı bilmek, sorun giderme yaparken çok işine yarar. Mesela bir container'a özel DNS kaydı eklemek istiyorsan, --add-host parametresini kullanabilirsin:

docker run -d \
    --add-host myhost:192.168.1.100 \
    --add-host db.local:10.0.0.50 \
    --name api myapp

Bu, container'ın /etc/hosts dosyasına ek kayıtlar ekler. Özellikle container'dan host makineye erişmek için kullanışlı bir yöntem var:

docker run -d \
    --add-host host.docker.internal:host-gateway \
    --name api myapp

host.docker.internal ismi artık host makinenin IP'sine çözümlenecek. Container içinden host'ta çalışan servislere erişmek istediğinde bu çok işe yarar.


Legacy Linking — Kullanma, Ama Bil

Docker'ın eski sürümlerinde container'lar arası iletişim --link flag'i ile sağlanırdı. Artık deprecated (kullanımdan kaldırılmış) ama eski projelerde karşına çıkabilir, o yüzden bilmende fayda var:

# ❌ Eski yöntem (deprecated — kullanma)
docker run -d --name db postgres:16 -e POSTGRES_PASSWORD=secret
docker run -d --name api --link db:database myapp

--link tek yönlü çalışır (sadece api→db, tersi olmaz), çalışırken bağlama/koparma desteklemez ve network izolasyonu sağlamaz. Custom network bunların hepsini çok daha iyi yapıyor.

Eğer eski bir projede --link görürsen, yapılacak şey basit: custom network oluştur, container'ları bu network'e taşı, --link'leri kaldır.


Farklı Network'lerdeki Container'lar Nasıl Konuşur?

Bir önceki derste üç katmanlı mimariyi gördük. Şimdi bu konuyu biraz daha derinleştirelim çünkü pratikte çok karşılaşacaksın.

İki container farklı network'lerde olduğunda birbirini göremez — bu Docker'ın bize sağladığı izolasyon. Ama bazen bir container'ın iki farklı network'ü köprülemesi gerekir. Bu durumda o container'ı her iki network'e de bağlarsın.

docker network create frontend
docker network create backend

# DB sadece backend'de
docker run -d --name db --network backend -e POSTGRES_PASSWORD=secret postgres:16

# API hem frontend hem backend'de — köprü
docker run -d --name api --network backend \
    -e DATABASE_URL=postgres://postgres:secret@db:5432/postgres \
    node:20-alpine sleep 3600
docker network connect frontend api

# Web sadece frontend'de
docker run -d --name web --network frontend -p 80:80 nginx:alpine

Şimdi erişim testlerini yapalım:

# web → api: ✅ (ikisi de frontend network'te)
docker exec web ping -c 1 api
PING api (172.19.0.3): 56 data bytes
64 bytes from 172.19.0.3: ...
# api → db: ✅ (ikisi de backend network'te)
docker exec api ping -c 1 db
PING db (172.20.0.2): 56 data bytes
64 bytes from 172.20.0.2: ...
# web → db: ❌ (farklı network'ler)
docker exec web ping -c 1 db
ping: bad address 'db'

Güvenlik katmanı çalışıyor. Frontend, veritabanını göremiyor bile — DNS'te kaydı yok çünkü farklı network'teler. Tüm veri akışı API üzerinden geçmek zorunda.


Service Discovery Pattern'leri

Şimdiye kadar Docker'ın dahili DNS'ini kullandık. Bu çoğu proje için yeterli. Ama daha büyük ve karmaşık mimarilerde farklı service discovery yaklaşımları da kullanılır. Bunlara kısaca değinelim.

Pattern 1 — Docker DNS (Basit ve Etkili): Bu derste öğrendiğimiz yöntem. Container ismi veya alias kullan, Docker DNS çözümlesin. Küçük ve orta ölçekli projeler için mükemmel. Docker Compose ile birlikte kullandığında inanılmaz pratik.

docker network create appnet
docker run -d --name postgres --network appnet postgres:16
docker run -d --name api --network appnet \
    -e DATABASE_URL=postgres://postgres@postgres:5432/myapp myapp

Pattern 2 — Network Alias ile Soyutlama: Container isminden bağımsız, sabit bir alias üzerinden erişim. Versiyon geçişlerinde ve blue-green deployment'larda kullanışlı.

docker run -d --name postgres-v16-main \
    --network appnet \
    --network-alias db \
    postgres:16

Yarın postgres-v17-main ile değiştirirsin, db alias'ını verirsin — uygulama hiçbir şey farketmez.

Pattern 3 — Wait-for-it ile Hazırlık Kontrolü: Container'lar arası iletişimde sıkça karşılaşılan bir sorun var: API başladı ama veritabanı henüz hazır değil. DNS çözümleniyor, IP var, ama PostgreSQL henüz bağlantı kabul etmiyor. Bu durumda "wait-for-it" pattern'ı kullanırsın:

docker exec api sh -c '
while ! nc -z db 5432; do
    echo "Veritabanı bekleniyor..."
    sleep 2
done
echo "Veritabanı hazır!"
'

Bu script, veritabanının 5432 portunu dinlemeye başlayana kadar bekler. Docker Compose'da bunu daha zarif şekilde healthcheck ve depends_on ile çözeceğiz — ilerideki bölümlerde göreceğiz.

Pattern 4 — Consul/etcd ile Gelişmiş Discovery: Microservice mimarilerinde düzinelerce, hatta yüzlerce servis olduğunda Docker DNS yetmeyebilir. Bu durumda Consul veya etcd gibi araçlar kullanılır — servisler kendilerini kayıt eder, sağlık kontrolü yapılır, dinamik konfigürasyon sağlanır. Ama bu kurs kapsamında bu kadar derine inmeyeceğiz.


DNS Sorun Giderme

Container'lar arası iletişim çalışmadığında yapacağın ilk şey DNS'i kontrol etmek. Sırayla gidelim.

Sorun 1 — Container ismi çözümlenemiyor:

docker exec api ping db
# ping: bad address 'db'

Muhtemelen iki container farklı network'lerde. Kontrol et:

docker network inspect mynet --format '{{range .Containers}}{{.Name}} {{end}}'

Eğer db bu listede yoksa, onu network'e ekle:

docker network connect mynet db

Sorun 2 — DNS çözümleniyor ama bağlantı kurulamıyor:

docker exec api nslookup db
# 172.18.0.4 ← IP var

docker exec api nc -zv db 5432
# Connection refused

IP çözümleniyor ama port dinlemiyor. Muhtemelen veritabanı henüz başlamamış. Logları kontrol et:

docker logs db

Eğer "Initdb running..." gibi bir mesaj görüyorsan, veritabanı hâlâ başlatılıyor demektir. Birkaç saniye bekle.

Sorun 3 — Varsayılan bridge'de DNS yok:

Eğer --network parametresi vermeden container başlattıysan, varsayılan bridge'desin ve DNS çalışmaz. Çözüm: custom network oluştur ve container'ları oraya taşı.


Bağlantı Test Araçları

Container'lar arası iletişimi test etmek için birkaç araç bilmende fayda var. Hepsini container içinden çalıştırırsın:

ping — En basit erişim testi. Container'ın diğerine ulaşıp ulaşamadığını kontrol eder:

docker exec api ping -c 3 db

wget — HTTP endpoint testi. Web servislerinin yanıt verip vermediğini kontrol eder:

docker exec api wget -qO- http://web:80/

nc (netcat) — Port erişim testi. Belirli bir portun açık olup olmadığını kontrol eder:

docker exec api nc -zv db 5432
# db (172.18.0.4:5432) open

nslookup — DNS çözümleme testi. Bir ismin hangi IP'ye çözümlendiğini gösterir:

docker exec api nslookup web

Ve en güçlü araç olarak netshoot — tüm network debug araçlarını içeren özel bir container:

docker run --rm -it --network mynet nicolaka/netshoot bash

İçeride curl, dig, tcpdump, iftop, ss, traceroute ve daha birçok araç hazır bekliyor. Network sorunu yaşadığında bu container'ı network'e bağla ve detaylı analiz yap.


Gerçek Dünya Senaryosu: E-Ticaret Microservice İletişimi

Öğrendiklerimizi bir araya getiren gerçekçi bir senaryo kuralım. Bir e-ticaret uygulaması yapıyorsun: veritabanı, cache, auth servisi, product servisi ve bir API gateway var.

docker network create ecommerce

# Veritabanı
docker run -d --name postgres \
    --network ecommerce \
    --network-alias db \
    -e POSTGRES_PASSWORD=secret \
    -v pgdata:/var/lib/postgresql/data \
    postgres:16

# Cache
docker run -d --name redis \
    --network ecommerce \
    --network-alias cache \
    redis:7 --maxmemory 256mb

# Auth servisi
docker run -d --name auth-service \
    --network ecommerce \
    --network-alias auth \
    -e DATABASE_URL=postgres://postgres:secret@db:5432/auth \
    -e REDIS_URL=redis://cache:6379 \
    node:20-alpine sleep 3600

# Product servisi
docker run -d --name product-service \
    --network ecommerce \
    --network-alias products \
    -e DATABASE_URL=postgres://postgres:secret@db:5432/products \
    -e AUTH_SERVICE_URL=http://auth:3000 \
    node:20-alpine sleep 3600

# API Gateway
docker run -d --name gateway \
    --network ecommerce \
    -p 80:80 \
    -e AUTH_URL=http://auth:3000 \
    -e PRODUCTS_URL=http://products:3000 \
    nginx:alpine

Dikkat et, her servis birbirini isimle (alias ile) buluyor. Hiçbir yerde IP adresi yok. Bağlantı URL'leri anlamlı ve okunabilir: postgres://postgres@db:5432, redis://cache:6379, http://auth:3000.

İletişim akışı şöyle oluşuyor:

Client → Gateway (port 80) → Auth/Products → DB/Redis

Her servis sadece ihtiyaç duyduğu servislere erişiyor. Ve hepsi tek bir network'te olduğu için DNS çözümlemesi otomatik çalışıyor.

Tabii gerçek production'da bunları Docker Compose ile yönetirsin — ilerideki bölümlerde göreceğiz. Ama temelde olan şey tam budur: container'lar isimlerle birbirini buluyor, Docker DNS arka planda her şeyi yönetiyor.


Özel DNS Ayarları

Bazen container'ların DNS davranışını özelleştirmek istersin. Docker buna birkaç seçenek sunuyor.

Özel DNS sunucusu belirtmek için:

docker run -d --dns 8.8.8.8 --dns 8.8.4.4 --name api myapp

DNS arama domain'i eklemek için:

docker run -d --dns-search mycompany.com --name api myapp

Bu ayarla, container içinde sadece server diye arasan, Docker server.mycompany.com olarak da deneyecek.

Ve daha önce gördüğümüz gibi, /etc/hosts'a özel kayıt eklemek için:

docker run -d --add-host mydb:10.0.0.50 --name api myapp

Bu özellikler genellikle kurumsal ortamlarda, özel DNS sunucuları olan ağlarda veya test senaryolarında kullanılır.


Bu Derste Ne Öğrendik?

Bu dersi özetleyelim:

  • Docker custom network'lerde otomatik DNS sunucusu (127.0.0.11) çalışır — container'lar birbirini isimle bulur.

  • Container IP'leri restart'ta değişebilir — bu yüzden asla IP hardcode etme, her zaman isim kullan.

  • Network alias ile bir container'a birden fazla DNS ismi verebilirsin — soyutlama ve esneklik sağlar.

  • DNS çözümleme sırası: /etc/hosts → Docker DNS → Host DNS.

  • Farklı network'lerdeki container'lar birbirini göremez — bir container'ı köprü olarak iki network'e bağlayarak iletişim sağlarsın.

  • Sorun giderme için ping, nslookup, nc ve netshoot araçlarını kullan.

Bir sonraki derste port mapping konusuna dalacağız — container'larını dış dünyaya nasıl açarsın, güvenli port mapping nasıl yapılır ve Docker'ın firewall'ı nasıl bypass edebildiğini öğreneceğiz.