Docker mı Sanal Makine mi: Farkları ve Kullanım Senaryoları
Yıllardır şu soruyu alıyorum: “Hocam, Docker mı kursak yoksa VM mi?” Bunu soran arkadaşa her seferinde aynı şeyi söylüyorum: “İkisi farklı sorunları çözüyor, doğru soruyu sorman lazım.” Bu yazıda hem farkları net koyacağım hem de hangi senaryoda ne kullanmanız gerektiğini somut örneklerle anlatacağım.
Temelden Başlayalım: Ne Yapar Bunlar?
Sanal makine, fiziksel bir sunucunun üzerinde tam anlamıyla başka bir bilgisayar çalıştırır. VMware, Hyper-V veya KVM gibi bir hypervisor, donanım kaynaklarını soyutlar ve her VM’e kendi işletim sistemi çekirdeği, belleği, disk alanı tahsis eder. Windows sunucunuzun üzerinde Ubuntu çalıştırmak istiyorsanız, Ubuntu tam olarak orada çalışır, kendi kernel’ıyla, kendi init sistemiyle.
Docker ise farklı bir paradigma. Container’lar, host işletim sisteminin çekirdeğini paylaşır. Linux namespace’leri ve cgroup’lar sayesinde izolasyon sağlanır ama tam anlamıyla ayrı bir işletim sistemi yoktur. Bu yüzden Docker container’ı başlatmak milisaniyeler alırken bir VM’i ayağa kaldırmak dakikalar sürebilir.
Şunu net söyleyeyim: Docker bir sanallaştırma teknolojisi değil, bir izolasyon teknolojisidir. Bu ayrımı anlamadan doğru teknoloji seçimi yapmak mümkün değil.
Mimari Farklar
Hypervisor Katmanı vs Kernel Paylaşımı
VM mimarisinde şu katmanlar var:
- Fiziksel Donanım: CPU, RAM, disk
- Host OS veya Bare Metal: Sunucunun kendisi
- Hypervisor: Type-1 (ESXi, Hyper-V) veya Type-2 (VirtualBox, VMware Workstation)
- Guest OS: Her VM için ayrı işletim sistemi
- Uygulama: Kendi kütüphaneleriyle birlikte
Docker mimarisinde ise:
- Fiziksel Donanım: Aynı
- Host OS: Linux kernel’ı olan bir sistem
- Docker Engine: Daemon + containerd + runc
- Container: Sadece uygulama ve bağımlılıkları, kernel yok
Pratik sonucu şu: 8 GB RAM’li bir sunucuda 3-4 VM ayakta tutabilirsiniz. Aynı sunucuda 30-40 container rahatça çalışır.
Kısa bir test yapalım. Bir Ubuntu sisteminde Docker kurulu olduğunu varsayıyorum:
# Kaç container çalışıyor ve ne kadar kaynak kullanıyorlar?
docker stats --no-stream
# Çıktıda CPU%, MEM USAGE/LIMIT, NET I/O görürsünüz
# VM'ler için aynı kontrolü yapmak çok daha maliyetlidir
Kaynak Tüketimi Karşılaştırması
Somut rakamlar verelim. Bir web uygulaması için:
- Minimal Ubuntu VM: yaklaşık 512 MB RAM (sadece OS), disk 10-20 GB
- Aynı uygulamayı çalıştıran Alpine tabanlı Docker container: 5-15 MB (base image), bellekte sadece uygulama süreci
Bu fark production’da gerçekten hissedilir. Özellikle mikroservis mimarisine geçerken.
Güvenlik İzolasyonu: En Kritik Fark
Bu konuda net olmak lazım. Container izolasyonu, VM izolasyonundan zayıftır. Bunu kabul etmek gerek.
Bir VM’de guest OS tamamen ayrıdır. Kernel exploiti bulmak çok daha zordur ve hypervisor’u aşmak için ayrı bir güvenlik açığı gerekir. Container’larda ise kernel paylaşıldığı için bir kernel güvenlik açığı tüm container’ları etkileyebilir.
# Container içindeki process'leri host'tan görebilirsiniz
# Bu VM'lerde mümkün değil
# Host üzerinde
ps aux | grep nginx
# Hem host nginx'ini hem de container içindeki nginx'i görebilirsiniz
# VM senaryosunda VM içindeki process'ler görünmez
Ama şunu da eklemek lazım: Doğru configure edilmiş Docker, çoğu iş yükü için yeterince güvenlidir. Sorun Docker’ın kendisinde değil, yanlış konfigürasyonlarda.
# Kötü pratik: Container'ı root olarak çalıştırmak
docker run -it ubuntu bash
# Root olarak çalışıyorsunuz, tehlikeli
# İyi pratik: Non-root user ile çalıştırmak
docker run -it --user 1000:1000 ubuntu bash
# Daha da iyisi: Seccomp profili ile çalıştırmak
docker run --security-opt seccomp=/path/to/seccomp-profile.json myapp
PCI-DSS veya Sıkı Compliance Gereksinimleri
Bankacılık veya sağlık sektöründe çalışan arkadaşlara özel bir not: Compliance gereksinimleriniz varsa, her iş yükünü Docker’a taşımanız beklenmeyen sorunlar yaratabilir. Özellikle PCI-DSS scope’unda olan sistemler için VM izolasyonu hala tercih edilen yaklaşım. Bunu deneyimden söylüyorum.
Gerçek Dünya Senaryoları
Senaryo 1: Mikroservis Mimarisi
Diyelim ki e-ticaret platformunuz var. Sipariş servisi, ödeme servisi, bildirim servisi, katalog servisi ayrı ayrı çalışıyor. Her biri farklı dilde yazılmış: biri Python, biri Go, biri Node.js.
Bu senaryo için Docker mükemmel. Neden?
- Her servis kendi container’ında izole
- CI/CD pipeline’ı kurmak çok kolay
- Geliştirici ortamıyla production ortamı arasındaki “bende çalışıyor” sorunu ortadan kalkıyor
- Ölçeklendirme servis bazında yapılabiliyor
# docker-compose.yml ile basit bir mikroservis örneği
cat > docker-compose.yml << 'EOF'
version: '3.8'
services:
order-service:
build: ./order-service
ports:
- "8001:8001"
environment:
- DATABASE_URL=postgresql://db:5432/orders
depends_on:
- db
restart: unless-stopped
payment-service:
build: ./payment-service
ports:
- "8002:8002"
environment:
- DATABASE_URL=postgresql://db:5432/payments
restart: unless-stopped
db:
image: postgres:15-alpine
volumes:
- postgres_data:/var/lib/postgresql/data
environment:
- POSTGRES_PASSWORD=secret
volumes:
postgres_data:
EOF
docker-compose up -d
Senaryo 2: Farklı İşletim Sistemi Gereksinimleri
Windows sunucunuzda Linux tabanlı bir uygulama çalıştırmanız gerekiyor. Ya da tersi. Bu senaryo için Docker size yardımcı olmaz, VM lazım.
Örnek: Şirketinizin ERP sistemi sadece Windows Server 2019 üzerinde çalışıyor ama altyapınız tamamıyla Linux. Bunu bir Hyper-V ya da KVM VM’i olarak izole etmek zorundasınız.
# KVM ile VM oluşturma örneği
virt-install
--name windows-erp
--ram 8192
--vcpus 4
--disk path=/var/lib/libvirt/images/windows-erp.qcow2,size=100
--cdrom /iso/windows-server-2019.iso
--os-type windows
--os-variant win2k19
--network bridge=br0
--graphics vnc,listen=0.0.0.0
# VM listesini görmek için
virsh list --all
Senaryo 3: Development Ortamı Standardizasyonu
Bu konuda Docker’ın gerçek değerini görüyorsunuz. Yeni bir developer geldi, projeye dahil edilmesi lazım. Eski dünyada: “Python 3.9 kur, bu kütüphaneleri kur, şu servis çalıştır, şu config’i ayarla…” Yarım gün gidiyor.
Yeni dünyada:
# Geliştirici ortamını ayağa kaldırmak
git clone https://github.com/yourcompany/yourproject.git
cd yourproject
docker-compose up -d
# Bitti. Gerçekten.
# Veritabanı, cache, uygulama sunucusu, hepsi ayakta.
Bu sadece zaman kazancı değil, “bende çalışıyor ama CI’da çalışmıyor” türü hataları da ortadan kaldırıyor. Ortam tutarlılığı DevOps kültürünün temel taşlarından biri ve Docker bunu gerçekten çözüyor.
Senaryo 4: Legacy Sistem Yönetimi
2008 yılında yazılmış, Python 2.7 gerektiren bir uygulama düşünün. Modern bir Linux dağıtımında Python 2.7 artık varsayılan gelmiyor. Bu uygulamayı ayakta tutmanız gerekiyor ama tüm sistemi eski tutmak istemiyorsunuz.
İki seçenek:
Seçenek A – Docker: Python 2.7 tabanlı bir image oluşturun, uygulamayı container’da çalıştırın.
# Dockerfile.legacy
FROM ubuntu:18.04
RUN apt-get update && apt-get install -y python2.7 python-pip
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
CMD ["python2.7", "app.py"]
docker build -f Dockerfile.legacy -t legacy-app:python27 .
docker run -d --name legacy-app -p 8080:8080 legacy-app:python27
Seçenek B – VM: Eski bir Ubuntu veya CentOS VM’i oluşturun, orada çalıştırın.
Hangisi doğru? Eğer uygulama sadece network üzerinden erişilen bir servis ise Docker daha temiz. Eğer donanım sürücüsüne erişimi olan ya da sistem servislerine derinlemesine entegre bir uygulama ise VM daha güvenli seçim.
Ağ Mimarisi Farkları
VM’ler genellikle kendi MAC adresi ve IP’siyle ağ üzerinde tam bir makine gibi görünür. Container’lar ise default olarak bridge network üzerinde çalışır ve host’un arkasında NAT ile iletişim kurar.
Production’da Docker network’ü doğru yapılandırmak kritik:
# Custom network oluşturma
docker network create --driver bridge
--subnet 172.20.0.0/16
--ip-range 172.20.240.0/20
production-network
# Container'ı bu network'e bağlamak
docker run -d
--name api-server
--network production-network
--ip 172.20.240.10
myapi:latest
# Container'lar arasındaki iletişimi kontrol etmek
docker network inspect production-network
# İzole container'lar arası iletişimi test etmek
docker exec -it api-server ping 172.20.240.11
Kubernetes kullandığınızda bu daha da karmaşık hale gelir ama temel mantık aynı: container’lar arasındaki ağ trafiği, VM’ler arasındaki kadar izole değil.
Performans Profilleri
I/O yoğun iş yükleri için VM mi Docker mi sorusu sık gelir. Genel kural şu: CPU ve memory açısından Docker, VM’e göre çok az overhead’e sahip. Ama storage ve network I/O için durum biraz farklı.
# Container'ın I/O performansını test etmek
docker run --rm -it ubuntu bash -c
"dd if=/dev/zero of=/tmp/testfile bs=1G count=1 oflag=dsync"
# Host'ta aynı testi yapmak için karşılaştırma
dd if=/dev/zero of=/tmp/testfile bs=1G count=1 oflag=dsync
# Farkı göreceksiniz, genellikle %5-15 arası overhead
Veritabanı gibi I/O yoğun iş yükleri için volume mount’ları doğru yapılandırmak çok önemli:
# Named volume kullanmak (Docker yönetir, performans iyi)
docker volume create pgdata
docker run -d
--name postgres
-v pgdata:/var/lib/postgresql/data
-e POSTGRES_PASSWORD=secret
postgres:15
# Bind mount ile karşılaştırma (host directory, biraz daha yavaş olabilir)
docker run -d
--name postgres-bm
-v /data/postgres:/var/lib/postgresql/data
-e POSTGRES_PASSWORD=secret
postgres:15
Hangi Durumda Ne Kullanmalı?
Bu sorunun cevabı bazen çok nettir, bazen her iki teknoloji birlikte kullanılmalıdır.
Docker tercih edin:
- Mikroservis mimarisi kuruyorsanız
- CI/CD pipeline’ı otomatize ediyorsanız
- Development ortamı standardize etmek istiyorsanız
- Hızlı scale out/in yapmanız gerekiyorsanız
- Uygulama sayısı fazla, kaynak verimliliği önemliyse
- Stateless web uygulamaları ve API servisleri için
VM tercih edin:
- Farklı işletim sistemi gerekiyorsa (Windows üzerinde Linux ya da tersi)
- Tam izolasyon kritikse (compliance, güvenlik gereksinimleri)
- Kernel düzeyinde özelleştirme lazımsa
- Stateful, donanıma yakın uygulamalar çalıştırıyorsanız
- Güvenilir snapshot ve rollback mekanizması kritikse
- Eski lisanslı yazılımlar için izole ortam gerekiyorsa
Her ikisini birlikte kullanın:
- VM’ler üzerinde Kubernetes node’ları çalıştırmak (en yaygın production yaklaşımı budur)
- Geliştirme ortamında Docker, test için VM
- Public cloud’da EC2/Compute Engine instance’ları üzerinde container orchestration
# Yaygın production pattern: VM içinde Docker
# KVM VM oluştur, içine Docker kur, Kubernetes node olarak kullan
# VM'de Docker kurulum (Ubuntu 22.04)
curl -fsSL https://download.docker.com/linux/ubuntu/gpg |
sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg]
https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" |
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io
# Kullanıcıyı docker grubuna ekle
sudo usermod -aG docker $USER
Operasyonel Farklar: Günlük Yaşam
Şunu da konuşalım: Bunları yönetmek nasıl?
VM yönetiminde virt-manager, vCenter, Hyper-V Manager gibi araçlar var. Snapshot almak, rollback yapmak kolay. Sorun çıktığında VM içine girip klasik troubleshooting yapabiliyorsunuz. Ama VM’leri güncel tutmak, patch yönetimi, her biri ayrı bir OS olduğu için ciddi iş.
Container yönetimi başta daha çekici görünüyor ama kendi karmaşıklığını getiriyor. Stateless olmayı gerçekten anlamadan container operasyonu yapmak sorun yaratıyor.
# Container loglarına bakmak
docker logs -f --tail 100 mycontainer
# Container içine girmek (debug için)
docker exec -it mycontainer /bin/bash
# Container'ın kaynak kullanımını anlık görmek
docker stats mycontainer
# Çalışan tüm container'ların özetini görmek
docker ps --format "table {{.Names}}t{{.Status}}t{{.Ports}}"
# Durmuş container'ları temizlemek
docker container prune -f
# Kullanılmayan image'ları temizlemek
docker image prune -a -f
VM’ler için benzer operasyonel kontroller:
# KVM VM'lerinin durumunu görmek
virsh list --all
# VM'in kaynak kullanımını görmek
virt-top
# Snapshot almak
virsh snapshot-create-as --domain myvm
--name "before-update-$(date +%Y%m%d)"
--description "Update öncesi snapshot"
# Snapshot listesi
virsh snapshot-list myvm
Sonuç
“Docker mı VM mi?” sorusunun evrensel bir cevabı yok. Bu soruyu soran birine her zaman şunu soruyorum: “Ne çalıştırıyorsunuz, güvenlik gereksinimleriniz ne, takımınızın deneyimi nerede?”
Pratik özet:
- Yeni, cloud-native uygulamalar için Docker ve Kubernetes kombinasyonu neredeyse standart haline geldi. Doğru seçim büyük ihtimalle bu.
- Legacy sistemler, mixed OS ortamları, sıkı compliance gereksinimleri için VM’ler hala sağlam ve güvenilir seçim.
- Production’da en sağlıklı mimari genellikle ikisini birlikte kullanmak: VM’ler üzerinde container orchestration.
- Güvenlik konusunda kendinizi kandırmayın. Container izolasyonu VM izolasyonu kadar güçlü değil. Bu gerçeği kabul edip buna göre güvenlik katmanları oluşturun.
Son olarak şunu söyleyeyim: Yıllarca VM yöneterek geçirdiğim zaman boşa gitmedi. Docker’ı anlamak için sanallaştırmanın temellerini bilmek büyük avantaj sağlıyor. Teknoloji değişiyor ama problem çözme mantığı aynı kalıyor. İkisini de öğrenin, ikisini de elinizin altında tutun. Doğru araç, doğru iş için.
