"Benim bilgisayarımda çalışıyordu" cümlesini duyduysanız Docker'ın çözdüğü problemi zaten biliyorsunuz. Bir uygulama sizin makinenizde sorunsuz çalışır, ama arkadaşınızın bilgisayarında ya da sunucuda farklı bir sürüm, eksik bir kütüphane veya yanlış bir ayar yüzünden hata verir. Bu yazıda Docker'ın ne olduğunu, image ve container kavramlarını ve Docker'ın bu problemi nasıl çözdüğünü anlatacağız. Sonunda da kendi yazdığınız küçük bir uygulamayı Docker ile paketleyip çalıştırmış olacaksınız.
Docker, uygulamaları çalışması için gereken her şeyle birlikte paketleyip her ortamda aynı şekilde çalıştırmayı sağlayan açık kaynaklı bir platformdur.
"Gereken her şey" derken şunları kastediyoruz:
Bu paketin adı image, bu paketten çalıştırılan uygulamanın adı ise container'dır. Container (konteyner), uygulamanın bilgisayarın geri kalanından yalıtılmış biçimde çalıştığı kapalı bir ortamdır.
Docker'ın logosundaki gemi de bu fikirden gelir: Yük gemilerinde kullanılan standart konteynerler, içinde ne olursa olsun her gemiye, her vince ve her tıra uyar. Docker container'ları da içinde hangi uygulama olursa olsun Docker'ın kurulu olduğu her makinede aynı şekilde çalışır.
Docker'ın günlük hayatta sağladığı faydalar somut olarak şunlardır:
Docker'ı anlamak için dört kavramı birbirinden ayırmak yeterli.
| Kavram | Ne işe yarar? | Benzetme |
|---|---|---|
| Dockerfile | Image'ın nasıl oluşturulacağını adım adım tarif eden metin dosyası | Yemek tarifi |
| Image | Uygulamanın ve tüm bağımlılıklarının bulunduğu, değiştirilemeyen paket | Tariften hazırlanıp dondurulmuş yemek |
| Container | Bir image'ın çalışan hali | Isıtılıp servis edilen porsiyon |
| Registry | Image'ların saklandığı ve paylaşıldığı depo (en bilineni Docker Hub) | Market rafı |
En çok karıştırılan nokta image ile container arasındaki farktır:
Programlamaya aşinaysanız şöyle düşünebilirsiniz: Image bir sınıf (class), container ise o sınıftan oluşturulan bir nesne (object) gibidir.
Docker'ı ilk duyan birçok kişi "Bu bir sanal makine mi?" diye sorar. Hayır, ama amaçları benzer: ikisi de uygulamayı yalıtılmış bir ortamda çalıştırır. Fark, bu yalıtımın nasıl yapıldığındadır.
Sanal makine (Virtual Machine, VM), donanımı taklit eden ve içinde tam bir işletim sistemi çalıştıran bir bilgisayardır. Her sanal makinenin kendi işletim sistemi çekirdeği (kernel) vardır.
Container ise ayrı bir işletim sistemi çalıştırmaz. Ana makinenin işletim sistemi çekirdeğini paylaşır, yalnızca uygulamanın ihtiyaç duyduğu dosyaları ve kütüphaneleri içerir.
| Özellik | Sanal makine | Container |
|---|---|---|
| İçinde tam işletim sistemi var mı? | Evet | Hayır, ana makinenin çekirdeğini kullanır |
| Tipik boyut | Birkaç GB | Birkaç MB ile birkaç yüz MB arası |
| Başlama süresi | Genellikle dakikalar | Genellikle saniyeler veya daha kısa |
| Yalıtım seviyesi | Daha güçlü (ayrı çekirdek) | Daha hafif (çekirdek paylaşılır) |
| Farklı işletim sistemi çalıştırma | Linux üzerinde Windows çalıştırabilir | Linux container'ı Linux çekirdeği ister |
Son satır önemli: Linux container'ları bir Linux çekirdeğine ihtiyaç duyar. Windows ve macOS'ta Docker Desktop bu yüzden arka planda hafif bir Linux sanal makinesi çalıştırır (Windows'ta bu genellikle WSL 2 üzerinden yapılır). Kullanıcı olarak bunu fark etmezsiniz, ama Docker'ın Windows ve Mac'te neden biraz daha fazla bellek kullandığını açıklar.
Kurulum işletim sistemine göre değişir:
Kurulumdan sonra terminali açıp şu komutu çalıştırın:
docker version
Çıktıda hem Client hem de Server bölümlerini görüyorsanız Docker çalışıyor demektir. Yalnızca Client görünüp bir hata alıyorsanız aşağıdaki "Sık karşılaşılan hatalar" bölümüne bakın.
Kurulumu test etmenin klasik yolu ise şudur:
docker run hello-world
Bu komut Docker Hub'dan hello-world adlı küçük bir image indirir, ondan bir container oluşturur ve ekrana bir karşılama mesajı yazdırır. Mesajı görüyorsanız Docker image indirebiliyor ve container çalıştırabiliyor demektir.
Şimdi asıl işe geçelim. Python ile yazılmış, tarayıcıdan gelen isteğe metinle cevap veren küçük bir web sunucusunu Docker ile paketleyeceğiz. Bilgisayarınızda Python kurulu olmasına gerek yok; Python'u container içinde Docker sağlayacak.
Bu örneği Linux üzerinde Docker Engine 29.6 ile çalıştırarak test ettik. Windows ve macOS'ta Docker Desktop ile de aynı komutlar kullanılır.
mkdir ilk-docker-uygulamam
cd ilk-docker-uygulamam
Klasörün içinde app.py adlı bir dosya oluşturun ve şu kodu yapıştırın:
from http.server import HTTPServer, BaseHTTPRequestHandler
class Handler(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header("Content-Type", "text/plain; charset=utf-8")
self.end_headers()
self.wfile.write("Merhaba! Bu yanıt bir Docker container'ından geliyor.\n".encode("utf-8"))
print("Sunucu 8000 portunda çalışıyor...", flush=True)
HTTPServer(("0.0.0.0", 8000), Handler).serve_forever()
Bu kod Python'un kendi standart kütüphanesindeki http.server modülünü kullanır, yani ek paket kurmaya gerek yoktur. Önemli satırlar:
do_GET metodu, sunucuya her GET isteği geldiğinde çalışır ve "Merhaba!" mesajını döndürür.HTTPServer(("0.0.0.0", 8000), Handler) sunucuyu 8000 portunda başlatır. Buradaki 0.0.0.0 önemlidir: Sunucunun yalnızca container'ın kendi içinden değil, dışarıdan gelen bağlantıları da kabul etmesini sağlar. 127.0.0.1 yazsaydık container dışından erişemezdik.flush=True, mesajın hemen yazdırılmasını sağlar. Böylece docker logs komutuyla bu satırı anında görebiliriz.Aynı klasörde, uzantısı olmayan Dockerfile adlı bir dosya oluşturun:
FROM python:3.13-slim
WORKDIR /app
COPY app.py .
EXPOSE 8000
CMD ["python", "app.py"]
Her satır image'a bir adım ekler:
| Satır | Ne yapar? |
|---|---|
FROM python:3.13-slim | Temel olarak Python 3.13 kurulu, küçültülmüş (slim) bir Linux image'ı kullanır. Her Dockerfile bir FROM satırıyla başlar. |
WORKDIR /app | Container içindeki çalışma klasörünü /app yapar. Klasör yoksa oluşturur. |
COPY app.py . | Bilgisayarınızdaki app.py dosyasını container içindeki /app klasörüne kopyalar. |
EXPOSE 8000 | Uygulamanın 8000 portunu kullandığını belgeler. Tek başına portu dışarı açmaz; bu iş docker run sırasında yapılır. |
CMD ["python", "app.py"] | Container başladığında çalışacak komutu belirtir. |
docker build -t ilk-uygulamam .
docker build, Dockerfile'ı okuyup image oluşturur.-t ilk-uygulamam, image'a bir isim (tag) verir.. ise "Dockerfile ve dosyalar bu klasörde" anlamına gelir. Bu noktayı unutmak yeni başlayanların sık yaptığı bir hatadır.İlk çalıştırmada Docker, python:3.13-slim image'ını indireceği için işlem biraz sürebilir. Sonraki build'ler önbellek sayesinde çok daha hızlıdır.
Image'ın oluştuğunu kontrol edin:
docker images ilk-uygulamam
Listede ilk-uygulamam adlı image'ı görmelisiniz. Bizim testimizde image'ın diskte kapladığı alan yaklaşık 187 MB idi. Bu boyutun neredeyse tamamı Python çalışma ortamından gelir, bizim kodumuz yalnızca birkaç yüz bayttır.
docker run -d -p 8080:8000 --name benim-uygulamam ilk-uygulamam
Bu komuttaki parametreler:
-d (detached): Container'ı arka planda çalıştırır, terminaliniz serbest kalır.-p 8080:8000: Bilgisayarınızın 8080 portunu container'ın 8000 portuna bağlar. Biçim her zaman bilgisayar_portu:container_portu şeklindedir.--name benim-uygulamam: Container'a hatırlanabilir bir isim verir. Vermezseniz Docker rastgele bir isim atar.ilk-uygulamam: Hangi image'dan container oluşturulacağı.Şimdi tarayıcınızda http://localhost:8080 adresini açın veya terminalde şunu çalıştırın:
curl http://localhost:8080
Şu yanıtı görmelisiniz:
Merhaba! Bu yanıt bir Docker container'ından geliyor.
Tebrikler, ilk uygulamanızı Docker ile paketleyip çalıştırdınız.
Günlük kullanımda en çok ihtiyaç duyacağınız komutlar şunlardır:
# Çalışan container'ları listele
docker ps
# Durmuş olanlar dahil tüm container'ları listele
docker ps -a
# Container'ın çıktısını (loglarını) gör
docker logs benim-uygulamam
# Container'ı durdur
docker stop benim-uygulamam
# Durmuş container'ı tekrar başlat
docker start benim-uygulamam
# Container'ı sil (önce durdurulmuş olmalı)
docker rm benim-uygulamam
# Image'ı sil
docker rmi ilk-uygulamam
docker logs benim-uygulamam komutunu çalıştırdığınızda hem "Sunucu 8000 portunda çalışıyor..." satırını hem de tarayıcıdan yaptığınız isteklerin kaydını görürsünüz.
Kodda bir değişiklik yaptığınızda çalışan container kendiliğinden güncellenmez. Çünkü container, build anındaki image'dan oluşturulmuştur. Değişikliği görmek için image'ı yeniden build edip container'ı yeniden oluşturmanız gerekir:
docker stop benim-uygulamam
docker rm benim-uygulamam
docker build -t ilk-uygulamam .
docker run -d -p 8080:8000 --name benim-uygulamam ilk-uygulamam
Bu adımları her seferinde elle yazmak zahmetli gelebilir. İleride Docker Compose ile bunun nasıl kolaylaştığını göreceğiz.
Docker'ın en pratik kullanımlarından biri, geliştirme sırasında ihtiyaç duyduğunuz servisleri bilgisayarınıza kurmadan çalıştırmaktır. Örneğin bir proje için PostgreSQL gerekiyorsa:
docker run -d --name test-db -e POSTGRES_PASSWORD=gizli123 -p 5432:5432 postgres:17
Bu tek komut PostgreSQL 17'yi indirir ve 5432 portunda çalıştırır. -e parametresi container'a bir ortam değişkeni (environment variable) gönderir. PostgreSQL image'ı, yönetici şifresini POSTGRES_PASSWORD değişkeninden okur.
Bu yöntem geliştirme ve test için uygundur. Canlı ortamda şifreyi komut satırına açıkça yazmak yerine daha güvenli yöntemler kullanmalı ve verilerin kalıcı olması için volume tanımlamalısınız. Volume konusu ayrı bir yazının konusu.
Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
Neden olur: docker komutu bir istemcidir (client). Asıl işi arka planda çalışan Docker servisi (daemon) yapar. Bu hata, servisin çalışmadığını gösterir.
Çözüm:
sudo systemctl start docker
Neden olur: Linux'ta Docker komutlarını varsayılan olarak root kullanıcısı veya docker grubundaki kullanıcılar çalıştırabilir.
Çözüm: Komutun başına sudo ekleyebilir ya da kullanıcınızı docker grubuna ekleyebilirsiniz:
sudo usermod -aG docker $USER
Değişikliğin geçerli olması için oturumu kapatıp yeniden açmanız gerekir.
Güvenlik notu: docker grubundaki bir kullanıcı pratikte root yetkisine yakın yetkiye sahip olur. Bu yüzden bu gruba yalnızca güvendiğiniz kullanıcıları ekleyin, özellikle paylaşılan sunucularda dikkatli olun.
Bind for 0.0.0.0:8080 failed: port is already allocated
Neden olur: Bilgisayarınızın 8080 portunu başka bir container veya program zaten kullanıyor.
Çözüm: docker ps ile bu portu kullanan container'ı bulup durdurun ya da farklı bir port seçin:
docker run -d -p 8081:8000 --name benim-uygulamam ilk-uygulamam
Bu durumda uygulamaya http://localhost:8081 adresinden erişirsiniz. Container içindeki port (8000) değişmez.
Conflict. The container name "/benim-uygulamam" is already in use by container "..."
Neden olur: Aynı isimde bir container zaten var. Durdurulmuş container'lar da silinene kadar ismini korur.
Çözüm: Eski container'ı silin veya farklı bir isim kullanın:
docker rm -f benim-uygulamam
-f parametresi container çalışıyor olsa bile önce durdurup sonra siler.
İki yaygın sebep vardır:
-p parametresi unutulmuştur. EXPOSE satırı portu dışarı açmaz, docker run komutunda -p 8080:8000 yazmak gerekir.127.0.0.1 adresini dinliyordur. Container içindeki uygulama yalnızca 127.0.0.1'i dinlerse dışarıdan erişilemez. Örneğimizdeki gibi 0.0.0.0 kullanılmalıdır.Docker Engine açık kaynaklıdır ve ücretsizdir. Docker Desktop ise kişisel kullanım, eğitim, ticari olmayan açık kaynak projeler ve küçük işletmeler için ücretsizdir. Docker'ın lisans sayfasına göre küçük işletme, 250'den az çalışanı ve 10 milyon dolardan az yıllık geliri olan şirket anlamına gelir. Bu sınırların üzerindeki şirketlerde Docker Desktop'ın ticari kullanımı ücretli abonelik gerektirir. Lisans koşulları değişebileceği için güncel bilgi için resmi lisans sayfasını kontrol edin.
Container'lar sanal makinelere göre çok daha hafiftir. Ancak Windows ve macOS'ta Docker Desktop arka planda bir Linux sanal makinesi çalıştırdığı için açık olduğu sürece bir miktar bellek kullanır. Kullanmadığınızda Docker Desktop'ı kapatabilir veya ayarlar bölümünden kullanabileceği bellek ve işlemci miktarını sınırlayabilirsiniz.
Container içinde oluşturulan dosyalar container silindiğinde kaybolur. Veritabanı gibi kalıcı veri tutan uygulamalarda verileri volume adı verilen, container'dan bağımsız depolama alanlarında saklamak gerekir.
Docker'da kullanılan container'ların büyük çoğunluğu Linux container'larıdır. Windows Server ve Windows üzerinde Windows container'ları da çalıştırılabilir, ancak bunlar yalnızca Windows çekirdeği olan makinelerde çalışır ve kullanım alanları daha dardır. Masaüstü programlarını (grafik arayüzlü Windows uygulamalarını) Docker ile çalıştırmak pratik bir kullanım değildir.
Başlamak için gerekmez, bu yazıdaki örneklerin tamamını Windows veya macOS'ta da yapabilirsiniz. Ancak container'ların çoğu Linux tabanlı olduğu için temel Linux komutlarını (ls, cd, cat gibi) bilmek, container içinde sorun ararken işinizi kolaylaştırır.
Hayır. Docker container oluşturmanızı ve çalıştırmanızı sağlar. Kubernetes ise çok sayıda container'ın birden fazla sunucu üzerinde yönetilmesi, ölçeklenmesi ve çökenlerin yeniden başlatılması için kullanılan bir orkestrasyon sistemidir. Kubernetes'e geçmeden önce Docker'ı rahat kullanabiliyor olmak gerekir.
docker build ile image oluşturulur, docker run ile container başlatılır. -p parametresi olmadan uygulamanıza dışarıdan erişemezsiniz.Bu yazıda tek bir container çalıştırdık. Gerçek projelerde çoğu zaman bir web uygulaması, bir veritabanı ve belki bir önbellek servisi birlikte çalışır. Bunları tek tek docker run komutlarıyla yönetmek yerine Docker Compose kullanılır. Bir sonraki adım olarak Docker Compose'u, ardından Docker ile PostgreSQL kurulumunu ve bir ASP.NET Core uygulamasını Docker ile çalıştırmayı öğrenebilirsiniz.
Kurumsal web siteleri için içerik yönetim sistemi.