Docker ile bir uygulamayı çalıştırmak ilk bakışta kolaydır, ama gerçek dünyada işler tek bir konteynerle bitmez. Bir web uygulamasının önünde ters vekil (reverse proxy), arkasında bir veritabanı, yanında bir önbellek servisi ve belki de bir kuyruk yöneticisi bulunur. Bütün bu parçaların birbirini bulması, birbiriyle konuşması ama dış dünyaya yalnızca gerekli kapıların açılması gerekir. İşte tam bu noktada Docker ağı (network) devreye girer. Ağ yapılandırmasını doğru kurmadığınızda ya konteynerler birbirini göremez, ya da tam tersine veritabanınız tüm internete açık kalır. İkisi de istenmeyen sonuçlardır.
Bu rehberde Docker'ın sunduğu ağ sürücülerini (bridge, host, none ve overlay) sade örneklerle karşılaştıracak, kendi özel ağınızı nasıl oluşturacağınızı, konteynerlerin birbirini nasıl isimle (DNS) bulduğunu, -p ile portları dışarıya nasıl yayınladığınızı ve ağ izolasyonuyla güvenliği nasıl sağladığınızı adım adım göreceğiz. Docker'a yeniyseniz önce Docker nedir başlangıç rehberimize göz atmanızı, ardından buradan devam etmenizi öneririm. Amacımız, komutları ezberletmek değil; ağın nasıl çalıştığını anlamanızı sağlamak. Bir kez mantığı kavradığınızda, en karmaşık kurulumları bile rahatça çözebilirsiniz.
Docker Ağı Neden Önemlidir#
Docker, her konteyneri kendi izole ağ ad alanı (network namespace) içinde çalıştırır. Bu, her konteynerin kendi sanal ağ arayüzüne, kendi IP adresine ve kendi yönlendirme tablosuna sahip olması demektir. Konteynerler varsayılan olarak birbirinden yalıtılmıştır; onları bir araya getiren şey Docker'ın oluşturduğu sanal ağlardır. Bu tasarımın en büyük avantajı, hangi servisin hangisiyle konuşabileceğini siz belirlersiniz. Yani ağ, aynı zamanda bir güvenlik sınırıdır.
Pratikte iyi bir ağ yapılandırması size üç şey kazandırır. Birincisi, konteynerler sabit IP adresleri yerine servis isimleriyle haberleşir, böylece bir konteyner yeniden başlatılıp IP'si değişse bile bağlantı kopmaz. İkincisi, yalnızca gerçekten dışarıya açılması gereken portları yayınlarsınız; veritabanı, önbellek gibi iç servisler dış dünyadan tamamen gizli kalır. Üçüncüsü, farklı projeleri farklı ağlara ayırarak birbirini etkilemesini önlersiniz. Kısacası ağ, Docker altyapınızın hem sinir sistemi hem de güvenlik duvarıdır.
Docker Ağ Türleri (Sürücüleri)#
Docker, farklı ihtiyaçlar için farklı ağ sürücüleri (driver) sunar. Mevcut ağları listelemek için şu komutu kullanabilirsiniz:
docker network ls
Çıktıda taze bir kurulumda genellikle üç varsayılan ağ görürsünüz: bridge, host ve none. Bunlar Docker'ın yerleşik sürücüleridir. Aşağıdaki tablo hangi türün ne işe yaradığını özetliyor:
| Sürücü | Kullanım alanı | İzolasyon | DNS ile isim çözümleme |
|---|---|---|---|
| bridge | Tek sunucuda çoğu senaryo | Yüksek | Özel ağda evet, varsayılanda hayır |
| host | Maksimum ağ performansı gereken durumlar | Yok (host ağını paylaşır) | Doğrudan host'un kendisi |
| none | Ağa hiç ihtiyaç olmayan işler | Tam yalıtım | Ağ yok |
| overlay | Birden çok sunucu (Swarm/küme) | Yüksek | Evet |
| macvlan | Konteynere fiziksel ağda IP verme | Orta | Harici DNS |
bridge, günlük çalışmada en çok kullanacağınız sürücüdür. Docker, docker0 adında sanal bir köprü oluşturur ve konteynerler bu köprü üzerinden hem birbirine hem de NAT ile dış dünyaya erişir. Varsayılan bridge ağı işlevseldir ama kısıtlıdır; asıl gücü, birazdan göreceğimiz özel (user-defined) bridge ağlarında ortaya çıkar.
host sürücüsü, konteyneri host makinenin ağ yığınının doğrudan içine yerleştirir. Konteyner artık kendi IP'sine sahip değildir; host'un IP'sini ve portlarını kullanır. Ağ katmanında NAT olmadığı için performans en yüksektir, ancak izolasyon kaybolur ve port çakışmaları yaşayabilirsiniz. Yüksek trafikli bir yük dengeleyici gibi özel durumlar dışında dikkatli kullanılmalıdır.
docker run -d --network host nginx
none sürücüsü, konteynere hiçbir ağ arayüzü vermez (yalnızca loopback). Ağa hiç ihtiyaç duymayan, örneğin yalnızca bir dosyayı işleyip çıkan toplu işlem (batch) konteynerleri için idealdir ve saldırı yüzeyini sıfıra indirir.
overlay ise tek sunucuyu aştığınızda, yani Docker Swarm veya çok düğümlü bir kümede konteynerlerin farklı fiziksel sunucular üzerinden aynı sanal ağda haberleşmesini sağlar. Bu rehberde odağımız tek sunucu olduğu için overlay'i kavramsal düzeyde bilmeniz yeterli.
Özel Bridge Ağı Oluşturma#
Üretim ortamında neredeyse her zaman kendi özel bridge ağınızı oluşturmalısınız. Varsayılan bridge ağının en büyük eksiği, konteynerlerin birbirini yalnızca IP ile bulabilmesidir; özel bir ağda ise Docker size gömülü bir DNS sunucusu verir ve konteynerler birbirini isimle bulur. Ayrıca özel ağlar birbirinden yalıtıktır, bu da güvenlik açısından çok değerlidir.
Yeni bir ağ oluşturmak tek satırlık iştir:
docker network create uygulama-agi
İsterseniz alt ağ (subnet) ve ağ geçidini (gateway) de kendiniz belirleyebilirsiniz. Bu, IP çakışmalarını önlemek veya belirli bir aralık kullanmak istediğinizde işe yarar:
docker network create \
--driver bridge \
--subnet 172.28.0.0/16 \
--gateway 172.28.0.1 \
uygulama-agi
Bir ağın ayrıntılarını, hangi konteynerlerin bağlı olduğunu ve IP dağılımını görmek için:
docker network inspect uygulama-agi
Konteynerleri bu ağa bağlamanın iki yolu vardır. Konteyneri başlatırken --network bayrağıyla:
docker run -d --name veritabani --network uygulama-agi postgres:16
docker run -d --name web --network uygulama-agi nginx
Ya da zaten çalışan bir konteyneri sonradan ağa ekleyerek:
docker network connect uygulama-agi mevcut-konteyner
Bir konteyner aynı anda birden fazla ağa bağlı olabilir. Bu, örneğin bir ters vekil konteynerini hem "ön yüz" ağına hem de "arka yüz" ağına bağlayarak iki katmanı köprülemek istediğinizde çok kullanışlıdır.
Konteynerlerin DNS ile İsimle Haberleşmesi#
Özel bir bridge ağının en pratik faydası, gömülü DNS çözümlemesidir. Aynı özel ağa bağlı konteynerler, birbirlerine konteyner adı ya da --network-alias ile verilen takma adla erişebilir. Docker, bu isimleri arka planda 127.0.0.11 üzerinde çalışan iç DNS çözücüsüyle doğru IP'ye çevirir. Böylece uygulama yapılandırmanıza asla sabit IP yazmazsınız.
Yukarıdaki örnekte web konteyneri, veritabani konteynerine tam olarak bu isimle bağlanabilir. Bunu doğrulayalım:
docker exec -it web ping -c 2 veritabani
Uygulamanızın veritabanı bağlantı ayarında da IP yerine bu ismi kullanırsınız. Örneğin bir uygulamanın ortam değişkenlerinde bağlantı adresi şöyle görünür:
DB_HOST=veritabani
DB_PORT=5432
DB_NAME=uretim
DB_USER=app
Burada kritik nokta şudur: DNS ile isim çözümleme yalnızca özel ağlarda çalışır. Varsayılan bridge ağında iki konteyneri isimle bağlamaya çalışırsanız çözümleme başarısız olur ve eski --link yöntemine mecbur kalırsınız ki bu artık önerilmez. Dolayısıyla "konteynerim veritabanını göremiyor" sorununun en yaygın sebebi, konteynerlerin ya farklı ağlarda olması ya da hiçbirinin özel bir ağa bağlı olmamasıdır. Çözüm neredeyse her zaman ikisini de aynı özel ağa koymaktır.
Bir servisi ölçeklendirdiğinizde (aynı imajdan birden çok kopya) --network-alias sayesinde Docker DNS, aynı takma ad için birden çok IP döndürür ve basit bir yük dağılımı (round-robin) sağlar:
docker run -d --network uygulama-agi --network-alias api api-imaji
docker run -d --network uygulama-agi --network-alias api api-imaji
Port Yayınlama (-p) ile Dış Dünyaya Açılma#
Şimdiye kadar konuştuğumuz her şey, konteynerlerin birbiriyle iletişimiydi. Peki dış dünya, yani tarayıcınız veya bir müşteri, konteynerinize nasıl erişir? İşte burada port yayınlama devreye girer. Varsayılan olarak bir konteynerin açtığı portlar yalnızca kendi ağı içinden erişilebilir; host makinenin dışına açılmaz. -p (veya --publish) bayrağı, host üzerindeki bir portu konteynerin bir portuna eşler.
Söz diziminin mantığı host_portu:konteyner_portu şeklindedir:
docker run -d -p 8080:80 --name web nginx
Bu komut, host'un 8080 portuna gelen trafiği konteynerin 80 portuna yönlendirir. Artık tarayıcıdan http://sunucu-ip:8080 adresine gittiğinizde Nginx'i görürsünüz. Birden çok port yayınlamak için bayrağı tekrarlarsınız:
docker run -d -p 80:80 -p 443:443 --name web nginx
Güvenlik açısından en önemli ayrıntılardan biri, portu hangi arayüze bağladığınızdır. Yalnızca -p 5432:5432 yazarsanız port tüm ağ arayüzlerine (0.0.0.0) açılır ve internetten erişilebilir hale gelir. Bir veritabanı için bu felakettir. Portu yalnızca yerelden erişilebilir yapmak istiyorsanız IP'yi de belirtin:
docker run -d -p 127.0.0.1:5432:5432 postgres:16
Bu şekilde veritabanı yalnızca aynı sunucudan (örneğin bir SSH tüneliyle) erişilebilir olur, dışarıya kapalı kalır. Aşağıdaki tablo yaygın yayınlama biçimlerini karşılaştırıyor:
| Yazım | Anlamı | Dışarıdan erişim |
|---|---|---|
-p 80:80 | Tüm arayüzlerde 80 | Açık (herkese) |
-p 127.0.0.1:5432:5432 | Yalnızca localhost | Kapalı |
-p 8080:80 | Host 8080 → konteyner 80 | Açık (8080) |
--expose 6379 | Yalnızca ağ içine bildirir | Yayınlanmaz |
Buradaki altın kural nettir: yalnızca kullanıcıların gerçekten ulaşması gereken portları (genelde 80 ve 443) tüm arayüzlere yayınlayın; veritabanı, önbellek, kuyruk gibi iç servisleri asla halka açık portlara bağlamayın. Bu servisleri, konteynerler zaten özel ağ üzerinden isimle bulacağı için dışarıya açmanıza gerek yoktur.
Ağ İzolasyonu ve Güvenlik#
Ağların yalıtık olması, Docker güvenliğinin belkemiğidir. İki farklı özel ağ oluşturursanız, birindeki konteyner diğerindekini varsayılan olarak hiç göremez. Bu özelliği kullanarak klasik bir üç katmanlı mimariyi güvenli biçimde kurabilirsiniz: ön yüz (frontend), arka yüz (backend) ve veritabanı katmanı ayrı ağlarda dursun, yalnızca ihtiyaç duyanlar köprülensin.
Örnek bir senaryo düşünelim. Bir ters vekil (Nginx) hem dış dünyaya bakan frontend ağında hem de uygulamayla konuştuğu backend ağında olsun. Uygulama konteyneri backend ve veritabani-agi ağlarında olsun. Veritabanı ise yalnızca veritabani-agi ağında kalsın. Böylece Nginx veritabanına hiçbir zaman doğrudan erişemez.
docker network create frontend
docker network create backend
docker network create veritabani-agi
docker run -d --name proxy --network frontend -p 80:80 nginx
docker network connect backend proxy
docker run -d --name uygulama --network backend uygulama-imaji
docker network connect veritabani-agi uygulama
docker run -d --name db --network veritabani-agi postgres:16
Bu kurulumda veritabanının hiçbir portu -p ile yayınlanmadığına dikkat edin. Dışarıdan ona ulaşmanın yolu yoktur; yalnızca uygulama konteyneri, aynı ağda olduğu için db ismiyle bağlanır. Bu tür bir katmanlama, bir konteyner ele geçirilse bile saldırganın yanal hareketini (lateral movement) ciddi biçimde sınırlar.
İzolasyonu tamamlayan bir başka önlem, konteynerler arası kör iletişimi kapatmaktır. Özel bir ağ oluştururken --internal bayrağını verirseniz, o ağdaki konteynerler dış dünyaya (internete) hiç çıkamaz; yalnızca ağ içinde konuşurlar. Bu, hassas veritabanı katmanları için mükemmeldir:
docker network create --internal veritabani-agi
Ağ güvenliğini uygulama katmanı güvenliğiyle birlikte düşünmek gerekir. Dışarıya açtığınız 443 portunun önüne bir SSL sertifikası koymak, bir WAF (web uygulama güvenlik duvarı) ile katman eklemek ve hacimsel saldırılara karşı DDoS koruması almak, Docker ağ izolasyonuyla birleştiğinde çok katmanlı ve sağlam bir savunma oluşturur. Konteyner içindeki ters vekil yapılandırmasını derinlemesine kurmak isterseniz Nginx kurulum rehberimiz tam da bu köprü katmanını anlatır.
Docker Compose ile Otomatik Ağ Yönetimi#
Birden çok konteyneri elle docker network create ve docker network connect komutlarıyla yönetmek küçük kurulumlarda mümkündür, ama işler büyüdükçe zahmetli ve hataya açık hale gelir. İşte Docker Compose burada devreye girer ve az önce elle yaptığımız her şeyi otomatikleştirir. Bir docker-compose.yml dosyası oluşturduğunuzda Compose, projeniz için otomatik olarak özel bir bridge ağı yaratır ve tüm servisleri bu ağa bağlar.
Bunun anlamı şudur: Compose ile tanımladığınız servisler, hiçbir ek yapılandırma yapmadan birbirini servis adıyla bulur. Aşağıdaki örnekte web servisi, veritabani servisine tam olarak bu isimle bağlanabilir, çünkü ikisi de Compose'un otomatik oluşturduğu aynı ağdadır:
services:
web:
image: nginx
ports:
- "80:80"
depends_on:
- veritabani
networks:
- uygulama
veritabani:
image: postgres:16
environment:
POSTGRES_PASSWORD: gizli-parola
networks:
- uygulama
networks:
uygulama:
driver: bridge
Burada ports bölümü, komut satırındaki -p ile birebir aynı işi yapar; yani host portunu konteyner portuna yayınlar. veritabani servisinde hiçbir port yayınlamadığımıza dikkat edin; o yalnızca iç ağda erişilebilir. Katmanlı izolasyon senaryosunu da Compose'ta birebir kurabilirsiniz; bir servisi birden çok ağa yazarak köprü kurmanız yeterlidir:
services:
proxy:
image: nginx
ports:
- "443:443"
networks:
- frontend
- backend
uygulama:
image: uygulama-imaji
networks:
- backend
- veritabani-agi
db:
image: postgres:16
networks:
- veritabani-agi
networks:
frontend:
backend:
veritabani-agi:
internal: true
Bu dosyada veritabani-agi ağını internal: true ile işaretledik, böylece veritabanı katmanı internete hiç çıkamaz. proxy iki ağı köprüler, db ise en dipte yalıtık kalır. Compose'u docker compose up -d ile çalıştırdığınızda tüm ağlar, DNS çözümlemesi ve bağlantılar sizin için otomatik kurulur. Projeyi docker compose down ile kapattığınızda oluşturulan ağlar da temizlenir. Elle yaptığınız her şeyin bildirimsel (declarative) ve tekrarlanabilir hali budur; bu yüzden birden fazla konteyner çalıştıran her ciddi kurulumda Compose kullanmanızı şiddetle öneririm.
Sık Karşılaşılan Ağ Sorunları ve Çözümleri#
Ağ yapılandırmasında en çok karşılaşılan sorun, konteynerlerin birbirini bulamamasıdır. Bunun sebebi neredeyse her zaman aynıdır: konteynerler farklı ağlardadır ya da hiçbiri özel bir ağa bağlı değildir. docker network inspect ag-adi komutuyla hangi konteynerlerin gerçekten o ağda olduğunu kontrol edin; beklediğiniz konteyneri listede göremiyorsanız docker network connect ile ekleyin.
İkinci sık sorun, port çakışmalarıdır. -p 80:80 denediğinizde "port is already allocated" hatası alıyorsanız, host'un 80 portunu başka bir servis (örneğin bir sistem Nginx'i veya başka bir konteyner) zaten kullanıyordur. docker ps ve ss -tlnp ile portu kimin tuttuğunu bulun, ya çakışan servisi durdurun ya da farklı bir host portu seçin. Üçüncü olarak, "bağlantı reddedildi" hataları çoğu zaman uygulamanın konteyner içinde 127.0.0.1e bağlanmasından kaynaklanır; konteyner içindeki servisler dışarıdan erişilebilmek için 0.0.0.0 üzerinde dinlemelidir. Bu üç kontrol, ağ sorunlarının büyük çoğunluğunu çözer.
Sıkça Sorulan Sorular#
Varsayılan bridge ağı ile özel bridge ağı arasındaki fark nedir?#
Varsayılan bridge ağı Docker kurulumuyla hazır gelir ancak konteynerler burada birbirini yalnızca IP adresiyle bulabilir; gömülü DNS ile isim çözümleme çalışmaz. Özel (user-defined) bridge ağı oluşturduğunuzda ise konteynerler birbirini konteyner adıyla bulur, ağlar birbirinden daha iyi yalıtılır ve konteynerleri çalışırken ağa ekleyip çıkarabilirsiniz. Bu nedenle üretim ortamında her zaman özel bir ağ oluşturmanız önerilir.
İki konteyner birbirine neden bağlanamıyor?#
Bunun en yaygın nedeni iki konteynerin aynı özel ağda olmamasıdır. Docker'ın DNS ile isim çözümlemesi yalnızca aynı özel bridge ağına bağlı konteynerler arasında çalışır, dolayısıyla önce docker network inspect ile ikisinin de aynı ağda olduğunu doğrulamalısınız. Eğer varsayılan bridge ağını kullanıyorsanız isimle çözümleme çalışmayacağı için ortak bir özel ağ oluşturup her iki konteyneri de ona bağlamanız gerekir.
-p ile --expose arasındaki fark nedir?#
-p (publish) bayrağı bir konteyner portunu host makinenin bir portuna eşler ve o portu dış dünyaya açar, yani tarayıcıdan veya başka bir sunucudan erişilebilir hale getirir. --expose ise portu yalnızca aynı Docker ağındaki diğer konteynerlere bildirir, host üzerinde herhangi bir port açmaz. Kısacası dış erişim için -p, yalnızca konteynerler arası iç iletişim için --expose kullanılır.
Veritabanı konteynerimi güvenli tutmak için ne yapmalıyım?#
Veritabanı konteynerini asla -p 5432:5432 gibi tüm arayüzlere açık bir şekilde yayınlamayın; onun yerine port yayınlamayı tamamen atlayıp yalnızca özel ağ üzerinden erişmesine izin verin. Uygulama konteyneriniz zaten aynı ağda olduğu için veritabanına isimle bağlanabilir ve dış dünyaya hiçbir kapı açmanıza gerek kalmaz. Eğer yönetim için yerel erişim şartsa -p 127.0.0.1:5432:5432 yazarak portu yalnızca sunucunun kendisine kısıtlayabilir, ek olarak veritabanı ağını --internal işaretiyle internete kapatabilirsiniz.
Docker Compose ağları elle oluşturmama gerek bırakıyor mu?#
Evet, Docker Compose bir projeyi başlattığınızda otomatik olarak özel bir bridge ağı oluşturur ve tüm servisleri bu ağa bağlar, böylece elle docker network create çalıştırmanıza gerek kalmaz. Servisler bu ağda birbirini doğrudan servis adıyla bulur ve siz yalnızca özel bir topoloji istediğinizde networks bölümünü tanımlarsınız. Proje kapatıldığında Compose oluşturduğu ağları da temizlediği için ortamınız tertemiz kalır.
host ağ modunu ne zaman kullanmalıyım?#
host ağ modunu yalnızca ağ performansının kritik olduğu ve NAT katmanının getirdiği küçük gecikmeyi bile kaldıramayacağınız özel durumlarda kullanmalısınız. Bu modda konteyner host'un ağ yığınını doğrudan paylaşır, kendi IP'si olmaz ve port yayınlama devre dışı kalır, bu da izolasyonu ortadan kaldırır. Çoğu web uygulaması için özel bir bridge ağı hem daha güvenli hem yeterince hızlı olduğundan host modunu istisnai bir tercih olarak görmelisiniz.
Kapanış#
Docker ağı, ilk bakışta soyut görünse de aslında son derece mantıklı bir sistemdir: konteynerler özel bir bridge ağında isimle haberleşir, yalnızca gerçekten gerekli portlar -p ile dışarıya yayınlanır ve farklı katmanlar ayrı ağlarla yalıtılarak güvenlik sağlanır. Docker Compose ise tüm bu yapıyı bildirimsel ve tekrarlanabilir hale getirir. Bu dört ilkeyi kavradığınızda, en karmaşık çok konteynerli mimarileri bile rahatça kurabilir ve yönetebilirsiniz.
Öğrendiklerinizi çalıştıracağınız kararlı ve hızlı bir altyapıya ihtiyaç duyduğunuzda Clou.TR yanınızda. Kaynaklarını tam kontrol edebileceğiniz VDS sunucularımız ve sanal sunucularımız Docker iş yükleri için idealdir; kurulum ve bakım yükünü bize bırakmak isterseniz sunucu yönetimi hizmetimize göz atabilir, daha hafif projeleriniz için hosting paketlerimizi inceleyebilirsiniz. Uygulamalarınızı yayına almadan önce SSL ve DDoS koruması ile güvenliğinizi tamamlamayı da unutmayın. Docker'a yolculuğunuza yeni başlıyorsanız Docker nedir başlangıç rehberimizle temeli sağlamlaştırın; sağlıklı yayınlar dileriz.