0. Bu Yazı Nasıl Okunmalı?
Bu dokümantasyon, Docker’ı sıfırdan öğrenmek isteyenler için kapsamlı bir rehber olarak hazırlandı. Ancak bu sadece bir blog yazısı değil — aynı zamanda bir başvuru kaynağı ve pratik bir kılavuz.
Yazının Yapısı ve Okuma Stratejisi
Bu yazı kademeli derinleşme prensibiyle oluşturuldu. İlk bölümler temel kavramları tanıtırken, ilerleyen bölümler production ortamları, güvenlik, performans optimizasyonu gibi ileri seviye konulara giriyor. Bu yüzden:
Eğer yeni başlıyorsanız: Sırayla, baştan sona okuyun. Her bölüm bir öncekinin üzerine inşa edilmiştir. Konuları atlamak, ileride anlama zorluğu yaratabilir.
Eğer belirli bir sorunu çözmek istiyorsanız: İçindekiler bölümünden ihtiyacınız olan konuya atlayın. Her bölüm mümkün olduğunca bağımsız yazılmaya çalışıldı.
Eğer bilginizi pekiştirmek istiyorsanız: İlgilendiğiniz bölümleri seçerek okuyun, ancak örnekleri mutlaka test edin.
Önemli Uyarılar
-
Yavaş okuyun. Özellikle 10. bölümden sonra teknik detaylar artıyor. Acele ederseniz önemli noktalari kaçırabilirsiniz.
-
Pratik yapın. Sadece okumak yeterli değil. Örnekleri kendi bilgisayarınızda çalıştırın, hata yapın, düzeltin. Yazılım öğrenmek bir zanaat — deneyerek öğrenilir.
-
Parçalara bölün. Bu yazıyı tek oturuşta okumaya çalışmayın. Her gün bir-iki bölüm üzerinde çalışmak, tüm yazıyı bir gecede geçiştirmekten çok daha etkilidir.
-
Not alın. Önemli komutları, kendi projeleriniz için uyarlayabileceğiniz pattern’leri, karşılaştığınız sorunları not edin. Bu yazı bir referans kaynağıdır, sık sık geri döneceksiniz.
Hedef Kitle
- Yazılım geliştiriciler (backend, frontend, full-stack)
- DevOps mühendisleri ve sistem yöneticileri
- Docker’a yeni başlayanlar
- Mevcut Docker bilgisini derinleştirmek isteyenler
- Production ortamlarında Docker kullanacak ekipler
Bu Yazının Felsefesi
Teori + Pratik = Öğrenme. Her kavram hem teorik olarak açıklandı hem de çalışan kod örnekleriyle gösterildi. “Neden?” ve “Nasıl?” sorularının ikisine de yanıt vermeye çalıştım.
Sade dil. Gereksiz jargondan kaçındım. Teknik terimler kullanıldığında açıklandı. Anlaşılır olmak, teknik derinlikten daha önemli.
Gerçek dünya senaryoları. Production ortamında karşılaşacağınız problemler, anti-pattern’ler ve çözümleri de dahil edildi. Bu sadece “nasıl çalıştırılır” değil, “nasıl doğru çalıştırılır” rehberi.
Başlamadan Önce
Docker’ı bilgisayarınıza kurmuş olduğunuzdan emin olun. Kurulum talimatları Bölüm 3’te detaylı anlatılıyor. Terminal veya PowerShell kullanmaktan çekinmiyorsanız, hazırsınız demektir.
Şimdi, lafı daha fazla uzatmadan, Docker’ın ne olduğunu ve neden bu kadar önemli olduğunu anlamaya başlayalım.
1. Giriş / Neden Docker?
1.1 Kısa Özet: Konteyner Nedir, VM’den Farkı?
Uygulamaları çalıştırmanın iki yolunu elimize alalım:
-
Doğrudan işletim sistemine kurmak
-
Sanal makine (VM) kullanmak
Sanal makinelerde (örneğin VirtualBox, VMware) her bir makine kendi işletim sistemine sahiptir. Bu da çok fazla sistem kaynağı tükenmesine (RAM, CPU, disk alanı) ve başlatılmasınınzaman alması demektir.
Konteyner teknolojisi ise farklı bir yaklaşım getirir. Konteynerler, işletim sisteminin çekirdeğini ortak kullanır; sadece uygulamanın çalışması için gerekli kütüphaneleri ve bağımlılıkları içerir. Sadece gereklilikleri izole olarak çalıştırır, Yani:
-
Daha hafif,
-
Daha hızlı açılır,
-
Taşınabilir (her yerde çalışır) hale gelir.
Özetle:
-
VM = Tüm bilgisayarı taklit eder.
-
Konteyner = Sadece uygulamanın ihtiyacını izole edip çalıştırır.
1.2 Neden Docker?
Peki konteynerleri yönetmek için neden Docker kullanıyoruz? Çünkü Docker:
-
Taşınabilirlik sağlar: Bir uygulamayı kendi bilgisayarında çalıştırdığın şekilde sunucuda da çalıştırabilirsin. “Benim bilgisayarımda çalışıyor ama sende çalışmıyor” sorunu ortadan kalkar.
-
Hızlı dağıtım sunar: Birkaç saniyede konteyneri ayağa kaldırıp silebilirsin. Geleneksel kurulum süreçleri saatler sürebilirken Docker ile dakikalar, hatta saniyeler yeterli. Tüm sistemi kurmanıza gerek kalmaz sadece gereklilikleri kurar ve projeyi ayağa kaldırmanızı sağlar.
-
Standart bir ekosistemdir: Docker Hub üzerinden milyonlarca hazır imajı (nginx, mysql, redis gibi) indirip anında kullanabilirsin.
-
Modern yazılım pratiklerine uygundur: Mikroservis mimarisi, CI/CD, DevOps süreçlerinde Docker artık neredeyse zorunlu bir araç hâline gelmiştir.
1.3 Bu Yazıdan Kimler Nasıl Faydalanır?
Bu yazı, Docker’a yeni başlayanlar, yazılımcılar, teknik altyapıya ilgi duyanlar ve sistem yöneticileri için hazırlanmış hem bilgilendirici hem de rehber niteliğinde bir blog olarak tasarlandı.
Amacım, Docker kavramlarını sadece teknik terimlerle değil, basit ve anlaşılır bir dille anlatmak. Teoriyi pratiğe dönüştürerek, okuyucuların Docker’ı kendi projelerinde güvenle kullanabilmesini sağlamayı hedefliyorum.
Bu blog-dökümantasyon:
- Linux ve Windows ortamlarında uygulanabilir,
- Teoriden çok pratiğe odaklı,
- Karmaşık jargon yerine sade bir anlatım sunar,
- Adım adım ilerleyen bir öğrenme yolu sağlar.
Kısacası: Bu yazı, Docker’a yeni başlayanlardan sistem yöneticilerine kadar herkes için hazırlanmış, hem blog hem de rehber niteliğinde bir kaynaktır. Basit, jargon içermeyen anlatımıyla teoriyi pratiğe dönüştürerek Docker’ı öğrenmeyi hızlı, sade ve etkili hâle getirmeyi hedefledim.
2. Docker Ekosistemi ve Temel Bileşenler (Docker’ın Kendi Araçları)
Docker sadece “konteyner çalıştıran bir yazılım” değildir. Onun etrafında birçok araç, bileşen ve standart gelişmiştir. Bunları bilmek, Docker’ın nasıl çalıştığını anlamak ve onu kullanabilmek için önemlidir.
2.1 Docker Engine (Daemon & CLI)
Docker Engine, Docker’ın kalbidir. Arka planda çalışan daemon (dockerd) konteynerlerin yönetiminden sorumludur. Örnek olarak: başlatma, durdurma, network, volume
Burada bizim kullandığımız kısım Docker CLI (docker run, docker ps, docker build gibi komutlardır). CLI, daemon ile konuşur ve emirleri uygulatır.

Özet: CLI = Kullanıcı arabirimi, Daemon = Motor.
2.2 Docker CLI komutları
Docker container
Konteyner yönetimi komutları:
| Komut | Açıklama |
|---|---|
docker container run |
Yeni bir konteyner oluşturur ve çalıştırır (Docker Documentation) |
docker container create |
Konteyneri oluşturur ama çalıştırmaz (Docker Documentation) |
docker container ls / docker container list |
Çalışan konteynerleri listeler (Docker Documentation) |
docker container stop |
Çalışan konteyneri durdurur (Docker Documentation) |
docker container start |
Durdurulmuş bir konteyneri başlatır (Docker Documentation) |
docker container rm |
Bir konteyneri siler (kapatılmış olmalı) (Docker Documentation) |
docker container logs |
Bir konteynerin log verilerini gösterir (Docker Documentation) |
docker container exec |
Çalışan bir konteyner içinde komut çalıştırır (Docker Documentation) |
docker container inspect |
Konteynerin detaylı yapılandırmasını gösterir (Docker Documentation) |
docker container stats |
Kaynak kullanım istatistiklerini gerçek zamanlı gösterir (Docker Documentation) |
docker container pause / docker container unpause |
Konteyneri geçici durdur / tekrar devam ettir (Docker Documentation) |
docker container kill |
Konteyneri aniden durdurur (SIGKILL) (Docker Documentation) |
Docker image
İmaj (image) yönetimi:
-
docker image ls/docker images— sistemdeki imajları listeler (Docker Documentation) -
docker image rm/docker rmi— bir ya da daha fazla imajı siler (Docker Documentation) -
docker image prune— kullanılmayan (dangling) imajları temizler (Docker Documentation)
Docker build
Dockerfile’dan imaj oluşturma:
-
docker build— Dockerfile’a göre imaj inşa eder (Docker Documentation) -
--no-cachegibi bayraklarla cache kullanımını devre dışı bırakma (Docker Documentation)
Genel Komutlar
-
docker version— CLI ile daemon versiyon bilgisini gösterir -
docker info— Docker ortamının durumunu, sistem detaylarını gösterir -
docker system— sistemle ilgili komutlar (örneğin kaynak temizleme, disk kullanımı) -
docker --helpveyadocker <komut> --help— komutların yardım bilgisini gösterir
2.2.1 Docker CLI Parametreleri — Detaylı Açıklama ve Örnekler
Docker CLI komutlarında kullanılan parametreler:
| Parametre | Açıklama | Örnek (Linux) |
|---|---|---|
-it |
İnteraktif terminal sağlar. | docker run -it ubuntu bash |
-d |
Detached mode (arka planda çalıştırır). | docker run -d nginx |
--rm |
Konteyner durdurulduğunda otomatik siler. | docker run --rm alpine echo "Hello" |
-p |
Port yönlendirme (host:container). | docker run -p 8080:80 nginx |
-v / --volume |
Volume mount ile dosya paylaşımı sağlar. | docker run -v /host/data:/container/data alpine |
--name |
Konteyner için özel isim atar. | docker run --name mynginx -d nginx |
-e / --env |
Ortam değişkenleri tanımlar. | docker run -e MYVAR=value alpine env |
--network |
Konteynerin bağlanacağı network’ü belirler. | docker run --network mynet alpine |
--restart |
Konteyner yeniden başlatma politikasını ayarlar. | docker run --restart always nginx |
2.3 Dockerfile & BuildKit (build optimizasyonu)
Dockerfile, Docker imajlarını tanımlayan tarif dosyasıdır. İçinde hangi temel imaj kullanılacağı, hangi paketlerin kurulacağı, hangi dosyaların kopyalanacağı ve hangi komutların çalıştırılacağı gibi bilgiler yer alır.
Dockerfile Temelleri
Örnek basit Dockerfile:
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["python", "app.py"]
Temel komutlar:
FROM→ temel imaj belirlerWORKDIR→ çalışma dizinini ayarlarCOPY/ADD→ dosya kopyalamaRUN→ imaj inşa sürecinde komut çalıştırmaCMD→ konteyner başlatıldığında çalışacak komut
Docker BuildKit Nedir?
BuildKit, Docker’ın build sürecini daha hızlı, verimli ve güvenli hale getiren modern build motorudur.
Docker 18.09’dan itibaren opsiyonel olarak kullanılabilmiş, Docker 20+ ile artık varsayılan olarak aktiftir.
Avantajları:
- Paralel build adımları (daha hızlı)
- Layer cache optimizasyonu (disk ve zaman tasarrufu)
- Inline cache kullanımı
- Build output’ların daha iyi kontrolü
- Build secrets yönetimi
- Daha temiz ve küçük imajlar
BuildKit Kullanımı
Docker’da BuildKit’i aktif etmek için:
export DOCKER_BUILDKIT=1 # Linux/macOS
setx DOCKER_BUILDKIT 1 # Windows (PowerShell)
Docker build komutu:
docker build -t myapp:latest .
BuildKit ile aynı komut:
DOCKER_BUILDKIT=1 docker build -t myapp:latest .
BuildKit’in Özellikleri
-
Secret Yönetimi Şifreler, API anahtarları gibi hassas bilgileri build sürecinde güvenli şekilde kullanabilirsin.
# syntax=docker/dockerfile:1.4 FROM alpine RUN --mount=type=secret,id=mysecret cat /run/secrets/mysecretBuild komutu:
DOCKER_BUILDKIT=1 docker build --secret id=mysecret,src=secret.txt . -
Cache Yönetimi
--cache-fromile önceden build edilmiş imajlardan cache kullanabilirsin.docker build --cache-from=myapp:cache -t myapp:latest . -
Parallel Build Bağımsız katmanlar aynı anda build edilir, build süresi kısalır.
-
Multi-stage Builds Daha küçük ve optimize edilmiş imajlar için build sürecini birden çok aşamada tanımlayabilirsin.
FROM golang:1.20 AS builder
WORKDIR /app
COPY . .
RUN go build -o app
FROM alpine:latest
WORKDIR /app
COPY --from=builder /app/app .
CMD ["./app"]
BuildKit, Docker build sürecinde performans, güvenlik ve yönetilebilirlik sağlar. Büyük ve karmaşık projelerde BuildKit kullanmak, imaj boyutunu azaltır, build süresini kısaltır ve hassas bilgilerin güvenliğini artırır.
2.4 Docker Compose (tek makinede multi-container)
Çoğu gerçek hayat uygulaması tek bir konteyner ile çalışmaz. Genelde modüler olması için her işlem için ayrı bir konteyner açılır. Eğer bir sistem çökerse diğer sistemlerinde çökmemesi için bu gereklidir. Bu yapının adı Modüler Yapıdır. Misal bir saas projesinde her api için ayrı bir konteyner açmak hem sistemde bir sorun olduğunda diğer sistemlerin çökmesini ve bozulmasını engeller ve sistem sorunları ile baş etmede yardımcı olur.
Örneğin:
-
Bir web uygulaması + veritabanı (MySQL/Postgres) + önbellek (Redis)
-
Bir API servisi + mesaj kuyruğu (RabbitMQ/Kafka)
Ek olarak bu sistemi kurduğunuzda hepsini tek tek docker run ile başlatmak karmaşık ve hataya açık olur. İşte burada Docker Compose devreye girer. Hem modüler bir yapı yapmanızda hemde bunun kontrolünü kaybetmemeniz için en güzel seçeneğiniz Docker Compose olacaktır.
Docker Compose Nedir?
-
Bir YAML dosyası (
docker-compose.yml) üzerinden, birden fazla servisi tanımlayıp yönetmeye yarar. -
Tek komutla (
docker compose up) tüm sistemi ayağa kaldırabilir,docker compose downile temizleyebilirsiniz. -
Ortak ağ (network) ve volume tanımlarını kolayca yapar.
-
Genelde geliştirme ve test ortamlarında tercih edilir, prod ortamında ise Kubernetes gibi orkestrasyon araçlarına geçilir.
Örnek docker-compose.yml
Basit bir Django + Postgres uygulamasını düşünelim:
version: "3.9" # Compose dosyası sürümünü belirtir.
services:
web: # 1.servis (Web Servis)
build: . # (Bulunduğun klasördeki `Dockerfile` kullanılarak imaj üretilir)
ports:
- "8000:8000" # 8000 portuna yönlendirme
depends_on: # Bu servis çalışmadan önce `db` servisinin ayağa kalkmasını garanti eder.
- db
environment:
- DATABASE_URL=postgres://postgres:secret@db:5432/appdb
networks:
- my_network # Manuel network ataması
db: # 2.servis (PostgreSQL veritabanı)
image: postgres:16
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: secret
POSTGRES_DB: appdb
volumes:
- db_data:/var/lib/postgresql/data
networks:
- my_network # Manuel network ataması
volumes:
db_data:
networks: # Manuel network tanımı
my_network:
driver: bridge # Bridge network tipi (en yaygın)
Temel Komutlar
docker compose up→ YAML’deki tüm servisleri ayağa kaldırır.docker compose down→ Tüm servisleri ve network’ü kapatır.docker compose ps→ Compose ile açılan konteynerleri listeler.docker compose logs -f→ Servislerin loglarını canlı izlersin.docker compose exec web bash→ Web konteynerine girip komut çalıştırırsın.
2.5 Docker Desktop (Windows/macOS için)
Docker, Linux üzerinde doğrudan kernel üzerinde çalışabilir. Ancak Windows veya macOS üzerinde bu mümkün değildir çünkü Docker Linux çekirdeği (kernel) gerektirir. İşte bu yüzden Docker Desktop devreye girer.
Docker Desktop Nedir?
-
Docker Desktop, Windows ve macOS kullanıcıları için Docker’ın resmi uygulamasıdır.
-
Linux çekirdeği üzerinde Docker çalıştırabilmek için içinde hafif bir sanal makine (VM) barındırır.
-
Kullanıcıya bu süreci şeffaf bir şekilde sunar: sanki Docker Linux üzerinde çalışıyormuş gibi komutlar yazarsın.
2.6 Docker Registry / Docker Hub / private registry
Docker Registry, Docker imajlarının depolandığı ve paylaşıldığı sunucu veya servisdir. İmajlar, bir Registry üzerinde tutulur ve ihtiyaç duyulduğunda buradan çekilir (pull) veya yüklenir (push).
Docker ekosisteminde en yaygın kullanılan registry türleri Docker Hub ve Private Registry’dir.

Docker Hub
-
Docker’ın resmi registry servisidir.
-
hub.docker.com üzerinden milyonlarca hazır imaj erişilebilir. (nginx, mysql, redis vb.)
-
Avantajları:
- Kolay erişim, büyük topluluk desteği
- Resmi imajlar güvenlik güncellemeleriyle desteklenir
- Ücretsiz plan ile sınırlı kullanıma imkan verir
Kullanım örneği:
docker pull nginx:latest # Docker Hub’dan nginx imajını çek
docker run -d -p 80:80 nginx:latest
Private Registry
-
Şirket içi veya özel projeler için kendi registry’ni kurabilirsin.
-
Örneğin, hassas bir uygulamanın imajlarını yalnızca kendi ağında saklamak isteyebilirsin.
-
Avantajları:
- Tam kontrol (erişim, güvenlik, depolama)
- Gizlilik ve özel dağıtım
-
Kurulum örneği:
docker run -d -p 5000:5000 --name registry registry:2
Bu komut ile yerel bir registry ayağa kalkar.
Artık imajlarını kendi registry’ne push/pull edebilirsin.
Örnek:
docker tag myapp localhost:5000/myapp:latest
docker push localhost:5000/myapp:latest
docker pull localhost:5000/myapp:latest
Registry Kullanım Workflow’u
- İmaj oluştur (
docker build) - İmaj etiketle (
docker tag) - Registry’ye yükle (
docker push) - Registry’den çek (
docker pull)
Docker Hub ile Private Registry kısaca karşılaştırmamız gerekirse:
| Tür | Avantajlar | Dezavantajlar |
|---|---|---|
| Docker Hub | Hazır imajlar, kolay erişim, ücretsiz plan | Özel imajlarda güvenlik, erişim kontrolü sınırlı |
| Private Registry | Gizlilik, tam kontrol, özel dağıtım | Kurulum ve bakım gerektirir |
2.7 Docker Swarm (native orchestration)
Docker Swarm, Docker’ın kendi içinde gelen, birden çok bilgisayarda çalışan konteynerleri tek bir sistem gibi yönetmemizi sağlayan bir özelliktir. Yani:
- Normalde Docker’ı tek bir bilgisayarda çalıştırırsın.
- Eğer yüzlerce konteyneri farklı bilgisayarlarda çalıştırmak istersen, bunu manuel yapmak çok zor olur.
- Docker Swarm, bu işi otomatikleştirir: konteynerleri hangi bilgisayarda çalıştıracağını, kaç tane çalışacağını ve birbirleriyle nasıl konuşacağını kendisi yönetir.
Bir benzetme yapalım:
Docker Swarm, bir orkestra şefi gibidir.
- Tek bir müzisyen (bilgisayar) yerine, birden çok müzisyen (bilgisayar) vardır.
- Şef (Swarm) herkese ne çalacağını, ne zaman çalacağını ve nasıl uyum içinde çalışacaklarını söyler.
- Ortaya düzgün bir müzik (çalışan sistem) çıkar.
Docker Swarm’un Temel Özellikleri
-
Cluster Yönetimi
Birden çok Docker host’unu tek bir sanal Docker host gibi yönetir.
Bu host’lara “node” denir. -
Yük Dengeleme (Load Balancing)
Swarm, servis taleplerini otomatik olarak uygun node’lara yönlendirir. -
Servis Keşfi (Service Discovery)
Swarm, servisleri otomatik olarak birbirine tanıtır.
Servis isimleri üzerinden erişim sağlanabilir. -
Otomatik Failover
Bir node arızalanırsa, Swarm konteynerleri otomatik olarak başka node’lara taşır.
Docker Swarm Yapısı
Swarm cluster’ı iki tip node’dan oluşur:
-
Manager Node
-
Cluster yönetimini üstlenir.
-
Servis dağıtımı, cluster durum kontrolü ve yük dengeleme işlemlerini yapar.
-
-
Worker Node
- Manager node’dan aldığı görevleri çalıştırır.
Docker Swarm Kullanım Örneği
1. Swarm başlatma (manager node)
docker swarm init
Bu komut ile mevcut Docker host, Swarm cluster’ının manager node’u olur.
2. Node ekleme (worker node)
docker swarm join --token <token> <manager-ip>:2377
Bu komut, worker node’u cluster’a ekler. <token> ve <manager-ip> Swarm tarafından sağlanır.
3. Servis oluşturma
docker service create --name myweb --replicas 3 -p 80:80 nginx
-
--replicas 3: Servis için 3 konteyner kopyası çalıştırır. -
-p 80:80: Port yönlendirmesi yapar.
4. Servis durumu kontrolü
docker service ls
docker service ps myweb
Özetle Docker Swarm, küçük ve orta ölçekli projelerde basit, hızlı ve yerleşik bir orkestrasyon çözümüdür.
Kubernetes gibi daha karmaşık sistemlere geçmeden önce hızlı prototipler ve küçük cluster’lar için idealdir.
| Avantajlar | Dezavantajlar |
|---|---|
| Docker ekosistemine entegre, ekstra kurulum gerektirmez | Kubernetes kadar kapsamlı değil |
| Basit yapılandırma | Büyük ölçekli altyapılarda sınırlı özellikler |
| Servis dağıtımı ve otomatik ölçekleme | |
| Load balancing ve service discovery dahili olarak sağlanır |
2.8 containerd / runc (altyapı) — kısa not

Docker çalışırken aslında birden fazla katmanda işler yürütülür.
containerd ve runc, Docker’ın en temel altyapı bileşenlerindendir.
containerd
- Docker’ın konteyner yaşam döngüsünü yöneten yüksek seviyeli runtime’ıdır.
- Konteyner oluşturma, çalıştırma, durdurma, silme gibi görevleri yönetir.
- Görüntü (image) yönetimi, network, storage ve konteyner lifecycle gibi işlemler containerd üzerinden yapılır.
runc
- containerd tarafından kullanılan düşük seviyeli runtime’dır.
- Open Container Initiative (OCI) standardına uygun olarak konteynerleri çalıştırır.
- Temel olarak Linux çekirdeği üzerinde konteynerlerin çalışmasını sağlar.
Özetle:
- containerd → Docker’ın konteyner yönetim motoru
- runc → Konteynerleri çekirdek üzerinde çalıştıran motor
Bu ikisi Docker’ın “motoru” gibidir; Docker CLI ise “direksiyon” görevindedir.
3. Kurulum & İlk Adımlar (Linux vs Windows)
Docker kurulumu işletim sistemine göre farklılık gösterir. Bu bölümde Linux ve Windows üzerinde kurulum adımlarını açıklayacağız.
3.A Linux (alt dağıtımlar: Ubuntu/Debian, RHEL/CentOS, Arch)
Docker’ı Linux üzerinde kurarken genellikle şu adımlar uygulanır:
- Paket deposu ekleme → Docker’ın resmi paket deposunu sisteme ekleriz.
- GPG anahtarı ekleme → Paket bütünlüğünü doğrulamak için gerekli.
- Docker ve containerd kurulumu → Docker Engine’i ve container runtime’ını kurarız.
- Docker servisini aktif etme → Sistem başlatıldığında Docker’ın çalışmasını sağlar.
- Kullanıcı yetkilendirmesi → Docker komutlarını root olmadan çalıştırabilmek için kullanıcıyı docker grubuna ekleriz.
Temel komutlar (Ubuntu örneği)
sudo apt update
sudo apt install -y ca-certificates curl gnupg lsb-release
# Docker’ın GPG anahtarını ekleme
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
# Docker repository ekleme
echo \
"deb [arch=$(dpkg --print-architecture) 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 update
# Docker Engine, CLI, containerd ve Compose kurulumu
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
# Docker servisini aktif etme
sudo systemctl enable --now docker
# Kullanıcıyı docker grubuna ekleme (root olmadan docker kullanımı)
sudo usermod -aG docker $USER
Açıklama:
systemctl enable --now docker: Docker servisini etkinleştirir ve anında başlatır.sudo usermod -aG docker $USER: Kullanıcıyı docker grubuna ekler, böylece her komut içinsudogerekmez (çıkış yapıp tekrar giriş yapmak gerekir).
3.B Windows (Docker Desktop + WSL2 ve Windows konteynerler)
Windows üzerinde Docker çalıştırmak için Docker Desktop kullanılır. Docker Desktop, Windows üzerinde Docker’ı çalıştırmak için WSL2 (Windows Subsystem for Linux) veya Hyper-V teknolojilerini kullanır.
Kurulum Aşamaları
1. Docker Desktop Kurulumu
-
Docker Desktop’ı resmi siteden indir.
-
Kurulum sırasında WSL2 entegrasyonu seçeneğini işaretle.
2. WSL2 Kontrolü ve Kurulumu
PowerShell üzerinde:
wsl --list --verbose
Eğer WSL2 kurulu değilse:
wsl --install -d Ubuntu
Bu komut Ubuntu’yu WSL2 üzerinde kurar ve çalıştırır.
3. Hyper-V ve Containers Özelliklerini Etkinleştirme
Docker Desktop’ın düzgün çalışması için Hyper-V ve Containers özellikleri aktif olmalıdır.
PowerShell üzerinde:
dism.exe /online /enable-feature /featurename:Microsoft-Hyper-V /all
dism.exe /online /enable-feature /featurename:Containers /all
4. Docker Desktop’ı Başlatma
- Kurulum tamamlandığında Docker Desktop’ı çalıştır.
- Settings → General bölümünde “Use the WSL 2 based engine” seçeneğini işaretle.
5. Windows Konteynerler vs Linux Konteynerler
Docker Desktop’ta konteyner türünü değiştirebilirsin:
- Linux containers → Varsayılan, çoğu uygulama için önerilen.
- Windows containers → Windows tabanlı uygulamalar için kullanılır.
4. Dockerfile — Adım Adım (Linux ve Windows tabanlı örnekler)
Dockerfile, Docker imajlarının nasıl oluşturulacağını tanımlayan metin dosyasıdır. Bir Docker imajı, bu dosyadaki yönergelere göre adım adım inşa edilir. Dockerfile yazmak, uygulamanın çalışacağı ortamı standartlaştırmak ve dağıtım sürecini kolaylaştırmak için kritik bir adımdır.
Bu bölümde Dockerfile’ın temel direktiflerini, multi-stage build yaklaşımını ve Windows container’larda Dockerfile kullanımını detaylı şekilde ele alacağız.
4.1 Dockerfile Temel Direktifler
Dockerfile içinde kullanılan temel direktifler:
| Direktif | Açıklama |
|---|---|
FROM |
Base image’i tanımlar. |
WORKDIR |
Çalışma dizinini belirler. |
COPY / ADD |
Dosya/dizin kopyalama işlemleri için kullanılır. |
RUN |
Konteyner içinde komut çalıştırır. |
CMD |
Konteyner çalıştırıldığında varsayılan komutu belirler. |
ENTRYPOINT |
CMD ile birlikte çalışır, komutun sabit kısmını tanımlar. |
ENV |
Ortam değişkenlerini tanımlar. |
EXPOSE |
Dinlenecek portu belirtir. |
USER |
Konteynerde çalışacak kullanıcıyı belirtir. |
Not: Direktiflerin sırası, Docker cache mekanizması açısından önemlidir.
4.2 Multi-Stage Builds — Neden, Nasıl?
Multi-stage build, imaj boyutunu azaltmak ve gereksiz bağımlılıkları final imajdan çıkarmak için kullanılır.
Örnek: Node.js Multi-Stage Build
# Stage 1: Build
FROM node:18-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Stage 2: Production
FROM node:18-alpine
WORKDIR /app
COPY --from=build /app/dist ./dist
COPY package*.json ./
RUN npm ci --only=production
CMD ["node", "dist/index.js"]
4.3 Windows Container Dockerfile
Windows container’lar, Linux container’lara göre farklı Dockerfile direktifleri ve base image’lar kullanır.
Örnek: Windows PowerShell Base Image
FROM mcr.microsoft.com/windows/servercore:ltsc2022
SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop';"]
RUN Write-Host 'Hello from Windows container'
CMD ["powershell.exe"]
Ek Bilgi: Windows container imajları genellikle Linux’a göre daha büyük boyutludur.
4.4 Örnek: Linux Node.js Dockerfile
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
USER node
CMD ["node", "index.js"]
4.5 İyi Pratikler
Dockerfile yazarken katman sayısını azaltmak için RUN komutlarını birleştirmeli, .dockerignore ile gereksiz dosyaları build sürecinden çıkarmalı, küçük base image’ler (Alpine, distroless vb.) tercih etmeli, multi-stage build kullanmalı ve ortam değişkenlerini ENV direktifiyle yönetmelisiniz.
Özetle:
- Katman sayısını azaltmak için RUN komutlarını birleştirin.
.dockerignoreile gereksiz dosyaları build sürecinden çıkarın.- Küçük base image seçin (Alpine, distroless vb.).
- Multi-stage build kullanın.
- Ortam değişkenlerini ENV ile yönetin.
4.6 Dockerfile Optimizasyonu
Dockerfile optimizasyonu, build süresini kısaltır, imaj boyutunu küçültür ve dağıtım sürecini hızlandırır.
Temel optimizasyon teknikleri:
- Cache kullanımını iyi yönetmek: Direktiflerin sırasını mantıklı tutmak (
COPY package*.json→RUN npm ci→COPY . .gibi). - Katman sayısını azaltmak: RUN komutlarını zincirlemek (
&&ile). - Küçük base image kullanmak: Alpine, distroless veya slim image’lar.
- .dockerignore dosyası oluşturmak: Gereksiz dosyaları dışarıda bırakmak.
- Multi-stage build kullanmak: Gereksiz bağımlılıkları final imajdan çıkarmak.
4.7 Best Practices ve Performans İpuçları
COPYkomutlarını minimumda tutun.- Ağ üzerinden dosya indirmeleri için
RUN curl/wgetyerine build argümanlarını kullanın. - Gereksiz paketleri kaldırın (
apt-get clean,rm -rf /var/lib/apt/lists/*). - Build sürecinde mümkünse
--no-cacheseçeneğini test amaçlı kullanın. - Ortam değişkenleri ile konfigürasyonu yönetin, hard-code etmeyin.
4.8 Özet ve İleri Okuma
Dockerfile, konteyner tabanlı uygulama geliştirme sürecinde en kritik parçadır. İyi bir Dockerfile:
- Hızlı build olur,
- Küçük boyutlu imaj üretir,
- Kolay bakım sağlar.
İleri Okuma için Kaynaklar:
- Dockerfile Reference — Docker Docs
- Best Practices for Writing Dockerfiles
- Multi-Stage Builds — Docker Docs
5. Image Yönetimi ve Optimizasyon
Docker imajları, uygulamanızın çalışması için gerekli tüm dosya, bağımlılık ve konfigürasyonu içerir. Etkili imaj yönetimi, hem depolama alanını hem de konteyner başlatma süresini doğrudan etkiler. Bu bölümde imaj yönetiminin temellerini ve optimizasyon tekniklerini ele alacağız.
5.1 Layer Mantığı ve Cache Mekanizması
Docker imajları layer (katman) yapısına dayanır. Her Dockerfile satırı bir katman oluşturur. Bu katmanlar, build sürecinde yeniden kullanılabilir. Bu sayede:
- İmaj oluşturma süresi kısalır,
- Disk kullanımı azalır.
Önemli nokta: Katmanların yeniden kullanılabilmesi için Dockerfile’da yapılan değişiklikler minimum olmalıdır.
5.2 .dockerignore, Katman Sayısını Azaltma, Küçük Base Image Seçme
.dockerignoredosyası →.gitignoregibi çalışır; build sırasında gereksiz dosyaların konteynere eklenmesini engeller.- Katman sayısını azaltma → Gereksiz RUN komutlarını birleştirerek katman sayısını düşürmek, imaj boyutunu azaltır.
- Küçük base image seçme →
alpine,busyboxgibi minimal base imajlar, gereksiz bağımlılıkları ortadan kaldırarak imaj boyutunu ciddi şekilde küçültür.
5.3 Önemli Image Yönetimi Komutları
docker build --no-cache→ Katman önbelleğini kullanmadan imaj oluşturur.docker history <image>→ İmajın layer geçmişini gösterir.docker image prune→ Kullanılmayan imajları temizler.
Örnekler:
docker build --no-cache -t myapp:latest .
docker history myapp:latest
docker image prune -a
5.4 Multi-Arch ve docker buildx
Modern uygulamalar farklı platformlarda çalışabilir. Multi-arch (çoklu mimari) imajlar, tek build ile farklı CPU mimarilerine uygun imajlar oluşturmayı sağlar.
docker buildx, Docker’ın gelişmiş build özelliğidir ve multi-arch imajlar için kullanılır.
Örnek:
docker buildx create --use
docker buildx build --platform linux/amd64,linux/arm64 -t myapp:multiarch .
Bu komut ile amd64 ve arm64 mimarileri için tek seferde imaj oluşturulur.
6. Volumes ve Storage (Linux vs Windows farkları)
Docker’da konteynerler geçici ortamlardır — konteyner silindiğinde içindeki tüm veriler kaybolur. Bu nedenle verilerin kalıcılığını sağlamak için volumes ve farklı storage yöntemleri kullanılır. Ancak Linux ve Windows arasında mount yolları, izinler ve davranışlar farklıdır.
Bu bölümde:
- Named volumes, bind mounts ve tmpfs’in farklarını,
- SELinux ile ilgili izin etiketlerini,
- Windows’ta yol yazımı ve izin farklarını,
- Volume yedekleme ve geri yükleme yöntemlerini detaylıca öğreneceksiniz.
6.1 Named Volumes vs Bind Mounts vs tmpfs
Docker’da veri kalıcılığı için üç ana yöntem vardır:
| Tür | Açıklama | Kullanım Durumu |
|---|---|---|
| Named Volumes | Docker tarafından yönetilen, konteynerler arası paylaşılabilen ve kalıcı veri depolama alanları. | Veri saklama, veri paylaşımı, veri yedekleme. |
| Bind Mounts | Host makinedeki belirli bir dizini konteynere bağlama yöntemi. | Geliştirme sürecinde kod paylaşımı, konfigürasyon dosyaları. |
| tmpfs | RAM üzerinde çalışan geçici depolama. Kalıcı değildir, konteyner kapandığında silinir. | Geçici veriler, hız gerektiren işlemler, güvenlik açısından RAM üzerinde çalışma. |
Named Volume Örneği
docker volume create mydata
docker run -d -v mydata:/app/data myimage
docker volume createkomutu volume oluşturur./app/dataiçinde konteyner verisi kalıcı olur.- Container silinse bile volume içeriği korunur.
Bind Mount Örneği
Linux:
docker run -v /home/me/app:/app myimage
Windows (PowerShell):
docker run -v "C:\Users\Me\app":/app myimage
- Bind mount, host sistemi ile konteyner arasında doğrudan dosya paylaşımı sağlar.
- Kod geliştirme ve test süreçlerinde sık kullanılır.
tmpfs Örneği
docker run --tmpfs /app/tmp:rw,size=100m myimage
/app/tmpgeçici olarak RAM’de tutulur.- Konteyner kapandığında veriler kaybolur.
- Performans kritik işlemler için uygundur.
6.2 SELinux :z / :Z Etiketi Linux’ta Neden Gereklidir
SELinux güvenlik politikaları, bind mount’ların konteyner içinde kullanılabilmesi için ek etiketler gerektirir.
Bu etiketler mount edilen dizinin izinlerini belirler:
:z→ Mount edilen dizinin paylaşılması için genel erişim izni sağlar.:Z→ Mount edilen dizinin yalnızca ilgili konteyner için erişim izni olmasını sağlar.
Örnek:
docker run -v /home/me/app:/app:Z myimage
SELinux etkin olan sistemlerde bu etiketleri kullanmazsanız bind mount çalışmaz veya izin hataları alırsınız.
6.3 Windows’ta Yol Yazımı ve İzin Farklılıkları
Windows’ta bind mount’larda yol yazımı ve izinler Linux’tan farklıdır. Windows’ta bind mount kullanırken dikkat edilmesi gerekenler:
- Yol formatı PowerShell için çift tırnak içinde olmalıdır.
\yerine/kullanılabilir, ancak"C:\path\to\dir"formatı güvenlidir.- Windows ACL izinleri bind mount’larda etkili olabilir, bu yüzden izinleri kontrol etmek gerekir.
Örnek Bind Mount:
Linux:
docker run -v /home/me/app:/app myimage
Windows (PowerShell):
docker run -v "C:\Users\Me\app":/app myimage
6.4 Yedekleme / Geri Yükleme: tar ile Volume Yedekleme Yöntemi
Docker volume’larını yedeklemek veya geri yüklemek için tar komutu kullanılabilir. Bu yöntem hem Linux hem Windows için uygundur.
Yedekleme
docker run --rm -v myvolume:/volume -v $(pwd):/backup alpine \
tar czf /backup/myvolume-backup.tar.gz -C /volume .
Açıklama:
--rm→ Container durduğunda otomatik silinir.-v myvolume:/volume→ Yedeklenecek volume bağlanır.-v $(pwd):/backup→ Host dizini bağlanır, yedekleme dosyası buraya kaydedilir.tar czf→ Verileri sıkıştırarak.tar.gzformatında yedekler.
Geri Yükleme
docker run --rm -v myvolume:/volume -v $(pwd):/backup alpine \
tar xzf /backup/myvolume-backup.tar.gz -C /volume
Geri yükleme öncesinde volume’ın boş olduğundan emin olun. Aksi halde eski veriler üzerine yazılır.
7. Ağ (Networking)
Docker’da ağ yönetimi, konteynerlerin birbirleriyle ve host sistemiyle iletişim kurabilmesini sağlar. Docker, varsayılan olarak konteynerler arasında izolasyon sağlar ve farklı ağ modları sunar. Ağlar, Docker’ın en kritik kavramlarından biridir, çünkü uygulamaların güvenliği, ölçeklenebilirliği ve yönetilebilirliği doğrudan ağ yapılandırmasına bağlıdır.
Bu bölümde:
- Docker’ın varsayılan ağ modlarını,
- Custom bridge network oluşturmayı,
- Container içi DNS ve servis keşfini,
- Host networking modunu ve kısıtlarını,
- Overlay, macvlan ve transparent network yapılarını ele alacağız.
7.1 Default Bridge ve Custom Bridge Oluşturma
Docker kurulduğunda otomatik olarak bir default bridge ağı oluşturur.
Bu ağ üzerinde konteynerler, IP adresleri üzerinden birbirlerini görebilir, ancak host ile iletişim için port yönlendirme gerekir.
Default Bridge Örneği:
docker run -d --name web -p 8080:80 nginx
-p 8080:80 → Host’un 8080 portunu container’ın 80 portuna yönlendirir.
Custom Bridge Oluşturma
Custom bridge ağlar, izolasyon ve servis keşfi için daha esnek yapı sağlar.
Ağ oluşturma:
docker network create mynet
Container’ı custom bridge ile başlatma:
docker run -dit --name a --network mynet busybox
docker run -dit --name b --network mynet busybox
Test:
docker exec -it a ping b
( Container a, container b’yi DNS adı ile görebilir.)
7.2 Container İçi DNS ve Servis Keşfi (Compose ile)
Docker Compose, container’lar arasında otomatik DNS çözümlemesi sağlar.
Servis adı, container adı olarak kullanılabilir.
docker-compose.yml Örneği:
version: "3"
services:
web:
image: nginx
networks:
- mynet
app:
image: busybox
command: ping web
networks:
- mynet
networks:
mynet:
( Burada app container’ı, web container’ını DNS ismi üzerinden bulabilir.)
7.3 --network host (Linux’ta) ve Host Networking’in Kısıtları
Host networking modu, container’ın host’un ağ stack’ini paylaşmasını sağlar.
Bu durumda port yönlendirme gerekmez.
Linux örneği:
docker run --network host nginx
hostmodu Linux’ta çalışır ancak Windows/macOS üzerinde Docker Desktop’ta desteklenmez.
Güvenlik açısından
hostmodu dikkatli kullanılmalıdır, çünkü container host ağını doğrudan etkiler.
7.4 Overlay Network (Swarm), macvlan, Transparent Networks (Windows)
Overlay Network (Docker Swarm)
- Farklı host makinelerdeki container’ların birbirleriyle iletişim kurmasını sağlar.
- Docker Swarm cluster’larında kullanılır.
Overlay Network oluşturma:
docker network create -d overlay my_overlay
Macvlan Network
- Container’lara host ağı üzerinde kendi MAC adresini verir.
- Fiziksel ağda ayrı bir cihaz gibi görünmesini sağlar.
Örnek:
docker network create -d macvlan \
--subnet=192.168.1.0/24 \
--gateway=192.168.1.1 \
-o parent=eth0 my_macvlan
Transparent Network (Windows)
- Windows container’larda fiziksel ağ ile doğrudan bağlantı kurmak için kullanılır.
- Genellikle enterprise ağ senaryolarında tercih edilir.
7.5 Örnek: Custom Bridge Network Kullanımı
docker network create mynet
docker run -dit --name a --network mynet busybox
docker run -dit --name b --network mynet busybox
Ping testi:
docker exec -it a ping b
Container’lar aynı custom bridge üzerinde DNS ile birbirlerine ulaşabilir.
7.6 Özet İpuçları
- Default bridge hızlı başlamak için uygundur ama DNS çözümleme özelliği sınırlıdır.
- Custom bridge izolasyon ve DNS desteği sağlar.
- Host networking performans avantajı sağlar ama Linux dışındaki platformlarda sınırlıdır.
- Overlay network multi-host senaryolarında çok kullanışlıdır.
- macvlan ve transparent networks fiziksel ağ entegrasyonu gereken durumlarda tercih edilir.
8. Docker Swarm / Stack (Native Orchestrator)
Docker Swarm, 2.7’de kısa tanıttık. Şimdi gerçek dünya kullanımı için detaylı bir rehber hazırlayalım.
Docker Swarm, Docker’ın yerleşik orkestrasyon aracıdır. Birden fazla sunucuyu (node) tek bir cluster olarak yönetir, konteynerleri otomatik dağıtır, ölçeklendirir ve arıza durumlarında yük dengelemesi yapar.
Ne zaman kullanılır?
- Küçük-orta ölçekli projeler
- Kubernetes’ten daha basit bir yapı isteyenler
- Hızlı prototip ve test ortamları
- Docker’a zaten aşina olan ekipler
Kubernetes ile farkı:
- Swarm daha basit, kurulumu ve yönetimi kolay
- Kubernetes daha güçlü ama karmaşık
- Swarm, Docker ekosistemiyle tam entegre
8.1 Swarm Cluster Kurulumu ve Servis Yönetimi
8.1.1 Swarm Başlatma (docker swarm init)
Manager Node Kurulumu:
docker swarm init --advertise-addr 192.168.1.10
Açıklama:
--advertise-addr: Bu node’un IP adresi. Diğer node’lar bu IP üzerinden bağlanacak.- Komut çalıştıktan sonra bir join token verir.
Çıktı örneği:
Swarm initialized: current node (abc123) is now a manager.
To add a worker to this swarm, run the following command:
docker swarm join --token SWMTKN-1-xxxxx 192.168.1.10:2377
8.1.2 Worker Node Ekleme
Worker node’da şu komutu çalıştır:
docker swarm join --token SWMTKN-1-xxxxx 192.168.1.10:2377
Node’ları kontrol etme (manager’da):
docker node ls
Çıktı:
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
abc123 * node1 Ready Active Leader
def456 node2 Ready Active
8.1.3 Servis Oluşturma (docker service create)
Basit bir web servisi:
docker service create \
--name myweb \
--replicas 3 \
--publish 80:80 \
nginx:alpine
Parametreler:
--name: Servis adı--replicas: Kaç konteyner kopyası çalışacak--publish: Port yönlendirme (host:container)
Servis durumunu kontrol:
docker service ls
docker service ps myweb
Çıktı:
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE
abc1 myweb.1 nginx:alpine node1 Running Running 2 mins
abc2 myweb.2 nginx:alpine node2 Running Running 2 mins
abc3 myweb.3 nginx:alpine node1 Running Running 2 mins
8.1.4 Servis Güncelleme
Image güncelleme:
docker service update --image nginx:latest myweb
Replika sayısını değiştirme:
docker service scale myweb=5
Port ekleme:
docker service update --publish-add 8080:80 myweb
8.1.5 Servis Silme
docker service rm myweb
8.2 Replication, Rolling Update, Constraints, Configs & Secrets
8.2.1 Replication (Çoklama)
Swarm, belirlenen sayıda replika çalıştırır. Bir konteyner çökerse otomatik yenisini başlatır.
Manuel scaling:
docker service scale myweb=10
Otomatik yük dengeleme: Swarm, gelen istekleri tüm replikalara dağıtır.
8.2.2 Rolling Update (Sıfır Kesinti Güncellemesi)
Servisleri kesintisiz güncellemek için rolling update kullanılır.
Örnek: Nginx 1.20’den 1.21’e geçiş
docker service update \
--image nginx:1.21-alpine \
--update-delay 10s \
--update-parallelism 2 \
myweb
Parametreler:
--update-delay: Her güncelleme arasında bekleme süresi--update-parallelism: Aynı anda kaç konteyner güncellenecek
Rollback (Geri alma):
docker service rollback myweb
8.2.3 Constraints (Yerleştirme Kısıtları)
Belirli node’larda servis çalıştırmak için constraint kullanılır.
Örnek: Sadece “production” etiketli node’larda çalışsın
docker service create \
--name prodapp \
--constraint 'node.labels.env==production' \
nginx:alpine
Node’a etiket ekleme:
docker node update --label-add env=production node2
Örnek: Manager node’larda çalıştırma
docker service create \
--name monitoring \
--constraint 'node.role==manager' \
--mode global \
prometheus
8.2.4 Configs (Yapılandırma Dosyaları)
Swarm, hassas olmayan yapılandırma dosyalarını config olarak saklar.
Config oluşturma:
echo "server { listen 80; }" > nginx.conf
docker config create nginx_config nginx.conf
Serviste kullanma:
docker service create \
--name web \
--config source=nginx_config,target=/etc/nginx/nginx.conf \
nginx:alpine
Config’leri listeleme:
docker config ls
8.2.5 Secrets (Şifre Yönetimi)
Secrets, hassas bilgileri (şifreler, API anahtarları) güvenli şekilde saklar.
Secret oluşturma:
echo "myDBpassword" | docker secret create db_password -
Serviste kullanma:
docker service create \
--name myapp \
--secret db_password \
myimage
Konteyner içinde erişim:
cat /run/secrets/db_password
Secret’ler şifrelenir ve sadece yetkili konteynerler erişebilir.
Secret’leri listeleme:
docker secret ls
Secret silme:
docker secret rm db_password
8.3 Compose ile Swarm: Migration (Geçiş)
Docker Compose dosyaları, küçük değişikliklerle Swarm’da kullanılabilir.
8.3.1 Compose’dan Stack’e Geçiş
docker-compose.yml (Development):
version: "3.8"
services:
web:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./html:/usr/share/nginx/html
depends_on:
- db
db:
image: postgres:15
environment:
POSTGRES_PASSWORD: secret
volumes:
- db_data:/var/lib/postgresql/data
volumes:
db_data:
docker-stack.yml (Production - Swarm):
version: "3.8"
services:
web:
image: nginx:alpine
ports:
- "80:80"
deploy:
replicas: 3
update_config:
parallelism: 1
delay: 10s
restart_policy:
condition: on-failure
networks:
- webnet
db:
image: postgres:15
environment:
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_password
volumes:
- db_data:/var/lib/postgresql/data
deploy:
replicas: 1
placement:
constraints:
- node.role == manager
networks:
- webnet
volumes:
db_data:
secrets:
db_password:
external: true
networks:
webnet:
driver: overlay
Farklar:
deploybölümü eklendi (replicas, update_config, placement)depends_onkaldırıldı (Swarm’da çalışmaz)secretskullanıldı- Network driver
overlayolarak değiştirildi
8.3.2 Stack Deploy
Secret oluşturma:
echo "myDBpassword" | docker secret create db_password -
Stack’i deploy etme:
docker stack deploy -c docker-stack.yml myapp
Stack durumunu kontrol:
docker stack ls
docker stack services myapp
docker stack ps myapp
Stack’i kaldırma:
docker stack rm myapp
8.4 Pratik Örnekler
Örnek 1: WordPress + MySQL Stack
stack.yml:
version: "3.8"
services:
wordpress:
image: wordpress:latest
ports:
- "8080:80"
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD_FILE: /run/secrets/db_password
WORDPRESS_DB_NAME: wordpress
secrets:
- db_password
deploy:
replicas: 2
networks:
- wpnet
db:
image: mysql:8
environment:
MYSQL_ROOT_PASSWORD_FILE: /run/secrets/db_password
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_password
volumes:
- db_data:/var/lib/mysql
deploy:
replicas: 1
placement:
constraints:
- node.role == manager
networks:
- wpnet
volumes:
db_data:
secrets:
db_password:
external: true
networks:
wpnet:
driver: overlay
Deploy:
echo "mySecretPassword123" | docker secret create db_password -
docker stack deploy -c stack.yml wordpress
Örnek 2: Load Balancer + API
version: "3.8"
services:
nginx:
image: nginx:alpine
ports:
- "80:80"
configs:
- source: nginx_config
target: /etc/nginx/nginx.conf
deploy:
replicas: 1
networks:
- frontend
api:
image: myapi:latest
deploy:
replicas: 5
update_config:
parallelism: 2
delay: 10s
networks:
- frontend
configs:
nginx_config:
external: true
networks:
frontend:
driver: overlay
8.5 Swarm Komutları Özet
| Komut | Açıklama |
|---|---|
docker swarm init |
Swarm cluster başlat |
docker swarm join |
Worker node ekle |
docker node ls |
Node’ları listele |
docker service create |
Servis oluştur |
docker service ls |
Servisleri listele |
docker service ps <service> |
Servis detayları |
docker service scale <service>=N |
Replika sayısını değiştir |
docker service update |
Servis güncelle |
docker service rollback |
Önceki versiyona dön |
docker stack deploy |
Stack deploy et |
docker stack ls |
Stack’leri listele |
docker stack rm |
Stack’i kaldır |
docker secret create |
Secret oluştur |
docker config create |
Config oluştur |
8.6 Özet ve İleri Okuma
Docker Swarm, kolay kurulum ve yönetim sunar. Kubernetes’e geçmeden önce ideal bir orkestrasyon çözümüdür.
Avantajları:
- Docker ile tam entegre
- Basit komutlar
- Hızlı kurulum
- Yerleşik load balancing
Dezavantajları:
- Büyük ölçekli projelerde Kubernetes kadar güçlü değil
- Topluluk desteği daha az
Ne zaman kullanılır?
- 10-50 node arası cluster’lar
- Hızlı prototipleme
- Docker’a aşina ekipler
İleri Okuma:
9. Kubernetes ile Karşılaştırma
Docker Swarm’ı öğrendikten sonra orkestrasyon araçları arasındaki farkları anlamak önemlidir. Bu bölümde Docker Compose, Swarm ve Kubernetes’in teknik karşılaştırmasını yapacağız ve hangi senaryoda hangi aracın tercih edilmesi gerektiğini inceleyeceğiz.
9.1 Orkestrasyon Araçlarının Genel Bakışı
Docker ekosisteminde üç temel orkestrasyon yaklaşımı bulunur. Docker Compose tek sunucuda birden fazla konteyneri yönetirken, Docker Swarm birden fazla sunucuyu cluster olarak yönetir ve Kubernetes ise büyük ölçekli, karmaşık sistemler için tasarlanmış güçlü bir orkestrasyon platformudur.
Her birinin farklı kullanım senaryoları ve karmaşıklık seviyeleri vardır. Docker Compose geliştirme ortamları için idealdir, Docker Swarm küçük ve orta ölçekli production ortamları için yeterlidir, Kubernetes ise büyük ölçekli ve karmaşık sistemler için en uygun seçenektir.
Kullanım Alanları ve Ölçek
| Araç | Sunucu Sayısı | Kullanım Alanı | Karmaşıklık |
|---|---|---|---|
| Docker Compose | 1 sunucu | Geliştirme, test ortamları | Düşük |
| Docker Swarm | 2-50 sunucu | Küçük-orta production | Orta |
| Kubernetes | 10+ sunucu | Büyük ölçekli production | Yüksek |
9.2 Teknik Özellik Karşılaştırması
Her üç aracın da farklı teknik özellikleri ve yetenekleri vardır. Kurulum süresi, öğrenme eğrisi, ölçekleme yetenekleri ve diğer önemli özellikler aşağıdaki tabloda karşılaştırılmıştır.
| Özellik | Docker Compose | Docker Swarm | Kubernetes |
|---|---|---|---|
| Kurulum | Tek komut | 5 dakika | Saatler |
| Öğrenme Süresi | 1 gün | 1 hafta | 1-3 ay |
| Ölçekleme | Manuel | Otomatik (basit) | Otomatik (gelişmiş) |
| Load Balancing | Harici araç gerekli | Yerleşik | Yerleşik + gelişmiş |
| Self-Healing | Yok | Var | Gelişmiş |
| Rolling Update | Manuel | Var | Gelişmiş (canary, blue-green) |
| Multi-Host | Desteklenmez | Desteklenir | Desteklenir |
| Secrets | Environment variables | Docker secrets | Kubernetes secrets + vault |
| Monitoring | Harici | Harici | Prometheus entegrasyonu |
| Cloud Desteği | Yok | Sınırlı | EKS, GKE, AKS |
Docker Compose’un en büyük avantajı basitliğidir ancak tek sunucu ile sınırlıdır. Docker Swarm, Docker CLI ile tam entegre çalışır ve Compose dosyalarıyla uyumludur. Kubernetes ise en güçlü özelliklere sahip olmakla birlikte en karmaşık yapıya sahiptir.
9.3 Avantajlar ve Dezavantajlar
Docker Compose
Docker Compose, yerel geliştirme ve tek sunuculu uygulamalar için tasarlanmış basit bir araçtır. YAML dosyası son derece okunabilir ve anlaşılır bir yapıya sahiptir. Tek bir komutla tüm sistemi ayağa kaldırabilir, geliştirme sürecini hızlandırır. Öğrenmesi çok kolaydır ve hızlı prototipleme için idealdir.
Ancak bazı önemli sınırlamaları vardır. Tek sunucu ile sınırlı olduğu için büyüyen projeler için uygun değildir. Otomatik ölçekleme özelliği yoktur ve load balancing işlemleri manuel olarak yapılmalıdır. Production ortamları için yetersizdir ve multi-host desteği bulunmamaktadır.
| Avantajlar | Dezavantajlar |
|---|---|
| YAML dosyası basit ve okunabilir | Tek sunucu sınırlaması |
| Tek komutla sistem ayağa kalkar | Otomatik ölçekleme yok |
| Yerel geliştirme için ideal | Production için yetersiz |
| Hızlı prototipleme | Load balancing manuel |
| Öğrenmesi çok kolay | Multi-host desteği yok |
Uygun Olduğu Durumlar: Geliştirme ortamları, tek sunuculu uygulamalar, MVP ve prototip projeler için idealdir.
Docker Swarm
Docker Swarm, Docker ekosisteminin doğal bir parçası olarak tasarlanmıştır. Docker CLI ile tam entegre çalışır ve mevcut Docker bilginizi kullanarak kolayca öğrenebilirsiniz. Compose dosyalarınızı küçük değişikliklerle Swarm’da kullanabilirsiniz. Kurulumu yaklaşık 5 dakika sürer ve yerleşik load balancing özelliğine sahiptir. Öğrenme eğrisi Kubernetes’e göre çok daha düşüktür.
Bununla birlikte bazı kısıtlamaları vardır. Ölçekleme kapasitesi Kubernetes kadar güçlü değildir. Auto-scaling gibi gelişmiş özellikler basit seviyededir. Topluluk desteği Kubernetes’e göre daha azdır ve cloud provider entegrasyonu sınırlıdır.
| Avantajlar | Dezavantajlar |
|---|---|
| Docker CLI ile tam entegrasyon | Ölçekleme kapasitesi sınırlı |
| Compose dosyalarıyla uyumlu | Gelişmiş özellikler eksik |
| Hızlı kurulum (5 dakika) | Topluluk desteği az |
| Yerleşik load balancing | Cloud entegrasyonu sınırlı |
| Düşük öğrenme eğrisi | Auto-scaling basit seviyede |
Uygun Olduğu Durumlar: 5-50 sunuculu yapılar, Docker bilgisi olan ekipler, orta ölçekli production ortamları ve basit mikroservis mimarileri için uygundur.
Kubernetes
Kubernetes, container orkestrasyon dünyasının en güçlü ve kapsamlı platformudur. HPA (Horizontal Pod Autoscaler) ve VPA (Vertical Pod Autoscaler) gibi güçlü otomatik ölçekleme mekanizmalarına sahiptir. Self-healing yetenekleri sayesinde arızalanan pod’ları otomatik olarak yeniden başlatır. Canary ve blue-green gibi gelişmiş deployment stratejilerini destekler. Çok büyük bir topluluk ve ekosisteme sahiptir. AWS EKS, Google GKE ve Azure AKS gibi tüm büyük cloud provider’lar tarafından tam olarak desteklenir. Istio ve Linkerd gibi service mesh araçlarıyla entegre çalışabilir.
Ancak bu güçlü özellikler bazı maliyetlerle gelir. Kurulum ve yapılandırma karmaşıktır, saatler sürebilir. Öğrenme eğrisi çok diktir, 1-3 ay gibi bir süre gerektirebilir. Master node’lar nedeniyle kaynak tüketimi yüksektir. Yönetim maliyeti ve operasyonel karmaşıklık fazladır. Küçük projeler için aşırı karmaşık bir çözümdür (overkill).
| Avantajlar | Dezavantajlar |
|---|---|
| Güçlü otomatik ölçekleme (HPA, VPA) | Karmaşık kurulum ve yapılandırma |
| Self-healing mekanizmaları | Dik öğrenme eğrisi |
| Gelişmiş deployment stratejileri | Yüksek kaynak tüketimi |
| Büyük topluluk ve ekosistem | Yönetim maliyeti yüksek |
| Cloud provider desteği tam | Küçük projeler için aşırı karmaşık |
| Service mesh entegrasyonu | Master node overhead |
Uygun Olduğu Durumlar: 50+ sunuculu yapılar, karmaşık mikroservis mimarileri, multi-cloud stratejileri ve yüksek trafikli uygulamalar için en uygun seçenektir.
9.4 Compose’dan Kubernetes’e Geçiş
Docker Compose dosyalarınızı Kubernetes’e taşımak için iki yöntem kullanabilirsiniz: otomatik dönüştürme aracı Kompose veya manuel dönüştürme. Kompose aracı, mevcut Compose dosyalarınızı Kubernetes YAML formatına otomatik olarak çevirir.
Kompose Aracı ile Otomatik Dönüştürme
Kompose aracını Linux, macOS veya Windows üzerinde kurabilirsiniz. Linux için curl komutuyla binary dosyayı indirip çalıştırılabilir hale getirmeniz yeterlidir. macOS’ta Homebrew ile, Windows’ta ise Chocolatey ile kurulum yapabilirsiniz.
Kurulum:
# Linux
curl -L https://github.com/kubernetes/kompose/releases/download/v1.31.0/kompose-linux-amd64 -o kompose
chmod +x kompose
sudo mv ./kompose /usr/local/bin/kompose
# macOS
brew install kompose
# Windows
choco install kompose
Kurulum tamamlandıktan sonra mevcut docker-compose.yml dosyanızı dönüştürmek için kompose convert komutunu kullanabilirsiniz. Bu komut, Compose dosyanızı analiz eder ve karşılık gelen Kubernetes Service, Deployment ve PersistentVolumeClaim dosyalarını oluşturur.
Dönüştürme:
kompose convert -f docker-compose.yml
Kompose, her servis için ayrı YAML dosyaları oluşturur. Örneğin bir web servisi için hem Service hem de Deployment dosyası, bir veritabanı için ise ek olarak PersistentVolumeClaim dosyası oluşturulur.
Çıktı:
INFO Kubernetes file "web-service.yaml" created
INFO Kubernetes file "web-deployment.yaml" created
INFO Kubernetes file "db-persistentvolumeclaim.yaml" created
Oluşturulan dosyaları Kubernetes cluster’ınıza deploy etmek için kubectl aracını kullanabilirsiniz. Apply komutu, bulunduğunuz dizindeki tüm YAML dosyalarını okur ve cluster’a uygular.
Deploy:
kubectl apply -f .
Manuel Dönüştürme Örneği
Bazen otomatik dönüştürme yetersiz kalabilir veya daha fazla kontrol isteyebilirsiniz. Bu durumda manuel dönüştürme yapmanız gerekir. Aşağıda basit bir Docker Compose dosyasının Kubernetes eşdeğerine nasıl çevrileceğini görebilirsiniz.
Docker Compose’da bir servisi tanımlamak çok basittir. Image adını, replika sayısını ve port yönlendirmesini belirtmeniz yeterlidir. Ancak Kubernetes’te aynı işlevi gerçekleştirmek için hem Deployment hem de Service nesnesi oluşturmanız gerekir.
Docker Compose:
version: "3.8"
services:
web:
image: nginx:alpine
replicas: 3
ports:
- "80:80"
Kubernetes Deployment objesi, kaç pod çalışacağını, hangi image kullanılacağını ve pod’ların nasıl etiketleneceğini tanımlar. Service objesi ise bu pod’lara dışarıdan erişimi sağlar ve load balancing yapar. LoadBalancer tipi service, cloud provider’dan otomatik olarak bir external IP alır.
Kubernetes Deployment:
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
spec:
replicas: 3
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: web
image: nginx:alpine
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: web
spec:
type: LoadBalancer
ports:
- port: 80
targetPort: 80
selector:
app: web
9.5 Senaryo Bazlı Öneriler
Farklı proje büyüklükleri ve gereksinimleri için farklı araçlar daha uygun olabilir. Aşağıdaki tablo, yaygın senaryolar için önerilen orkestrasyon araçlarını ve gerekçelerini göstermektedir.
Basit bir blog sitesi gibi küçük projelerde Docker Compose yeterlidir. Startup MVP’ler için Compose ile başlayıp büyüdükçe Swarm’a geçiş yapılabilir. Orta ölçekli e-ticaret siteleri için Swarm’ın otomatik ölçekleme ve load balancing özellikleri yeterlidir. Binlerce kullanıcılı SaaS platformları ise Kubernetes’in güçlü özelliklerini gerektirir.
| Senaryo | Önerilen Araç | Gerekçe |
|---|---|---|
| Blog sitesi (tek sunucu) | Docker Compose | Basit yapı, tek sunucu yeterli |
| Startup MVP (10-100 kullanıcı) | Docker Compose → Swarm | Hızlı geliştirme, gerektiğinde Swarm’a kolay geçiş |
| E-ticaret (1000+ kullanıcı) | Docker Swarm | Otomatik ölçekleme, load balancing, yönetilebilir karmaşıklık |
| SaaS Platform (10,000+ kullanıcı) | Kubernetes | Gelişmiş ölçekleme, multi-cloud, karmaşık mikroservisler |
9.6 Geçiş Yol Haritası
Bir orkestrasyon aracından diğerine geçiş aşamalı olarak yapılmalıdır. Her aşamada sisteminizin ihtiyaçlarını ve ekibinizin yeteneklerini değerlendirerek ilerlemeniz önemlidir.
İlk aşamada geliştirme ortamınızda Docker Compose kullanarak başlamalısınız. Bu aşamada basit bir YAML dosyası ile yerel geliştirme ortamınızı kolayca yönetebilirsiniz. Build direktifi sayesinde Dockerfile’ınızdan otomatik olarak image oluşturulur.
Aşama 1: Geliştirme (Docker Compose)
version: "3.8"
services:
web:
build: .
ports:
- "80:80"
Projeniz büyüdükçe ve birden fazla sunucuya ihtiyaç duymaya başladığınızda Docker Swarm’a geçiş yapabilirsiniz. Bu aşamada Compose dosyanızı küçük değişikliklerle Swarm stack dosyasına dönüştürürsünüz. Build yerine hazır image kullanır, deploy bölümünde replika sayısını ve güncelleme stratejisini belirtirsiniz.
Aşama 2: Orta Ölçek (Docker Swarm)
version: "3.8"
services:
web:
image: myapp:latest
deploy:
replicas: 3
update_config:
parallelism: 1
ports:
- "80:80"
Projeniz çok daha büyüdüğünde ve karmaşık mikroservis mimarisine geçtiğinizde Kubernetes’e migration yapabilirsiniz. Bu noktada Kompose aracını kullanarak mevcut stack dosyanızı Kubernetes YAML’ine çevirebilir veya sıfırdan Kubernetes manifestleri yazabilirsiniz.
Aşama 3: Büyük Ölçek (Kubernetes)
kompose convert -f docker-stack.yml
kubectl apply -f .
Her geçiş aşamasında sisteminizi test etmeli ve ekibinizin yeni araca adapte olması için zaman tanımalısınız. Aşamalı geçiş, riskleri minimize eder ve sorunları erkenden tespit etmenizi sağlar.
10. Güvenlik
Docker konteynerleri, uygulamalarınızı izole eder ancak bu izolasyon tek başına yeterli değildir. Güvenlik, Docker kullanımının en kritik yönlerinden biridir. Bu bölümde konteyner güvenliğini artırmak için kullanabileceğiniz araçları, teknikleri ve en iyi uygulamaları öğreneceksiniz.
Konteynerler varsayılan olarak bazı güvenlik özellikleriyle gelir ancak production ortamlarında ek güvenlik katmanları eklemeniz şarttır. Özellikle hassas verileri işleyen uygulamalarda, güvenlik açıklarını minimum seviyeye indirmek için çeşitli yöntemler uygulamalısınız.
10.1 Rootless Docker (Linux)
Normal Docker kurulumunda daemon root yetkisiyle çalışır. Bu, bir güvenlik açığı oluşturabilir çünkü konteyner içinden çıkış yapılırsa (container escape) saldırgan root erişimi kazanabilir. Rootless Docker, bu riski ortadan kaldırmak için daemon’u root olmayan bir kullanıcı ile çalıştırır.
Rootless Docker’ın mantığı şudur: daemon ve konteynerler normal kullanıcı yetkileriyle çalışır, böylece bir güvenlik açığı olsa bile saldırgan sadece o kullanıcının yetkilerine sahip olur, sistem genelinde root erişimi kazanamaz.
Rootless Docker Kurulumu (Ubuntu/Debian)
Öncelikle normal Docker daemon’unu durdurmanız gerekir. Ardından rootless kurulum scriptini çalıştırırsınız. Bu script gerekli ayarları yapar ve Docker’ı kullanıcı modu ile başlatır.
# Mevcut Docker'ı durdur
sudo systemctl disable --now docker.service docker.socket
# Rootless kurulum scriptini çalıştır
curl -fsSL https://get.docker.com/rootless | sh
# Ortam değişkenlerini ayarla
export PATH=/home/$USER/bin:$PATH
export DOCKER_HOST=unix:///run/user/$(id -u)/docker.sock
Kurulum tamamlandıktan sonra Docker komutlarını sudo olmadan çalıştırabilirsiniz. Daemon artık normal kullanıcı yetkileriyle çalışır ve konteynerler de aynı şekilde root yetkisi olmadan çalışır.
Rootless Docker’ın Avantajları ve Kısıtlamaları
Rootless Docker kullanmanın en büyük avantajı güvenliktir. Container escape senaryolarında bile saldırgan root erişimi kazanamaz. Ayrıca çok kullanıcılı sistemlerde her kullanıcı kendi Docker daemon’unu çalıştırabilir.
Ancak bazı kısıtlamaları vardır. 1024’ten küçük portları (80, 443 gibi) doğrudan bağlayamazsınız, bunun yerine port yönlendirme kullanmanız gerekir. Bazı storage driver’lar (overlay2 vb.) çalışmayabilir. Performans standart Docker’a göre biraz daha düşük olabilir.
| Avantajlar | Kısıtlamalar |
|---|---|
| Root erişimi riski yok | 1024’ten küçük portlar kullanılamaz |
| Çok kullanıcılı sistemlerde güvenli | Bazı storage driver’lar çalışmaz |
| Container escape riski minimum | Performans biraz daha düşük |
| Kullanıcı izolasyonu | Bazı network özellikleri sınırlı |
10.2 Linux Güvenlik Modülleri (Seccomp, AppArmor, SELinux)
Linux işletim sistemi, konteynerleri korumak için çeşitli güvenlik modülleri sunar. Bu modüller, konteynerlerin yapabileceklerini kısıtlar ve zararlı aktiviteleri engeller. Her biri farklı bir yaklaşımla güvenlik sağlar.
Seccomp (Secure Computing Mode)
Seccomp, konteynerin hangi sistem çağrılarını (system calls) yapabileceğini kontrol eder. Sistem çağrıları, bir programın işletim sisteminden bir şey talep etmesidir. Örneğin dosya okuma, network bağlantısı kurma veya yeni process başlatma sistem çağrılarıdır.
Docker varsayılan olarak bir seccomp profili kullanır ve tehlikeli sistem çağrılarını engeller. Örneğin reboot, swapon, mount gibi sistem çağrıları varsayılan olarak engellidir.
Kendi seccomp profilinizi de oluşturabilirsiniz. Aşağıda sadece read, write ve exit sistem çağrılarına izin veren bir profil örneği görüyorsunuz.
Örnek seccomp profili (seccomp.json):
{
"defaultAction": "SCMP_ACT_ERRNO",
"architectures": ["SCMP_ARCH_X86_64"],
"syscalls": [
{
"names": ["read", "write", "exit", "exit_group"],
"action": "SCMP_ACT_ALLOW"
}
]
}
Bu profili kullanarak bir konteyner başlatmak için:
docker run --security-opt seccomp=seccomp.json myimage
AppArmor
AppArmor, konteynerlerin dosya sistemine, network’e ve diğer kaynaklara erişimini kontrol eder. Ubuntu ve Debian sistemlerinde varsayılan olarak kuludur.
Docker otomatik olarak docker-default adlı bir AppArmor profili kullanır. Bu profil konteynerin hassas sistem dizinlerine yazmasını engeller, örneğin /sys, /proc gibi dizinler korunur.
Kendi AppArmor profilinizi de oluşturabilirsiniz. Örneğin sadece /tmp dizinine yazma izni veren bir profil:
# AppArmor profili oluştur (/etc/apparmor.d/docker-nginx)
profile docker-nginx flags=(attach_disconnected,mediate_deleted) {
#include <abstractions/base>
file,
/tmp/** rw,
deny /proc/** w,
deny /sys/** w,
}
# Profili yükle
sudo apparmor_parser -r -W /etc/apparmor.d/docker-nginx
# Konteyner başlatırken kullan
docker run --security-opt apparmor=docker-nginx nginx
SELinux
SELinux (Security-Enhanced Linux), Red Hat, CentOS ve Fedora sistemlerinde kullanılan güvenlik modülüdür. AppArmor’a benzer şekilde çalışır ancak daha karmaşık ve güçlüdür.
SELinux her dosyaya, process’e ve network portuna bir etiket (label) atar. Konteynerler varsayılan olarak svirt_lxc_net_t etiketiyle çalışır ve sadece svirt_sandbox_file_t etiketli dosyalara erişebilir.
Daha önce Bölüm 6’da gördüğünüz :Z etiketi de SELinux ile ilgilidir. Volume mount ederken :Z kullanırsanız, Docker o dizine otomatik olarak konteyner erişimi için doğru etiketi atar.
docker run -v /mydata:/data:Z myimage
Kernel Capabilities
Linux kernel, root yetkilerini küçük parçalara böler. Bu parçalara “capability” denir. Örneğin network ayarlarını değiştirmek için CAP_NET_ADMIN, dosya sahipliğini değiştirmek için CAP_CHOWN capability’si gerekir.
Docker varsayılan olarak konteynerlere sınırlı sayıda capability verir. Gereksiz capability’leri kaldırarak güvenliği artırabilirsiniz.
Tüm capability’leri kaldırma:
docker run --cap-drop=ALL myimage
Sadece belirli capability’leri verme:
docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE myimage
Bu örnekte tüm capability’ler kaldırılıyor ve sadece NET_BIND_SERVICE (1024’ten küçük portlara bind olma) veriliyor.
10.3 Image Tarama ve Güvenlik Araçları
Konteyner güvenliğinin önemli bir parçası da kullandığınız image’lerin güvenli olmasıdır. Image’ler içinde güvenlik açıkları (vulnerabilities) olabilir. Bu açıkları tespit etmek için image tarama araçları kullanılır.
Docker Bench for Security
Docker Bench for Security, Docker kurulumunuzu en iyi güvenlik pratiklerine göre kontrol eden otomatik bir scripttir. CIS Docker Benchmark standartlarını kontrol eder.
Kurulum ve kullanım:
git clone https://github.com/docker/docker-bench-security.git
cd docker-bench-security
sudo sh docker-bench-security.sh
Script çalıştırıldığında yüzlerce kontrol yapar ve sonuçları raporlar. Her kontrol için PASS, WARN veya INFO durumu verir.
Örnek çıktı:
[PASS] 1.1.1 - Ensure a separate partition for containers has been created
[WARN] 1.2.1 - Ensure Docker daemon is not running with experimental features
[INFO] 2.1 - Restrict network traffic between containers
WARN durumundaki uyarıları mutlaka incelemelisiniz. Bunlar potansiyel güvenlik sorunlarını gösterir.
Trivy ile Image Tarama
Trivy, Docker image’lerindeki güvenlik açıklarını tespit eden açık kaynaklı bir araçtır. Kullanımı çok basittir ve hızlı sonuç verir.
Kurulum:
# Linux
wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | sudo apt-key add -
echo "deb https://aquasecurity.github.io/trivy-repo/deb $(lsb_release -sc) main" | sudo tee -a /etc/apt/sources.list.d/trivy.list
sudo apt update
sudo apt install trivy
# macOS
brew install trivy
Image tarama:
trivy image nginx:latest
Trivy, image içindeki tüm paketleri kontrol eder ve bilinen güvenlik açıklarını listeler. Her açık için CVE numarası, şiddet seviyesi (CRITICAL, HIGH, MEDIUM, LOW) ve çözüm önerisi gösterir.
Örnek çıktı:
nginx:latest (debian 11.6)
==========================
Total: 45 (CRITICAL: 5, HIGH: 12, MEDIUM: 20, LOW: 8)
┌───────────────┬────────────────┬──────────┬────────┬─────────────────────┐
│ Library │ Vulnerability │ Severity │ Status │ Fixed Version │
├───────────────┼────────────────┼──────────┼────────┼─────────────────────┤
│ openssl │ CVE-2023-12345 │ CRITICAL │ fixed │ 1.1.1w-1 │
│ curl │ CVE-2023-54321 │ HIGH │ fixed │ 7.88.1-1 │
└───────────────┴────────────────┴──────────┴────────┴─────────────────────┘
CRITICAL ve HIGH seviyedeki açıkları mutlaka düzeltmelisiniz. Bunun için genellikle image’i güncellemeniz veya farklı bir base image kullanmanız gerekir.
Diğer Image Tarama Araçları
Trivy dışında başka araçlar da vardır:
| Araç | Açıklama | Kullanım |
|---|---|---|
| Clair | CoreOS tarafından geliştirilen image tarayıcı | API tabanlı, CI/CD’ye entegre edilebilir |
| Anchore | Detaylı policy kontrolü yapabilen tarayıcı | Kurum politikalarına göre image onaylama |
| Snyk | Hem image hem de kod taraması yapan ticari araç | Gelişmiş raporlama ve izleme |
| Grype | Trivy’ye benzer, hızlı ve basit tarayıcı | Komut satırı kullanımı kolay |
10.4 Secrets Yönetimi
Şifreler, API anahtarları, sertifikalar gibi hassas bilgileri (secrets) konteyner içinde güvenli şekilde saklamak kritiktir. Asla bu bilgileri Dockerfile’a veya image’e hard-code etmemelisiniz.
Docker Swarm Secrets
Docker Swarm, secrets için yerleşik bir sistem sunar. Secrets şifrelenerek saklanır ve sadece yetkili konteynerlere mount edilir.
Secret oluşturma:
# Dosyadan secret oluşturma
echo "myDBpassword123" | docker secret create db_password -
# Dosyadan secret oluşturma
docker secret create db_config /path/to/config.json
Serviste secret kullanma:
docker service create \
--name myapp \
--secret db_password \
myimage
Konteyner içinde secret /run/secrets/ dizininde dosya olarak görünür:
# Konteyner içinde
cat /run/secrets/db_password
# Çıktı: myDBpassword123
Docker Compose ile secret kullanımı:
version: "3.8"
services:
web:
image: myapp
secrets:
- db_password
secrets:
db_password:
external: true
Environment Variables ile Secret (Önerilmez)
Bazı durumlarda environment variable kullanmak zorunda kalabilirsiniz ancak bu yöntem güvenli değildir. Environment variable’lar docker inspect ile görülebilir.
docker run -e DB_PASSWORD=secret123 myimage
Bu yöntem yerine mutlaka Docker secrets veya Vault kullanmalısınız.
HashiCorp Vault Entegrasyonu
Production ortamlarında daha gelişmiş secret yönetimi için HashiCorp Vault kullanılabilir. Vault, secrets’ı merkezi bir yerde saklar, şifreler ve erişim kontrolü sağlar.
Vault’un temel çalışma mantığı şudur: uygulamanız başladığında Vault’tan token alır, bu token ile secrets’ı çeker ve kullanır. Secrets asla image içinde veya environment variable’da saklanmaz.
Basit Vault kullanım örneği:
# Vault'a secret yazma
vault kv put secret/db password=myDBpassword
# Konteyner içinden secret okuma
vault kv get -field=password secret/db
Vault entegrasyonu için genellikle bir init container veya sidecar pattern kullanılır. Bu konular daha ileri seviye olduğu için burada detaya girmiyoruz.
10.5 Container Hardening Pratikleri
Konteynerlerinizi güvenli hale getirmek için uygulamanız gereken pratikler vardır. Bu pratikler savunmanızı katmanlı hale getirir (defense in depth).
USER Direktifi Kullanımı
Dockerfile’da varsayılan olarak konteyner root kullanıcısı ile çalışır. Bu büyük bir güvenlik riskidir. Mutlaka non-root kullanıcı ile çalıştırmalısınız.
Kötü örnek:
FROM node:18
WORKDIR /app
COPY . .
CMD ["node", "app.js"]
# Root olarak çalışıyor!
İyi örnek:
FROM node:18
WORKDIR /app
COPY . .
# Non-root kullanıcı oluştur
RUN useradd -m -u 1001 appuser && \
chown -R appuser:appuser /app
# Bu kullanıcıya geç
USER appuser
CMD ["node", "app.js"]
Artık konteyner appuser ile çalışır. Bir güvenlik açığı olsa bile saldırgan root yetkisi kazanamaz.
Read-Only Filesystem
Konteynerin filesystem’ini read-only yaparak saldırganın zararlı dosya yazmasını engelleyebilirsiniz.
docker run --read-only --tmpfs /tmp myimage
Uygulama geçici dosyalar yazmak zorundaysa tmpfs kullanabilirsiniz. tmpfs RAM’de çalışır ve konteyner kapandığında silinir.
Docker Compose ile:
services:
web:
image: myapp
read_only: true
tmpfs:
- /tmp
Gereksiz Capability’leri Kaldırma
Daha önce bahsettiğimiz gibi capability’leri kaldırarak saldırganın yapabileceklerini kısıtlayabilirsiniz.
docker run \
--cap-drop=ALL \
--cap-add=NET_BIND_SERVICE \
myimage
Network İzolasyonu
Her konteyner için ayrı network oluşturarak servisleri izole edebilirsiniz. Böylece bir konteyner hack’lense bile diğerlerine erişemez.
# Frontend network
docker network create frontend
# Backend network
docker network create backend
# Web servisi sadece frontend'e bağlı
docker run --network frontend web
# API servisi her ikisine de bağlı
docker run --network frontend --network backend api
# Database sadece backend'e bağlı
docker run --network backend db
Resource Limitleri
Konteyner kaynak tüketimini sınırlandırarak DoS (Denial of Service) saldırılarını engelleyebilirsiniz.
docker run \
--memory="512m" \
--cpus="1.0" \
--pids-limit=100 \
myimage
Bu limitler sayesinde bir konteyner tüm sistemi çökertmez.
Image Güncelliği
Kullandığınız base image’leri düzenli olarak güncellemelisiniz. Eski image’ler bilinen güvenlik açıkları içerebilir.
# Image'leri güncelle
docker pull nginx:latest
docker pull node:18-alpine
Ayrıca production’da latest tag’i yerine belirli versiyonlar kullanmalısınız:
# Kötü
FROM node:latest
# İyi
FROM node:18.19.0-alpine
Güvenlik Kontrol Listesi
Konteyner güvenliği için uygulamanız gereken temel pratiklerin özeti:
Dockerfile Güvenliği:
- Non-root kullanıcı kullan (USER direktifi)
- Minimal base image seç (alpine, distroless)
- Multi-stage build ile gereksiz araçları kaldır
- Secrets’ı image’e gömme
- Belirli image versiyonları kullan (latest değil)
Runtime Güvenliği:
- Rootless Docker kullan
- Read-only filesystem aktif et
- Gereksiz capability’leri kaldır
- Resource limitleri belirle
- Network izolasyonu yap
- Seccomp/AppArmor/SELinux kullan
Image Güvenliği:
- Düzenli image tarama yap (Trivy)
- Base image’leri güncelle
- Docker Bench for Security çalıştır
- Sadece güvenilir registry’lerden image çek
Secrets Yönetimi:
- Docker Swarm secrets veya Vault kullan
- Environment variable’da secret saklama
- Secrets’ı logla
- Secrets’ı version control’e koyma
Bu pratikleri uyguladığınızda konteynerlerinizin güvenliği önemli ölçüde artar. Güvenlik katmanlı bir yaklaşım gerektirir, tek bir yöntem yeterli olmaz.
11. Kaynak Kısıtları & Performans Yönetimi
Docker konteynerleri varsayılan olarak host sistemin tüm kaynaklarını kullanabilir. Bu durumda bir konteyner tüm CPU’yu veya RAM’i tüketebilir ve diğer konteynerlerin veya sistemin çökmesine neden olabilir. Kaynak kısıtlamaları belirlemek, hem sistem kararlılığı hem de performans için kritiktir.
Bu bölümde konteynerlere kaynak limitleri nasıl koyulur, Linux’un bu limitleri nasıl uyguladığı ve farklı platformlarda kaynak yönetiminin nasıl yapıldığını öğreneceksiniz.
11.1 Memory ve CPU Limitleri
Docker, konteynerlerin kullanabileceği bellek ve işlemci miktarını sınırlandırmanıza olanak tanır. Bu limitler sayesinde bir konteynerin aşırı kaynak tüketmesini engelleyebilir, sistemin kararlı çalışmasını sağlayabilirsiniz.
Memory (Bellek) Limitleri
Bellek limitleri belirlemek, konteyner çökmelerini ve sistem genelinde bellek tükenmesini önler. Bir konteyner belirlenen limiti aşmaya çalıştığında Linux kernel’i OOM (Out Of Memory) Killer devreye girer ve konteyneri durdurur.
Basit memory limiti:
docker run --memory="512m" nginx
Bu örnekte konteyner maksimum 512 MB RAM kullanabilir. Limit aşılırsa konteyner otomatik olarak kapatılır.
Memory swap ayarı:
docker run --memory="512m" --memory-swap="1g" nginx
--memory-swap parametresi toplam bellek ve swap alanını belirtir. Bu örnekte 512 MB RAM ve 512 MB swap kullanılabilir (1g - 512m = 512m swap).
Swap’ı tamamen kapatma:
docker run --memory="512m" --memory-swap="512m" nginx
Memory ve memory-swap değeri aynı olursa swap kullanımı devre dışı kalır.
Memory reservation (yumuşak limit):
docker run --memory="1g" --memory-reservation="750m" nginx
Memory reservation, konteynerin normal koşullarda kullanması beklenen bellek miktarıdır. Sistem bellek baskısı altındayken Docker bu limiti uygular. Normal koşullarda konteyner daha fazla bellek kullanabilir ancak sistem kaynak sıkıntısı çektiğinde reservation değerine düşürülür.
OOM (Out of Memory) Killer davranışı:
docker run --memory="512m" --oom-kill-disable nginx
--oom-kill-disable parametresi tehlikeli olabilir. Konteyner bellek limitini aşsa bile kapatılmaz, bu durumda host sistem çökebilir. Sadece test ortamlarında kullanılmalıdır.
CPU Limitleri
CPU limitleri, konteynerin ne kadar işlemci gücü kullanabileceğini belirler. Memory’den farklı olarak CPU paylaşımlıdır, yani bir konteyner CPU limitini aşarsa sadece yavaşlar, çökmez.
CPU sayısı limiti:
docker run --cpus="1.5" nginx
Bu konteyner maksimum 1.5 CPU core kullanabilir. Yani tam zamanlı 1 core ve bir diğer core’un yarısını kullanabilir.
CPU share (ağırlık) sistemi:
docker run --cpu-shares=512 --name container1 nginx
docker run --cpu-shares=1024 --name container2 nginx
CPU shares, konteynerlerin CPU’yu nasıl paylaşacağını belirler. Varsayılan değer 1024’tür. Bu örnekte container2, container1’den iki kat daha fazla CPU zamanı alır (1024/512 = 2).
CPU shares sadece sistem yük altındayken devreye girer. Eğer sistem boştaysa tüm konteynerler istedikleri kadar CPU kullanabilir.
Belirli CPU core’lara sabitleme:
docker run --cpuset-cpus="0,1" nginx
Bu konteyner sadece 0 ve 1 numaralı CPU core’larında çalışır. Çok core’lu sistemlerde workload’ı dağıtmak için kullanılır.
CPU period ve quota:
docker run --cpu-period=100000 --cpu-quota=50000 nginx
Bu parametreler daha detaylı CPU kontrolü sağlar. Period, zaman dilimini mikrosaniye cinsinden belirtir (100000 = 100ms). Quota, bu süre içinde konteynerin kaç mikrosaniye CPU kullanabileceğini belirtir. Bu örnekte her 100ms’de 50ms CPU zamanı kullanılabilir, yani %50 CPU.
Pratik Örnekler
Web sunucusu için tipik ayarlar:
docker run -d \
--name web \
--memory="512m" \
--memory-reservation="256m" \
--cpus="1.0" \
--restart=unless-stopped \
nginx
Veritabanı için yüksek kaynak ayarları:
docker run -d \
--name postgres \
--memory="2g" \
--memory-swap="2g" \
--cpus="2.0" \
--cpu-shares=1024 \
postgres:15
Arka plan işi için düşük öncelik:
docker run -d \
--name background-job \
--memory="256m" \
--cpus="0.5" \
--cpu-shares=512 \
myworker
Docker Compose ile Kaynak Limitleri
Docker Compose dosyasında kaynak limitlerini deploy bölümünde belirtirsiniz:
version: "3.8"
services:
web:
image: nginx
deploy:
resources:
limits:
cpus: '1.0'
memory: 512M
reservations:
cpus: '0.5'
memory: 256M
Limits üst limiti, reservations ise minimum garantili kaynakları belirtir.
11.2 Ulimit Ayarları
Ulimit, bir process’in kullanabileceği sistem kaynaklarını sınırlar. Örneğin açık dosya sayısı, process sayısı veya stack boyutu gibi limitler belirleyebilirsiniz.
Ulimit türleri:
| Ulimit | Açıklama | Varsayılan |
|---|---|---|
nofile |
Açık dosya sayısı | 1024 |
nproc |
Process sayısı | Sınırsız |
core |
Core dump boyutu | 0 |
stack |
Stack boyutu | 8388608 |
Ulimit ayarlama:
docker run --ulimit nofile=1024:2048 nginx
Bu örnekte soft limit 1024, hard limit 2048’dir. Soft limit normal çalışma limiti, hard limit maksimum limit olarak çalışır.
Birden fazla ulimit:
docker run \
--ulimit nofile=1024:2048 \
--ulimit nproc=512:1024 \
myapp
Docker Compose ile:
services:
web:
image: myapp
ulimits:
nofile:
soft: 1024
hard: 2048
nproc:
soft: 512
hard: 1024
Ulimit ayarları özellikle veritabanları ve web sunucuları için önemlidir. Örneğin Nginx ve PostgreSQL çok sayıda dosya açar, bu yüzden nofile limitini artırmanız gerekebilir.
11.3 Linux Cgroups (Control Groups)
Cgroups, Linux kernel’inin kaynak yönetim sistemidir. Docker, konteynerlere kaynak limitleri uygulamak için cgroups kullanır. Her konteyner ayrı bir cgroup içinde çalışır ve belirlenen limitlere göre kaynak alır.
Cgroups v1 vs v2
Linux’ta iki cgroups versiyonu bulunur ve aralarında önemli farklar vardır.
Cgroups v1:
- 2008 yılından beri kullanılıyor
- Her kaynak tipi için ayrı hiyerarşi var (cpu, memory, blkio vb.)
- Daha eski sistemlerde varsayılan
- Karmaşık yapı, bazen çelişkili limitler olabiliyor
Cgroups v2:
- 2016’da tanıtıldı
- Tek birleşik hiyerarşi
- Daha basit ve tutarlı API
- Modern Linux dağıtımlarında varsayılan (Ubuntu 22.04+, Fedora 31+)
Hangi versiyonu kullandığınızı kontrol etme:
stat -fc %T /sys/fs/cgroup/
Çıktı cgroup2fs ise cgroups v2, tmpfs ise v1 kullanıyorsunuz.
Cgroups v2 avantajları:
Cgroups v2’de kaynak limitleri daha tutarlı uygulanır. Örneğin v1’de memory ve CPU limitlerini ayrı ayrı yönetirken çelişkiler oluşabiliyordu. V2’de tüm kaynaklar tek hiyerarşide yönetilir.
Ayrıca v2’de “pressure stall information” (PSI) özelliği var. Bu özellik sayesinde konteynerin ne kadar kaynak baskısı altında olduğunu görebilirsiniz.
Cgroups bilgilerini görüntüleme:
# Konteyner cgroup yolunu bulma
docker inspect --format='{{.State.Pid}}' mycontainer
# Çıktı: 12345
# Cgroup limitlerini görme (v2)
cat /sys/fs/cgroup/system.slice/docker-<container-id>.scope/memory.max
cat /sys/fs/cgroup/system.slice/docker-<container-id>.scope/cpu.max
Çoğu kullanıcı cgroups detaylarıyla uğraşmaz. Docker CLI parametreleri (–memory, –cpus vb.) arka planda cgroups’u otomatik yapılandırır. Ancak özel durumlar veya debugging için cgroups bilgisi faydalıdır.
11.4 Docker Desktop’ta Kaynak Ayarları (Windows/macOS)
Docker Desktop, Windows ve macOS’ta bir sanal makine (VM) üzerinde çalışır. Bu VM’in kendisinin de kaynak limitleri vardır. Konteynerlere ayırdığınız kaynaklar önce bu VM’e, sonra konteynerlere dağıtılır.
Windows’ta Docker Desktop Kaynak Ayarları
Windows’ta Docker Desktop ayarlarını açmak için system tray’deki Docker ikonuna sağ tıklayın ve “Settings” seçin.
Resources bölümünde ayarlayabileceğiniz limitler:
Memory: Docker VM’inin kullanacağı maksimum RAM miktarı. Varsayılan olarak sistemin yarısı kadar RAM ayrılır. Örneğin 16 GB RAM’iniz varsa 8 GB Docker’a ayrılır.
Ayarlanabilir değer: 2 GB ile toplam RAM arasında. Production workload için en az 4-8 GB önerilir.
CPUs: Docker VM’inin kullanacağı CPU core sayısı. Varsayılan olarak tüm core’lar kullanılabilir.
Önerilen değer: Sisteminizin yarısı kadar core. Örneğin 8 core’unuz varsa 4 core Docker’a verin.
Disk: Docker image’leri, volume’lar ve container’ların kullanacağı maksimum disk alanı. Varsayılan 64 GB’dır.
Swap: VM’in kullanacağı swap alanı. Varsayılan 1 GB’dır. Production ortamlarında swap’ı artırmanız önerilir.
WSL2 Entegrasyonu:
Windows’ta WSL2 kullanıyorsanız kaynak yönetimi biraz farklıdır. WSL2 VM’i dinamik olarak kaynak alır ve bırakır.
WSL2 için manuel limit belirlemek isterseniz %UserProfile%\.wslconfig dosyası oluşturun:
[wsl2]
memory=8GB
processors=4
swap=2GB
Bu ayarları uygulamak için WSL’i yeniden başlatın:
wsl --shutdown
macOS’ta Docker Desktop Kaynak Ayarları
macOS’ta da benzer şekilde Docker Desktop Settings > Resources bölümünden ayarlar yapılır.
macOS’a özgü notlar:
Apple Silicon (M1/M2) Mac’lerde Docker daha verimli çalışır çünkü ARM tabanlı konteynerler native olarak çalışır. Ancak x86 image’leri için emülasyon kullanılır ve performans düşer.
Rosetta 2 entegrasyonunu aktif ederseniz x86 image’leri daha hızlı çalışır:
Settings > General > “Use Rosetta for x86/amd64 emulation on Apple Silicon”
Disk kullanımı optimizasyonu:
Docker Desktop macOS’ta bir disk image dosyası kullanır. Bu dosya zamanla şişebilir. Temizlemek için:
# Kullanılmayan image'leri ve volume'ları temizle
docker system prune -a --volumes
# Docker disk image'ini sıkıştır
# Settings > Resources > Disk image location > "Reset disk image"
Performance İpuçları
Docker Desktop performansını artırmak için:
File Sharing: Bind mount yavaş olabilir. Sadece gerçekten gerekli dizinleri paylaşın. Settings > Resources > File Sharing bölümünden kontrol edin.
Exclude directories: Virus tarayıcıların Docker dizinlerini atlamasını sağlayın. Windows Defender’da Docker Desktop yükleme dizinini ve WSL dizinlerini hariç tutun.
Use Volume mounts instead of bind mounts: Bind mount’lar (özellikle Windows/macOS’ta) yavaştır. Mümkünse named volume kullanın:
# Yavaş
docker run -v /Users/me/app:/app myimage
# Hızlı
docker volume create myapp-data
docker run -v myapp-data:/app myimage
Örnek Senaryo: Development Ortamı
Bir development ortamı için önerilen Docker Desktop ayarları:
Sistem: 16 GB RAM, 8 Core CPU
Memory: 8 GB
CPUs: 4
Swap: 2 GB
Disk: 128 GB
docker-compose.yml:
version: "3.8"
services:
web:
image: nginx
deploy:
resources:
limits:
cpus: '1.0'
memory: 512M
ports:
- "8080:80"
db:
image: postgres:15
deploy:
resources:
limits:
cpus: '2.0'
memory: 2G
volumes:
- db_data:/var/lib/postgresql/data
redis:
image: redis:alpine
deploy:
resources:
limits:
cpus: '0.5'
memory: 256M
volumes:
db_data:
Bu yapılandırmada toplam 3.5 CPU ve 2.75 GB RAM kullanılır. Docker Desktop’a 4 CPU ve 8 GB ayırdığınız için yeterli alan kalır.
Monitoring ve Troubleshooting
Kaynak kullanımını izlemek için:
# Tüm konteynerlerin kaynak kullanımı
docker stats
# Belirli konteyner
docker stats mycontainer
# JSON formatında
docker stats --no-stream --format "{{json .}}"
Docker stats çıktısı şunları gösterir:
- CPU kullanım yüzdesi
- Bellek kullanımı ve limit
- Bellek yüzdesi
- Network I/O
- Block I/O
- Process sayısı
Bir konteyner sürekli limit’e ulaşıyorsa iki seçenek vardır: limiti artırın veya uygulamayı optimize edin. Limitten sonra uygulamanın davranışını loglardan kontrol edin:
docker logs mycontainer
OOM (Out of Memory) hatası görüyorsanız memory limitini artırın. CPU throttling görüyorsanız CPU limitini artırın veya uygulamanızı optimize edin.
Kaynak yönetimi doğru yapıldığında sistem kararlı çalışır, konteynerler birbirini etkilemez ve beklenmedik çökmeler olmaz. Production ortamlarında mutlaka kaynak limitleri belirleyin ve izleyin.
12. Logging, Monitoring ve Observability
Docker konteynerlerinde logging ve monitoring, production ortamlarında sistem sağlığını takip etmek, sorunları tespit etmek ve performansı optimize etmek için kritiktir. Konteynerler geçici (ephemeral) yapıları nedeniyle logları merkezi bir yerde toplamak ve sistem metriklerini sürekli izlemek zorunludur.
Bu bölümde Docker’ın yerleşik logging araçlarını, farklı log driver’ları, monitoring mimarilerini ve merkezi logging sistemlerini detaylı şekilde inceleyeceğiz.
12.1 docker logs, docker stats, docker events
Docker, konteynerlerin durumunu ve loglarını izlemek için üç temel komut sunar.
docker logs — Konteyner Loglarını Görüntüleme
docker logs komutu, bir konteynerin stdout ve stderr çıktısını gösterir. Bu, uygulamanın konsola yazdığı tüm mesajları içerir.
Basit kullanım:
docker logs mycontainer
Bu komut konteynerin tüm loglarını ekrana basar.
Canlı log izleme (tail -f gibi):
docker logs -f mycontainer
-f (follow) parametresi, yeni logları gerçek zamanlı olarak gösterir. Konteyner çalışmaya devam ettikçe yeni satırlar ekrana gelir.
Son N satırı gösterme:
docker logs --tail 100 mycontainer
Sadece son 100 satırı gösterir. Büyük log dosyalarında performans için önemlidir.
Zaman damgası ekleme:
docker logs -t mycontainer
Her log satırının başına timestamp ekler:
2025-09-29T10:30:45.123456789Z [INFO] Application started
2025-09-29T10:30:46.234567890Z [INFO] Database connected
Belirli zaman aralığındaki loglar:
# Son 1 saatteki loglar
docker logs --since 1h mycontainer
# Belirli tarihten sonraki loglar
docker logs --since 2025-09-29T10:00:00 mycontainer
# Belirli tarihten önceki loglar
docker logs --until 2025-09-29T12:00:00 mycontainer
Kombinasyon örneği:
docker logs -f --tail 50 --since 10m mycontainer
Son 10 dakikadaki loglardan son 50 satırı gösterir ve yeni logları canlı takip eder.
docker stats — Kaynak Kullanım İstatistikleri
docker stats komutu, konteynerlerin gerçek zamanlı kaynak kullanımını gösterir. CPU, memory, network ve disk I/O metriklerini izleyebilirsiniz.
Tüm çalışan konteynerlerin istatistikleri:
docker stats
Çıktı örneği:
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O
abc123def456 web 2.50% 256MiB / 512MiB 50.00% 1.2MB / 850KB 12MB / 5MB
def456abc789 db 15.20% 1.5GiB / 2GiB 75.00% 500KB / 300KB 500MB / 200MB
Açıklama:
- CPU %: Konteyner CPU kullanım yüzdesi
- MEM USAGE / LIMIT: Kullanılan bellek / Maksimum limit
- MEM %: Bellek kullanım yüzdesi
- NET I/O: Ağ giriş/çıkış trafiği
- BLOCK I/O: Disk okuma/yazma trafiği
Tek konteyner için stats:
docker stats mycontainer
Stream olmadan tek snapshot:
docker stats --no-stream
Bu komut bir kez çalışır ve çıkar. Script’lerde kullanışlıdır.
JSON formatında:
docker stats --no-stream --format "{{json .}}"
Programatik işlemler için JSON çıktı alırsınız:
{"BlockIO":"12.3MB / 5.6MB","CPUPerc":"2.50%","Container":"web","ID":"abc123","MemPerc":"50.00%","MemUsage":"256MiB / 512MiB","Name":"web","NetIO":"1.2MB / 850KB","PIDs":"15"}
Custom format örneği:
docker stats --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}"
Sadece isim, CPU ve memory kullanımını tablo formatında gösterir.
docker events — Sistem Olaylarını İzleme
docker events komutu, Docker daemon’da gerçekleşen olayları gerçek zamanlı olarak gösterir. Konteyner başlatma, durdurma, ağ oluşturma, volume mount gibi tüm olaylar loglanır.
Tüm olayları izleme:
docker events
Çıktı örneği:
2025-09-29T10:30:45.123456789Z container create abc123 (image=nginx, name=web)
2025-09-29T10:30:45.234567890Z container start abc123 (image=nginx, name=web)
2025-09-29T10:30:46.345678901Z network connect bridge abc123
Belirli olayları filtreleme:
# Sadece konteyner olayları
docker events --filter type=container
# Belirli konteyner için
docker events --filter container=mycontainer
# Belirli olay tipi için
docker events --filter event=start
# Belirli image için
docker events --filter image=nginx
Zaman aralığı ile filtreleme:
# Son 1 saatteki olaylar
docker events --since 1h
# Belirli tarih aralığı
docker events --since 2025-09-29T10:00:00 --until 2025-09-29T12:00:00
JSON formatında:
docker events --format '{{json .}}'
Pratik örnek — Konteyner durumunu izleme scripti:
#!/bin/bash
docker events --filter type=container --format '{{.Time}} {{.Action}} {{.Actor.Attributes.name}}' | \
while read timestamp action container; do
echo "[$timestamp] Container '$container' $action"
if [ "$action" = "die" ]; then
echo "WARNING: Container $container stopped unexpectedly!"
fi
done
Bu script konteynerlerin durumunu izler ve beklenmedik kapanmaları bildirir.
12.2 Log Driver’lar (json-file, journald, syslog, gelf)
Docker, konteynerlerin loglarını farklı backend’lere yönlendirmek için log driver sistemi kullanır. Varsayılan olarak json-file driver’ı kullanılır ancak ihtiyaca göre farklı driver’lar seçilebilir.
Log Driver Türleri
Docker’da yaygın kullanılan log driver’lar:
| Driver | Açıklama | Kullanım Alanı |
|---|---|---|
json-file |
JSON formatında dosyaya yazar (varsayılan) | Yerel development, küçük sistemler |
journald |
systemd journal’a yazar | Linux sistemler, merkezi systemd |
syslog |
Syslog protokolü ile uzak sunucuya gönderir | Geleneksel syslog altyapıları |
gelf |
Graylog Extended Log Format | Graylog, ELK stack |
fluentd |
Fluentd log collector’a gönderir | Kubernetes, büyük sistemler |
awslogs |
AWS CloudWatch Logs’a gönderir | AWS ortamları |
gcplogs |
Google Cloud Logging’e gönderir | GCP ortamları |
splunk |
Splunk’a gönderir | Enterprise monitoring |
json-file (Varsayılan Driver)
json-file driver’ı, logları host’ta JSON formatında dosyalara yazar. Her log satırı bir JSON objesi olarak saklanır.
Log dosyası konumu:
/var/lib/docker/containers/<container-id>/<container-id>-json.log
Örnek JSON log satırı:
{"log":"Hello from container\n","stream":"stdout","time":"2025-09-29T10:30:45.123456789Z"}
json-file ile konteyner başlatma:
docker run -d \
--log-driver json-file \
--log-opt max-size=10m \
--log-opt max-file=3 \
nginx
Log seçenekleri:
max-size: Her log dosyasının maksimum boyutu (örn: 10m, 100k)max-file: Tutulacak maksimum log dosyası sayısıcompress: Eski log dosyalarını sıkıştır (true/false)
Docker Compose ile:
services:
web:
image: nginx
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
compress: "true"
Avantajlar:
- Basit ve hızlı
docker logskomutuyla uyumlu- Kurulum gerektirmez
Dezavantajlar:
- Disk dolabilir (log rotation gerekli)
- Merkezi log yönetimi yok
- Arama ve analiz zor
journald
journald, systemd’nin log sistemidir. Modern Linux dağıtımlarında (Ubuntu 16.04+, CentOS 7+) varsayılan olarak gelir.
journald ile konteyner başlatma:
docker run -d \
--log-driver journald \
nginx
journalctl ile log görüntüleme:
# Konteyner ID ile
journalctl CONTAINER_ID=abc123
# Konteyner ismi ile
journalctl CONTAINER_NAME=mycontainer
# Son 100 satır
journalctl -n 100 CONTAINER_NAME=mycontainer
# Canlı takip
journalctl -f CONTAINER_NAME=mycontainer
Docker Compose ile:
services:
web:
image: nginx
logging:
driver: journald
options:
tag: "{{.Name}}/{{.ID}}"
Avantajlar:
- Sistem loglarıyla entegre
- Güçlü filtreleme ve arama
- Otomatik log rotation
- Merkezi journal yönetimi
Dezavantajlar:
docker logskomutu çalışmaz- Sadece systemd kullanan sistemlerde var
- Uzak sunucuya göndermek için ek yapılandırma gerekli
syslog
Syslog, geleneksel Unix log protokolüdür. Logları uzak bir syslog sunucusuna göndermek için kullanılır.
Syslog ile konteyner başlatma:
docker run -d \
--log-driver syslog \
--log-opt syslog-address=tcp://192.168.1.100:514 \
--log-opt tag="docker/{{.Name}}" \
nginx
Syslog seçenekleri:
syslog-address: Syslog sunucu adresi (tcp://host:port veya udp://host:port)tag: Log mesajlarına eklenecek etiketsyslog-facility: Syslog facility (daemon, local0-7)syslog-format: Mesaj formatı (rfc5424, rfc3164)
Docker Compose ile:
services:
web:
image: nginx
logging:
driver: syslog
options:
syslog-address: "tcp://192.168.1.100:514"
tag: "web"
syslog-facility: "local0"
Avantajlar:
- Merkezi log yönetimi
- Uzak sunucuya otomatik gönderim
- Mevcut syslog altyapısıyla uyumlu
Dezavantajlar:
docker logsçalışmaz- Network bağlantısı gerekli
- Performans overhead
gelf (Graylog Extended Log Format)
GELF, Graylog tarafından geliştirilen log formatıdır. Structured logging için optimize edilmiştir ve ELK stack’te de kullanılabilir.
GELF ile konteyner başlatma:
docker run -d \
--log-driver gelf \
--log-opt gelf-address=udp://192.168.1.100:12201 \
--log-opt tag="nginx" \
nginx
GELF seçenekleri:
gelf-address: Graylog sunucu adresitag: Log etiketigelf-compression-type: Sıkıştırma tipi (gzip, zlib, none)
Docker Compose ile:
services:
web:
image: nginx
logging:
driver: gelf
options:
gelf-address: "udp://graylog:12201"
tag: "nginx"
gelf-compression-type: "gzip"
Avantajlar:
- Structured logging desteği
- Sıkıştırma ile ağ trafiği azaltma
- Graylog ve ELK ile kolay entegrasyon
Dezavantajlar:
docker logsçalışmaz- Graylog veya GELF-compatible sunucu gerekli
Log Driver Değiştirme
Çalışan bir konteynerin log driver’ını değiştiremezsiniz. Konteyneri silip yeniden oluşturmanız gerekir.
Daemon seviyesinde varsayılan log driver:
/etc/docker/daemon.json dosyasını düzenleyin:
{
"log-driver": "journald",
"log-opts": {
"tag": "{{.Name}}"
}
}
Docker’ı yeniden başlatın:
sudo systemctl restart docker
Artık yeni oluşturulan tüm konteynerler varsayılan olarak journald kullanır.
12.3 cAdvisor + Prometheus + Grafana Entegrasyonu
Production ortamlarında Docker konteynerlerini izlemek için popüler bir mimari: cAdvisor metriklerini toplar, Prometheus metriklerini saklar, Grafana görselleştirir.
Mimari Genel Bakış

Akış:
- cAdvisor her konteynerin CPU, memory, network, disk metriklerini toplar
- Prometheus cAdvisor’dan metriklerini düzenli aralıklarla çeker (scraping)
- Grafana Prometheus’tan veri okuyarak dashboard’lar oluşturur
Kurulum — docker-compose.yml
Tüm sistemi tek bir Compose dosyası ile ayağa kaldırabilirsiniz:
version: "3.8"
services:
cadvisor:
image: gcr.io/cadvisor/cadvisor:latest
container_name: cadvisor
ports:
- "8080:8080"
volumes:
- /:/rootfs:ro
- /var/run:/var/run:ro
- /sys:/sys:ro
- /var/lib/docker/:/var/lib/docker:ro
privileged: true
networks:
- monitoring
prometheus:
image: prom/prometheus:latest
container_name: prometheus
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
- prometheus-data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
networks:
- monitoring
grafana:
image: grafana/grafana:latest
container_name: grafana
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
volumes:
- grafana-data:/var/lib/grafana
networks:
- monitoring
volumes:
prometheus-data:
grafana-data:
networks:
monitoring:
prometheus.yml yapılandırması:
global:
scrape_interval: 15s
scrape_configs:
- job_name: 'cadvisor'
static_configs:
- targets: ['cadvisor:8080']
Sistemi başlatma:
docker compose up -d
Servislere erişim:
- cAdvisor: http://localhost:8080
- Prometheus: http://localhost:9090
- Grafana: http://localhost:3000 (admin/admin)
cAdvisor Kullanımı
cAdvisor web arayüzünde (http://localhost:8080) tüm konteynerlerin gerçek zamanlı metriklerini görebilirsiniz.
cAdvisor metrik örnekleri:
container_cpu_usage_seconds_total: CPU kullanım süresicontainer_memory_usage_bytes: Bellek kullanımıcontainer_network_receive_bytes_total: Alınan ağ trafiğicontainer_network_transmit_bytes_total: Gönderilen ağ trafiğicontainer_fs_usage_bytes: Disk kullanımı
Prometheus Sorguları
Prometheus web arayüzünde (http://localhost:9090) PromQL sorguları yazabilirsiniz.
Örnek sorgular:
Konteyner CPU kullanımı:
rate(container_cpu_usage_seconds_total{name="mycontainer"}[5m])
Konteyner bellek kullanımı (MB):
container_memory_usage_bytes{name="mycontainer"} / 1024 / 1024
Network trafiği (son 5 dakika ortalaması):
rate(container_network_receive_bytes_total{name="mycontainer"}[5m])
En çok CPU kullanan 5 konteyner:
topk(5, rate(container_cpu_usage_seconds_total[5m]))
Grafana Dashboard Oluşturma
- Grafana’ya giriş yapın (http://localhost:3000, admin/admin)
- Configuration > Data Sources > Add data source
- Prometheus seçin
- URL:
http://prometheus:9090 - Save & Test
Dashboard ekleme:
- Dashboards > Import
- Dashboard ID: 193 (Docker and System Monitoring)
- Load > Import
Artık tüm konteynerlerinizin metriklerini görselleştirebilirsiniz.
Custom panel oluşturma:
- Create > Dashboard > Add new panel
- Query:
rate(container_cpu_usage_seconds_total{name="mycontainer"}[5m]) - Visualization: Graph
- Apply
Alert Kuralları
Prometheus ile otomatik alertler oluşturabilirsiniz.
prometheus.yml’e alert rules ekleme:
rule_files:
- 'alerts.yml'
alerting:
alertmanagers:
- static_configs:
- targets: ['alertmanager:9093']
alerts.yml:
groups:
- name: container_alerts
interval: 30s
rules:
- alert: HighMemoryUsage
expr: container_memory_usage_bytes > 1000000000
for: 5m
labels:
severity: warning
annotations:
summary: "Container {{ $labels.name }} memory usage high"
description: "Memory usage is above 1GB for 5 minutes"
- alert: ContainerDown
expr: up{job="cadvisor"} == 0
for: 1m
labels:
severity: critical
annotations:
summary: "cAdvisor is down"
12.4 Merkezi Logging (EFK/ELK/Fluentd)
Büyük sistemlerde logları merkezi bir yerde toplamak şarttır. En popüler çözümler ELK (Elasticsearch, Logstash, Kibana) ve EFK (Elasticsearch, Fluentd, Kibana) stack’leridir.
ELK Stack Mimarisi

EFK Stack Kurulumu
EFK stack’te Logstash yerine daha hafif Fluentd kullanılır.
docker-compose.yml:
version: "3.8"
services:
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:8.11.0
container_name: elasticsearch
environment:
- discovery.type=single-node
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
- xpack.security.enabled=false
ports:
- "9200:9200"
volumes:
- es-data:/usr/share/elasticsearch/data
networks:
- efk
fluentd:
image: fluent/fluentd:v1.16-1
container_name: fluentd
volumes:
- ./fluentd.conf:/fluentd/etc/fluent.conf:ro
- /var/lib/docker/containers:/var/lib/docker/containers:ro
ports:
- "24224:24224"
depends_on:
- elasticsearch
networks:
- efk
kibana:
image: docker.elastic.co/kibana/kibana:8.11.0
container_name: kibana
ports:
- "5601:5601"
environment:
- ELASTICSEARCH_HOSTS=http://elasticsearch:9200
depends_on:
- elasticsearch
networks:
- efk
volumes:
es-data:
networks:
efk:
fluentd.conf:
<source>
@type forward
port 24224
</source>
<filter docker.**>
@type parser
key_name log
<parse>
@type json
</parse>
</filter>
<match docker.**>
@type elasticsearch
host elasticsearch
port 9200
logstash_format true
logstash_prefix docker
include_tag_key true
tag_key @log_name
flush_interval 10s
</match>
Uygulama konteynerini Fluentd’ye bağlama:
docker run -d \
--log-driver=fluentd \
--log-opt fluentd-address=localhost:24224 \
--log-opt tag="docker.{{.Name}}" \
nginx
Docker Compose ile:
services:
web:
image: nginx
logging:
driver: fluentd
options:
fluentd-address: localhost:24224
tag: docker.nginx
networks:
- efk
Kibana’da Log Görüntüleme
- Kibana’ya giriş yapın (http://localhost:5601)
- Management > Index Patterns > Create index pattern
- Pattern:
docker-* - Next step > Time field:
@timestamp - Create index pattern
- Discover menüsünden logları görüntüleyin
Kibana’da filtreleme:
- Container name ile:
docker.name: "nginx" - Log level ile:
level: "error" - Zaman aralığı ile: Sağ üstten time range seçin
Structured Logging
Uygulamalarınızın loglarını JSON formatında yazdırması arama ve filtrelemeyi kolaylaştırır.
Node.js örneği (Winston logger):
const winston = require('winston');
const logger = winston.createLogger({
format: winston.format.json(),
transports: [
new winston.transports.Console()
]
});
logger.info('User logged in', { userId: 123, ip: '192.168.1.1' });
Çıktı:
{"level":"info","message":"User logged in","userId":123,"ip":"192.168.1.1","timestamp":"2025-09-29T10:30:45.123Z"}
Bu format Elasticsearch’te aranabilir fieldlar olarak indexlenir.
Log Retention ve Performans
Elasticsearch zamanla çok büyüyebilir. Index rotation ve silme politikaları belirlemelisiniz.
ILM (Index Lifecycle Management) örneği:
PUT _ilm/policy/docker-logs-policy
{
"policy": {
"phases": {
"hot": {
"actions": {
"rollover": {
"max_size": "50GB",
"max_age": "7d"
}
}
},
"delete": {
"min_age": "30d",
"actions": {
"delete": {}
}
}
}
}
}
Bu politika:
- Her index 50GB’a veya 7 güne ulaştığında yeni index oluşturur
- 30 gün sonra eski index’leri siler
Alternatif: Grafana Loki
Loki, Grafana’nın log toplama sistemidir. Elasticsearch’ten daha hafiftir.
docker-compose.yml:
services:
loki:
image: grafana/loki:latest
ports:
- "3100:3100"
volumes:
- ./loki-config.yml:/etc/loki/local-config.yaml
promtail:
image: grafana/promtail:latest
volumes:
- /var/log:/var/log:ro
- /var/lib/docker/containers:/var/lib/docker/containers:ro
- ./promtail-config.yml:/etc/promtail/config.yml
command: -config.file=/etc/promtail/config.yml
grafana:
image: grafana/grafana:latest
ports:
- "3000:3000"
Loki daha az kaynak tüketir ve Grafana ile native entegredir.
Özet ve Best Practices
Logging best practices:
- Structured logging kullanın (JSON format)
- Log level’ları düzgün ayarlayın (DEBUG, INFO, WARN, ERROR)
- Sensitive bilgileri loglamayın
- Log rotation uygulayın
- Merkezi logging sistemi kurun
Monitoring best practices:
- Kritik metrikleri sürekli izleyin
- Alert kuralları belirleyin
- Dashboard’ları basit ve okunabilir tutun
- Retention politikaları belirleyin
- Backup alın
Araç seçimi:
| Senaryo | Önerilen Araçlar |
|---|---|
| Küçük projeler | docker logs + docker stats |
| Orta ölçek | journald + Prometheus + Grafana |
| Büyük ölçek | EFK/ELK + Prometheus + Grafana |
| Cloud ortamları | CloudWatch, Stackdriver, Azure Monitor |
Production ortamlarında logging ve monitoring ihmal edilmemesi gereken kritik konulardır. Doğru kurulum ve yapılandırma ile sistem sağlığını 7/24 izleyebilir, sorunları erken tespit edebilir ve performans optimizasyonu yapabilirsiniz.
Logging Dokümantasyonları
Eğer ELK, EFK, Prometheus + Grafana gibi sistemleri kurarken hata alırsan ya da takılırsan, aşağıdaki dokümantasyonları inceleyebilirsin:
| Konu / Yönlendirme | Açıklama | Kaynak Linki |
|---|---|---|
| EFK stack + Docker Compose örneği | Docker Compose ile EFK (Elasticsearch + Fluentd + Kibana) yapısını kurmak istiyorsanız bu rehber faydalı olur | https://faun.pub/setting-up-centralized-logging-environment-using-efk-stack-with-docker-compose-c96bb3bebf7 |
| Elastdocker – Full ELK + Ek Bileşenlerle Docker yapı | ELK + APM + SIEM gibi bileşenlerle hazır yapı isteyenler için | https://github.com/sherifabdlnaby/elastdocker |
| Grafana + Prometheus başlangıç klavuzu | Prometheus ile veri çekip Grafana’da görselleştirmek isterseniz | https://grafana.com/docs/grafana/latest/getting-started/get-started-grafana-prometheus |
| Docker Daemon’u Prometheus ile izleme | Docker’ın yerleşik metriklerini Prometheus ile çıkarma ayarları için | https://docs.docker.com/engine/daemon/prometheus |
| Docker log driver’ı: Fluentd | Docker konteyner log’larını Fluentd üzerinden yönlendirmek için | https://docs.docker.com/engine/logging/drivers/fluentd |
| Fluentd + Prometheus entegrasyonu | Fluentd’nin metriklerini Prometheus ile toplamak için rehber | https://docs.fluentd.org/0.12/articles/monitoring-prometheus |
| Docker + EFK ile logging yapılandırması | Docker + Fluentd + Elasticsearch + Kibana entegrasyonu örnek yapı | https://docs.fluentd.org/0.12/articles/docker-logging-efk-compose |
Not: Kurulum sırasında “bağlantı hatası”, “port çakışması”, “kaynak yetersizliği” gibi problemler çıkabilir.
Böyle durumlarda önce hata mesajlarını dikkatle oku, ardından yukarıdaki kaynakların ilgili bölümüne (örneğin “Configuration”, “Troubleshooting”, ya da “FAQ”) bak — çoğu sorunun çözümü zaten orada yer alır.
13. Debugging & Troubleshooting (Pratik İpuçları)
Docker konteynerleriyle çalışırken sorunlarla karşılaşmak kaçınılmazdır. Konteyner başlamıyor, ağ bağlantısı çalışmıyor, beklenmedik davranışlar gösteriyor olabilir. Bu bölümde Docker’da hata ayıklama ve sorun giderme için kullanabileceğiniz araçları, komutları ve pratik yaklaşımları detaylı şekilde inceleyeceğiz.
13.1 docker inspect, docker exec -it, docker top, docker diff
Docker’ın yerleşik debugging araçları, konteynerlerin durumunu incelemek ve sorunları tespit etmek için güçlü özellikler sunar.
docker inspect — Detaylı Konteyner Bilgisi
docker inspect komutu, bir konteyner, imaj, network veya volume hakkında tüm teknik detayları JSON formatında gösterir. Bu komut debugging’in temel taşıdır.
Basit kullanım:
docker inspect mycontainer
Bu komut yüzlerce satır JSON çıktı verir. İçinde network ayarları, volume mount’lar, environment variables, resource limits gibi tüm bilgiler bulunur.
Belirli bilgiyi çıkarma (–format):
# IP adresini öğrenme
docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' mycontainer
# Port mapping'leri gösterme
docker inspect --format='{{json .NetworkSettings.Ports}}' mycontainer
# Environment variables
docker inspect --format='{{json .Config.Env}}' mycontainer
# Volume mount'ları gösterme
docker inspect --format='{{json .Mounts}}' mycontainer
# Konteyner durumu
docker inspect --format='{{.State.Status}}' mycontainer
# Restart count
docker inspect --format='{{.RestartCount}}' mycontainer
Çıktıyı jq ile filtreleme:
jq komutu JSON’ı daha okunabilir hale getirir ve detaylı filtreleme yapar.
# Tüm çıktıyı okunabilir hale getir
docker inspect mycontainer | jq '.'
# Sadece network bilgilerini göster
docker inspect mycontainer | jq '.[0].NetworkSettings'
# Environment variables'ı listele
docker inspect mycontainer | jq '.[0].Config.Env[]'
# Mount edilen volume'ları göster
docker inspect mycontainer | jq '.[0].Mounts[] | {Source, Destination, Mode}'
Pratik inspect örnekleri:
Konteyner neden durdu?
docker inspect --format='{{.State.Status}} - Exit Code: {{.State.ExitCode}}' mycontainer
Exit code’lar:
0: Normal çıkış1: Genel hata137: SIGKILL (OOM Killer tarafından öldürülmüş olabilir)139: Segmentation fault143: SIGTERM
OOM (Out of Memory) kontrolü:
docker inspect --format='{{.State.OOMKilled}}' mycontainer
Eğer true ise konteyner bellek limitini aşmış ve kernel tarafından öldürülmüş demektir.
Log path’ini bulma:
docker inspect --format='{{.LogPath}}' mycontainer
docker exec -it — Çalışan Konteynere Bağlanma
docker exec komutu, çalışan bir konteyner içinde komut çalıştırmanıza olanak tanır. Debugging için en çok kullanılan komuttur.
İnteraktif shell açma:
docker exec -it mycontainer bash
Eğer bash yoksa (Alpine gibi minimal image’lerde):
docker exec -it mycontainer sh
Tek komut çalıştırma:
# Process listesini görme
docker exec mycontainer ps aux
# Dosya içeriğini okuma
docker exec mycontainer cat /etc/hosts
# Ağ bağlantısını test etme
docker exec mycontainer ping -c 3 google.com
# Disk kullanımı
docker exec mycontainer df -h
Root yetkisi ile bağlanma:
Konteyner non-root kullanıcı ile çalışıyorsa ama root yetkisi gerekiyorsa:
docker exec -it --user root mycontainer bash
Çalışma dizinini değiştirme:
docker exec -it --workdir /app mycontainer bash
Environment variable ekleme:
docker exec -it -e DEBUG=true mycontainer bash
Pratik debugging senaryoları:
Senaryo 1: Web sunucusu çalışıyor mu?
# Nginx process'i çalışıyor mu?
docker exec mycontainer ps aux | grep nginx
# Port dinleniyor mu?
docker exec mycontainer netstat -tlnp | grep 80
# Curl ile test (eğer kuruluysa)
docker exec mycontainer curl -I http://localhost:80
Senaryo 2: Veritabanı bağlantısı çalışıyor mu?
# PostgreSQL bağlantısını test et
docker exec mypostgres psql -U postgres -c "SELECT 1"
# MySQL bağlantısını test et
docker exec mymysql mysql -u root -p'password' -e "SELECT 1"
Senaryo 3: Log dosyalarını kontrol etme
# Nginx error log
docker exec mynginx tail -f /var/log/nginx/error.log
# Application log
docker exec myapp tail -f /var/log/app/error.log
docker top — Process Listesi
docker top komutu, konteyner içinde çalışan process’leri gösterir. Hangi process’lerin çalıştığını ve kaynak kullanımını kontrol etmek için kullanılır.
Basit kullanım:
docker top mycontainer
Çıktı örneği:
UID PID PPID C STIME TTY TIME CMD
root 12345 12340 0 10:30 ? 00:00:00 nginx: master process
www-data 12346 12345 0 10:30 ? 00:00:01 nginx: worker process
Custom format (ps komut seçenekleri):
# Detaylı bilgi
docker top mycontainer aux
# Bellek kullanımına göre sıralama
docker top mycontainer -o %mem
Process kontrolü:
# Nginx master process çalışıyor mu?
docker top mynginx | grep "nginx: master"
# Zombie process var mı?
docker top mycontainer aux | grep defunct
docker diff — Filesystem Değişiklikleri
docker diff komutu, konteyner başladıktan sonra filesystem’de yapılan değişiklikleri gösterir. Hangi dosyaların eklendiğini, değiştirildiğini veya silindiğini görebilirsiniz.
Basit kullanım:
docker diff mycontainer
Çıktı örneği:
A /tmp/test.txt
C /etc/nginx/nginx.conf
D /var/log/old.log
Semboller:
A(Added): Yeni eklenen dosyaC(Changed): Değiştirilen dosyaD(Deleted): Silinen dosya
Pratik kullanım:
Konteyner içinde hangi dosyalar değişti?
docker diff mycontainer | grep ^C
Yeni log dosyaları oluşturulmuş mu?
docker diff mycontainer | grep ^A | grep log
Debug: Beklenmeyen dosya değişiklikleri
Bazen konteyner beklenmedik davranır. docker diff ile hangi dosyaların değiştiğini görerek sorunu bulabilirsiniz.
# Tüm değişiklikleri listele
docker diff mycontainer
# Sadece /etc dizinindeki değişiklikler
docker diff mycontainer | grep "^C /etc"
13.2 Ağ Sorunları için docker network inspect, tcpdump Kullanımı
Network sorunları Docker’da en yaygın problemlerden biridir. Konteynerler birbirini görmüyor, dışarı çıkamıyor veya port’lar çalışmıyor olabilir.
docker network inspect — Network Detayları
docker network inspect komutu, bir network’ün konfigürasyonunu, bağlı konteynerleri ve IP adreslerini gösterir.
Basit kullanım:
docker network inspect bridge
Network’e bağlı konteynerleri gösterme:
docker network inspect bridge --format='{{range .Containers}}{{.Name}}: {{.IPv4Address}}{{"\n"}}{{end}}'
Çıktı örneği:
web: 172.17.0.2/16
db: 172.17.0.3/16
redis: 172.17.0.4/16
Network subnet ve gateway:
docker network inspect mynetwork --format='{{range .IPAM.Config}}Subnet: {{.Subnet}}, Gateway: {{.Gateway}}{{end}}'
Pratik network debugging:
Sorun: Konteynerler birbirini göremiyor
# İki konteyner aynı network'te mi?
docker network inspect mynetwork
# Çıktıda her iki konteyner de "Containers" bölümünde olmalı
Sorun: DNS çözümleme çalışmıyor
# Konteyner içinden DNS test
docker exec mycontainer nslookup other-container
# Docker DNS server kontrolü
docker exec mycontainer cat /etc/resolv.conf
Docker’ın internal DNS server’ı genellikle 127.0.0.11 olarak görünür.
ping ve curl ile Network Testi
Konteyner içinde network bağlantısını test etmek için temel araçlar kullanabilirsiniz.
Ping ile bağlantı testi:
# Başka konteynere ping
docker exec container1 ping -c 3 container2
# Dış dünyaya ping
docker exec mycontainer ping -c 3 8.8.8.8
# DNS çözümleme testi
docker exec mycontainer ping -c 3 google.com
Curl ile HTTP testi:
# Başka konteynere HTTP isteği
docker exec container1 curl http://container2:80
# Dış siteye istek
docker exec mycontainer curl -I https://google.com
# Timeout ayarlayarak
docker exec mycontainer curl --max-time 5 http://slow-service
Netstat ile port kontrolü:
# Hangi portlar dinleniyor?
docker exec mycontainer netstat -tlnp
# Belirli port dinleniyor mu?
docker exec mycontainer netstat -tlnp | grep :80
tcpdump ile Paket Analizi (Linux Host)
tcpdump, network trafiğini yakalamak ve analiz etmek için güçlü bir araçtır. Konteyner içinde veya host üzerinde çalıştırılabilir.
Host üzerinde konteyner trafiğini yakalama:
# Tüm Docker network trafiğini yakala
sudo tcpdump -i docker0
# Belirli konteyner IP'sine giden trafiği yakala
sudo tcpdump -i docker0 host 172.17.0.2
# HTTP trafiğini yakala (port 80)
sudo tcpdump -i docker0 port 80
# Trafiği dosyaya kaydet
sudo tcpdump -i docker0 -w capture.pcap
Konteyner içinde tcpdump:
Çoğu konteyner imajında tcpdump yoktur. Kurulum gerekebilir:
# Alpine
docker exec mycontainer apk add tcpdump
# Ubuntu/Debian
docker exec mycontainer apt-get update && apt-get install -y tcpdump
# tcpdump çalıştır
docker exec mycontainer tcpdump -i eth0 -n
Pratik tcpdump örnekleri:
Sorun: Konteyner dış dünyaya çıkamıyor
# Konteynerden çıkan DNS isteklerini izle
sudo tcpdump -i docker0 port 53
# Ardından konteyner içinden ping at
docker exec mycontainer ping google.com
Eğer tcpdump’ta paket görmüyorsanız routing sorunu var demektir.
Sorun: İki konteyner arasında bağlantı yok
# container1'den container2'ye giden trafiği izle
sudo tcpdump -i docker0 host 172.17.0.2 and host 172.17.0.3
# container1'den curl at
docker exec container1 curl http://container2:8080
Paketleri görüyorsanız ama yanıt alamıyorsanız, container2’de uygulama çalışmıyor olabilir.
nsenter ile Host’tan Konteyner Network Namespace’ine Girme
nsenter komutu, konteynerin network namespace’ine doğrudan host’tan girebilmenizi sağlar. Gelişmiş debugging için kullanılır.
Konteyner PID’sini bulma:
PID=$(docker inspect --format '{{.State.Pid}}' mycontainer)
Network namespace’e girme:
sudo nsenter -t $PID -n ip addr
Bu komut konteynerin network interface’lerini gösterir.
Network routing tablosunu görme:
sudo nsenter -t $PID -n ip route
tcpdump çalıştırma:
sudo nsenter -t $PID -n tcpdump -i eth0
13.3 “Container Çalışmıyor” Durumlarında Hızlı Kontrol Listesi
Konteyner başlamıyor veya hemen kapanıyorsa sistematik bir yaklaşımla sorunu bulabilirsiniz.
Adım 1: Konteyner Durumunu Kontrol Et
docker ps -a
Konteyner Exited durumundaysa exit code’a bakın:
docker inspect --format='{{.State.ExitCode}}' mycontainer
Exit code anlamları:
0: Normal çıkış (sorun yok, konteyner işini bitirip kapandı)1: Uygulama hatası125: Docker daemon hatası126: Komut çalıştırılamadı127: Komut bulunamadı137: SIGKILL (OOM veya manuel kill)143: SIGTERM (graceful shutdown)
Adım 2: Logları İncele
docker logs mycontainer
Son 50 satırı görmek için:
docker logs --tail 50 mycontainer
Hata mesajları genellikle loglarda bellidir:
Address already in use: Port başka bir process tarafından kullanılıyorPermission denied: Dosya izin sorunuConnection refused: Hedef servis çalışmıyorNo such file or directory: Dosya veya path yanlış
Adım 3: Dockerfile ve Komut Kontrolü
CMD veya ENTRYPOINT yanlış olabilir:
docker inspect --format='{{.Config.Cmd}}' mycontainer
docker inspect --format='{{.Config.Entrypoint}}' mycontainer
Test: Konteyner içinde manuel komut çalıştırma
Eğer konteyner hemen kapanıyorsa, shell ile başlatıp manuel test edin:
docker run -it --entrypoint /bin/sh myimage
Ardından orijinal komutu manuel çalıştırın ve hatayı görün.
Adım 4: Resource Limitlerini Kontrol Et
Konteyner OOM (Out of Memory) killed mi?
docker inspect --format='{{.State.OOMKilled}}' mycontainer
Eğer true ise memory limitini artırın:
docker run --memory="1g" myimage
Adım 5: Volume ve Bind Mount Kontrolü
Mount’lar doğru mu?
docker inspect --format='{{json .Mounts}}' mycontainer | jq '.'
Kontrol edilecekler:
- Kaynak path host’ta var mı?
- İzinler doğru mu?
- SELinux/AppArmor engelliyor mu? (Linux’ta
:Zetiketi deneyin)
Test: Volume olmadan çalıştırma
docker run --rm myimage
Eğer volume olmadan çalışıyorsa sorun mount’tadır.
Adım 6: Network Kontrolü
Konteyner network’e bağlı mı?
docker network inspect mynetwork
Port mapping doğru mu?
docker inspect --format='{{json .NetworkSettings.Ports}}' mycontainer
Test: Host network ile çalıştırma
docker run --network host myimage
Eğer host network ile çalışıyorsa bridge network’te sorun var demektir.
Adım 7: Dependency Kontrolü
depends_on çalışmıyor:
Docker Compose’da depends_on sadece başlatma sırasını garanti eder, servisin hazır olmasını beklemez.
Çözüm: Healthcheck veya wait script kullanın
services:
web:
image: myapp
depends_on:
db:
condition: service_healthy
db:
image: postgres
healthcheck:
test: ["CMD", "pg_isready", "-U", "postgres"]
interval: 10s
timeout: 5s
retries: 5
Hızlı Kontrol Listesi Özet
docker ps -a— Konteyner durumu ve exit codedocker logs mycontainer— Hata mesajlarıdocker inspect mycontainer— Detaylı konfigürasyondocker run -it --entrypoint /bin/sh myimage— Manuel test- Volume ve network ayarlarını kontrol et
- Resource limitleri kontrol et
- Healthcheck ve dependency durumunu kontrol et
13.4 Windows Konteyner Debug İpuçları (PowerShell vs CMD)
Windows konteynerler Linux konteynerlerden farklı çalışır ve debugging yaklaşımları da farklıdır.
Windows Konteyner Türleri
Windows Server Core:
- Tam Windows API desteği
- Daha büyük image boyutu (birkaç GB)
- Eski uygulamalar için uyumlu
Nano Server:
- Minimal Windows image
- Daha küçük (hundreds of MB)
- PowerShell Core içerir, tam framework yok
PowerShell ile Debugging
Windows konteynerlerinde genellikle PowerShell kullanılır.
PowerShell shell açma:
docker exec -it mycontainer powershell
CMD ile çalıştırma:
docker exec -it mycontainer cmd
Pratik PowerShell komutları:
Process listesi:
docker exec mycontainer powershell "Get-Process"
Servis durumu:
docker exec mycontainer powershell "Get-Service"
Network bağlantıları:
docker exec mycontainer powershell "Test-NetConnection google.com"
Dosya sistemi kontrolü:
docker exec mycontainer powershell "Get-ChildItem C:\app"
Event log kontrolü:
docker exec mycontainer powershell "Get-EventLog -LogName Application -Newest 10"
Windows Konteyner Network Debugging
Windows konteynerlerinde network debugging Linux’tan biraz farklıdır.
IP konfigürasyonu:
docker exec mycontainer ipconfig /all
Route tablosu:
docker exec mycontainer route print
DNS cache:
docker exec mycontainer ipconfig /displaydns
Ping testi:
docker exec mycontainer ping -n 3 google.com
Port kontrolü:
docker exec mycontainer netstat -ano
Windows Konteyner Logları
Windows konteynerlerinde log yönetimi farklıdır.
Event Log okuma:
docker exec mycontainer powershell "Get-EventLog -LogName Application | Select-Object -First 20"
IIS logları (eğer IIS kullanılıyorsa):
docker exec mycontainer powershell "Get-Content C:\inetpub\logs\LogFiles\W3SVC1\*.log -Tail 50"
Dockerfile Debugging (Windows)
Örnek Windows Dockerfile:
FROM mcr.microsoft.com/windows/servercore:ltsc2022
WORKDIR C:\app
COPY app.exe .
CMD ["app.exe"]
Debug için shell eklemek:
FROM mcr.microsoft.com/windows/servercore:ltsc2022
WORKDIR C:\app
COPY app.exe .
# Debug için shell başlat
ENTRYPOINT ["powershell.exe"]
Ardından konteyner içinde manuel test:
docker run -it myimage
# PowerShell açılacak
PS C:\app> .\app.exe
Windows vs Linux Konteyner Farkları
| Özellik | Linux | Windows |
|---|---|---|
| Base image boyutu | 5-50 MB | 300 MB - 4 GB |
| Shell | bash, sh | PowerShell, CMD |
| Process izolasyonu | Namespaces | Job Objects |
| Filesystem | overlay2, aufs | windowsfilter |
| Network driver | bridge, overlay | nat, transparent |
| Debugging tools | strace, tcpdump | Process Monitor, Event Viewer |
Yaygın Windows Konteyner Sorunları
Sorun 1: “The container operating system does not match the host operating system”
Açıklama: Windows konteyner versiyonu host versiyonuyla uyumsuz.
Çözüm: Hyper-V isolation kullan:
docker run --isolation=hyperv myimage
Sorun 2: Volume mount çalışmıyor
Açıklama: Windows path’leri farklı format kullanır.
Yanlış:
docker run -v C:\data:/data myimage
Doğru:
docker run -v C:\data:C:\data myimage
Sorun 3: Port forwarding çalışmıyor
Açıklama: Windows NAT network kısıtlamaları.
Kontrol:
# NAT network kontrolü
docker network inspect nat
# Port mapping kontrolü
docker port mycontainer
Çözüm: Transparent network kullanmayı deneyin:
docker network create -d transparent mytransparent
docker run --network mytransparent myimage
Windows Performance Monitoring
Resource kullanımı:
docker stats
Detaylı performance counters:
docker exec mycontainer powershell "Get-Counter '\Processor(_Total)\% Processor Time'"
Memory kullanımı:
docker exec mycontainer powershell "Get-Process | Sort-Object WS -Descending | Select-Object -First 10"
Özet ve Best Practices
Debugging checklist:
docker ps -aile durum kontrolüdocker logsile hata mesajlarını okudocker inspectile detaylı konfigürasyonu inceledocker execile konteyner içine gir ve manuel test et- Network bağlantılarını test et (ping, curl, tcpdump)
- Resource limitlerini kontrol et
- Volume mount’ları ve izinleri kontrol et
Araç önerileri:
- Linux:
tcpdump,strace,htop - Windows: PowerShell, Event Viewer, Process Monitor
- Her platform:
docker logs,docker inspect,docker exec
Dokümantasyon:
Sorunları çözdükten sonra notlar alın. Hangi hatayı nasıl çözdüğünüzü dokümante edin. Bu, gelecekte benzer sorunlarla karşılaştığınızda zaman kazandırır.
Debugging sistematik bir süreçtir. Panik yapmadan adım adım ilerleyerek çoğu sorunu çözebilirsiniz. Docker’ın sağladığı araçları iyi bilmek, production ortamlarında kritik sorunları hızlıca çözmenizi sağlar.
14. CI/CD ile Entegrasyon (Docker Native Yaklaşımlar)
Modern yazılım geliştirmede CI/CD (Continuous Integration/Continuous Deployment) pipeline’ları vazgeçilmezdir. Docker, bu süreçlerde merkezi bir rol oynar. Bu bölümde Docker’ı CI/CD pipeline’larına nasıl entegre edeceğinizi, multi-platform image build süreçlerini ve image etiketleme stratejilerini detaylı şekilde inceleyeceğiz.
14.1 Build → Test → Push Pipeline Örneği
CI/CD pipeline’ı tipik olarak şu aşamalardan oluşur:
- Build: Dockerfile’dan image oluşturma
- Test: Image’i test etme (unit test, integration test)
- Scan: Güvenlik açıklarını tarama
- Push: Registry’ye yükleme
- Deploy: Production’a dağıtım
GitHub Actions Pipeline Örneği
GitHub Actions, GitHub üzerinde çalışan popüler bir CI/CD platformudur.
Basit Python uygulaması için tam pipeline:
.github/workflows/docker-ci.yml:
name: Docker CI/CD Pipeline
on:
push:
branches: [ main, develop ]
tags:
- 'v*'
pull_request:
branches: [ main ]
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build-and-test:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
# Kod checkout
- name: Checkout repository
uses: actions/checkout@v4
# Docker Buildx kurulumu
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
# Registry'ye login
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
# Metadata oluşturma (tags, labels)
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=sha,prefix={{branch}}-
# Image build
- name: Build Docker image
uses: docker/build-push-action@v5
with:
context: .
load: true
tags: test-image:latest
cache-from: type=gha
cache-to: type=gha,mode=max
# Test çalıştırma
- name: Run tests
run: |
docker run --rm test-image:latest pytest tests/
# Güvenlik taraması (Trivy)
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
image-ref: test-image:latest
format: 'sarif'
output: 'trivy-results.sarif'
# Trivy sonuçlarını GitHub'a yükle
- name: Upload Trivy results to GitHub Security
uses: github/codeql-action/upload-sarif@v2
if: always()
with:
sarif_file: 'trivy-results.sarif'
# Push (sadece main branch ve tag'ler için)
- name: Build and push Docker image
if: github.event_name != 'pull_request'
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
Açıklama:
- Trigger: main ve develop branch’lerine push, PR’ler ve v tag’leri
- Buildx: Multi-platform build için
- Cache: GitHub Actions cache kullanarak build süresini kısaltma
- Test: Image içinde pytest çalıştırma
- Security scan: Trivy ile güvenlik taraması
- Conditional push: Sadece main branch ve tag’ler için registry’ye push
Örnek Dockerfile (Python uygulaması):
FROM python:3.11-slim as builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir --user -r requirements.txt
FROM python:3.11-slim
WORKDIR /app
COPY --from=builder /root/.local /root/.local
COPY . .
ENV PATH=/root/.local/bin:$PATH
# Non-root user
RUN useradd -m appuser && chown -R appuser:appuser /app
USER appuser
CMD ["python", "app.py"]
GitLab CI Pipeline Örneği
GitLab CI, GitLab’ın yerleşik CI/CD sistemidir.
.gitlab-ci.yml:
stages:
- build
- test
- scan
- push
- deploy
variables:
DOCKER_DRIVER: overlay2
DOCKER_TLS_CERTDIR: "/certs"
IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG
DOCKER_BUILDKIT: 1
# Build stage
build:
stage: build
image: docker:24-dind
services:
- docker:24-dind
before_script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
script:
- docker build -t $IMAGE_TAG .
- docker save $IMAGE_TAG -o image.tar
artifacts:
paths:
- image.tar
expire_in: 1 hour
# Test stage
test:
stage: test
image: docker:24-dind
services:
- docker:24-dind
dependencies:
- build
before_script:
- docker load -i image.tar
script:
- docker run --rm $IMAGE_TAG pytest tests/
- docker run --rm $IMAGE_TAG python -m pylint app/
# Security scan
security-scan:
stage: scan
image: aquasec/trivy:latest
dependencies:
- build
before_script:
- docker load -i image.tar
script:
- trivy image --exit-code 0 --severity HIGH,CRITICAL $IMAGE_TAG
allow_failure: true
# Push to registry
push:
stage: push
image: docker:24-dind
services:
- docker:24-dind
dependencies:
- build
only:
- main
- tags
before_script:
- docker load -i image.tar
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
script:
- docker push $IMAGE_TAG
- |
if [[ "$CI_COMMIT_TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
docker tag $IMAGE_TAG $CI_REGISTRY_IMAGE:latest
docker push $CI_REGISTRY_IMAGE:latest
fi
# Deploy to staging
deploy-staging:
stage: deploy
image: alpine:latest
only:
- develop
before_script:
- apk add --no-cache openssh-client
- eval $(ssh-agent -s)
- echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
script:
- ssh -o StrictHostKeyChecking=no user@staging-server "docker pull $IMAGE_TAG && docker-compose up -d"
# Deploy to production
deploy-production:
stage: deploy
image: alpine:latest
only:
- tags
when: manual
before_script:
- apk add --no-cache openssh-client
- eval $(ssh-agent -s)
- echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
script:
- ssh -o StrictHostKeyChecking=no user@prod-server "docker pull $IMAGE_TAG && docker-compose up -d"
Özellikler:
- Artifacts: Build edilen image sonraki stage’lerde kullanılmak üzere saklanır
- Dependencies: Her stage sadece ihtiyacı olan artifact’ları indirir
- Conditional execution: Push ve deploy sadece belirli branch’lerde çalışır
- Manual deployment: Production deploy manuel onay gerektirir
Docker-in-Docker (DinD) vs Docker Socket Mount
CI/CD pipeline’larında Docker kullanmanın iki yolu vardır:
1. Docker-in-Docker (DinD):
services:
- docker:24-dind
- Avantajlar: İzolasyon, güvenli
- Dezavantajlar: Daha yavaş, daha fazla kaynak tüketir
2. Docker Socket Mount:
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- Avantajlar: Hızlı, hafif
- Dezavantajlar: Güvenlik riski (host Docker’a tam erişim)
Öneri: Production ortamlarında DinD kullanın. Local development’ta socket mount kullanabilirsiniz.
14.2 docker buildx ile Multi-Platform Image Build & Push
Modern uygulamalar farklı CPU mimarilerinde çalışabilmelidir (x86_64, ARM64, ARM). docker buildx bu işi kolaylaştırır.
Docker Buildx Nedir?
Buildx, Docker’ın gelişmiş build motorudur. BuildKit backend’ini kullanır ve çoklu platform için image build edebilir.
Özellikler:
- Multi-platform build (amd64, arm64, arm/v7 vb.)
- Build cache yönetimi
- Build secret desteği
- SSH agent forwarding
- Paralel build
Buildx Kurulumu
Docker Desktop’ta varsayılan olarak gelir. Linux’ta manuel kurulum:
# Buildx binary indir
BUILDX_VERSION=v0.12.0
curl -LO https://github.com/docker/buildx/releases/download/${BUILDX_VERSION}/buildx-${BUILDX_VERSION}.linux-amd64
# Kurulum
mkdir -p ~/.docker/cli-plugins
mv buildx-${BUILDX_VERSION}.linux-amd64 ~/.docker/cli-plugins/docker-buildx
chmod +x ~/.docker/cli-plugins/docker-buildx
# Kontrol
docker buildx version
Builder Instance Oluşturma
# Yeni builder oluştur
docker buildx create --name mybuilder --use
# QEMU için binfmt kurulumu (farklı mimariler için emülasyon)
docker run --privileged --rm tonistiigi/binfmt --install all
# Builder'ı başlat
docker buildx inspect --bootstrap
Multi-Platform Build Örneği
Basit örnek:
docker buildx build \
--platform linux/amd64,linux/arm64,linux/arm/v7 \
-t username/myapp:latest \
--push \
.
Bu komut üç farklı platform için image build eder ve registry’ye push eder.
Dockerfile örneği (platform-aware):
FROM --platform=$BUILDPLATFORM golang:1.21 AS builder
ARG TARGETPLATFORM
ARG BUILDPLATFORM
ARG TARGETOS
ARG TARGETARCH
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} \
go build -o myapp .
FROM alpine:latest
WORKDIR /app
COPY --from=builder /app/myapp .
CMD ["./myapp"]
Açıklama:
--platform=$BUILDPLATFORM: Build işlemi host platformunda yapılır (hızlı)TARGETOSveTARGETARCH: Hedef platform için binary oluşturulur- Cross-compilation sayesinde farklı platformlar için hızlı build
GitHub Actions ile Multi-Platform Build
name: Multi-Platform Docker Build
on:
push:
tags:
- 'v*'
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: username/myapp
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
platforms: linux/amd64,linux/arm64,linux/arm/v7
push: true
tags: ${{ steps.meta.outputs.tags }}
cache-from: type=gha
cache-to: type=gha,mode=max
Manifest Inspection
Multi-platform image push edildikten sonra manifest’i kontrol edebilirsiniz:
docker buildx imagetools inspect username/myapp:latest
Çıktı:
Name: docker.io/username/myapp:latest
MediaType: application/vnd.docker.distribution.manifest.list.v2+json
Digest: sha256:abc123...
Manifests:
Name: docker.io/username/myapp:latest@sha256:def456...
MediaType: application/vnd.docker.distribution.manifest.v2+json
Platform: linux/amd64
Name: docker.io/username/myapp:latest@sha256:ghi789...
MediaType: application/vnd.docker.distribution.manifest.v2+json
Platform: linux/arm64
Local Multi-Arch Test
Farklı platformları local’de test edebilirsiniz:
# ARM64 image'i x86_64 makinede çalıştırma
docker run --platform linux/arm64 username/myapp:latest
# Platform bilgisini görme
docker run --rm username/myapp:latest uname -m
14.3 Image Etiketi Stratejileri (semver, latest vs digest)
Image etiketleme stratejisi, versiyonlama ve deployment güvenliği için kritiktir.
Etiketleme Yöntemleri
1. Semantic Versioning (semver)
docker tag myapp:build myapp:1.2.3
docker tag myapp:build myapp:1.2
docker tag myapp:build myapp:1
Avantajlar:
- Versiyon takibi kolay
- Rollback basit
- Production’da güvenli
Kullanım:
1.2.3: Tam versiyon (patch dahil)1.2: Minor versiyon (patch güncellemelerini otomatik al)1: Major versiyon (tüm 1.x güncellemelerini al)
2. latest Tag
docker tag myapp:1.2.3 myapp:latest
Avantajlar:
- Basit ve anlaşılır
- Her zaman en yeni versiyonu işaret eder
Dezavantajlar:
- Production’da tehlikeli (beklenmedik güncellemeler)
- Rollback zor
- Hangi versiyonun çalıştığı belirsiz
Öneri: latest tag’ini sadece development ortamlarında kullanın.
3. Git Commit SHA
docker tag myapp:build myapp:abc123
Avantajlar:
- Her build eşsiz
- Git commit’e geri takip mümkün
- Reproducible builds
4. Branch Name + SHA
docker tag myapp:build myapp:main-abc123
docker tag myapp:build myapp:develop-def456
5. Timestamp
docker tag myapp:build myapp:20250929-103045
Önerilen Etiketleme Stratejisi
Production için best practice:
# Tag git commit ile
docker tag myapp:build myapp:${GIT_COMMIT_SHA}
# Tag semver ile
docker tag myapp:build myapp:${VERSION}
# Tag latest ile (opsiyonel)
docker tag myapp:build myapp:latest
# Push all tags
docker push myapp:${GIT_COMMIT_SHA}
docker push myapp:${VERSION}
docker push myapp:latest
GitHub Actions örneği:
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: username/myapp
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=sha,prefix=sha-
type=raw,value=latest,enable={{is_default_branch}}
Bu konfigürasyon şu tag’leri oluşturur:
main(branch name)1.2.3(semver tam versiyon)1.2(semver minor)sha-abc123(git commit SHA)latest(sadece main branch’te)
Image Digest Kullanımı
Digest, image’in SHA256 hash’idir. Immutable ve güvenlidir.
Digest nedir?
docker pull nginx:latest
# Output: Digest: sha256:abc123...
Digest ile pull:
docker pull nginx@sha256:abc123...
Avantajlar:
- Tamamen immutable (değiştirilemez)
- Registry’de değişmez (latest değişebilir ama digest asla)
- Güvenlik açısından en iyi yöntem
Kubernetes’te digest kullanımı:
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
template:
spec:
containers:
- name: myapp
image: username/myapp@sha256:abc123...
Digest’i otomatik alma (CI/CD):
# Image push et ve digest'i al
DIGEST=$(docker inspect --format='{{index .RepoDigests 0}}' username/myapp:1.2.3)
# Deployment YAML'inde kullan
sed -i "s|IMAGE_PLACEHOLDER|${DIGEST}|g" deployment.yaml
Etiketleme Anti-Patterns
** Yapmamanız gerekenler:**
# Geliştirici ismi kullanma
docker tag myapp:john-dev
# Test/prod ayrımı tag'te
docker tag myapp:test
docker tag myapp:prod
# Tarih formatı olmadan timestamp
docker tag myapp:103045
** Yapmanız gerekenler:**
# Semantic versioning
docker tag myapp:1.2.3
# Git SHA
docker tag myapp:abc123f
# Branch + SHA
docker tag myapp:main-abc123f
Tag Yönetimi ve Cleanup
Registry zamanla şişebilir. Eski tag’leri temizlemek gerekir.
Docker Hub’da tag silme:
# Docker Hub API ile tag silme
curl -X DELETE \
-H "Authorization: JWT ${TOKEN}" \
https://hub.docker.com/v2/repositories/username/myapp/tags/old-tag/
Harbor registry’de policy:
Harbor gibi private registry’lerde otomatik cleanup policy’leri ayarlayabilirsiniz:
- Son N tag’i tut
- X günden eski tag’leri sil
- Regex pattern’e göre sil
Özet ve Best Practices
CI/CD Pipeline:
- Automated build, test, scan, push süreci kurun
- Cache kullanarak build süresini kısaltın
- Security scan’i mutlaka ekleyin
- Manual approval ile production deploy yapın
Multi-Platform Build:
docker buildxkullanın- ARM64 desteği ekleyin (Apple Silicon, AWS Graviton için)
- Cross-compilation ile build süresini kısaltın
- Manifest’leri kontrol edin
Image Tagging:
- Semantic versioning kullanın (1.2.3)
- Git commit SHA ekleyin (traceability için)
latesttag’ini production’da kullanmayın- Digest kullanarak immutability sağlayın
- Düzenli tag cleanup yapın
Security:
- Image scan’i pipeline’a ekleyin (Trivy, Snyk)
- Secrets’ı environment variable veya CI/CD secret olarak yönetin
- Non-root user kullanın
- Minimal base image seçin (alpine, distroless)
CI/CD ve Docker entegrasyonu doğru yapıldığında deployment süreciniz hızlı, güvenli ve tekrarlanabilir hale gelir. Her commit otomatik olarak test edilir, güvenlik açıkları taranır ve production’a güvenle deploy edilebilir.
15. Kötü Kokular / Anti-Pattern’ler ve Nasıl Düzeltilir
Docker kullanırken bazı yaygın hatalar yapılır. Bu hatalar performans sorunlarına, güvenlik açıklarına ve bakım zorluklarına yol açar. Bu bölümde Docker anti-pattern’lerini, neden sorun olduklarını ve nasıl düzeltileceğini detaylı şekilde inceleyeceğiz.
15.1 Büyük Image’lar / Çok Fazla Katman
Sorun: Gereksiz Büyük Image’lar
Büyük Docker image’ları birçok soruna yol açar:
- Yavaş deployment: Image indirme süresi artar
- Disk alanı tüketimi: Her node’da GB’larca yer kaplar
- Güvenlik açıkları: Gereksiz paketler saldırı yüzeyi artırır
- Build süresi: Layer cache etkinliği düşer
Kötü örnek:**
FROM ubuntu:22.04
# Tüm geliştirme araçlarını kur
RUN apt-get update && apt-get install -y \
build-essential \
gcc \
g++ \
make \
cmake \
git \
curl \
wget \
vim \
nano \
python3 \
python3-pip \
nodejs \
npm
WORKDIR /app
COPY . .
RUN pip3 install -r requirements.txt
CMD ["python3", "app.py"]
Bu Dockerfile’ın sorunları:
- Ubuntu base image zaten büyük (77 MB)
- Gereksiz geliştirme araçları (gcc, make, cmake)
- Text editor’ler (vim, nano) production’da gereksiz
- apt-get cache temizlenmemiş
- Tek bir RUN katmanı yerine birden fazla
Image boyutu: ~1.2 GB
** İyi örnek (Alpine + Multi-stage):**
# Build stage
FROM python:3.11-alpine AS builder
WORKDIR /app
# Sadece build için gerekli paketler
RUN apk add --no-cache gcc musl-dev libffi-dev
COPY requirements.txt .
RUN pip install --no-cache-dir --user -r requirements.txt
# Runtime stage
FROM python:3.11-alpine
WORKDIR /app
# Builder stage'den sadece gerekli dosyaları kopyala
COPY --from=builder /root/.local /root/.local
COPY . .
# Non-root user oluştur
RUN adduser -D appuser && chown -R appuser:appuser /app
USER appuser
ENV PATH=/root/.local/bin:$PATH
CMD ["python", "app.py"]
Image boyutu: ~50 MB (24 kat daha küçük!)
İyileştirmeler:
- Alpine base image (5 MB yerine 77 MB)
- Multi-stage build (build tools final image’de yok)
--no-cache-dirile pip cache kaldırıldı- Non-root user güvenlik için
- Sadece runtime dependency’ler
Sorun: Çok Fazla Layer (Katman)
Her Dockerfile komutu (RUN, COPY, ADD) yeni bir layer oluşturur. Çok fazla layer performansı düşürür.
** Kötü örnek:**
FROM ubuntu:22.04
RUN apt-get update
RUN apt-get install -y python3
RUN apt-get install -y python3-pip
RUN apt-get install -y curl
RUN apt-get install -y git
RUN rm -rf /var/lib/apt/lists/*
COPY requirements.txt .
RUN pip3 install flask
RUN pip3 install requests
RUN pip3 install psycopg2-binary
COPY app.py .
COPY config.py .
COPY utils.py .
Layer sayısı: 12 layer
Sorunlar:
- Her RUN ayrı layer (apt-get 6 layer!)
- apt-get cache sadece son layer’da temizlenmiş (önceki layer’larda hala var)
- pip install’lar ayrı ayrı (3 layer)
- COPY komutları ayrı ayrı (3 layer)
** İyi örnek:**
FROM ubuntu:22.04
# Tek RUN komutu ile tüm kurulumlar
RUN apt-get update && apt-get install -y --no-install-recommends \
python3 \
python3-pip \
curl \
git \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
# requirements.txt önce kopyala (cache için)
COPY requirements.txt .
RUN pip3 install --no-cache-dir -r requirements.txt
# Tüm uygulama dosyaları tek seferde
COPY . .
CMD ["python3", "app.py"]
Layer sayısı: 5 layer
İyileştirmeler:
- apt-get komutları birleştirildi (
&&ile) - Cache aynı RUN komutunda temizlendi
--no-install-recommendsile gereksiz paketler kaldırıldı- pip paketleri requirements.txt üzerinden tek seferde
- COPY komutları minimize edildi
Layer Cache Stratejisi
Docker build sırasında değişmeyen layer’ları cache’ler. Cache stratejisi önemlidir:
** Kötü cache kullanımı:**
FROM node:18-alpine
WORKDIR /app
# Tüm dosyaları kopyala
COPY . .
# Dependencies kur
RUN npm install
CMD ["node", "app.js"]
Sorun: Kod değiştiğinde (ki sık değişir) COPY komutu cache’i bozar ve npm install her seferinde yeniden çalışır.
** İyi cache kullanımı:**
FROM node:18-alpine
WORKDIR /app
# Önce sadece package.json'ı kopyala
COPY package*.json ./
# Dependencies kur (cache'lenecek)
RUN npm ci --only=production
# Sonra kodu kopyala
COPY . .
# Non-root user
RUN adduser -D appuser && chown -R appuser:appuser /app
USER appuser
CMD ["node", "app.js"]
Avantaj: Kod değişse bile package.json değişmedikçe npm ci cache’den gelir. Build süresi saniyelerle ölçülür.
Distroless Images
Google’ın distroless image’ları, sadece uygulamanın çalışması için gereken minimum dosyaları içerir. Shell bile yoktur.
Örnek (Go uygulaması):
# Build stage
FROM golang:1.21 AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o myapp
# Runtime stage - distroless
FROM gcr.io/distroless/static-debian11
WORKDIR /app
COPY --from=builder /app/myapp .
USER nonroot:nonroot
ENTRYPOINT ["/app/myapp"]
Image boyutu: ~2 MB (sadece static binary + minimal OS)
Avantajlar:
- Minimal saldırı yüzeyi
- Shell yok = RCE exploit zorlaşır
- Çok küçük boyut
Dezavantaj: Debug zor (shell yok, exec çalışmaz)
Image Boyutu Karşılaştırması
| Base Image | Boyut | Kullanım |
|---|---|---|
| ubuntu:22.04 | 77 MB | Genel amaçlı, kolay debug |
| debian:bookworm-slim | 74 MB | Ubuntu’ya benzer ama biraz daha küçük |
| alpine:latest | 7 MB | Minimal, production için ideal |
| python:3.11-slim | 130 MB | Python için optimize |
| python:3.11-alpine | 50 MB | Python + Alpine (en küçük) |
| gcr.io/distroless/python3 | 55 MB | Distroless Python |
| scratch | 0 MB | Boş (sadece static binary için) |
15.2 Container İçinde State Saklama
Sorun: Kalıcı Veriyi Container İçinde Tutmak
Container’lar ephemeral (geçici) tasarlanmıştır. Silindiğinde içindeki tüm veriler kaybolur.
** Kötü örnek:**
FROM postgres:15
# Database dosyaları container içinde (varsayılan)
# /var/lib/postgresql/data
CMD ["postgres"]
docker run -d --name mydb postgres:15
# Database kullanıldı, veriler yazıldı
docker stop mydb
docker rm mydb
# TÜM VERİLER SİLİNDİ!
Sorunlar:
- Container silininçe veriler kaybolur
- Yedekleme zor
- Migration zorlaşır
- Ölçekleme imkansız (her container’da farklı veri)
** İyi örnek (Volume kullanımı):**
# Named volume oluştur
docker volume create pgdata
# Volume'u mount et
docker run -d \
--name mydb \
-v pgdata:/var/lib/postgresql/data \
postgres:15
# Container silinse bile veriler korunur
docker stop mydb
docker rm mydb
# Aynı volume ile yeni container
docker run -d \
--name mydb2 \
-v pgdata:/var/lib/postgresql/data \
postgres:15
# Veriler hala orada!
Docker Compose ile:
version: "3.8"
services:
db:
image: postgres:15
volumes:
- pgdata:/var/lib/postgresql/data
environment:
POSTGRES_PASSWORD: secret
volumes:
pgdata:
Stateful vs Stateless Uygulamalar
Stateless (tercih edilen):
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
# Session'ı Redis'te sakla (container'da değil)
ENV SESSION_STORE=redis
ENV REDIS_URL=redis://redis:6379
CMD ["node", "app.js"]
Uygulama kodu (Express.js):
const session = require('express-session');
const RedisStore = require('connect-redis')(session);
const redis = require('redis');
const redisClient = redis.createClient({
url: process.env.REDIS_URL
});
app.use(session({
store: new RedisStore({ client: redisClient }),
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false
}));
Avantajlar:
- Container ölse bile session korunur
- Yatay ölçekleme (horizontal scaling) mümkün
- Load balancer arkasında çalışabilir
Konfigürasyon Dosyaları
Konfigürasyon dosyaları da state sayılır ve container’da hard-code edilmemelidir.
** Kötü örnek:**
FROM nginx:alpine
# Config dosyası image'e gömülü
COPY nginx.conf /etc/nginx/nginx.conf
CMD ["nginx", "-g", "daemon off;"]
Sorun: Config değişince yeni image build etmek gerekir.
** İyi örnek (ConfigMap/Volume ile):**
version: "3.8"
services:
web:
image: nginx:alpine
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
ports:
- "80:80"
Kubernetes’te:
apiVersion: v1
kind: ConfigMap
metadata:
name: nginx-config
data:
nginx.conf: |
server {
listen 80;
location / {
proxy_pass http://backend:8080;
}
}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
template:
spec:
containers:
- name: nginx
image: nginx:alpine
volumeMounts:
- name: config
mountPath: /etc/nginx/nginx.conf
subPath: nginx.conf
volumes:
- name: config
configMap:
name: nginx-config
Upload Edilen Dosyalar
Kullanıcıların upload ettiği dosyalar mutlaka volume’da saklanmalıdır.
** Kötü örnek:**
# Flask uygulaması
UPLOAD_FOLDER = '/app/uploads' # Container içinde!
@app.route('/upload', methods=['POST'])
def upload_file():
file = request.files['file']
file.save(os.path.join(UPLOAD_FOLDER, file.filename))
return 'OK'
** İyi örnek (S3 veya Volume):**
import boto3
s3 = boto3.client('s3')
@app.route('/upload', methods=['POST'])
def upload_file():
file = request.files['file']
s3.upload_fileobj(
file,
'my-bucket',
file.filename
)
return 'OK'
Veya volume ile:
services:
web:
image: myapp
volumes:
- uploads:/app/uploads
volumes:
uploads:
15.3 Secrets’ı Image İçinde Saklamak
Sorun: Hassas Bilgileri Image’e Gömmek
Bu en tehlikeli anti-pattern’dir. Image’ler genellikle registry’de saklanır ve birçok kişi erişebilir.
** ÇOK KÖTÜ ÖRNEK (ASLA YAPMAYIN!):**
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
# SECRETS IMAGE'E GÖMÜLMÜş!
ENV DATABASE_PASSWORD=SuperSecret123
ENV API_KEY=sk-abc123xyz456
ENV AWS_SECRET_KEY=AKIAIOSFODNN7EXAMPLE
CMD ["node", "app.js"]
Neden çok kötü:
- Image layer’larında kalır (silseniz bile history’de var)
- Registry’ye push edilince herkes görebilir
docker historyile görülebilirdocker inspectile görülebilir- Git’e commit edilmiş olabilir
Secrets’ı görme:
docker history myapp:latest
docker inspect myapp:latest | grep -i password
Doğru Yöntem 1: Environment Variables (Runtime)
docker run -d \
-e DATABASE_PASSWORD=SuperSecret123 \
-e API_KEY=sk-abc123xyz456 \
myapp:latest
Docker Compose:
services:
web:
image: myapp
environment:
- DATABASE_PASSWORD=${DATABASE_PASSWORD}
- API_KEY=${API_KEY}
.env dosyası (Git’e eklemeyin!):
DATABASE_PASSWORD=SuperSecret123
API_KEY=sk-abc123xyz456
.gitignore:
.env
Avantajlar:
- Image’de saklanmaz
- Ortama göre değişebilir (dev/staging/prod)
- Kolay rotation
Dezavantaj: docker inspect ile hala görülebilir.
Doğru Yöntem 2: Docker Secrets (Swarm)
# Secret oluştur
echo "SuperSecret123" | docker secret create db_password -
# Serviste kullan
docker service create \
--name myapp \
--secret db_password \
myapp:latest
Uygulama kodu:
const fs = require('fs');
// Secret dosyadan oku
const dbPassword = fs.readFileSync(
'/run/secrets/db_password',
'utf8'
).trim();
const dbConfig = {
password: dbPassword,
// ...
};
Docker Compose (Swarm mode):
version: "3.8"
services:
web:
image: myapp
secrets:
- db_password
deploy:
replicas: 3
secrets:
db_password:
external: true
Avantajlar:
- Şifreli saklanır
- Sadece yetkili container’lar erişir
- Memory’de mount edilir (disk’te yazılmaz)
docker inspectile görünmez
Doğru Yöntem 3: HashiCorp Vault
Vault, enterprise-grade secret yönetim sistemidir.
Vault kurulumu:
services:
vault:
image: vault:latest
ports:
- "8200:8200"
environment:
VAULT_DEV_ROOT_TOKEN_ID: myroot
cap_add:
- IPC_LOCK
app:
image: myapp
environment:
VAULT_ADDR: http://vault:8200
VAULT_TOKEN: myroot
Uygulama kodu (Node.js):
const vault = require('node-vault')({
endpoint: process.env.VAULT_ADDR,
token: process.env.VAULT_TOKEN
});
async function getSecrets() {
const result = await vault.read('secret/data/myapp');
return result.data.data;
}
getSecrets().then(secrets => {
const dbPassword = secrets.db_password;
// Database bağlantısı...
});
Vault’a secret yazma:
docker exec -it vault vault kv put secret/myapp \
db_password=SuperSecret123 \
api_key=sk-abc123xyz456
Doğru Yöntem 4: Cloud Provider Secrets (AWS, Azure, GCP)
AWS Secrets Manager örneği:
FROM python:3.11-alpine
RUN pip install boto3
COPY app.py .
CMD ["python", "app.py"]
app.py:
import boto3
import json
def get_secret():
client = boto3.client('secretsmanager', region_name='us-east-1')
response = client.get_secret_value(SecretId='myapp/db-password')
secret = json.loads(response['SecretString'])
return secret['password']
db_password = get_secret()
# Database bağlantısı...
IAM role ile çalıştırma:
docker run -d \
-e AWS_REGION=us-east-1 \
-v ~/.aws:/root/.aws:ro \
myapp:latest
BuildKit Secrets (Build-time Secrets)
Bazen build sırasında secret gerekir (private npm registry, git clone vb.).
** Kötü örnek:**
FROM node:18-alpine
WORKDIR /app
# NPM token image'de kalır
ENV NPM_TOKEN=npm_abc123xyz
RUN echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > .npmrc
COPY package*.json ./
RUN npm install
# Token silsen bile layer history'de var!
RUN rm .npmrc
COPY . .
CMD ["node", "app.js"]
** İyi örnek (BuildKit secrets):**
# syntax=docker/dockerfile:1.4
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
# Secret mount (image'e gömülmez!)
RUN --mount=type=secret,id=npmrc,target=/root/.npmrc \
npm install
COPY . .
CMD ["node", "app.js"]
Build komutu:
DOCKER_BUILDKIT=1 docker build \
--secret id=npmrc,src=$HOME/.npmrc \
-t myapp:latest .
Avantajlar:
- Secret sadece build sırasında kullanılır
- Final image’de saklanmaz
- Layer history’de görünmez
.dockerignore ile Hassas Dosyaları Koruma
.dockerignore dosyası, build context’e dahil edilmemesi gereken dosyaları belirtir.
.dockerignore:
# Secrets
.env
.env.*
*.key
*.pem
credentials.json
# Git
.git
.gitignore
# IDE
.vscode
.idea
# Logs
*.log
logs/
# Dependencies
node_modules
__pycache__
Secrets Rotation
Secrets düzenli olarak değiştirilmelidir (rotation).
Manual rotation:
# Yeni secret oluştur
echo "NewPassword456" | docker secret create db_password_v2 -
# Servisi güncelle
docker service update \
--secret-rm db_password \
--secret-add db_password_v2 \
myapp
# Eski secret'i sil
docker secret rm db_password
Automated rotation (Vault):
Vault otomatik secret rotation yapabilir:
vault write database/rotate-role/myapp-role
Özet ve En İyi Pratikler
Image Boyutu:
- Alpine veya distroless base image kullanın
- Multi-stage build ile build tools’u ayırın
- Layer’ları minimize edin (RUN komutlarını birleştirin)
- Cache stratejisi uygulayın (requirements önce, kod sonra)
--no-cache-dir,--no-install-recommendskullanın
State Yönetimi:
- Container’da kalıcı veri saklamayın
- Volume kullanın (named volumes)
- Stateless uygulamalar tasarlayın
- Session’ı external store’da tutun (Redis, DB)
- Config’leri volume veya ConfigMap ile yönetin
Secrets Yönetimi:
- ASLA secrets’ı image’e gömmeyin
- Runtime environment variables kullanın
- Docker Secrets (Swarm) veya Kubernetes Secrets
- Vault gibi enterprise çözümler
- BuildKit secrets (build-time için)
.dockerignoreile hassas dosyaları koruyun- Düzenli secret rotation yapın
Kontrol listesi:
# Image boyutunu kontrol et
docker images myapp
# Layer history'sini incele
docker history myapp:latest
# Secrets var mı kontrol et
docker history myapp:latest | grep -i password
# Image'i tara
trivy image myapp:latest
Bu anti-pattern’lerden kaçınarak güvenli, performanslı ve bakımı kolay Docker image’ları oluşturabilirsiniz. Production ortamlarında bu pratiklere sıkı sıkıya bağlı kalmak kritiktir.
16. Registry & Dağıtım Stratejileri
Docker image’larını saklamak ve dağıtmak için registry’ler kullanılır. Registry seçimi ve yönetimi, deployment stratejiniz için kritiktir. Bu bölümde Docker Hub, private registry kurulumu, erişim yönetimi ve rate limit sorunlarına çözümler sunacağız.
16.1 Docker Hub vs Private Registry
Docker Hub
Docker Hub, Docker’ın resmi public registry’sidir. Milyonlarca hazır image barındırır.
Avantajlar:
- Hazır official image’lar (nginx, postgres, redis vb.)
- Ücretsiz public repository’ler
- Otomatik build (GitHub/Bitbucket entegrasyonu)
- Webhook desteği
- Topluluk desteği
Dezavantajlar:
- Pull rate limitleri (ücretsiz hesapta 100 pull/6 saat)
- Private repository limiti (ücretsiz hesapta 1 adet)
- Network latency (internet bağlantısı gerekli)
- Compliance sorunları (bazı şirketler public cloud kullanamaz)
Docker Hub kullanımı:
# Login
docker login
# Tag image
docker tag myapp:latest username/myapp:latest
# Push
docker push username/myapp:latest
# Pull
docker pull username/myapp:latest
Private Registry (registry:2)
Private registry, kendi sunucunuzda çalışan Docker registry’dir. Tam kontrole sahip olursunuz.
Basit private registry kurulumu:
docker run -d \
-p 5000:5000 \
--name registry \
--restart=always \
-v registry-data:/var/lib/registry \
registry:2
Bu komut local’de port 5000’de bir registry başlatır.
Image push etme:
# Image'i local registry için tag'le
docker tag myapp:latest localhost:5000/myapp:latest
# Push et
docker push localhost:5000/myapp:latest
# Pull et
docker pull localhost:5000/myapp:latest
Production-Ready Private Registry Kurulumu
Production ortamında güvenlik ve dayanıklılık önemlidir.
docker-compose.yml:
version: "3.8"
services:
registry:
image: registry:2
ports:
- "5000:5000"
environment:
REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY: /data
REGISTRY_AUTH: htpasswd
REGISTRY_AUTH_HTPASSWD_REALM: Registry Realm
REGISTRY_AUTH_HTPASSWD_PATH: /auth/htpasswd
REGISTRY_HTTP_TLS_CERTIFICATE: /certs/domain.crt
REGISTRY_HTTP_TLS_KEY: /certs/domain.key
volumes:
- registry-data:/data
- ./auth:/auth
- ./certs:/certs
restart: always
volumes:
registry-data:
Kullanıcı oluşturma (htpasswd):
# htpasswd kurulumu (Ubuntu/Debian)
sudo apt-get install apache2-utils
# Auth dizini oluştur
mkdir auth
# Kullanıcı ekle
htpasswd -Bc auth/htpasswd admin
# Şifre soracak
# Başka kullanıcı ekle (append mode)
htpasswd -B auth/htpasswd developer
SSL sertifikası oluşturma (self-signed test için):
mkdir certs
openssl req -newkey rsa:4096 -nodes -sha256 \
-keyout certs/domain.key \
-x509 -days 365 \
-out certs/domain.crt \
-subj "/CN=registry.local"
Registry’yi başlatma:
docker-compose up -d
Registry’ye login:
docker login registry.local:5000
# Username: admin
# Password: (oluşturduğunuz şifre)
Registry Configuration (config.yml)
Daha gelişmiş konfigürasyon için config.yml kullanabilirsiniz.
config.yml:
version: 0.1
log:
level: info
fields:
service: registry
storage:
filesystem:
rootdirectory: /var/lib/registry
delete:
enabled: true
http:
addr: :5000
headers:
X-Content-Type-Options: [nosniff]
tls:
certificate: /certs/domain.crt
key: /certs/domain.key
auth:
htpasswd:
realm: basic-realm
path: /auth/htpasswd
health:
storagedriver:
enabled: true
interval: 10s
threshold: 3
docker-compose.yml’ye ekleme:
services:
registry:
image: registry:2
volumes:
- ./config.yml:/etc/docker/registry/config.yml
# ...
S3 Backend ile Registry
Disk yerine S3 (veya uyumlu storage) kullanabilirsiniz.
config.yml (S3):
version: 0.1
storage:
s3:
accesskey: AKIAIOSFODNN7EXAMPLE
secretkey: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
region: us-east-1
bucket: my-docker-registry
encrypt: true
secure: true
# ... diğer ayarlar
Avantajlar:
- Sınırsız depolama
- Otomatik yedekleme
- Multi-AZ dayanıklılık
- Pay-as-you-go
Registry UI (Web Arayüzü)
Registry varsayılan olarak web arayüzü sunmaz. Eklemek için:
docker-compose.yml:
services:
registry:
# ... registry config
registry-ui:
image: joxit/docker-registry-ui:latest
ports:
- "8080:80"
environment:
REGISTRY_TITLE: My Private Registry
REGISTRY_URL: http://registry:5000
DELETE_IMAGES: true
SHOW_CONTENT_DIGEST: true
depends_on:
- registry
Web arayüzüne erişim: http://localhost:8080
Özellikler:
- Image’ları listele ve ara
- Tag’leri görüntüle
- Image detaylarını incele
- Image silme (eğer registry’de delete enabled ise)
Alternative: Harbor
Harbor, CNCF projesidir ve enterprise özellikler sunar.
Harbor özellikleri:
- Web UI
- RBAC (Role-Based Access Control)
- Image scanning (Trivy, Clair entegrasyonu)
- Image replication (multi-datacenter)
- Webhook’lar
- Helm chart repository
- OCI artifact desteği
Harbor kurulumu:
# Harbor installer indir
wget https://github.com/goharbor/harbor/releases/download/v2.10.0/harbor-online-installer-v2.10.0.tgz
tar xzvf harbor-online-installer-v2.10.0.tgz
cd harbor
# harbor.yml düzenle
cp harbor.yml.tmpl harbor.yml
vim harbor.yml
# Kurulum
sudo ./install.sh
harbor.yml örneği:
hostname: harbor.local
http:
port: 80
https:
port: 443
certificate: /data/cert/server.crt
private_key: /data/cert/server.key
harbor_admin_password: Harbor12345
database:
password: root123
data_volume: /data
log:
level: info
16.2 docker login, docker push ve Erişim Yönetimi
docker login
docker login komutu, registry’ye kimlik doğrulama yapar.
Public Docker Hub:
docker login
# Username: yourusername
# Password: ********
Private registry:
docker login registry.local:5000
# Username: admin
# Password: ********
Non-interactive login (CI/CD için):
echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin
GitHub Container Registry:
echo "$GITHUB_TOKEN" | docker login ghcr.io -u "$GITHUB_USERNAME" --password-stdin
AWS ECR:
aws ecr get-login-password --region us-east-1 | \
docker login --username AWS --password-stdin 123456789.dkr.ecr.us-east-1.amazonaws.com
Credential Storage
Docker credentials varsayılan olarak ~/.docker/config.json dosyasında saklanır.
config.json örneği:
{
"auths": {
"https://index.docker.io/v1/": {
"auth": "dXNlcm5hbWU6cGFzc3dvcmQ="
},
"registry.local:5000": {
"auth": "YWRtaW46c2VjcmV0"
}
}
}
Sorun: auth field’ı base64 encoded username:password içerir (güvenli değil).
Credential Helpers
Daha güvenli credential yönetimi için helper’lar kullanılır.
Docker Credential Helper (Linux):
# pass (password store) kurulumu
sudo apt-get install pass gnupg2
# GPG key oluştur
gpg --gen-key
# pass initialize
pass init your-gpg-key-id
# Docker credential helper kur
wget https://github.com/docker/docker-credential-helpers/releases/download/v0.8.0/docker-credential-pass-v0.8.0.linux-amd64
chmod +x docker-credential-pass-v0.8.0.linux-amd64
sudo mv docker-credential-pass-v0.8.0.linux-amd64 /usr/local/bin/docker-credential-pass
# config.json'da aktif et
vim ~/.docker/config.json
config.json:
{
"credsStore": "pass"
}
Artık docker login yaptığınızda credential’lar pass ile şifrelenir.
macOS (keychain):
macOS’ta Docker Desktop otomatik olarak keychain kullanır.
config.json:
{
"credsStore": "osxkeychain"
}
Windows (wincred):
{
"credsStore": "wincred"
}
docker push ve pull
Image push:
# Image'i tag'le
docker tag myapp:latest registry.local:5000/myapp:1.0.0
# Push et
docker push registry.local:5000/myapp:1.0.0
Multiple tag’le push:
docker tag myapp:latest registry.local:5000/myapp:1.0.0
docker tag myapp:latest registry.local:5000/myapp:1.0
docker tag myapp:latest registry.local:5000/myapp:latest
docker push registry.local:5000/myapp:1.0.0
docker push registry.local:5000/myapp:1.0
docker push registry.local:5000/myapp:latest
Tüm tag’leri push etme:
docker push --all-tags registry.local:5000/myapp
Access Control (Harbor RBAC Örneği)
Harbor’da projeler ve kullanıcılar üzerinden erişim kontrolü yapılır.
Harbor project yapısı:
library/
├── nginx:latest
├── postgres:15
└── redis:alpine
myapp/
├── frontend:1.0.0
├── backend:1.0.0
└── worker:1.0.0
Role’ler:
- Project Admin: Tüm yetkiler
- Master: Push, pull, image silme
- Developer: Push, pull
- Guest: Sadece pull
Kullanıcı ekleme (Harbor UI):
- Administration > Users > New User
- Username, Email, Password
- Projects > myapp > Members > Add
- Kullanıcı seç, role ata
Robot accounts (CI/CD için):
Harbor, programmatic access için robot account’lar sunar.
- Projects > myapp > Robot Accounts > New Robot Account
- Name: cicd-bot
- Expiration: 30 days
- Permissions: Push, Pull
- Token’ı kaydet (bir kere gösterilir)
CI/CD’de kullanım:
# GitHub Actions
- name: Login to Harbor
uses: docker/login-action@v3
with:
registry: harbor.local
username: robot$cicd-bot
password: ${{ secrets.HARBOR_ROBOT_TOKEN }}
Registry API Kullanımı
Docker Registry HTTP API v2 sunar.
Image listesi:
curl -u admin:password https://registry.local:5000/v2/_catalog
Yanıt:
{
"repositories": [
"myapp",
"nginx",
"postgres"
]
}
Image tag’leri:
curl -u admin:password https://registry.local:5000/v2/myapp/tags/list
Image silme:
# Önce digest al
DIGEST=$(curl -I -u admin:password \
-H "Accept: application/vnd.docker.distribution.manifest.v2+json" \
https://registry.local:5000/v2/myapp/manifests/1.0.0 \
| grep Docker-Content-Digest | awk '{print $2}' | tr -d '\r')
# Sil
curl -X DELETE -u admin:password \
https://registry.local:5000/v2/myapp/manifests/$DIGEST
Not: Silme işlemi sadece metadata’yı siler. Disk alanını geri kazanmak için garbage collection gerekir:
docker exec registry bin/registry garbage-collect /etc/docker/registry/config.yml
16.3 Pull Rate Limitler & Mirror Stratejileri
Docker Hub Rate Limits
Docker Hub, pull sayısını sınırlar:
| Hesap Tipi | Limit |
|---|---|
| Anonymous | 100 pull / 6 saat (IP bazlı) |
| Free | 200 pull / 6 saat (kullanıcı bazlı) |
| Pro | 5000 pull / gün |
| Team | Sınırsız |
Rate limit kontrolü:
TOKEN=$(curl "https://auth.docker.io/token?service=registry.docker.io&scope=repository:ratelimitpreview/test:pull" | jq -r .token)
curl --head -H "Authorization: Bearer $TOKEN" https://registry-1.docker.io/v2/ratelimitpreview/test/manifests/latest
Response header’ları:
ratelimit-limit: 100
ratelimit-remaining: 95
Sorun: CI/CD’de Rate Limit
Her build’de base image’ler pull edilir. Çok sayıda build rate limiti aşabilir.
** Sorunlu durum:**
# GitHub Actions - Her build'de pull
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: docker build -t myapp .
Dockerfile:
FROM node:18-alpine # Her build'de Docker Hub'dan pull edilir
# ...
100 build/6 saat → Rate limit aşıldı!
Çözüm 1: Docker Login
Authenticated pull daha yüksek limit sunar.
GitHub Actions:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# Docker Hub'a login
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build
run: docker build -t myapp .
Limit: 100 → 200 pull/6 saat
Çözüm 2: Registry Mirror (Pull-Through Cache)
Registry mirror, Docker Hub’dan çekilen image’leri cache’ler.
Mirror registry kurulumu:
config.yml:
version: 0.1
storage:
filesystem:
rootdirectory: /var/lib/registry
http:
addr: :5000
proxy:
remoteurl: https://registry-1.docker.io
username: yourusername # Docker Hub credentials
password: yourpassword
docker-compose.yml:
services:
registry-mirror:
image: registry:2
ports:
- "5000:5000"
volumes:
- ./config.yml:/etc/docker/registry/config.yml
- mirror-data:/var/lib/registry
restart: always
volumes:
mirror-data:
Docker daemon’da mirror kullanımı:
/etc/docker/daemon.json:
{
"registry-mirrors": ["http://localhost:5000"]
}
Docker’ı yeniden başlat:
sudo systemctl restart docker
Test:
docker pull nginx:alpine
İlk pull Docker Hub’dan gelir ve mirror’a cache’lenir. Sonraki pull’lar mirror’dan gelir.
Çözüm 3: GitHub Container Registry (GHCR)
Docker Hub yerine GitHub Container Registry kullanabilirsiniz.
장점:
- Rate limit yok (GitHub Actions’da)
- GitHub ile entegre
- Public ve private repository’ler ücretsiz
Base image’leri GHCR’a push:
# Docker Hub'dan pull
docker pull node:18-alpine
# GHCR için tag
docker tag node:18-alpine ghcr.io/yourorg/node:18-alpine
# GHCR'a push
docker push ghcr.io/yourorg/node:18-alpine
Dockerfile:
FROM ghcr.io/yourorg/node:18-alpine
# ...
Çözüm 4: Layer Cache (GitHub Actions)
GitHub Actions, layer cache ile build süresini ve pull sayısını azaltır.
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build
uses: docker/build-push-action@v5
with:
context: .
cache-from: type=gha
cache-to: type=gha,mode=max
Base image layer’ları cache’lenir, her build’de pull edilmez.
Çözüm 5: Self-Hosted Runner
GitHub Actions self-hosted runner kullanırsanız kendi registry mirror’ınızı kullanabilirsiniz.
Self-hosted runner’da Docker:
{
"registry-mirrors": ["http://internal-mirror:5000"]
}
Multi-Registry Strategy
Production ortamlarında çoklu registry stratejisi kullanılır.
Senaryo:
- Internal registry: Private image’lar
- Mirror registry: Public image cache
- Docker Hub: Fallback
docker-compose.yml örneği:
services:
frontend:
image: internal-registry:5000/myapp/frontend:1.0.0
# Private image
nginx:
image: mirror-registry:5000/nginx:alpine
# Cached public image
postgres:
image: postgres:15
# Fallback to Docker Hub
Registry Replication (Harbor)
Harbor, registry replication yaparak multi-datacenter senaryolarında yardımcı olur.
Replication policy oluşturma:
- Harbor UI > Replication
- New Replication Rule
- Source registry: harbor-us-east
- Destination registry: harbor-eu-west
- Trigger: Event based (her push’ta)
- Filter: Tüm repository’ler veya belirli pattern
Avantajlar:
- Düşük latency (her region kendi cache’i)
- Disaster recovery
- Compliance (data residency)
Monitoring ve Alerting
Registry health’ini izleyin.
Registry metrics (Prometheus):
Registry varsayılan olarak /metrics endpoint sunar.
prometheus.yml:
scrape_configs:
- job_name: 'registry'
static_configs:
- targets: ['registry:5000']
Önemli metrikler:
registry_http_requests_total: Toplam HTTP request sayısıregistry_storage_action_seconds: Storage operasyon sürelerigo_goroutines: Goroutine sayısı (memory leak kontrolü)
Alert örneği:
groups:
- name: registry_alerts
rules:
- alert: RegistryDown
expr: up{job="registry"} == 0
for: 5m
annotations:
summary: "Registry is down"
- alert: HighPullLatency
expr: registry_storage_action_seconds{action="Get"} > 5
for: 10m
annotations:
summary: "Registry pull latency is high"
Özet ve Best Practices
Registry Seçimi:
- Küçük projeler: Docker Hub (free tier)
- Orta projeler: Private registry (registry:2)
- Büyük projeler: Harbor (RBAC, scanning, replication)
- Enterprise: Cloud-managed (ECR, ACR, GCR)
Güvenlik:
- HTTPS kullanın (TLS sertifikası)
- Authentication aktif edin (htpasswd, LDAP)
- RBAC uygulayın (Harbor)
- Image scanning yapın (Trivy, Clair)
- Credential helper kullanın (keychain, pass)
Performance:
- Registry mirror kurun (pull-through cache)
- Layer cache kullanın (CI/CD)
- S3 backend kullanın (scalability)
- Multi-region replication (global apps)
Rate Limit Çözümleri:
- Docker Hub login (200 pull/6 saat)
- Registry mirror (sınırsız local pull)
- GHCR kullanımı (GitHub Actions için)
- Self-hosted runner (kendi mirror’ınız)
Operasyon:
- Düzenli garbage collection
- Monitoring ve alerting
- Backup stratejisi
- Access log tutma
- Disk kullanımını izleme
Registry stratejiniz doğru belirlendiğinde image dağıtımı hızlı, güvenli ve ölçeklenebilir olur. Production ortamlarında registry altyapısı kritik öneme sahiptir ve ihmal edilmemelidir.
17. İmaj Doğrulama ve Güven Zinciri
Docker image’larının güvenliği sadece güvenlik taraması ile sınırlı değildir. Image’in gerçekten beklenen kaynaktan geldiğini ve değiştirilmediğini doğrulamak da kritiktir. Bu bölümde image signing (imza) ve verification (doğrulama) mekanizmalarını inceleyeceğiz.
17.1 Docker Content Trust / Notary
Docker Content Trust (DCT), image’lerin bütünlüğünü ve kaynağını doğrulamak için kriptografik imza kullanan bir sistemdir. Arka planda The Update Framework (TUF) ve Notary projelerini kullanır.
Docker Content Trust Nedir?
DCT, image’lerin güvenilir bir kaynaktan geldiğini ve transit sırasında değiştirilmediğini garanti eder. Man-in-the-middle saldırılarına karşı koruma sağlar.
Temel kavramlar:
- Publisher: Image’i oluşturan ve imzalayan kişi/sistem
- Root key: En üst seviye anahtar, offline saklanmalı
- Targets key: Image tag’lerini imzalar
- Snapshot key: Metadata tutarlılığını sağlar
- Timestamp key: Replay attack’lere karşı korur
DCT Aktivasyonu
DCT varsayılan olarak kapalıdır. Aktif etmek için:
export DOCKER_CONTENT_TRUST=1
Bu değişken aktif olduğunda Docker sadece imzalanmış image’leri pull eder.
Test:
# DCT aktif
export DOCKER_CONTENT_TRUST=1
# İmzalı image pull (çalışır)
docker pull alpine:latest
# İmzasız image pull (hata verir)
docker pull unsigned-image:latest
# Error: remote trust data does not exist
Image İmzalama (Signing)
Image push ettiğinizde DCT aktifse otomatik olarak imzalanır.
İlk push (key generation):
export DOCKER_CONTENT_TRUST=1
docker tag myapp:latest username/myapp:1.0.0
docker push username/myapp:1.0.0
İlk push’ta Docker sizden passphrase isteyecek:
Enter root key passphrase:
Repeat passphrase:
Enter targets key passphrase:
Repeat passphrase:
Root key: ~/.docker/trust/private/root_keys/ dizininde saklanır
Targets key: ~/.docker/trust/private/tuf_keys/ dizininde saklanır
Önemli: Root key’i güvenli bir yerde yedekleyin! Kaybederseniz image’leri güncelleyemezsiniz.
İmza Doğrulama (Verification)
DCT aktifken pull işlemi otomatik olarak imzayı doğrular.
export DOCKER_CONTENT_TRUST=1
docker pull username/myapp:1.0.0
Çıktı:
Pull (1 of 1): username/myapp:1.0.0@sha256:abc123...
sha256:abc123... Pulling from username/myapp
Digest: sha256:abc123...
Status: Downloaded newer image for username/myapp@sha256:abc123...
Tagging username/myapp@sha256:abc123... as username/myapp:1.0.0
Digest’in belirtilmesi, doğrulamanın başarılı olduğunu gösterir.
Notary Server
Notary, image metadata ve imzaları saklayan sunucudur. Docker Hub kendi Notary server’ına sahiptir.
Private Notary server kurulumu:
version: "3.8"
services:
notary-server:
image: notary:server-0.7.0
ports:
- "4443:4443"
volumes:
- ./notary-server-config.json:/etc/notary/server-config.json
- notary-data:/var/lib/notary
environment:
NOTARY_SERVER_DB_URL: mysql://server@mysql:3306/notaryserver
notary-signer:
image: notary:signer-0.7.0
ports:
- "7899:7899"
volumes:
- ./notary-signer-config.json:/etc/notary/signer-config.json
- notary-signer-data:/var/lib/notary
mysql:
image: mysql:8
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: notaryserver
volumes:
notary-data:
notary-signer-data:
CI/CD’de DCT Kullanımı
CI/CD pipeline’ında DCT kullanmak için key’leri güvenli şekilde yönetmek gerekir.
GitHub Actions örneği:
name: Build and Sign
on:
push:
tags:
- 'v*'
jobs:
build-and-sign:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
# DCT root key'i restore et
- name: Setup DCT keys
env:
DCT_ROOT_KEY: ${{ secrets.DCT_ROOT_KEY }}
DCT_ROOT_KEY_PASSPHRASE: ${{ secrets.DCT_ROOT_KEY_PASSPHRASE }}
run: |
mkdir -p ~/.docker/trust/private
echo "$DCT_ROOT_KEY" | base64 -d > ~/.docker/trust/private/root_key.key
chmod 600 ~/.docker/trust/private/root_key.key
- name: Build image
run: docker build -t username/myapp:${{ github.ref_name }} .
# DCT aktif et ve push
- name: Sign and push
env:
DOCKER_CONTENT_TRUST: 1
DOCKER_CONTENT_TRUST_ROOT_PASSPHRASE: ${{ secrets.DCT_ROOT_KEY_PASSPHRASE }}
DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE: ${{ secrets.DCT_TARGETS_KEY_PASSPHRASE }}
run: |
docker push username/myapp:${{ github.ref_name }}
Secret’lar:
DCT_ROOT_KEY: Root key dosyasının base64 encoded haliDCT_ROOT_KEY_PASSPHRASE: Root key şifresiDCT_TARGETS_KEY_PASSPHRASE: Targets key şifresi
DCT Sınırlamaları
Docker Content Trust bazı kısıtlamalara sahiptir:
Dezavantajlar:
- Sadece Docker Hub ve Docker Trusted Registry ile çalışır (diğer registry’ler için Notary kurulumu gerekli)
- Key yönetimi karmaşık
- Multi-arch image desteği sınırlı
- OCI standartlarına tam uyumlu değil
Bu nedenlerle modern alternatifler geliştirilmiştir.
17.2 Modern Alternatifler: cosign ve OCI Image Signing
Cosign Nedir?
Cosign, Sigstore projesi tarafından geliştirilen modern bir image signing aracıdır. OCI standartlarına tam uyumludur ve keyless signing gibi gelişmiş özellikler sunar.
Avantajlar:
- OCI-native (tüm OCI uyumlu registry’lerde çalışır)
- Keyless signing (OpenID Connect ile)
- Kubernetes policy enforcement entegrasyonu
- Attestation desteği (SLSA provenance)
- Kolay kullanım
Cosign Kurulumu
Linux:
wget https://github.com/sigstore/cosign/releases/download/v2.2.0/cosign-linux-amd64
chmod +x cosign-linux-amd64
sudo mv cosign-linux-amd64 /usr/local/bin/cosign
macOS:
brew install cosign
Windows:
choco install cosign
Key-Based Signing
Geleneksel public/private key çifti ile imzalama.
Key çifti oluşturma:
cosign generate-key-pair
Bu komut iki dosya oluşturur:
cosign.key: Private key (güvenli saklanmalı)cosign.pub: Public key (herkes görebilir)
Image signing:
# Image'i sign et
cosign sign --key cosign.key username/myapp:1.0.0
# Passphrase girilecek
Image verification:
# İmzayı doğrula
cosign verify --key cosign.pub username/myapp:1.0.0
Çıktı (başarılı doğrulama):
[
{
"critical": {
"identity": {
"docker-reference": "index.docker.io/username/myapp"
},
"image": {
"docker-manifest-digest": "sha256:abc123..."
},
"type": "cosign container image signature"
},
"optional": {
"Bundle": {...}
}
}
]
Keyless Signing (OIDC)
Keyless signing, private key yönetimi olmadan imzalama yapmanızı sağlar. OpenID Connect (OIDC) kullanarak kimlik doğrulaması yapar.
Keyless signing:
cosign sign username/myapp:1.0.0
Bu komut tarayıcı açar ve OIDC provider (GitHub, Google, Microsoft) ile login yapmanızı ister.
Keyless verification:
cosign verify \
--certificate-identity=your-email@example.com \
--certificate-oidc-issuer=https://github.com/login/oauth \
username/myapp:1.0.0
Avantajlar:
- Private key yönetimi yok
- Key rotation gerekmez
- Revocation otomatik (certificate expiration)
- Audit trail (kim ne zaman imzaladı)
GitHub Actions ile Cosign
Workflow örneği:
name: Build and Sign with Cosign
on:
push:
tags:
- 'v*'
permissions:
contents: read
packages: write
id-token: write # OIDC için gerekli
jobs:
build-and-sign:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Install Cosign
uses: sigstore/cosign-installer@v3
- name: Build image
run: docker build -t ghcr.io/${{ github.repository }}:${{ github.ref_name }} .
- name: Push image
run: docker push ghcr.io/${{ github.repository }}:${{ github.ref_name }}
# Keyless signing (OIDC)
- name: Sign image
run: |
cosign sign --yes ghcr.io/${{ github.repository }}:${{ github.ref_name }}
Verification (başka bir workflow’da):
- name: Verify image signature
run: |
cosign verify \
--certificate-identity=https://github.com/${{ github.repository }}/.github/workflows/build.yml@refs/tags/${{ github.ref_name }} \
--certificate-oidc-issuer=https://token.actions.githubusercontent.com \
ghcr.io/${{ github.repository }}:${{ github.ref_name }}
Attestations (SLSA Provenance)
Attestation, image’in nasıl build edildiğine dair metadata içerir. SLSA (Supply-chain Levels for Software Artifacts) standardına uygun provenance bilgisi ekleyebilirsiniz.
Attestation oluşturma:
cosign attest --yes \
--predicate predicate.json \
--type slsaprovenance \
username/myapp:1.0.0
predicate.json örneği:
{
"buildType": "https://github.com/myorg/myrepo/.github/workflows/build.yml@main",
"builder": {
"id": "https://github.com/actions/runner"
},
"invocation": {
"configSource": {
"uri": "git+https://github.com/myorg/myrepo@refs/tags/v1.0.0",
"digest": {
"sha1": "abc123..."
}
}
},
"materials": [
{
"uri": "pkg:docker/node@18-alpine",
"digest": {
"sha256": "def456..."
}
}
]
}
Attestation verification:
cosign verify-attestation \
--key cosign.pub \
--type slsaprovenance \
username/myapp:1.0.0
Policy Enforcement (Kubernetes)
Kubernetes’te sadece imzalanmış image’lerin çalışmasını sağlamak için admission controller kullanılır.
Sigstore Policy Controller kurulumu:
kubectl apply -f https://github.com/sigstore/policy-controller/releases/latest/download/policy-controller.yaml
ClusterImagePolicy oluşturma:
apiVersion: policy.sigstore.dev/v1beta1
kind: ClusterImagePolicy
metadata:
name: require-signatures
spec:
images:
- glob: "ghcr.io/myorg/**"
authorities:
- keyless:
url: https://fulcio.sigstore.dev
identities:
- issuer: https://token.actions.githubusercontent.com
subject: https://github.com/myorg/myrepo/.github/workflows/*
Bu policy, ghcr.io/myorg/ namespace’indeki tüm image’lerin GitHub Actions tarafından imzalanmış olmasını zorunlu kılar.
Test:
# İmzalı image (çalışır)
kubectl run test --image=ghcr.io/myorg/myapp:1.0.0
# İmzasız image (reddedilir)
kubectl run test --image=ghcr.io/myorg/unsigned:latest
# Error: admission webhook denied the request
OCI Artifact ve Signature Storage
Cosign, signature’ları OCI artifact olarak saklar. Image ile aynı registry’de, özel bir tag pattern kullanarak.
Signature artifact:
username/myapp:1.0.0 # Orijinal image
username/myapp:sha256-abc123.sig # Signature artifact
Signature’ı gösterme:
cosign tree username/myapp:1.0.0
Çıktı:
📦 username/myapp:1.0.0
├── 🔐 Signature: sha256:def456...
└── 📄 Attestation: sha256:ghi789...
Multi-Signature Support
Bir image birden fazla kişi tarafından imzalanabilir (multi-party signing).
İlk imza:
cosign sign --key alice.key username/myapp:1.0.0
İkinci imza:
cosign sign --key bob.key username/myapp:1.0.0
Verification (her iki imza da kontrol edilir):
cosign verify --key alice.pub username/myapp:1.0.0
cosign verify --key bob.pub username/myapp:1.0.0
Harbor ile Cosign Entegrasyonu
Harbor 2.5+ cosign signature’larını native destekler.
Harbor UI’de görünüm:
- Artifacts > Image > Accessories
- Signature ve attestation artifact’ları listelenir
Harbor webhook ile otomatik scan:
Harbor, imzalı image push edildiğinde otomatik Trivy scan tetikleyebilir.
Karşılaştırma Tablosu
| Özellik | Docker Content Trust | Cosign |
|---|---|---|
| Standard | TUF (The Update Framework) | Sigstore + OCI |
| Registry desteği | Docker Hub, DTR | Tüm OCI registry’ler |
| Key yönetimi | Root + Targets keys | Key-based veya keyless (OIDC) |
| Kullanım kolaylığı | Orta | Kolay |
| CI/CD entegrasyonu | Karmaşık | Basit |
| Kubernetes policy | Yok | Sigstore Policy Controller |
| Attestation | Yok | SLSA provenance desteği |
| Multi-arch | Sınırlı | Tam destek |
| Topluluk | Azalan | Büyüyen (CNCF projesi) |
Özet ve Best Practices
Image Signing Neden Önemli:
- Supply chain attack’lere karşı koruma
- Image bütünlüğü garantisi
- Kaynak doğrulama (kim imzaladı?)
- Compliance gereksinimleri (SOC2, HIPAA)
Hangi Yöntem:
Küçük projeler:
- Cosign keyless signing
- GitHub Actions OIDC entegrasyonu
- Basit verification
Orta projeler:
- Cosign key-based signing
- Private key management (Vault, KMS)
- CI/CD automation
Büyük/Enterprise:
- Cosign + Sigstore Policy Controller
- SLSA attestations
- Multi-party signing
- Kubernetes admission control
- Audit logging
CI/CD Pipeline:
1. Build image
2. Security scan (Trivy)
3. Sign with cosign (keyless/OIDC)
4. Add attestation (SLSA provenance)
5. Push to registry
6. Verify signature (deployment stage)
7. Deploy (sadece imzalı image'ler)
Key Management:
- Key-based: HashiCorp Vault, AWS KMS, Azure Key Vault
- Keyless: GitHub OIDC, Google, Microsoft
- Multi-party signing: Çoklu onay gerektiren kritik image’ler için
Policy Enforcement:
Kubernetes’te ClusterImagePolicy ile sadece imzalanmış image’lerin çalışmasını sağlayın. Bu, compromised registry veya man-in-the-middle saldırılarına karşı son savunma hattıdır.
Image signing ve verification, modern software supply chain security’nin kritik bir parçasıdır. Cosign gibi araçlar bu süreci basitleştirerek wide adoption sağlamaktadır. Production ortamlarında mutlaka image signing uygulamalısınız.
18. Alternatifler, Ekosistem ve İleri Konular
Docker, konteyner teknolojisinin en popüler aracı olsa da tek seçenek değildir. Bu bölümde Docker’a alternatif araçları, BuildKit’in detaylı özelliklerini ve uzak host yönetimini inceleyeceğiz.
18.1 Podman (rootless), containerd, CRI-O (kısa farklar)
Podman
Podman, Red Hat tarafından geliştirilen daemonless (daemon gerektirmeyen) bir konteyner motorudur. Docker’a alternatif olarak tasarlanmıştır.
Temel özellikler:
- Daemonless: Arka planda sürekli çalışan daemon yok
- Rootless: Root yetkisi olmadan konteyner çalıştırma
- Docker uyumlu: Docker CLI komutlarının çoğu çalışır
- Pod desteği: Kubernetes benzeri pod konsepti
- systemd entegrasyonu: Konteynerler systemd servisi olarak çalışabilir
Kurulum (Fedora/RHEL):
sudo dnf install podman
Temel kullanım:
# Docker yerine podman kullan
podman run -d --name web -p 8080:80 nginx
# Konteyner listesi
podman ps
# Image build
podman build -t myapp .
# Image push
podman push myapp:latest docker.io/username/myapp:latest
Docker ile farklar:
# Docker alias oluştur (uyumluluk için)
alias docker=podman
# Artık docker komutları çalışır
docker run nginx
docker ps
Rootless Podman:
Podman’ın en güçlü özelliği root olmadan çalışmasıdır.
# Normal kullanıcı olarak
podman run -d --name web nginx
# Konteyner içinde root gibi görünür ama host'ta normal user
podman exec web whoami
# Çıktı: root (konteyner içinde)
# Host'ta kontrol
ps aux | grep nginx
# Çıktı: youruser 12345 0.0 0.1 ... nginx
User namespace mapping:
Rootless Podman, user namespace kullanarak konteyner içindeki UID’leri host’taki farklı UID’lere map eder.
# Mapping bilgisi
podman unshare cat /proc/self/uid_map
# Çıktı:
# 0 1000 1
# 1 100000 65536
# Konteyner içinde UID 0 (root) → Host'ta UID 1000 (sizin kullanıcınız)
# Konteyner içinde UID 1-65536 → Host'ta UID 100000-165536
Avantajlar:
- Güvenlik (container escape olsa bile saldırgan root değil)
- Multi-user sistemlerde izolasyon
- Rootless Kubernetes (k3s, kind ile)
Dezavantajlar:
- 1024’ten küçük portlar kullanılamaz (çözüm: port forwarding)
- Bazı volume mount’lar çalışmayabilir
- Performance biraz daha düşük
Podman Compose:
Docker Compose yerine podman-compose kullanılır.
pip install podman-compose
# Docker Compose dosyası aynen çalışır
podman-compose up -d
Systemd entegrasyonu:
Podman, konteynerleri systemd servisi olarak çalıştırabilir.
# Konteyner çalıştır
podman run -d --name web -p 8080:80 nginx
# systemd unit dosyası oluştur
podman generate systemd --new --files --name web
# Dosyayı systemd dizinine taşı
mkdir -p ~/.config/systemd/user
mv container-web.service ~/.config/systemd/user/
# Servisi aktif et
systemctl --user enable --now container-web.service
# Artık normal systemd servisi gibi
systemctl --user status container-web
systemctl --user restart container-web
Pod kavramı:
Podman, Kubernetes benzeri pod’ları destekler.
# Pod oluştur
podman pod create --name mypod -p 8080:80
# Pod'a konteyner ekle
podman run -d --pod mypod --name web nginx
podman run -d --pod mypod --name sidecar busybox sleep 3600
# Pod listesi
podman pod ps
# Pod içindeki konteynerler
podman ps --pod
containerd
containerd, Docker’ın dahili konteyner runtime’ıdır. Docker 1.11’den beri Docker Engine, containerd kullanır.
Mimari:
Docker CLI → Docker Engine → containerd → runc
containerd, OCI runtime’ları yönetir ve image transfer, storage gibi işlemleri yapar.
Bağımsız kullanım:
containerd, Docker olmadan da kullanılabilir.
Kurulum:
# Ubuntu/Debian
sudo apt-get install containerd
# Arch
sudo pacman -S containerd
ctr CLI:
containerd’nin CLI aracı ctr‘dir (Docker CLI kadar gelişmiş değil).
# Image pull
sudo ctr image pull docker.io/library/nginx:alpine
# Konteyner çalıştır
sudo ctr run -d docker.io/library/nginx:alpine nginx
# Konteyner listesi
sudo ctr containers ls
# Task (çalışan konteyner) listesi
sudo ctr tasks ls
Neden containerd doğrudan kullanılır:
- Kubernetes: Kubernetes 1.20+ Docker desteğini kaldırdı, containerd kullanıyor
- Minimal footprint: Docker Engine’den daha hafif
- OCI uyumlu: Standard container runtime
Kubernetes ile containerd:
# /etc/containerd/config.toml
version = 2
[plugins."io.containerd.grpc.v1.cri"]
[plugins."io.containerd.grpc.v1.cri".containerd]
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
runtime_type = "io.containerd.runc.v2"
CRI-O
CRI-O, Kubernetes için özel geliştirilmiş minimal container runtime’dır. CRI (Container Runtime Interface) standardını implement eder.
Özellikler:
- Sadece Kubernetes için tasarlanmış
- Minimal (gereksiz özellik yok)
- OCI uyumlu
- Çok hafif ve hızlı
Kullanım:
CRI-O doğrudan CLI kullanımı için tasarlanmamıştır. Kubernetes üzerinden yönetilir.
# Kurulum (Fedora)
sudo dnf install cri-o
# Kubernetes ile kullanım
# kubelet --container-runtime=remote --container-runtime-endpoint=unix:///var/run/crio/crio.sock
Karşılaştırma Tablosu
| Özellik | Docker | Podman | containerd | CRI-O |
|---|---|---|---|---|
| Daemon | Evet | Hayır | Evet | Evet |
| Rootless | Sınırlı | Tam destek | Sınırlı | Hayır |
| CLI | docker | podman (docker-compatible) | ctr (minimal) | crictl (debug) |
| Compose | docker-compose | podman-compose | Yok | Yok |
| Pod desteği | Hayır | Evet | Hayır | Evet |
| Kubernetes | Deprecated | k3s, kind | Varsayılan | Varsayılan |
| systemd | Manuel | Native | Manuel | Native |
| Image build | docker build | podman build | buildctl (BuildKit) | Yok (external) |
| Kullanım kolaylığı | Çok kolay | Kolay | Zor | Sadece K8s |
| Footprint | Büyük | Orta | Küçük | Minimal |
Hangi durumda ne kullanılır:
- Docker: Genel kullanım, geliştirme, öğrenme
- Podman: Rootless, güvenlik öncelikli, RHEL/Fedora sistemler
- containerd: Kubernetes production, minimal sistem
- CRI-O: Kubernetes-only ortamlar, OpenShift
18.2 BuildKit Detayları (cache kullanımı, frontend’ler)
BuildKit, Docker’ın modern build motorudur. Docker 18.09’dan itibaren opsiyonel, 23.0’dan itibaren varsayılan olarak gelir.
BuildKit Avantajları
1. Paralel build:
BuildKit, bağımsız katmanları paralel olarak build eder.
FROM alpine
RUN apk add --no-cache python3 # 1. adım
RUN apk add --no-cache nodejs # 2. adım (paralel çalışabilir)
2. Build cache optimizasyonu:
BuildKit, layer cache’i daha akıllı yönetir.
3. Unused stage skip:
Multi-stage build’de kullanılmayan stage’ler atlanır.
FROM golang:1.21 AS builder
RUN go build app.go
FROM alpine AS debug # Bu stage hiç kullanılmıyor
RUN apk add --no-cache gdb
FROM alpine # Sadece bu stage build edilir
COPY --from=builder /app .
BuildKit Aktivasyonu
Ortam değişkeni ile:
export DOCKER_BUILDKIT=1
docker build -t myapp .
daemon.json ile (kalıcı):
{
"features": {
"buildkit": true
}
}
buildx ile (önerilen):
docker buildx build -t myapp .
Cache Türleri
BuildKit birden fazla cache türü destekler.
1. Local cache (varsayılan):
Layer’lar local disk’te saklanır.
docker buildx build -t myapp .
2. Registry cache:
Cache layer’ları registry’de saklanır. CI/CD’de çok kullanışlıdır.
# Build ve cache'i registry'ye push
docker buildx build \
--cache-to type=registry,ref=username/myapp:cache \
-t username/myapp:latest \
--push \
.
# Sonraki build'de cache'i kullan
docker buildx build \
--cache-from type=registry,ref=username/myapp:cache \
-t username/myapp:latest \
.
3. GitHub Actions cache:
GitHub Actions’da GHA cache kullanılır.
- name: Build with cache
uses: docker/build-push-action@v5
with:
context: .
cache-from: type=gha
cache-to: type=gha,mode=max
mode=max: Tüm layer’ları cache’ler (daha fazla cache, daha hızlı build)
mode=min: Sadece final image layer’ları cache’ler (daha az disk kullanımı)
4. Inline cache:
Cache metadata image içinde saklanır.
docker buildx build \
--cache-to type=inline \
-t username/myapp:latest \
--push \
.
# Sonraki build
docker buildx build \
--cache-from username/myapp:latest \
-t username/myapp:latest \
.
Build Secrets
BuildKit, build sırasında secret’ları güvenli şekilde kullanmanızı sağlar.
Dockerfile:
# syntax=docker/dockerfile:1.4
FROM alpine
RUN --mount=type=secret,id=github_token \
GITHUB_TOKEN=$(cat /run/secrets/github_token) && \
git clone https://${GITHUB_TOKEN}@github.com/private/repo.git
Build:
docker buildx build \
--secret id=github_token,src=$HOME/.github-token \
-t myapp .
Secret, final image’de saklanmaz.
SSH Agent Forwarding
Private git repository clone için SSH kullanabilirsiniz.
Dockerfile:
# syntax=docker/dockerfile:1.4
FROM alpine
RUN apk add --no-cache git openssh-client
RUN --mount=type=ssh \
git clone git@github.com:private/repo.git
Build:
# SSH agent'ı başlat ve key ekle
eval $(ssh-agent)
ssh-add ~/.ssh/id_rsa
# Build
docker buildx build --ssh default -t myapp .
Cache Mount
Cache mount, RUN komutu çalıştıktan sonra da persist eden cache’ler oluşturur.
Örnek: Package manager cache:
# syntax=docker/dockerfile:1.4
FROM node:18
WORKDIR /app
# npm cache persist eder
RUN --mount=type=cache,target=/root/.npm \
npm install
Avantaj: Her build’de npm cache’i sıfırdan oluşturulmaz, önceki build’lerden devam eder.
Python pip örneği:
# syntax=docker/dockerfile:1.4
FROM python:3.11
WORKDIR /app
# pip cache persist
RUN --mount=type=cache,target=/root/.cache/pip \
pip install -r requirements.txt
Go module cache:
# syntax=docker/dockerfile:1.4
FROM golang:1.21
WORKDIR /app
# Go module cache
RUN --mount=type=cache,target=/go/pkg/mod \
go mod download
Bind Mount
Build sırasında host dosyalarına read-only erişim.
# syntax=docker/dockerfile:1.4
FROM golang:1.21
WORKDIR /app
# go.mod ve go.sum bind mount (copy yerine)
RUN --mount=type=bind,source=go.mod,target=go.mod \
--mount=type=bind,source=go.sum,target=go.sum \
go mod download
COPY . .
RUN go build -o app
Avantaj: go.mod değişmeden kod değişirse cache bozulmaz.
BuildKit Frontend’leri
BuildKit, pluggable frontend mimarisi kullanır. Dockerfile sadece bir frontend’dir.
Syntax direktifi:
# syntax=docker/dockerfile:1.4
Bu satır, hangi Dockerfile frontend versiyonunun kullanılacağını belirtir.
Custom frontend örneği:
# syntax=tonistiigi/dockerfile:master
Experimental özellikler için farklı frontend kullanılabilir.
Buildpacks frontend:
Cloud Native Buildpacks ile image build etme.
docker buildx build \
--frontend gateway.v0 \
--opt source=heroku/buildpacks \
-t myapp .
Multi-platform Build
BuildKit, farklı CPU mimarileri için image build edebilir.
Basit örnek:
docker buildx build \
--platform linux/amd64,linux/arm64,linux/arm/v7 \
-t username/myapp:latest \
--push \
.
Platform-specific optimizasyon:
# syntax=docker/dockerfile:1.4
FROM --platform=$BUILDPLATFORM golang:1.21 AS builder
ARG TARGETOS
ARG TARGETARCH
WORKDIR /app
COPY . .
RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} \
go build -o app
FROM alpine
COPY --from=builder /app/app .
CMD ["./app"]
Build Output
BuildKit, build çıktısını farklı formatlarda export edebilir.
1. Local export (image push etmeden):
docker buildx build \
-o type=local,dest=./output \
.
2. Tar export:
docker buildx build \
-o type=tar,dest=myapp.tar \
.
3. OCI format:
docker buildx build \
-o type=oci,dest=myapp-oci.tar \
.
BuildKit Metrics
BuildKit, Prometheus metrics sunar.
daemon.json:
{
"builder": {
"gc": {
"enabled": true,
"defaultKeepStorage": "10GB"
}
},
"metrics-addr": "127.0.0.1:9323"
}
Metrics: http://127.0.0.1:9323/metrics
18.3 docker context ile Uzak Host’lara Bağlanma
Docker context, farklı Docker daemon’larına bağlanmayı kolaylaştırır.
Context Nedir?
Context, Docker CLI’ın hangi daemon ile konuşacağını belirler. Local, remote veya Kubernetes olabilir.
Varsayılan context:
docker context ls
Çıktı:
NAME TYPE DESCRIPTION DOCKER ENDPOINT
default * moby Current DOCKER_HOST unix:///var/run/docker.sock
SSH ile Remote Host
Remote host’a SSH context oluşturma:
docker context create remote-server \
--docker "host=ssh://user@192.168.1.100"
Context’i kullanma:
docker context use remote-server
# Artık tüm komutlar remote host'ta çalışır
docker ps
docker run nginx
Tek seferlik kullanım:
docker --context remote-server ps
Context değiştirme:
docker context use default # Local'e geri dön
TCP ile Remote Host (Güvensiz)
Remote host’ta Docker daemon’u TCP’de expose etme:
/etc/docker/daemon.json:
{
"hosts": ["unix:///var/run/docker.sock", "tcp://0.0.0.0:2375"]
}
Güvenlik uyarısı: Bu yöntem güvensizdir. Sadece test için kullanın!
Context oluşturma:
docker context create remote-tcp \
--docker "host=tcp://192.168.1.100:2375"
TLS ile Güvenli TCP
Sertifika oluşturma (remote host’ta):
# CA key ve certificate
openssl genrsa -aes256 -out ca-key.pem 4096
openssl req -new -x509 -days 365 -key ca-key.pem -sha256 -out ca.pem
# Server key
openssl genrsa -out server-key.pem 4096
openssl req -subj "/CN=192.168.1.100" -sha256 -new -key server-key.pem -out server.csr
# Server certificate
echo subjectAltName = IP:192.168.1.100 > extfile.cnf
openssl x509 -req -days 365 -sha256 -in server.csr -CA ca.pem -CAkey ca-key.pem \
-CAcreateserial -out server-cert.pem -extfile extfile.cnf
# Client key ve certificate
openssl genrsa -out key.pem 4096
openssl req -subj '/CN=client' -new -key key.pem -out client.csr
echo extendedKeyUsage = clientAuth > extfile-client.cnf
openssl x509 -req -days 365 -sha256 -in client.csr -CA ca.pem -CAkey ca-key.pem \
-CAcreateserial -out cert.pem -extfile extfile-client.cnf
daemon.json (remote host):
{
"hosts": ["unix:///var/run/docker.sock", "tcp://0.0.0.0:2376"],
"tls": true,
"tlscacert": "/path/to/ca.pem",
"tlscert": "/path/to/server-cert.pem",
"tlskey": "/path/to/server-key.pem",
"tlsverify": true
}
Context oluşturma (local):
docker context create remote-tls \
--docker "host=tcp://192.168.1.100:2376,ca=/path/to/ca.pem,cert=/path/to/cert.pem,key=/path/to/key.pem"
Environment Variable ile Context
export DOCKER_HOST=ssh://user@192.168.1.100
docker ps # Remote host'ta çalışır
# Veya
export DOCKER_HOST=tcp://192.168.1.100:2376
export DOCKER_TLS_VERIFY=1
export DOCKER_CERT_PATH=/path/to/certs
docker ps
Kubernetes Context
Docker Desktop Kubernetes aktifse Kubernetes context oluşturulabilir.
docker context create k8s-context \
--kubernetes config-file=/path/to/kubeconfig
Not: Docker’ın Kubernetes desteği deprecated edilmiştir. kubectl kullanılması önerilir.
Context Export/Import
Context’leri paylaşabilirsiniz.
Export:
docker context export remote-server
# Çıktı: remote-server.dockercontext
Import:
docker context import remote-server remote-server.dockercontext
Pratik Kullanım Senaryoları
Senaryo 1: Development → Staging deployment
# Local'de build
docker context use default
docker build -t myapp:latest .
# Staging'e deploy
docker context use staging-server
docker tag myapp:latest myapp:$(git rev-parse --short HEAD)
docker push myapp:$(git rev-parse --short HEAD)
docker-compose up -d
Senaryo 2: Multi-host monitoring
#!/bin/bash
for context in default server1 server2 server3; do
echo "=== $context ==="
docker --context $context ps --format "table {{.Names}}\t{{.Status}}"
done
Senaryo 3: Remote debugging
# Local'de çalışan container'a remote host'tan bağlan
docker context use remote-server
docker exec -it myapp bash
Özet ve Best Practices
Alternatif Runtime’lar:
- Podman: Rootless ve güvenlik öncelikli projeler için
- containerd: Kubernetes production ortamları için
- CRI-O: OpenShift ve Kubernetes-only senaryolar için
- Docker: Genel kullanım ve geliştirme için hala en iyi seçenek
BuildKit:
- Her zaman DOCKER_BUILDKIT=1 kullanın
- Registry cache ile CI/CD build süresini kısaltın
- Cache mount ile package manager cache’lerini persist edin
- Secrets mount ile hassas bilgileri güvenli kullanın
- Multi-platform build için buildx kullanın
Remote Host Yönetimi:
- Güvenlik için SSH context kullanın
- Production’da TLS zorunlu
- Context’leri organize edin (dev, staging, prod)
- DOCKER_HOST yerine docker context tercih edin
- Remote operation’lar için timeout ayarlayın
Güvenlik:
- Asla güvensiz TCP (2375) production’da kullanmayın
- SSH key-based authentication kullanın
- TLS sertifikalarını güvenli saklayın
- Firewall kuralları ile port erişimini kısıtlayın
- Audit logging aktif edin
Docker ekosistemi sürekli gelişiyor. Alternatif araçları ve yeni özellikleri takip etmek, en iyi çözümü seçmenize yardımcı olur. BuildKit ve context gibi araçlar, Docker’ı daha güçlü ve esnek hale getirir.
19. Windows’a Özgü Derinlik: Windows Containers
Windows konteynerler, Linux konteynerlerden farklı çalışır ve kendine özgü özelliklere sahiptir. Bu bölümde Windows konteyner teknolojisini, isolation türlerini, base image seçimini ve yaygın sorunları detaylı şekilde inceleyeceğiz.
19.1 Windows Konteyner Türleri: Process vs Hyper-V Isolation
Windows konteynerleri iki farklı isolation modunda çalışabilir: Process Isolation ve Hyper-V Isolation.
Process Isolation
Process Isolation, Linux konteynerlerine benzer şekilde çalışır. Konteynerler host kernel’ını paylaşır.
Özellikler:
- Host ile aynı kernel version gerektirir
- Daha hızlı başlatma
- Daha düşük kaynak tüketimi
- Windows Server’da varsayılan mod
Çalıştırma:
docker run --isolation=process mcr.microsoft.com/windows/nanoserver:ltsc2022
Kısıtlamalar:
- Container OS version = Host OS version olmalı
- Windows Server 2016 host → Server 2016 container
- Windows Server 2022 host → Server 2022 container
- Version uyumsuzluğunda çalışmaz
Version kontrolü:
# Host version
[System.Environment]::OSVersion.Version
# Container version
docker run mcr.microsoft.com/windows/nanoserver:ltsc2022 cmd /c ver
Hyper-V Isolation
Hyper-V Isolation, her konteyneri hafif bir VM içinde çalıştırır. Kernel izolasyonu sağlar.
Özellikler:
- Farklı OS version’ları çalışabilir
- Daha güvenli (kernel izolasyonu)
- Daha yavaş başlatma
- Daha fazla kaynak tüketimi
- Windows 10/11’de varsayılan mod
Çalıştırma:
docker run --isolation=hyperv mcr.microsoft.com/windows/nanoserver:ltsc2019
Avantajlar:
Windows Server 2022 host üzerinde Windows Server 2019 container çalıştırabilirsiniz:
# Host: Windows Server 2022
# Container: Windows Server 2019 (Hyper-V isolation ile çalışır)
docker run --isolation=hyperv mcr.microsoft.com/windows/servercore:ltsc2019
Varsayılan Isolation Modu
Windows Server:
- Varsayılan: Process Isolation
- Hyper-V: Manuel belirtilmeli
Windows 10/11:
- Varsayılan: Hyper-V Isolation
- Process: Kullanılamaz (sadece Server’da)
daemon.json ile varsayılanı değiştirme:
{
"exec-opts": ["isolation=hyperv"]
}
Karşılaştırma Tablosu
| Özellik | Process Isolation | Hyper-V Isolation |
|---|---|---|
| Host Kernel | Paylaşır | Ayrı kernel |
| OS Version | Aynı olmalı | Farklı olabilir |
| Başlatma süresi | 1-2 saniye | 3-5 saniye |
| Bellek overhead | Minimal | ~100-200 MB |
| Güvenlik | Container escape riski | Kernel izolasyonu |
| Uyumluluk | Windows Server | Windows Server + Win10/11 |
| Performans | Daha hızlı | Biraz daha yavaş |
Pratik Kullanım
Development (Windows 10/11):
# Hyper-V isolation (otomatik)
docker run -it mcr.microsoft.com/windows/nanoserver:ltsc2022 cmd
Production (Windows Server 2022):
# Process isolation (daha hızlı)
docker run --isolation=process -d myapp:latest
# Eski version gerekliyse Hyper-V
docker run --isolation=hyperv -d legacy-app:ltsc2019
19.2 Base Image’lar: NanoServer vs ServerCore vs .NET Images
Windows container base image’ları boyut, özellik ve uyumluluk açısından farklıdır.
Windows Base Image Hiyerarşisi
Windows (Host OS)
├── Windows Server Core (~2-5 GB)
│ ├── ASP.NET (~5-8 GB)
│ └── .NET Framework (~4-6 GB)
└── Nano Server (~100-300 MB)
└── .NET (Core/5+) (~200-500 MB)
Nano Server
Nano Server, minimal Windows base image’dır. Hafif ve modern uygulamalar için tasarlanmıştır.
Özellikler:
- Boyut: ~100 MB (compressed), ~300 MB (extracted)
- Grafik arayüzü yok
- PowerShell Core (pwsh) var, Windows PowerShell yok
- .NET Framework yok, sadece .NET Core/5+
- IIS yok, minimal API’ler
Docker Hub tag’leri:
mcr.microsoft.com/windows/nanoserver:ltsc2022
mcr.microsoft.com/windows/nanoserver:ltsc2019
mcr.microsoft.com/windows/nanoserver:1809
Dockerfile örneği:
FROM mcr.microsoft.com/windows/nanoserver:ltsc2022
WORKDIR C:\app
COPY app.exe .
CMD ["app.exe"]
Kullanım alanları:
- .NET Core / .NET 5+ uygulamaları
- Node.js uygulamaları
- Go, Rust gibi statik binary’ler
- Mikroservisler
Kısıtlamalar:
- .NET Framework 4.x çalışmaz
- Eski Windows API’leri yok
- GUI uygulamaları desteklenmez
- Legacy DLL’ler uyumsuz olabilir
Windows Server Core
Server Core, tam Windows API desteği sunan image’dır.
Özellikler:
- Boyut: ~2 GB (compressed), ~5 GB (extracted)
- Tam Windows API
- PowerShell 5.1 (Windows PowerShell)
- .NET Framework 4.x dahil
- IIS desteklenir
- Windows servisleri çalışır
Docker Hub tag’leri:
mcr.microsoft.com/windows/servercore:ltsc2022
mcr.microsoft.com/windows/servercore:ltsc2019
Dockerfile örneği:
FROM mcr.microsoft.com/windows/servercore:ltsc2022
# IIS kurulumu
RUN powershell -Command \
Add-WindowsFeature Web-Server; \
Remove-Item -Recurse C:\inetpub\wwwroot\*
WORKDIR C:\inetpub\wwwroot
COPY website/ .
EXPOSE 80
CMD ["powershell", "Start-Service", "W3SVC"]
Kullanım alanları:
- .NET Framework 4.x uygulamaları
- IIS web uygulamaları
- Legacy Windows uygulamaları
- Windows servisleri gerektiren uygulamalar
ASP.NET Image
ASP.NET Framework için optimize edilmiş image’dır.
Özellikler:
- Base: Windows Server Core
- ASP.NET 4.x pre-installed
- IIS pre-configured
- Boyut: ~5-8 GB
Docker Hub tag’leri:
mcr.microsoft.com/dotnet/framework/aspnet:4.8-windowsservercore-ltsc2022
mcr.microsoft.com/dotnet/framework/aspnet:4.7.2-windowsservercore-ltsc2019
Dockerfile örneği:
FROM mcr.microsoft.com/dotnet/framework/aspnet:4.8
WORKDIR /inetpub/wwwroot
COPY published/ .
.NET Core / .NET 5+ Images
Modern .NET uygulamaları için Nano Server tabanlı image’lar.
Runtime image:
mcr.microsoft.com/dotnet/runtime:8.0-nanoserver-ltsc2022
mcr.microsoft.com/dotnet/aspnet:8.0-nanoserver-ltsc2022
SDK image (build için):
mcr.microsoft.com/dotnet/sdk:8.0-nanoserver-ltsc2022
Multi-stage Dockerfile örneği:
# Build stage
FROM mcr.microsoft.com/dotnet/sdk:8.0-nanoserver-ltsc2022 AS build
WORKDIR /src
COPY *.csproj .
RUN dotnet restore
COPY . .
RUN dotnet publish -c Release -o /app/publish
# Runtime stage
FROM mcr.microsoft.com/dotnet/aspnet:8.0-nanoserver-ltsc2022
WORKDIR /app
COPY --from=build /app/publish .
EXPOSE 8080
ENTRYPOINT ["dotnet", "MyApp.dll"]
Boyut karşılaştırması:
- SDK image: ~1.5 GB
- Runtime image: ~300 MB
- Published app: ~50 MB
- Total final image: ~350 MB
Base Image Seçim Kılavuzu
Hangi durumda ne kullanılır:
Yeni .NET 5+ uygulaması → mcr.microsoft.com/dotnet/aspnet:8.0-nanoserver
Eski .NET 4.x uygulaması → mcr.microsoft.com/dotnet/framework/aspnet:4.8
Legacy Windows app → mcr.microsoft.com/windows/servercore:ltsc2022
Minimal binary (Go, Rust) → mcr.microsoft.com/windows/nanoserver:ltsc2022
Version Compatibility Matrix
| Container Image | Windows Server 2016 | Windows Server 2019 | Windows Server 2022 | Win 10/11 |
|---|---|---|---|---|
| ltsc2016 | Process | Hyper-V | Hyper-V | Hyper-V |
| ltsc2019 | ❌ | Process | Hyper-V | Hyper-V |
| ltsc2022 | ❌ | ❌ | Process | Hyper-V |
LTSC: Long-Term Servicing Channel (5 yıl destek)
19.3 Windows Container Networking, Named Pipes, Windows Servisleri
Windows Container Network Modes
Windows konteynerleri farklı network driver’ları destekler.
1. NAT (Network Address Translation)
Varsayılan network driver’dır. Linux bridge’e benzer.
# Varsayılan nat network
docker network ls
Çıktı:
NETWORK ID NAME DRIVER SCOPE
abc123... nat nat local
Konteyner başlatma:
docker run -d -p 8080:80 --name web myapp:latest
Özellikler:
- Outbound connectivity: Var
- Inbound connectivity: Port mapping gerekli
- Container-to-container: Container name ile
2. Transparent Network
Konteynerlere host network’ünden IP adresi verir.
# Transparent network oluşturma
docker network create -d transparent MyTransparentNetwork
# Kullanım
docker run -d --network=MyTransparentNetwork myapp:latest
Özellikler:
- Container’lar host ile aynı subnet’te
- External network’ten doğrudan erişilebilir
- IP adresi DHCP veya static atanır
- Port mapping gerekmez
3. Overlay Network (Swarm)
Multi-host networking için.
docker network create -d overlay MyOverlayNetwork
4. L2Bridge
Layer 2 bridge network, transparent’a benzer ama daha esnek.
docker network create -d l2bridge MyL2Network
Named Pipes
Windows konteynerleri, named pipe’ları destekler. Bu, Windows-native IPC (Inter-Process Communication) mekanizmasıdır.
Named pipe mount:
docker run -d -v \\.\pipe\docker_engine:\\.\pipe\docker_engine myapp
Örnek: Docker-in-Docker (Windows)
docker run -it -v \\.\pipe\docker_engine:\\.\pipe\docker_engine `
mcr.microsoft.com/windows/servercore:ltsc2022 powershell
Container içinde:
# Host Docker'ına named pipe üzerinden erişim
docker ps
SQL Server Named Pipe:
docker run -d `
-e "ACCEPT_EULA=Y" `
-e "SA_PASSWORD=YourPassword123" `
-v \\.\pipe\sql\query:\\.\pipe\sql\query `
mcr.microsoft.com/mssql/server:2022-latest
Windows Servisleri Container İçinde
Windows servisleri, container içinde çalıştırılabilir.
IIS servisi örneği:
FROM mcr.microsoft.com/windows/servercore:ltsc2022
RUN powershell -Command Add-WindowsFeature Web-Server
EXPOSE 80
CMD ["powershell", "-Command", "Start-Service W3SVC; Start-Sleep -Seconds 3600"]
Problem: Container kapandığında servis de duruyor.
Çözüm: ServiceMonitor.exe
Microsoft’un ServiceMonitor.exe aracı, Windows servislerini container’da düzgün çalıştırır.
FROM mcr.microsoft.com/windows/servercore:ltsc2022
RUN powershell -Command Add-WindowsFeature Web-Server
# ServiceMonitor.exe indir
ADD https://dotnetbinaries.blob.core.windows.net/servicemonitor/2.0.1.10/ServiceMonitor.exe C:\ServiceMonitor.exe
EXPOSE 80
ENTRYPOINT ["C:\\ServiceMonitor.exe", "w3svc"]
ServiceMonitor:
- Servisi başlatır ve izler
- Servis durdursa container exit eder
- Health check yaparak servis durumunu kontrol eder
SQL Server örneği:
FROM mcr.microsoft.com/mssql/server:2022-latest
COPY ServiceMonitor.exe C:\
ENV ACCEPT_EULA=Y
ENV SA_PASSWORD=YourPassword123
ENTRYPOINT ["C:\\ServiceMonitor.exe", "MSSQLSERVER"]
Multiple Services (Supervisor Pattern)
Birden fazla servis çalıştırmak için PowerShell script kullanılır.
start-services.ps1:
# IIS başlat
Start-Service W3SVC
# Background task başlat
Start-Process -FilePath "C:\app\worker.exe" -NoNewWindow
# Ana process olarak log izle (container canlı kalır)
Get-Content -Path "C:\inetpub\logs\LogFiles\W3SVC1\*.log" -Wait
Dockerfile:
FROM mcr.microsoft.com/windows/servercore:ltsc2022
RUN powershell -Command Add-WindowsFeature Web-Server
COPY start-services.ps1 C:\
COPY app/ C:\app\
CMD ["powershell", "-File", "C:\\start-services.ps1"]
DNS ve Service Discovery
Windows container’lar, embedded DNS kullanır.
# Network oluştur
docker network create mynet
# Container 1
docker run -d --name web --network mynet myapp:latest
# Container 2 (web'e hostname ile erişir)
docker run -it --network mynet mcr.microsoft.com/windows/nanoserver:ltsc2022 powershell
# Container 2 içinde
ping web
curl http://web
19.4 Sık Karşılaşılan Uyumluluk Problemleri ve Çözümleri
Sorun 1: “The container operating system does not match the host operating system”
Hata mesajı:
Error response from daemon: container <id> encountered an error during
CreateProcess: failure in a Windows system call: The container operating
system does not match the host operating system.
Neden:
Container image’ın OS version’ı host’la uyumlu değil. Process Isolation kullanırken version’lar eşleşmeli.
Çözüm 1: Hyper-V Isolation kullan
docker run --isolation=hyperv myapp:ltsc2019
Çözüm 2: Doğru base image kullan
# Host version kontrolü
[System.Environment]::OSVersion.Version
# Çıktı: Major: 10, Minor: 0, Build: 20348 (Windows Server 2022)
# Uygun image
docker pull mcr.microsoft.com/windows/servercore:ltsc2022
Çözüm 3: Multi-stage build ile esnek image
ARG WINDOWS_VERSION=ltsc2022
FROM mcr.microsoft.com/windows/servercore:${WINDOWS_VERSION}
Build:
docker build --build-arg WINDOWS_VERSION=ltsc2022 -t myapp:ltsc2022 .
docker build --build-arg WINDOWS_VERSION=ltsc2019 -t myapp:ltsc2019 .
Sorun 2: Port Binding Başarısız
Hata:
Error starting userland proxy: listen tcp 0.0.0.0:80: bind: An attempt was
made to access a socket in a way forbidden by its access permissions.
Neden:
Windows’ta bazı portlar reserved (ayrılmış) olabilir veya başka servis kullanıyor.
Reserved port kontrolü:
netsh interface ipv4 show excludedportrange protocol=tcp
Çözüm 1: Farklı port kullan
docker run -p 8080:80 myapp
Çözüm 2: Reserved port’u release et
# Administrator PowerShell
net stop winnat
docker start mycontainer
net start winnat
Sorun 3: Volume Mount İzin Hatası
Hata:
Error response from daemon: error while creating mount source path
'C:\Users\...': mkdir C:\Users\...: Access is denied.
Neden:
Windows file permissions veya path formatı yanlış.
Çözüm 1: Absolute path kullan
# Yanlış
docker run -v .\app:C:\app myapp
# Doğru
docker run -v C:\Users\Me\app:C:\app myapp
Çözüm 2: Docker Desktop file sharing
Docker Desktop → Settings → Resources → File Sharing → Add path
Çözüm 3: Named volume kullan
docker volume create mydata
docker run -v mydata:C:\app\data myapp
Sorun 4: Yavaş Image Build
Neden:
Windows base image’ları büyük (GB’larca). Defender real-time scan yavaşlatır.
Çözüm 1: BuildKit cache
$env:DOCKER_BUILDKIT=1
docker build --cache-from myapp:cache -t myapp:latest .
Çözüm 2: Defender exclusion
Windows Defender → Add exclusion:
C:\ProgramData\Docker
C:\Users\<Username>\.docker
Çözüm 3: Multi-stage ile minimize
FROM mcr.microsoft.com/dotnet/sdk:8.0-nanoserver-ltsc2022 AS build
# Build işlemleri
FROM mcr.microsoft.com/dotnet/aspnet:8.0-nanoserver-ltsc2022
COPY --from=build /app .
Sorun 5: Container Restart Loop
Belirti:
Container sürekli restart oluyor.
Debug:
# Logs
docker logs mycontainer
# Inspect
docker inspect mycontainer
# Event stream
docker events --filter container=mycontainer
Yaygın nedenler:
1. Ana process hemen exit ediyor
# Yanlış (CMD hemen biter)
CMD ["echo", "Hello"]
# Doğru (blocking process)
CMD ["powershell", "-NoExit", "-Command", "Start-Service W3SVC; Start-Sleep -Seconds 999999"]
2. Servis başlatılamıyor
# Container'a interactive bağlan
docker run -it myapp:latest powershell
# Manuel servis başlat ve hata kontrolü
Start-Service W3SVC
3. Dependency eksik
# .NET Framework runtime eksikse
RUN powershell -Command Install-WindowsFeature NET-Framework-45-Core
Sorun 6: DNS Resolution Başarısız
Belirti:
Container içinden internet’e çıkamıyor.
Test:
docker run -it mcr.microsoft.com/windows/nanoserver:ltsc2022 powershell
# Container içinde
Resolve-DnsName google.com
Çözüm 1: DNS server ayarla
docker run --dns 8.8.8.8 --dns 8.8.4.4 myapp
daemon.json:
{
"dns": ["8.8.8.8", "8.8.4.4"]
}
Çözüm 2: Network driver değiştir
docker network create -d transparent mytransparent
docker run --network mytransparent myapp
Sorun 7: Disk Space Issues
Belirti:
“No space left on device” hatası.
Çözüm 1: Cleanup
# Stopped containers
docker container prune
# Unused images
docker image prune -a
# Volumes
docker volume prune
# Everything
docker system prune -a --volumes
Çözüm 2: Docker disk size artır
Docker Desktop → Settings → Resources → Disk image size
Çözüm 3: Layer minimize
# Yanlış (her RUN ayrı layer)
RUN powershell -Command Install-Package A
RUN powershell -Command Install-Package B
RUN powershell -Command Install-Package C
# Doğru (tek layer)
RUN powershell -Command \
Install-Package A; \
Install-Package B; \
Install-Package C
Sorun 8: Windows Updates Container İçinde
Sorun:
Container içinde Windows Update çalışmıyor veya base image güncel değil.
Çözüm:
Microsoft düzenli olarak base image’ları günceller. Her zaman en son patch level’ı kullanın.
# Latest tag her zaman en güncel patch
docker pull mcr.microsoft.com/windows/servercore:ltsc2022
# Specific patch level (üretim için)
docker pull mcr.microsoft.com/windows/servercore:ltsc2022-amd64-20250101
Dockerfile’da automatic update (önerilmez):
FROM mcr.microsoft.com/windows/servercore:ltsc2022
# Windows Update (build'i çok yavaşlatır!)
RUN powershell -Command \
Install-Module PSWindowsUpdate -Force; \
Get-WindowsUpdate -Install -AcceptAll
Bu işlem saatler sürebilir. Bunun yerine güncel base image kullanın.
Best Practices Özeti
Image Seçimi:
- Modern apps → Nano Server
- Legacy apps → Server Core
- Minimal overhead → Nano Server
- Full compatibility → Server Core
Networking:
- Development → NAT (default)
- Production → Transparent veya L2Bridge
- Multi-host → Overlay
Performance:
- Multi-stage build kullanın
- BuildKit cache aktif edin
- Defender exclusion ekleyin
- Layer’ları minimize edin
Uyumluluk:
- Host ve container version’ları match edin
- Version uyumsuzlukta Hyper-V isolation
- Named pipe dikkatli kullanın
- Windows servisleri için ServiceMonitor
Troubleshooting:
docker logsher zaman ilk adımdocker inspectdetaylı bilgi için- Interactive mode (
-it) debug için - Event stream (
docker events) monitoring için
Windows container teknolojisi Linux’tan farklı zorluklara sahip olsa da, doğru yaklaşımla production-ready sistemler oluşturulabilir. En önemli nokta, base image seçimi, isolation mode ve network driver’ı projenizin ihtiyaçlarına göre seçmektir.
20. Linux’a Özgü Derinlik: Kernel Özellikleri & Güvenlik
Docker’ın Linux üzerinde çalışma prensibi, kernel seviyesindeki özelliklerden yararlanır. Bu bölümde namespace’ler, cgroups, storage driver’lar ve SELinux gibi Linux-specific konuları derinlemesine inceleyeceğiz.
20.1 Namespaces (PID, NET, MNT, UTS, IPC) ve cgroups Detayları
Linux Namespaces
Namespace’ler, global sistem kaynaklarını izole ederek her konteynerin kendi görünümüne sahip olmasını sağlar. Docker’ın temel izolasyon mekanizmasıdır.
Linux’ta 7 tip namespace vardır:
- PID Namespace (Process ID)
- NET Namespace (Network)
- MNT Namespace (Mount)
- UTS Namespace (Hostname)
- IPC Namespace (Inter-Process Communication)
- USER Namespace (User ID)
- CGROUP Namespace (Control Groups)
1. PID Namespace
PID namespace, her konteynerin kendi process tree’sine sahip olmasını sağlar.
Konteyner içinden:
docker run -it alpine ps aux
Çıktı:
PID USER COMMAND
1 root /bin/sh
7 root ps aux
Konteyner içinde PID 1’den başlar.
Host’tan:
ps aux | grep alpine
Çıktı:
root 12345 0.0 0.0 /bin/sh
Host’ta farklı PID (12345).
Namespace inceleme:
# Konteyner process'ini bul
CONTAINER_PID=$(docker inspect --format '{{.State.Pid}}' mycontainer)
# Namespace'leri listele
ls -l /proc/$CONTAINER_PID/ns/
Çıktı:
lrwxrwxrwx 1 root root 0 pid:[4026532194]
lrwxrwxrwx 1 root root 0 net:[4026532197]
lrwxrwxrwx 1 root root 0 mnt:[4026532195]
...
Her namespace unique bir inode numarasına sahiptir.
PID namespace hiyerarşisi:
Init (PID 1, Host)
├── dockerd
│ └── containerd
│ └── container (PID 1 in namespace)
│ └── app process (PID 2 in namespace)
Parent namespace’den child görebilir ama tersi değil:
# Host'tan container process'leri görebilirsin
ps aux | grep container
# Container içinden host process'leri göremezsin
docker exec mycontainer ps aux # Sadece container process'leri
2. NET Namespace
Network namespace, her konteynerin kendi network stack’ine sahip olmasını sağlar.
Network namespace yapısı:
Host Network Namespace
├── eth0 (physical interface)
├── docker0 (bridge)
└── veth pairs
├── vethXXX (host side) ↔ eth0 (container side)
└── vethYYY (host side) ↔ eth0 (container side)
Namespace inceleme:
# Konteyner network namespace
sudo nsenter -t $CONTAINER_PID -n ip addr
# Host network namespace
ip addr
veth pair kontrolü:
# Container'ın veth interface'ini bul
docker exec mycontainer cat /sys/class/net/eth0/iflink
# Çıktı: 12
# Host'ta karşılık gelen interface
ip link | grep "^12:"
# Çıktı: 12: veth1a2b3c4@if11: <BROADCAST,MULTICAST,UP>
Host network mode:
docker run --network host nginx
Bu durumda container host’un network namespace’ini paylaşır.
3. MNT Namespace
Mount namespace, her konteynerin kendi filesystem görünümüne sahip olmasını sağlar.
Container filesystem:
# Container'ın root filesystem'i
docker inspect --format '{{.GraphDriver.Data.MergedDir}}' mycontainer
Mount propagation:
Docker, mount propagation ile host ve container arasında mount paylaşımını kontrol eder.
# Private (default): Mount'lar yayılmaz
docker run -v /host/path:/container/path myapp
# Shared: Bidirectional mount propagation
docker run -v /host/path:/container/path:shared myapp
# Slave: Host → Container tek yönlü
docker run -v /host/path:/container/path:slave myapp
4. UTS Namespace
UTS (UNIX Time-Sharing) namespace, hostname ve domain name izolasyonu sağlar.
# Container içinde hostname
docker run alpine hostname
# Çıktı: a1b2c3d4e5f6 (container ID)
# Host hostname
hostname
# Çıktı: myserver
Custom hostname:
docker run --hostname myapp alpine hostname
# Çıktı: myapp
5. IPC Namespace
IPC (Inter-Process Communication) namespace, shared memory, semaphores ve message queues’u izole eder.
# Container içinde IPC
docker exec mycontainer ipcs
# IPC namespace paylaşma
docker run --ipc=container:other_container myapp
6. USER Namespace
User namespace, container içindeki UID/GID’leri host’taki farklı UID/GID’lere map eder.
Rootless container örneği:
# Host'ta user ID 1000
id
# uid=1000(john)
# Container içinde root
docker run --user 0:0 alpine id
# uid=0(root) gid=0(root)
# Ancak host'ta process user 1000 olarak çalışır
ps aux | grep alpine
# john 12345 ...
User namespace mapping:
Container UID → Host UID
0 → 1000
1 → 100000
2 → 100001
...
65536 → 165536
Aktivasyon (daemon.json):
{
"userns-remap": "default"
}
7. CGROUP Namespace
Cgroup namespace, container’ın cgroup görünümünü izole eder.
# Container cgroup'ları
docker exec mycontainer cat /proc/self/cgroup
Cgroups (Control Groups)
Cgroups, kaynak limitlerini ve accounting’i yönetir.
Cgroups v1 vs v2:
| Özellik | Cgroups v1 | Cgroups v2 |
|---|---|---|
| Hiyerarşi | Her controller ayrı | Tek unified hiyerarşi |
| File structure | /sys/fs/cgroup/<controller>/ |
/sys/fs/cgroup/ |
| Delegation | Karmaşık | Basit ve güvenli |
| Pressure stall info | Yok | Var (PSI) |
Controller’lar:
cpu: CPU zamanımemory: Bellek limitleriblkio: Disk I/Odevices: Device erişimipids: Process sayısı limiticpuset: CPU core assignment
Container cgroup path:
# Cgroup path
cat /sys/fs/cgroup/system.slice/docker-<container_id>.scope/cgroup.controllers
# Memory limit
cat /sys/fs/cgroup/system.slice/docker-<container_id>.scope/memory.max
# CPU limit
cat /sys/fs/cgroup/system.slice/docker-<container_id>.scope/cpu.max
Manuel cgroup kontrolü:
# Container PID
CONTAINER_PID=$(docker inspect --format '{{.State.Pid}}' mycontainer)
# Cgroup yolu bul
cat /proc/$CONTAINER_PID/cgroup
# Memory kullanımı
cat /sys/fs/cgroup/system.slice/docker-$CONTAINER_ID.scope/memory.current
# CPU throttling
cat /sys/fs/cgroup/system.slice/docker-$CONTAINER_ID.scope/cpu.stat
PSI (Pressure Stall Information) - Cgroups v2:
# Memory pressure
cat /sys/fs/cgroup/system.slice/docker-$CONTAINER_ID.scope/memory.pressure
# CPU pressure
cat /sys/fs/cgroup/system.slice/docker-$CONTAINER_ID.scope/cpu.pressure
Örnek çıktı:
some avg10=0.00 avg60=0.00 avg300=0.00 total=0
full avg10=0.00 avg60=0.00 avg300=0.00 total=0
some: Bazı process’ler kaynak bekliyor
full: Tüm process’ler kaynak bekliyor
20.2 OverlayFS, aufs, devicemapper, btrfs Farkları
Docker, farklı storage driver’lar kullanarak image layer’larını yönetir.
Storage Driver Seçimi
Mevcut driver’ı görme:
docker info | grep "Storage Driver"
Çıktı:
Storage Driver: overlay2
1. OverlayFS (overlay2)
Modern Linux sistemlerinde varsayılan ve önerilen driver’dır.
Mimari:
Container Layer (Read-Write)
↓
Image Layer 3 (Read-Only)
↓
Image Layer 2 (Read-Only)
↓
Image Layer 1 (Read-Only)
↓
Base Layer (Read-Only)
OverlayFS nasıl çalışır:
- Lower dir: Read-only layer’lar (image layers)
- Upper dir: Read-write layer (container layer)
- Merged dir: Birleştirilmiş görünüm (container görür)
- Work dir: Internal overlay kullanımı
Dizin yapısı:
/var/lib/docker/overlay2/
├── l/ # Symbolic link'ler (layer short names)
├── <layer-id>/
│ ├── diff/ # Layer içeriği
│ ├── link # Short name
│ ├── lower # Alt layer'lar
│ └── work/ # Overlay work dir
└── <container-id>/
├── diff/ # Container değişiklikleri
├── merged/ # Birleştirilmiş view
└── work/
Avantajlar:
- Hızlı (kernel-native)
- Düşük overhead
- İyi performans
- Copy-on-write (CoW) optimize
Dezavantajlar:
- Çok deep layer’larda (100+) yavaşlama
rename(2)cross-layer’da pahalı- OverlayFS limitasyonları (örn: inode sayısı)
Kullanım örneği:
# Layer inceleme
docker inspect myimage | jq '.[0].GraphDriver'
Çıktı:
{
"Data": {
"LowerDir": "/var/lib/docker/overlay2/abc123/diff:/var/lib/docker/overlay2/def456/diff",
"MergedDir": "/var/lib/docker/overlay2/ghi789/merged",
"UpperDir": "/var/lib/docker/overlay2/ghi789/diff",
"WorkDir": "/var/lib/docker/overlay2/ghi789/work"
},
"Name": "overlay2"
}
2. AUFS (Another Union File System)
Eski Ubuntu sistemlerinde kullanılan union filesystem.
Özellikler:
- Union mount destekler
- Copy-on-write
- OverlayFS’den eski
Durum:
- Modern kernellerde deprecated
- Ubuntu 18.04+ overlay2 kullanır
- Yeni kurulumlar için önerilmez
Aktivasyon (legacy):
{
"storage-driver": "aufs"
}
3. Device Mapper
Block-level storage driver’dır. LVM tabanlı.
İki mod:
loop-lvm (varsayılan, önerilmez):
- Sparse file üzerinde LVM
- Development için uygun
- Production’da yavaş
direct-lvm (production):
- Dedicated block device
- LVM thin provisioning
- Yüksek performans
Yapılandırma:
{
"storage-driver": "devicemapper",
"storage-opts": [
"dm.thinpooldev=/dev/mapper/docker-thinpool",
"dm.use_deferred_removal=true",
"dm.use_deferred_deletion=true"
]
}
Avantajlar:
- Block-level CoW
- Snapshot desteği
- LVM özellikleri
Dezavantajlar:
- Karmaşık kurulum
- Performance overhead
- Disk yönetimi zor
4. Btrfs
B-tree file system, CoW ve snapshot native destekler.
Özellikler:
- Native CoW
- Subvolume’ler
- Snapshot’lar
- Compression
Aktivasyon:
# Btrfs filesystem oluştur
mkfs.btrfs /dev/sdb
mount /dev/sdb /var/lib/docker
# daemon.json
{
"storage-driver": "btrfs"
}
Avantajlar:
- Filesystem-level CoW
- Efficient cloning
- Compression desteği
- Deduplication
Dezavantajlar:
- Btrfs disk gerektirir
- Filesystem karmaşıklığı
- Performance bazen tutarsız
5. ZFS
Solaris’ten gelen gelişmiş filesystem.
Özellikler:
- CoW
- Snapshot’lar
- Compression
- Deduplication
- RAID-Z
Kullanım:
# ZFS pool oluştur
zpool create -f zpool-docker /dev/sdb
# Docker storage
zfs create -o mountpoint=/var/lib/docker zpool-docker/docker
# daemon.json
{
"storage-driver": "zfs"
}
Avantajlar:
- Enterprise-grade özellikleri
- Data integrity
- Snapshot ve cloning
Dezavantajlar:
- Lisans (CDDL, Linux’a dahil değil)
- Yüksek RAM kullanımı
- Karmaşık yönetim
Storage Driver Karşılaştırma
| Driver | Performans | Stability | Disk Space | Kullanım |
|---|---|---|---|---|
| overlay2 | Mükemmel | Kararlı | Verimli | Default, önerilen |
| aufs | İyi | Kararlı | Verimli | Deprecated |
| devicemapper | Orta | Kararlı | Orta | Production (direct-lvm) |
| btrfs | İyi | Orta | Çok verimli | Btrfs disk gerekli |
| zfs | İyi | Kararlı | Çok verimli | Enterprise, ZFS gerekli |
| vfs | Yavaş | Kararlı | Kötü | Debug only, CoW yok |
Storage Driver Seçim Kılavuzu
Modern Linux (kernel 4.0+) → overlay2
Enterprise features → ZFS
LVM altyapısı mevcut → devicemapper (direct-lvm)
Btrfs filesystem → btrfs
Legacy system → aufs (ama overlay2'ye geç)
Storage Driver Değiştirme
Uyarı: Storage driver değiştirilince mevcut container’lar ve image’lar kaybolur!
Yedekleme:
# Image'leri export et
docker save -o images.tar $(docker images -q)
# Container'ları commit et
for c in $(docker ps -aq); do
docker commit $c backup_$c
done
Driver değiştirme:
# Docker'ı durdur
sudo systemctl stop docker
# Mevcut data'yı yedekle
sudo mv /var/lib/docker /var/lib/docker.bak
# daemon.json düzenle
sudo vim /etc/docker/daemon.json
# Docker'ı başlat
sudo systemctl start docker
# Image'leri import et
docker load -i images.tar
20.3 SELinux ve Volume Etiketi Pratikleri
SELinux (Security-Enhanced Linux), mandatory access control (MAC) sağlar. Red Hat, CentOS, Fedora’da varsayılan olarak aktiftir.
SELinux Temelleri
SELinux modları:
# Mevcut mod
getenforce
Çıktılar:
Enforcing: SELinux aktif, policy enforce ediliyorPermissive: SELinux aktif, sadece log yapıyor (enforce etmiyor)Disabled: SELinux kapalı
Geçici mod değiştirme:
# Permissive'e geç
sudo setenforce 0
# Enforcing'e geç
sudo setenforce 1
Kalıcı değiştirme:
# /etc/selinux/config
SELINUX=enforcing # veya permissive, disabled
SELinux ve Docker
Docker, SELinux ile entegre çalışır. Container process’lere svirt_lxc_net_t tipi atanır.
Container SELinux context:
# Container process'i
docker run -d --name web nginx
# SELinux context
ps -eZ | grep nginx
Çıktı:
system_u:system_r:svirt_lxc_net_t:s0:c123,c456 ... nginx
Label yapısı:
user:role:type:level:category
system_u: SELinux usersystem_r: SELinux rolesvirt_lxc_net_t: SELinux type (container process’ler için)s0: Sensitivity levelc123,c456: Categories (MCS - Multi-Category Security)
Her container farklı category’lere sahiptir, böylece container’lar birbirinden izole edilir.
Volume Mount ve SELinux
SELinux aktifken volume mount sorunları yaygındır.
Sorun:
docker run -v /host/data:/container/data nginx
Container içinde permission denied hatası:
nginx: [emerg] open() "/container/data/file" failed (13: Permission denied)
Neden:
Host dosyası default_t veya user_home_t etiketine sahip. Container svirt_lxc_net_t process’i bu dosyalara erişemez.
Çözüm 1: :z etiketi (shared access)
docker run -v /host/data:/container/data:z nginx
:z bayrağı, mount edilen dizine svirt_sandbox_file_t etiketi ekler. Birden fazla container erişebilir.
Label kontrolü:
ls -Z /host/data
Öncesi:
unconfined_u:object_r:user_home_t:s0 /host/data
Sonrası:
system_u:object_r:svirt_sandbox_file_t:s0 /host/data
Çözüm 2: :Z etiketi (private access)
docker run -v /host/data:/container/data:Z nginx
:Z bayrağı, mount edilen dizine container’a özel etiket ekler. Sadece bu container erişebilir.
Label:
system_u:object_r:svirt_sandbox_file_t:s0:c123,c456 /host/data
c123,c456 bu container’a özgüdür.
Farklar:
| Bayrak | Access | Label | Kullanım |
|---|---|---|---|
:z |
Shared (multi-container) | Genel svirt_sandbox_file_t |
Config files, shared data |
:Z |
Private (single-container) | Container-specific label | Database data, private files |
Manuel Relabeling
Bazen manuel relabel gerekir.
chcon ile:
# Dosyaya label ata
sudo chcon -t svirt_sandbox_file_t /host/data
# Recursive
sudo chcon -R -t svirt_sandbox_file_t /host/data
semanage ve restorecon ile (önerilen):
# Policy'ye ekle
sudo semanage fcontext -a -t svirt_sandbox_file_t "/host/data(/.*)?"
# Uygula
sudo restorecon -Rv /host/data
Bu yöntem persistent’tır. Sistem yeniden başlatıldığında label korunur.
SELinux Policy Modülleri
Custom policy oluşturabilirsiniz.
Policy oluşturma:
# Audit log'dan policy üret
sudo audit2allow -a -M mydocker
# Policy yükle
sudo semodule -i mydocker.pp
Örnek: Nginx custom port
Nginx 8080 portunda çalışıyorsa ve SELinux engelliyor:
# Port policy ekle
sudo semanage port -a -t http_port_t -p tcp 8080
# Kontrol
sudo semanage port -l | grep http_port_t
Docker SELinux Seçenekleri
SELinux disable (container için):
docker run --security-opt label=disable nginx
Uyarı: Güvenlik riski! Sadece debug için kullanın.
Custom label:
docker run --security-opt label=level:s0:c100,c200 nginx
Troubleshooting
SELinux denial’ları kontrol:
# Audit log
sudo ausearch -m AVC -ts recent
# Daha okunabilir
sudo ausearch -m AVC -ts recent | audit2why
Örnek denial:
type=AVC msg=audit(1234567890.123:456): avc: denied { read } for
pid=12345 comm="nginx" name="index.html" dev="sda1" ino=67890
scontext=system_u:system_r:svirt_lxc_net_t:s0:c123,c456
tcontext=unconfined_u:object_r:user_home_t:s0
tclass=file permissive=0
Çözüm:
# File context düzelt
sudo chcon -t svirt_sandbox_file_t /path/to/index.html
# Veya :z/:Z kullan
docker run -v /path:/container:z nginx
Best Practices
Volume mount:
- Read-only veriler için
:zkullanın - Database gibi özel veriler için
:Zkullanın - Gerektiğinde manuel relabel yapın (
restorecon)
Production:
- SELinux’u enforcing modda tutun
label=disablekullanmayın- Denial’ları düzenli kontrol edin
- Custom policy’ler dokümante edin
Development:
- Permissive mode (geçici)
- Denial’ları analiz edip çözün
- Production’a geçmeden enforcing test edin
Özet
Namespaces:
- İzolasyon mekanizması
- 7 tip namespace (PID, NET, MNT, UTS, IPC, USER, CGROUP)
- Her namespace unique inode
- Hiyerarşik yapı (parent → child)
Cgroups:
- Kaynak limitleri ve accounting
- v1 (ayrı controller’lar) vs v2 (unified)
- CPU, memory, blkio, pids limitleri
- PSI (Pressure Stall Information) v2’de
Storage Drivers:
- overlay2: Modern, hızlı, önerilen
- devicemapper: LVM tabanlı, enterprise
- btrfs/zfs: Advanced features
- Driver seçimi kernel ve kullanım senaryosuna bağlı
SELinux:
- MAC (Mandatory Access Control)
- Container process’ler
svirt_lxc_net_ttipi - Volume mount için
:z(shared) veya:Z(private) - Production’da enforcing modda tutun
- Denial’ları
ausearchveaudit2whyile analiz edin
Linux kernel özellikleri, Docker’ın güvenlik ve izolasyon mekanizmalarının temelidir. Bu özellikleri anlamak, production ortamlarında karşılaşılan sorunları çözmek ve güvenli sistemler oluşturmak için kritiktir.
21. Yedekleme / Kurtarma / Taşıma Senaryoları
Production ortamlarında veri kaybı felaket senaryolarıdır. Docker environment’ları için kapsamlı yedekleme ve kurtarma stratejileri oluşturmak kritiktir. Bu bölümde volume yedekleme, image transfer ve disaster recovery konularını detaylı şekilde inceleyeceğiz.
21.1 Volume Yedekleme, Image Export/Import
Volume Yedekleme Stratejileri
Docker volume’ları /var/lib/docker/volumes/ dizininde saklanır. Yedekleme için birden fazla yöntem vardır.
Yöntem 1: tar ile Yedekleme (En Yaygın)
Yedekleme:
# Volume'u kullanarak geçici container
docker run --rm \
-v myvolume:/volume \
-v $(pwd):/backup \
alpine \
tar czf /backup/myvolume-backup-$(date +%Y%m%d-%H%M%S).tar.gz -C /volume .
Açıklama:
--rm: Container işi bitince silinir-v myvolume:/volume: Yedeklenecek volume-v $(pwd):/backup: Host’taki backup dizinitar czf: Sıkıştırarak arşivle-C /volume .: Volume içeriğini arşivle
Geri yükleme:
# Yeni volume oluştur
docker volume create myvolume-restored
# Geri yükle
docker run --rm \
-v myvolume-restored:/volume \
-v $(pwd):/backup \
alpine \
tar xzf /backup/myvolume-backup-20250930-120000.tar.gz -C /volume
Yöntem 2: rsync ile İnkremental Yedekleme
Yedekleme:
# Volume'u mount eden container
docker run -d \
--name volume-backup-helper \
-v myvolume:/volume \
alpine sleep 3600
# rsync ile yedekleme
docker exec volume-backup-helper \
sh -c "apk add --no-cache rsync && \
rsync -av /volume/ /backup/"
# Temizlik
docker stop volume-backup-helper
docker rm volume-backup-helper
Avantaj: Sadece değişen dosyalar kopyalanır (inkremental).
Yöntem 3: Volume Plugin ile Yedekleme
REX-Ray, Portworx gibi plugin’ler:
# Snapshot oluştur
docker volume create --driver rexray/ebs \
--opt snapshot=vol-12345 \
myvolume-snapshot
Yöntem 4: Database-Specific Backup
PostgreSQL örneği:
# pg_dump ile yedekleme
docker exec postgres \
pg_dump -U postgres -d mydb \
> mydb-backup-$(date +%Y%m%d).sql
# Geri yükleme
docker exec -i postgres \
psql -U postgres -d mydb \
< mydb-backup-20250930.sql
MySQL örneği:
# mysqldump ile yedekleme
docker exec mysql \
mysqldump -u root -ppassword mydb \
> mydb-backup-$(date +%Y%m%d).sql
# Geri yükleme
docker exec -i mysql \
mysql -u root -ppassword mydb \
< mydb-backup-20250930.sql
Image Export/Import
Docker image’larını transfer etmek için iki yöntem vardır: save/load ve export/import.
docker save / docker load
save/load, image’ın tüm layer’larını ve metadata’sını korur.
Image save:
# Tek image
docker save -o nginx-backup.tar nginx:latest
# Birden fazla image
docker save -o images-backup.tar nginx:latest postgres:15 redis:alpine
# Pipe ile sıkıştırma
docker save nginx:latest | gzip > nginx-backup.tar.gz
Image load:
# tar'dan load
docker load -i nginx-backup.tar
# Sıkıştırılmış dosyadan
gunzip -c nginx-backup.tar.gz | docker load
Çıktı:
Loaded image: nginx:latest
Avantajlar:
- Tüm layer’lar korunur
- Image history korunur
- Tag’ler korunur
- Multi-arch image’lar desteklenir
Dezavantajlar:
- Büyük dosya boyutu (tüm layer’lar)
- Registry’ye push gerektirmez ama paylaşım zor
docker export / docker import
export/import, çalışan container’ın dosya sistemini flat image olarak dışa aktarır.
Container export:
# Çalışan container'ı export et
docker export mycontainer > container-backup.tar
# Sıkıştırarak
docker export mycontainer | gzip > container-backup.tar.gz
Container import:
# tar'ı image olarak import et
docker import container-backup.tar myapp:restored
# Sıkıştırılmış dosyadan
gunzip -c container-backup.tar.gz | docker import - myapp:restored
Farklar:
| Özellik | save/load | export/import |
|---|---|---|
| Layer’lar | Korunur | Flatten (tek layer) |
| History | Korunur | Kaybolur |
| Metadata | Korunur | Kaybolur (CMD, ENTRYPOINT vb.) |
| Boyut | Büyük | Daha küçük |
| Kullanım | Image transfer | Container snapshot |
Ne zaman ne kullanılır:
- save/load: Image’ı başka sisteme taşıma, offline deployment
- export/import: Container’ın mevcut durumunu yedekleme
Otomatik Yedekleme Script’i
backup.sh:
#!/bin/bash
BACKUP_DIR="/backup"
DATE=$(date +%Y%m%d-%H%M%S)
# Volume yedekleme
for volume in $(docker volume ls -q); do
echo "Backing up volume: $volume"
docker run --rm \
-v $volume:/volume \
-v $BACKUP_DIR:/backup \
alpine \
tar czf /backup/${volume}-${DATE}.tar.gz -C /volume .
done
# Image yedekleme
echo "Backing up images..."
docker save $(docker images -q) | gzip > $BACKUP_DIR/images-${DATE}.tar.gz
# Eski yedekleri temizle (30 günden eski)
find $BACKUP_DIR -name "*.tar.gz" -mtime +30 -delete
echo "Backup completed: $DATE"
Cron ile otomatik çalıştırma:
# Crontab düzenle
crontab -e
# Her gün saat 02:00'de yedekle
0 2 * * * /path/to/backup.sh >> /var/log/docker-backup.log 2>&1
Remote Backup (S3, Azure Blob, etc.)
AWS S3 örneği:
#!/bin/bash
BACKUP_FILE="backup-$(date +%Y%m%d-%H%M%S).tar.gz"
# Volume yedekle
docker run --rm \
-v myvolume:/volume \
-v $(pwd):/backup \
alpine \
tar czf /backup/$BACKUP_FILE -C /volume .
# S3'e yükle
aws s3 cp $BACKUP_FILE s3://my-backup-bucket/docker-backups/
# Local dosyayı sil
rm $BACKUP_FILE
echo "Backup uploaded to S3: $BACKUP_FILE"
Docker ile S3 sync:
docker run --rm \
-v myvolume:/data \
-e AWS_ACCESS_KEY_ID=... \
-e AWS_SECRET_ACCESS_KEY=... \
amazon/aws-cli \
s3 sync /data s3://my-backup-bucket/myvolume/
21.2 Veri Taşıma: Linux → Windows veya Tersine Pratik Zorluklar
Cross-platform veri taşıma, path farkları ve dosya sistemi uyumsuzlukları nedeniyle zorluklar içerir.
Linux → Windows Taşıma
Sorun 1: Path Separators
Linux:
/var/lib/docker/volumes/myvolume/_data
Windows:
C:\ProgramData\Docker\volumes\myvolume\_data
Çözüm: Platform-agnostic path’ler kullanın.
# Yanlış (Linux-specific)
WORKDIR /app/data
# Doğru (her platformda çalışır)
WORKDIR C:/app/data # Windows'ta C:\app\data olur
Sorun 2: Line Endings (CRLF vs LF)
Linux: \n (LF)
Windows: \r\n (CRLF)
Script dosyaları bozulabilir:
# Linux'ta oluşturulmuş script
#!/bin/bash
echo "Hello"
Windows’ta çalıştırıldığında:
bash: ./script.sh: /bin/bash^M: bad interpreter
Çözüm:
# dos2unix ile düzelt
dos2unix script.sh
# veya git
git config --global core.autocrlf input # Linux
git config --global core.autocrlf true # Windows
Dockerfile’da:
# Line ending'leri normalize et
RUN apt-get update && apt-get install -y dos2unix
COPY script.sh /app/
RUN dos2unix /app/script.sh
Sorun 3: File Permissions
Linux permissions (755, 644 vb.) Windows’ta anlamlı değildir.
Yedek sırasında permission kaybı:
# Linux'ta yedekle
docker run --rm -v myvolume:/volume -v $(pwd):/backup alpine \
tar czf /backup/myvolume.tar.gz -C /volume .
# Windows'ta geri yükle
# Permissions kaybolur!
Çözüm:
# ACL bilgilerini dahil et (Linux'ta)
tar --xattrs --acls -czf backup.tar.gz /volume
# Windows'ta permissions önemli değilse ignore et
Sorun 4: Symbolic Links
Linux symbolic link’leri Windows’ta çalışmayabilir.
Tespit:
# Symlink kontrolü
docker run --rm -v myvolume:/volume alpine find /volume -type l
Çözüm:
# Symlink'leri dereference et (gerçek dosyayı kopyala)
tar -czf backup.tar.gz --dereference /volume
Sorun 5: Case Sensitivity
Linux case-sensitive, Windows case-insensitive.
Sorun:
Linux volume:
/data/File.txt
/data/file.txt # Farklı dosyalar
Windows'ta restore edilince:
C:\data\File.txt # file.txt üzerine yazılabilir
Çözüm: Filename collision’ları önceden tespit edin.
# Duplicate name kontrolü
find /volume -type f | tr '[:upper:]' '[:lower:]' | sort | uniq -d
Windows → Linux Taşıma
Sorun 1: Named Pipes
Windows named pipe’ları (\\.\pipe\...) Linux’ta çalışmaz.
Çözüm: Platform-specific konfigürasyon.
# docker-compose.yml
services:
app:
volumes:
- type: bind
source: ${DOCKER_HOST:-unix:///var/run/docker.sock}
target: /var/run/docker.sock # Linux
# Windows: \\.\pipe\docker_engine
Sorun 2: Windows-Specific Binaries
.exe dosyaları Linux’ta çalışmaz.
Çözüm: Multi-platform build.
FROM --platform=$BUILDPLATFORM builder AS build
ARG TARGETOS
ARG TARGETARCH
RUN GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -o app
FROM alpine
COPY --from=build /app .
Taşıma Best Practices
1. Image’ları registry üzerinden taşıyın:
# Linux'ta build
docker build -t username/myapp:latest .
docker push username/myapp:latest
# Windows'ta pull
docker pull username/myapp:latest
2. Volume’ları platform-agnostic formatta yedekleyin:
# Metadata olmadan (pure data)
docker run --rm -v myvolume:/volume -v $(pwd):/backup alpine \
sh -c "cd /volume && tar czf /backup/data.tar.gz --no-acls --no-xattrs ."
3. Platform-specific dosyaları ayırın:
project/
├── docker-compose.yml
├── docker-compose.linux.yml
└── docker-compose.windows.yml
# Linux
docker-compose -f docker-compose.yml -f docker-compose.linux.yml up
# Windows
docker-compose -f docker-compose.yml -f docker-compose.windows.yml up
4. Environment variables kullanın:
services:
app:
volumes:
- ${DATA_PATH:-./data}:/app/data
# Linux
export DATA_PATH=/mnt/data
# Windows
set DATA_PATH=C:\data
21.3 Disaster Recovery Checklist
Yedeklenmesi Gerekenler
1. Docker Volumes
# Tüm volume'ları listele
docker volume ls
# Her volume için backup
for vol in $(docker volume ls -q); do
docker run --rm -v $vol:/volume -v /backup:/backup alpine \
tar czf /backup/$vol-$(date +%Y%m%d).tar.gz -C /volume .
done
2. Docker Images
# Kullanılan image'ları kaydet
docker images --format "{{.Repository}}:{{.Tag}}" > images.txt
# Image'ları export et
docker save $(cat images.txt) | gzip > images-backup.tar.gz
3. Docker Compose Files
# Tüm compose dosyalarını yedekle
tar czf compose-backup.tar.gz \
docker-compose.yml \
.env \
config/
4. Docker Network Configurations
# Network'leri kaydet
docker network ls --format "{{.Name}}\t{{.Driver}}\t{{.Scope}}" > networks.txt
# Custom network'leri export et
for net in $(docker network ls --filter type=custom -q); do
docker network inspect $net > network-$net.json
done
5. Docker Daemon Configuration
# daemon.json
cp /etc/docker/daemon.json daemon.json.backup
# systemd override
cp /etc/systemd/system/docker.service.d/*.conf docker-service-override.backup
6. Container Configuration
# Çalışan container'ları kaydet
docker ps --format "{{.Names}}\t{{.Image}}\t{{.Command}}" > running-containers.txt
# Her container'ın inspect bilgisi
for container in $(docker ps -q); do
docker inspect $container > container-$(docker ps --format "{{.Names}}" --filter id=$container).json
done
7. Registry Credentials
# Docker config
cp ~/.docker/config.json docker-config.json.backup
Disaster Recovery Plan
Seviye 1: Konteyner Kaybı
Senaryo: Tek konteyner çöktü veya silindi.
Kurtarma:
# Container'ı yeniden başlat
docker-compose up -d mycontainer
# Veya manuel
docker run -d \
--name mycontainer \
-v myvolume:/data \
myimage:latest
Süre: 1-5 dakika
Seviye 2: Volume Kaybı
Senaryo: Volume silindi veya corrupt oldu.
Kurtarma:
# Yeni volume oluştur
docker volume create myvolume-new
# Backup'tan geri yükle
docker run --rm \
-v myvolume-new:/volume \
-v /backup:/backup \
alpine \
tar xzf /backup/myvolume-20250930.tar.gz -C /volume
# Container'ı yeni volume ile başlat
docker run -d -v myvolume-new:/data myimage:latest
Süre: 5-30 dakika (volume boyutuna bağlı)
Seviye 3: Host Kaybı
Senaryo: Sunucu tamamen çöktü, yeni sunucu gerekli.
Kurtarma adımları:
1. Yeni host kurulumu:
# Docker kurulumu
curl -fsSL https://get.docker.com | sh
# Docker Compose kurulumu
sudo curl -L "https://github.com/docker/compose/releases/download/v2.23.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
2. Daemon konfigürasyonu:
# Backup'tan restore
sudo cp daemon.json.backup /etc/docker/daemon.json
sudo systemctl restart docker
3. Volume restore:
# Volume'ları oluştur
for vol in $(cat volume-list.txt); do
docker volume create $vol
done
# Backup'tan geri yükle
for backup in /backup/*.tar.gz; do
vol=$(basename $backup .tar.gz)
docker run --rm \
-v $vol:/volume \
-v /backup:/backup \
alpine \
tar xzf /backup/$backup -C /volume
done
4. Image restore:
# Image'ları load et
docker load -i images-backup.tar.gz
# Veya registry'den pull
while read image; do
docker pull $image
done < images.txt
5. Container’ları başlat:
# Compose ile
docker-compose up -d
# Veya manuel
while read line; do
name=$(echo $line | awk '{print $1}')
image=$(echo $line | awk '{print $2}')
docker run -d --name $name $image
done < running-containers.txt
Süre: 1-4 saat (sistem boyutuna bağlı)
Seviye 4: Datacenter Kaybı
Senaryo: Tüm datacenter erişilemez, farklı lokasyonda kurtarma gerekli.
Gereksinimler:
- Off-site backup (S3, Azure Blob, başka datacenter)
- Dokümante edilmiş DR procedure
- Test edilmiş restore process
Kurtarma:
# Remote backup'tan download
aws s3 sync s3://disaster-recovery-bucket/docker-backups/ /recovery/
# Normal Seviye 3 kurtarma prosedürü
# ...
Süre: 4-24 saat (network hızına bağlı)
DR Checklist
Hazırlık (Peacetime):
- Automated backup scriptleri kuruldu
- Backup’lar remote lokasyona kopyalanıyor
- Backup retention policy tanımlandı (30 gün, 12 ay, vb.)
- DR dokümantasyonu hazırlandı
- DR prosedürü test edildi (en az 6 ayda bir)
- Monitoring ve alerting aktif
- Yedek contact listesi güncel
Disaster Sırasında:
- Sorun severity belirlendi (Seviye 1-4)
- Stakeholder’lar bilgilendirildi
- Son backup tarihi kontrol edildi
- Yeni host/datacenter hazırlandı
- Backup’lar erişilebilir durumda
Kurtarma Sırasında:
- Sistem restore edildi
- Container’lar çalıştı
- Volume’lar restore edildi
- Network connectivity test edildi
- Uygulama health check’leri başarılı
- Monitoring yeniden aktif
- Log aggregation çalışıyor
Kurtarma Sonrası:
- Post-mortem raporu yazıldı
- Root cause analizi tamamlandı
- DR prosedürü güncellendi
- Eksik backup’lar belirlendi
- İyileştirmeler planlandı
Backup Retention Strategy
Daily: Son 7 gün
Weekly: Son 4 hafta
Monthly: Son 12 ay
Yearly: Son 5 yıl (compliance için)
Script örneği:
#!/bin/bash
BACKUP_DIR="/backup"
DATE=$(date +%Y%m%d)
DAY=$(date +%A)
MONTH=$(date +%B)
# Daily backup
docker run --rm -v myvolume:/volume -v $BACKUP_DIR/daily:/backup alpine \
tar czf /backup/$DATE.tar.gz -C /volume .
# Weekly backup (her Pazar)
if [ "$DAY" = "Sunday" ]; then
cp $BACKUP_DIR/daily/$DATE.tar.gz $BACKUP_DIR/weekly/week-$(date +%V).tar.gz
fi
# Monthly backup (ayın ilk günü)
if [ $(date +%d) = "01" ]; then
cp $BACKUP_DIR/daily/$DATE.tar.gz $BACKUP_DIR/monthly/$MONTH.tar.gz
fi
# Retention cleanup
find $BACKUP_DIR/daily -mtime +7 -delete
find $BACKUP_DIR/weekly -mtime +28 -delete
find $BACKUP_DIR/monthly -mtime +365 -delete
Testing DR Plan
Quarterly DR drill:
# 1. Simüle edilmiş failure
docker stop $(docker ps -q)
docker volume rm myvolume
# 2. Restore prosedürü
# (DR checklist'i takip et)
# 3. Verification
curl http://localhost/health
docker ps
docker volume ls
# 4. Metrics
# - Restore süresi
# - Data loss (varsa)
# - Encountered issues
Özet
Backup:
- Volume’ları tar ile yedekleyin
- Image’ları
docker saveile export edin - Otomatik backup script’leri oluşturun
- Remote backup lokasyonu kullanın
- Database’ler için native backup araçları kullanın
Cross-platform:
- Path separator’lara dikkat edin
- Line ending’leri normalize edin
- Permission issues farkında olun
- Registry üzerinden transfer tercih edin
Disaster Recovery:
- 4 seviyeli DR planı oluşturun
- Off-site backup zorunlu
- DR prosedürünü test edin
- Retention policy tanımlayın
- Post-mortem analizi yapın
Disaster recovery planı sadece backup almak değildir. Restore prosedürünün test edilmesi, dokümante edilmesi ve ekibin prosedüre aşina olması kritiktir. “Backup yapmak” kolay, “restore etmek” zordur - planınızı mutlaka test edin.
22. Performans ve İnce Ayar (Production İçin)
Production ortamlarında Docker performansı, uygulamanızın yanıt süresi ve kaynak kullanımını doğrudan etkiler. Bu bölümde storage driver optimizasyonu, ağ performansı ve sistem tuning’i detaylı şekilde inceleyeceğiz.
22.1 Storage Driver Seçimi ve Etkileri
Storage Driver Performance Karşılaştırması
Farklı workload’lar için farklı storage driver’lar daha uygun olabilir.
Benchmark kurulumu:
# FIO (Flexible I/O Tester) kurulumu
sudo apt-get install fio
# Test container
docker run -it --rm \
-v testvolume:/data \
ubuntu:22.04 bash
I/O Performance testi:
# Sequential read
fio --name=seqread --rw=read --bs=1M --size=1G --numjobs=1 --filename=/data/testfile
# Sequential write
fio --name=seqwrite --rw=write --bs=1M --size=1G --numjobs=1 --filename=/data/testfile
# Random read (IOPS)
fio --name=randread --rw=randread --bs=4k --size=1G --numjobs=4 --filename=/data/testfile
# Random write (IOPS)
fio --name=randwrite --rw=randwrite --bs=4k --size=1G --numjobs=4 --filename=/data/testfile
Benchmark sonuçları (örnek):
| Driver | Sequential Read | Sequential Write | Random Read IOPS | Random Write IOPS |
|---|---|---|---|---|
| overlay2 | 850 MB/s | 750 MB/s | 45K | 38K |
| devicemapper (direct-lvm) | 820 MB/s | 680 MB/s | 42K | 32K |
| btrfs | 780 MB/s | 650 MB/s | 38K | 28K |
| zfs | 800 MB/s | 700 MB/s | 40K | 35K |
| vfs (no CoW) | 900 MB/s | 800 MB/s | 50K | 42K |
Not: Sayılar donanıma göre değişir. Bu örnekler SSD disk üzerindeki göreceli performansı gösterir.
Workload’a Göre Driver Seçimi
1. Web uygulamaları (read-heavy):
Önerilen: overlay2
Neden: Hızlı read performance, düşük overhead
2. Database (write-intensive):
Önerilen: devicemapper (direct-lvm) veya ZFS
Neden: Consistent write performance, snapshot desteği
3. Build servers (çok sayıda layer):
Önerilen: overlay2 with pruning
Neden: Layer cache efficiency
4. Log-heavy uygulamalar:
Önerilen: Volume mount (bypass storage driver)
Neden: Direct disk I/O
Storage Driver Değiştirme Etkisi
Test senaryosu:
# overlay2 ile build
time docker build -t myapp:overlay2 .
# devicemapper ile build
# (daemon.json değişikliği sonrası)
time docker build -t myapp:devicemapper .
Tipik sonuçlar:
overlay2: Build time: 45s
devicemapper: Build time: 68s (50% daha yavaş)
btrfs: Build time: 72s (60% daha yavaş)
Volume vs Storage Driver
Performance karşılaştırması:
# Storage driver üzerinden (overlay2)
docker run --rm -v /container/path alpine dd if=/dev/zero of=/container/path/test bs=1M count=1000
# Named volume (direct mount)
docker volume create testvol
docker run --rm -v testvol:/data alpine dd if=/dev/zero of=/data/test bs=1M count=1000
# Bind mount
docker run --rm -v /host/path:/data alpine dd if=/dev/zero of=/data/test bs=1M count=1000
Sonuç:
Storage driver: ~600 MB/s
Named volume: ~850 MB/s (40% daha hızlı)
Bind mount: ~850 MB/s (40% daha hızlı)
Öneri: Database, log gibi I/O intensive veriler için volume kullanın.
22.2 Overlay2 Tuning, Devicemapper Parametreleri
Overlay2 Optimizasyonu
Overlay2, modern sistemlerde varsayılan driver olsa da tuning yapılabilir.
1. XFS Filesystem ile Overlay2
Overlay2, ext4 ve xfs üzerinde çalışır ancak xfs daha iyi performans verir.
XFS mount options:
# /etc/fstab
/dev/sdb1 /var/lib/docker xfs defaults,pquota 0 0
pquota: Project quotas (overlay2 için gerekli)
XFS mount kontrolü:
mount | grep docker
# /dev/sdb1 on /var/lib/docker type xfs (rw,relatime,attr2,inode64,logbufs=8,logbsize=32k,pquota)
2. Inode Limitleri
Overlay2, çok sayıda inode kullanabilir.
Inode kullanımı kontrolü:
df -i /var/lib/docker
Çıktı:
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/sdb1 512000 450000 62000 88% /var/lib/docker
%88 tehlikeli seviye!
Çözüm: Eski layer’ları temizle:
docker system prune -a
docker builder prune
3. Mount Options
daemon.json optimizasyonu:
{
"storage-driver": "overlay2",
"storage-opts": [
"overlay2.override_kernel_check=true",
"overlay2.size=10G"
]
}
overlay2.size: Container’ın maksimum disk kullanımı (quota)
4. Layer Limit
Çok derin layer’lar (100+) performansı düşürür.
Layer sayısı kontrolü:
docker history myimage --no-trunc | wc -l
Optimizasyon: Multi-stage build ile layer’ları minimize edin.
# Kötü: Her RUN ayrı layer (50+ layer)
FROM ubuntu
RUN apt-get update
RUN apt-get install -y python3
RUN apt-get install -y pip
# ... 47 satır daha
# İyi: Birleştirilmiş layer'lar (5-10 layer)
FROM ubuntu
RUN apt-get update && apt-get install -y \
python3 \
pip \
# ... diğer paketler
&& rm -rf /var/lib/apt/lists/*
5. Disk Space Management
Overlay2 disk kullanımı:
# Driver data kullanımı
docker system df
# Detaylı bilgi
docker system df -v
Otomatik temizlik:
# Cron job (her gün saat 02:00)
0 2 * * * /usr/bin/docker system prune -af --volumes --filter "until=72h"
Devicemapper Tuning
Devicemapper kullanıyorsanız (eski sistemler, RHEL 7, vb.) tuning kritiktir.
1. Direct-LVM Setup (Production için zorunlu)
loop-lvm (varsayılan) çok yavaştır, kullanmayın!
Direct-LVM kurulumu:
# LVM paketleri
sudo yum install -y lvm2 device-mapper-persistent-data
# Physical volume oluştur
sudo pvcreate /dev/sdb
# Volume group oluştur
sudo vgcreate docker /dev/sdb
# Thin pool oluştur (disk boyutunun %95'i)
sudo lvcreate --wipesignatures y -n thinpool docker -l 95%VG
sudo lvcreate --wipesignatures y -n thinpoolmeta docker -l 1%VG
# Thin pool'u dönüştür
sudo lvconvert -y --zero n -c 512K --thinpool docker/thinpool --poolmetadata docker/thinpoolmeta
# Auto-extend profili
sudo vim /etc/lvm/profile/docker-thinpool.profile
docker-thinpool.profile:
activation {
thin_pool_autoextend_threshold=80
thin_pool_autoextend_percent=20
}
Profili uygula:
sudo lvchange --metadataprofile docker-thinpool docker/thinpool
2. Devicemapper Daemon Config
/etc/docker/daemon.json:
{
"storage-driver": "devicemapper",
"storage-opts": [
"dm.thinpooldev=/dev/mapper/docker-thinpool",
"dm.use_deferred_removal=true",
"dm.use_deferred_deletion=true",
"dm.fs=ext4",
"dm.basesize=20G"
]
}
Parametreler:
dm.thinpooldev: Thin pool device pathdm.use_deferred_removal: Lazy device removal (performans)dm.use_deferred_deletion: Background deletiondm.fs: Filesystem tipi (ext4 veya xfs)dm.basesize: Her container’ın maksimum disk boyutu
3. Monitoring
Thin pool kullanımı:
# LVM status
sudo lvs -o+seg_monitor
# Docker devicemapper info
docker info | grep -A 20 "Storage Driver"
Çıktı:
Storage Driver: devicemapper
Pool Name: docker-thinpool
Pool Blocksize: 524.3 kB
Base Device Size: 21.47 GB
Data file: /dev/mapper/docker-thinpool
Metadata file: /dev/mapper/docker-thinpool_tmeta
Data Space Used: 15.2 GB
Data Space Total: 95.4 GB
Data Space Available: 80.2 GB
Metadata Space Used: 18.4 MB
Metadata Space Total: 1.01 GB
Metadata Space Available: 991.6 MB
Kritik metrikler:
- Data Space > %80 → Disk genişlet
- Metadata Space > %80 → Metadata genişlet
4. Performance Tuning
Block size optimizasyonu:
{
"storage-opts": [
"dm.blocksize=512K",
"dm.loopdatasize=200G",
"dm.loopmetadatasize=4G"
]
}
I/O Scheduler:
# Deadline scheduler (SSD için)
echo deadline > /sys/block/sdb/queue/scheduler
# /etc/udev/rules.d/60-scheduler.rules
ACTION=="add|change", KERNEL=="sd[a-z]", ATTR{queue/scheduler}="deadline"
22.3 Ağ Performansı, Kullanıcı Alanı Proxy’lerinin Etkisi
Docker Network Performance
Docker networking varsayılan olarak bridge mode kullanır. Bu, userland proxy ile çalışır ve performans overhead yaratır.
1. Userland Proxy vs Hairpin NAT
Userland proxy (varsayılan):
External Request → docker-proxy (userspace) → container
Hairpin NAT (iptables):
External Request → iptables (kernel) → container
Performance farkı:
Userland proxy: ~15-20% overhead
Hairpin NAT: ~2-5% overhead
Hairpin NAT aktivasyonu:
{
"userland-proxy": false
}
Restart gerekli:
sudo systemctl restart docker
Test:
# Container çalıştır
docker run -d -p 8080:80 nginx
# Netstat ile kontrol
sudo netstat -tlnp | grep 8080
Userland proxy aktifse:
tcp 0 0 0.0.0.0:8080 0.0.0.0:* LISTEN 12345/docker-proxy
Hairpin NAT aktifse:
# docker-proxy yok, sadece iptables rule'ları
sudo iptables -t nat -L -n | grep 8080
2. Host Network Mode
En yüksek performans için host network kullanın.
Bridge vs Host performance:
# Bridge mode
docker run -d --name web-bridge -p 8080:80 nginx
# Host mode
docker run -d --name web-host --network host nginx
Benchmark (wrk):
# Bridge mode
wrk -t4 -c100 -d30s http://localhost:8080
# Requests/sec: 35,000
# Host mode
wrk -t4 -c100 -d30s http://localhost:80
# Requests/sec: 52,000 (48% daha hızlı)
Trade-off: Host mode, port conflict riski taşır ve izolasyon yoktur.
3. macvlan Network
Container’lara fiziksel ağdan direkt IP vermek yüksek performans sağlar.
macvlan oluşturma:
docker network create -d macvlan \
--subnet=192.168.1.0/24 \
--gateway=192.168.1.1 \
-o parent=eth0 \
macvlan-net
Container başlatma:
docker run -d \
--network macvlan-net \
--ip 192.168.1.100 \
nginx
Performance: Bridge’e göre %20-30 daha hızlı.
4. Container-to-Container Communication
Aynı host’ta container’lar arası iletişim:
# Custom network (DNS enabled)
docker network create mynet
docker run -d --name web --network mynet nginx
docker run -d --name api --network mynet myapi
# 'web' container içinden 'api'ye erişim
docker exec web curl http://api:8080
Performance: Embedded DNS çözümleme ~0.1ms overhead.
Alternatif: /etc/hosts mount (daha hızlı ama statik):
docker run -d --add-host api:172.17.0.3 nginx
5. MTU (Maximum Transmission Unit) Tuning
MTU mismatch, packet fragmentation’a yol açar ve performansı düşürür.
MTU kontrolü:
# Host MTU
ip link show eth0 | grep mtu
# Docker bridge MTU
ip link show docker0 | grep mtu
# Container MTU
docker exec mycontainer ip link show eth0 | grep mtu
Eğer farklıysa, daemon.json’da ayarlayın:
{
"mtu": 1500
}
Jumbo frames kullanıyorsanız:
{
"mtu": 9000
}
6. Network Benchmark
iperf3 ile bandwidth testi:
Server container:
docker run -d --name iperf-server -p 5201:5201 networkstatic/iperf3 -s
Client container (aynı host):
docker run --rm networkstatic/iperf3 -c iperf-server
Çıktı:
[ ID] Interval Transfer Bitrate
[ 5] 0.00-10.00 sec 10.2 GBytes 8.76 Gbits/sec
Cross-host test (overlay network):
# Host 1
docker run -d --name iperf-server --network overlay-net -p 5201:5201 networkstatic/iperf3 -s
# Host 2
docker run --rm --network overlay-net networkstatic/iperf3 -c iperf-server
Typical results:
- Same host, host network: ~40 Gbps
- Same host, bridge: ~20 Gbps
- Cross-host, overlay (no encryption): ~9 Gbps
- Cross-host, overlay (encrypted): ~2 Gbps
7. Overlay Network Encryption Overhead
Docker Swarm overlay network’lerde encryption optional’dır.
Encrypted overlay:
docker network create --driver overlay --opt encrypted mynet
Performance impact: ~70-80% throughput düşüşü (encryption overhead)
Öneri: Güvenli network içindeyseniz encryption’ı devre dışı bırakın.
8. Connection Tracking (conntrack) Limitleri
Yüksek trafikli sistemlerde conntrack tablosu dolabilir.
Mevcut limit:
sysctl net.netfilter.nf_conntrack_max
Kullanım:
cat /proc/sys/net/netfilter/nf_conntrack_count
Limit artırma:
# /etc/sysctl.conf
net.netfilter.nf_conntrack_max = 262144
net.netfilter.nf_conntrack_tcp_timeout_established = 1200
# Uygula
sudo sysctl -p
9. TCP Tuning
Kernel TCP parametreleri Docker performance’ını etkiler.
Optimal ayarlar:
# /etc/sysctl.conf
# TCP buffer'ları artır
net.core.rmem_max = 134217728
net.core.wmem_max = 134217728
net.ipv4.tcp_rmem = 4096 87380 67108864
net.ipv4.tcp_wmem = 4096 65536 67108864
# TCP window scaling
net.ipv4.tcp_window_scaling = 1
# TCP timestamp
net.ipv4.tcp_timestamps = 1
# TCP congestion control (BBR)
net.core.default_qdisc = fq
net.ipv4.tcp_congestion_control = bbr
# Connection backlog
net.core.somaxconn = 4096
net.ipv4.tcp_max_syn_backlog = 8192
# TIME_WAIT socket reuse
net.ipv4.tcp_tw_reuse = 1
sudo sysctl -p
BBR congestion control (Google):
BBR, yüksek latency network’lerde %10-20 throughput artışı sağlar.
10. Load Balancer Optimizasyonu
Production’da Nginx/HAProxy load balancer kullanıyorsanız:
Nginx upstream keepalive:
upstream backend {
server container1:8080;
server container2:8080;
server container3:8080;
keepalive 32; # Connection pool
}
server {
location / {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
}
Performance impact: Connection reuse ile %30-40 latency düşüşü.
Monitoring ve Profiling
Network monitoring:
# Container network stats
docker stats --format "table {{.Name}}\t{{.NetIO}}"
# iftop (realtime bandwidth)
sudo docker run -it --rm --net=host \
williamyeh/iftop -i docker0
# tcpdump (packet capture)
sudo tcpdump -i docker0 -w capture.pcap
Analysis:
# Wireshark ile analiz
wireshark capture.pcap
# Retransmission oranı
tshark -r capture.pcap -q -z io,stat,1,"AVG(tcp.analysis.retransmission)COUNT(tcp.analysis.retransmission)"
Özet ve Best Practices
Storage:
- Modern sistemlerde overlay2 kullanın
- XFS filesystem tercih edin
- I/O intensive workload’lar için volume kullanın
- Düzenli
docker system pruneçalıştırın - Layer sayısını minimize edin (multi-stage build)
- Devicemapper kullanıyorsanız mutlaka direct-LVM
Network:
- Production’da
userland-proxy: falseyapın - Yüksek throughput için host network düşünün
- Container-to-container için custom network (DNS)
- MTU’yu host network ile eşleştirin
- Overlay encryption’ı sadece gerekirse kullanın
- TCP tuning yapın (BBR, buffer’lar)
- conntrack limitlerini artırın
Monitoring:
docker statsile kaynak kullanımını izleyin- cAdvisor + Prometheus + Grafana stack’i kurun
- Network latency’yi düzenli ölçün
- I/O wait’i izleyin (
iostat) - Bottleneck’leri profiling ile tespit edin
Testing:
- Benchmark yapın (fio, iperf3, wrk)
- Load testing (k6, Locust, JMeter)
- Chaos engineering (pumba, toxiproxy)
- Production-like test environment
Performance tuning, ölçüm → analiz → optimizasyon döngüsüdür. Değişiklikleri production’a almadan önce mutlaka test edin ve metriklerle doğrulayın. Premature optimization tehlikelidir - önce bottleneck’leri ölçün, sonra optimize edin.
23. Örnek Projeler / Case-Study’ler (Adım adım)
Bu bölümde teorik bilgileri pratiğe dönüştürerek gerçek dünya senaryolarında Docker kullanımını adım adım inceleyeceğiz. Her proje, baştan sona tüm detaylarıyla açıklanmıştır.
23.1 Basit Node.js Uygulamasını Containerize Etme (Linux Örneği) — Tam Setup
Proje Yapısı
Basit bir Express.js REST API oluşturacağız.
Dizin yapısı:
nodejs-app/
├── package.json
├── package-lock.json
├── server.js
├── .dockerignore
├── Dockerfile
├── docker-compose.yml
└── README.md
Adım 1: Node.js Uygulaması Oluşturma
package.json:
{
"name": "nodejs-docker-app",
"version": "1.0.0",
"description": "Simple Node.js app for Docker tutorial",
"main": "server.js",
"scripts": {
"start": "node server.js",
"dev": "nodemon server.js"
},
"dependencies": {
"express": "^4.18.2"
},
"devDependencies": {
"nodemon": "^3.0.1"
}
}
server.js:
const express = require('express');
const app = express();
const PORT = process.env.PORT || 3000;
app.use(express.json());
// Health check endpoint
app.get('/health', (req, res) => {
res.status(200).json({
status: 'healthy',
timestamp: new Date().toISOString(),
uptime: process.uptime()
});
});
// Main endpoint
app.get('/', (req, res) => {
res.json({
message: 'Hello from Docker!',
environment: process.env.NODE_ENV || 'development',
version: process.env.APP_VERSION || '1.0.0'
});
});
// Sample data endpoint
app.get('/api/users', (req, res) => {
const users = [
{ id: 1, name: 'Alice', email: 'alice@example.com' },
{ id: 2, name: 'Bob', email: 'bob@example.com' }
];
res.json(users);
});
// Error handling
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({ error: 'Something went wrong!' });
});
app.listen(PORT, '0.0.0.0', () => {
console.log(`Server running on port ${PORT}`);
console.log(`Environment: ${process.env.NODE_ENV || 'development'}`);
});
Adım 2: .dockerignore Oluşturma
.dockerignore:
node_modules
npm-debug.log
.git
.gitignore
README.md
.env
.DS_Store
Adım 3: Dockerfile Oluşturma
Dockerfile (Production-ready):
# syntax=docker/dockerfile:1.4
# Build stage
FROM node:18-alpine AS builder
WORKDIR /app
# Copy dependency files
COPY package*.json ./
# Install dependencies
RUN npm ci --only=production
# Production stage
FROM node:18-alpine
# Add metadata
LABEL maintainer="your-email@example.com"
LABEL version="1.0.0"
LABEL description="Node.js Express API"
WORKDIR /app
# Copy dependencies from builder
COPY --from=builder /app/node_modules ./node_modules
# Copy application code
COPY . .
# Create non-root user
RUN addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001 && \
chown -R nodejs:nodejs /app
USER nodejs
# Expose port
EXPOSE 3000
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD node -e "require('http').get('http://localhost:3000/health', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"
# Start application
CMD ["node", "server.js"]
Adım 4: Image Build Etme
# Build
docker build -t nodejs-app:1.0.0 .
# Build with BuildKit cache
DOCKER_BUILDKIT=1 docker build \
--cache-from nodejs-app:cache \
-t nodejs-app:1.0.0 \
-t nodejs-app:latest \
.
# Image boyutunu kontrol et
docker images nodejs-app
Çıktı:
REPOSITORY TAG SIZE
nodejs-app 1.0.0 125MB
nodejs-app latest 125MB
Adım 5: Container Çalıştırma
Basit çalıştırma:
docker run -d \
--name nodejs-app \
-p 3000:3000 \
-e NODE_ENV=production \
-e APP_VERSION=1.0.0 \
--restart unless-stopped \
nodejs-app:1.0.0
Test:
# Health check
curl http://localhost:3000/health
# Main endpoint
curl http://localhost:3000/
# API endpoint
curl http://localhost:3000/api/users
Adım 6: Docker Compose ile Orchestration
docker-compose.yml (Development):
version: "3.9"
services:
app:
build:
context: .
dockerfile: Dockerfile
container_name: nodejs-app-dev
ports:
- "3000:3000"
volumes:
- ./:/app
- /app/node_modules
environment:
- NODE_ENV=development
- PORT=3000
command: npm run dev
restart: unless-stopped
networks:
- app-network
networks:
app-network:
driver: bridge
docker-compose.prod.yml (Production):
version: "3.9"
services:
app:
image: nodejs-app:1.0.0
container_name: nodejs-app-prod
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- APP_VERSION=1.0.0
deploy:
resources:
limits:
cpus: '1.0'
memory: 512M
reservations:
cpus: '0.5'
memory: 256M
restart_policy:
condition: on-failure
max_attempts: 3
healthcheck:
test: ["CMD", "node", "-e", "require('http').get('http://localhost:3000/health', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"]
interval: 30s
timeout: 3s
retries: 3
networks:
- app-network
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
networks:
app-network:
driver: bridge
Çalıştırma:
# Development
docker-compose up -d
# Production
docker-compose -f docker-compose.prod.yml up -d
# Logs
docker-compose logs -f app
# Stop
docker-compose down
Adım 7: Monitoring ve Debugging
Logs:
# Container logs
docker logs -f nodejs-app
# Son 100 satır
docker logs --tail 100 nodejs-app
# Timestamp ile
docker logs -t nodejs-app
Container içine girme:
docker exec -it nodejs-app sh
# İçinde
ps aux
netstat -tlnp
env
Resource kullanımı:
docker stats nodejs-app
Adım 8: Production Optimization
Multi-stage build ile daha küçük image:
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production && npm cache clean --force
FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app/node_modules ./node_modules
COPY . .
RUN addgroup -g 1001 nodejs && adduser -S -u 1001 -G nodejs nodejs
USER nodejs
EXPOSE 3000
CMD ["node", "server.js"]
Sonuç: ~125MB → ~70MB (45% küçülme)
23.2 .NET Core Uygulamasını Windows Container’a Taşıma — Tam Walkthrough
Proje Yapısı
ASP.NET Core Web API projesi oluşturacağız.
dotnet-app/
├── DotnetApp/
│ ├── Controllers/
│ │ └── WeatherForecastController.cs
│ ├── Program.cs
│ ├── DotnetApp.csproj
│ └── appsettings.json
├── Dockerfile
├── .dockerignore
└── docker-compose.yml
Adım 1: .NET Core Projesi Oluşturma
# .NET SDK kurulu olmalı
dotnet --version
# Yeni Web API projesi
dotnet new webapi -n DotnetApp
cd DotnetApp
# Test
dotnet run
Program.cs:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
// Health checks
builder.Services.AddHealthChecks();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
// Health check endpoint
app.MapHealthChecks("/health");
app.Run();
WeatherForecastController.cs:
using Microsoft.AspNetCore.Mvc;
namespace DotnetApp.Controllers;
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild",
"Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
private readonly ILogger<WeatherForecastController> _logger;
public WeatherForecastController(ILogger<WeatherForecastController> logger)
{
_logger = logger;
}
[HttpGet(Name = "GetWeatherForecast")]
public IEnumerable<WeatherForecast> Get()
{
_logger.LogInformation("WeatherForecast endpoint called");
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
})
.ToArray();
}
}
public class WeatherForecast
{
public DateOnly Date { get; set; }
public int TemperatureC { get; set; }
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
public string? Summary { get; set; }
}
Adım 2: .dockerignore Oluşturma
.dockerignore:
bin/
obj/
*.user
*.suo
.vs/
.vscode/
*.log
Adım 3: Windows Container Dockerfile
Dockerfile:
# Build stage
FROM mcr.microsoft.com/dotnet/sdk:8.0-nanoserver-ltsc2022 AS build
WORKDIR /src
# Copy csproj and restore
COPY ["DotnetApp/DotnetApp.csproj", "DotnetApp/"]
RUN dotnet restore "DotnetApp/DotnetApp.csproj"
# Copy everything else and build
COPY . .
WORKDIR "/src/DotnetApp"
RUN dotnet build "DotnetApp.csproj" -c Release -o /app/build
# Publish stage
FROM build AS publish
RUN dotnet publish "DotnetApp.csproj" -c Release -o /app/publish /p:UseAppHost=false
# Runtime stage
FROM mcr.microsoft.com/dotnet/aspnet:8.0-nanoserver-ltsc2022
WORKDIR /app
# Copy published app
COPY --from=publish /app/publish .
# Expose port
EXPOSE 8080
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD powershell -command "try { \
$response = Invoke-WebRequest -Uri http://localhost:8080/health -UseBasicParsing; \
if ($response.StatusCode -eq 200) { exit 0 } else { exit 1 } \
} catch { exit 1 }"
# Entry point
ENTRYPOINT ["dotnet", "DotnetApp.dll"]
Adım 4: Build ve Run
Build:
# Windows Server 2022 host gerekli
docker build -t dotnet-app:1.0.0 .
# Image boyutu
docker images dotnet-app
Run:
docker run -d `
--name dotnet-app `
-p 8080:8080 `
-e ASPNETCORE_ENVIRONMENT=Production `
-e ASPNETCORE_URLS=http://+:8080 `
--restart unless-stopped `
dotnet-app:1.0.0
Test:
# Health check
Invoke-WebRequest -Uri http://localhost:8080/health
# API endpoint
Invoke-WebRequest -Uri http://localhost:8080/WeatherForecast | Select-Object -Expand Content
Adım 5: Docker Compose (Windows)
docker-compose.yml:
version: "3.9"
services:
dotnet-app:
build:
context: .
dockerfile: Dockerfile
container_name: dotnet-app
ports:
- "8080:8080"
environment:
- ASPNETCORE_ENVIRONMENT=Production
- ASPNETCORE_URLS=http://+:8080
networks:
- app-network
restart: unless-stopped
networks:
app-network:
driver: nat
Çalıştırma:
docker-compose up -d
docker-compose logs -f
docker-compose ps
docker-compose down
Adım 6: SQL Server Integration
docker-compose-full.yml:
version: "3.9"
services:
sqlserver:
image: mcr.microsoft.com/mssql/server:2022-latest
container_name: sqlserver
environment:
- ACCEPT_EULA=Y
- SA_PASSWORD=YourStrong@Password123
- MSSQL_PID=Developer
ports:
- "1433:1433"
volumes:
- sqldata:/var/opt/mssql
networks:
- app-network
dotnet-app:
build: .
container_name: dotnet-app
depends_on:
- sqlserver
ports:
- "8080:8080"
environment:
- ASPNETCORE_ENVIRONMENT=Production
- ConnectionStrings__DefaultConnection=Server=sqlserver;Database=AppDb;User Id=sa;Password=YourStrong@Password123;TrustServerCertificate=True
networks:
- app-network
volumes:
sqldata:
networks:
app-network:
driver: nat
Adım 7: Troubleshooting
Common issues:
1. “Container operating system does not match”
# Hyper-V isolation kullan
docker run --isolation=hyperv dotnet-app:1.0.0
2. Port binding hatası
# Reserved port kontrolü
netsh interface ipv4 show excludedportrange protocol=tcp
# Farklı port
docker run -p 8081:8080 dotnet-app:1.0.0
3. Volume mount sorunu
# Absolute path kullan
docker run -v "C:\data":"C:\app\data" dotnet-app:1.0.0
23.3 PostgreSQL + Web Uygulaması Compose ile (Prod vs Dev Farkları)
Proje Yapısı
fullstack-app/
├── backend/
│ ├── src/
│ ├── package.json
│ └── Dockerfile
├── frontend/
│ ├── src/
│ ├── package.json
│ └── Dockerfile
├── docker-compose.yml
├── docker-compose.dev.yml
├── docker-compose.prod.yml
├── .env.example
└── init-db.sql
Backend (Node.js + Express + PostgreSQL)
backend/package.json:
{
"name": "backend",
"version": "1.0.0",
"scripts": {
"start": "node src/server.js",
"dev": "nodemon src/server.js"
},
"dependencies": {
"express": "^4.18.2",
"pg": "^8.11.3",
"cors": "^2.8.5",
"dotenv": "^16.3.1"
},
"devDependencies": {
"nodemon": "^3.0.1"
}
}
backend/src/server.js:
const express = require('express');
const { Pool } = require('pg');
const cors = require('cors');
require('dotenv').config();
const app = express();
const PORT = process.env.PORT || 5000;
// Database connection
const pool = new Pool({
host: process.env.DB_HOST || 'postgres',
port: process.env.DB_PORT || 5432,
database: process.env.DB_NAME || 'appdb',
user: process.env.DB_USER || 'postgres',
password: process.env.DB_PASSWORD || 'postgres'
});
app.use(cors());
app.use(express.json());
// Health check
app.get('/health', async (req, res) => {
try {
await pool.query('SELECT 1');
res.json({ status: 'healthy', database: 'connected' });
} catch (err) {
res.status(500).json({ status: 'unhealthy', error: err.message });
}
});
// Get all users
app.get('/api/users', async (req, res) => {
try {
const result = await pool.query('SELECT * FROM users ORDER BY id');
res.json(result.rows);
} catch (err) {
res.status(500).json({ error: err.message });
}
});
// Create user
app.post('/api/users', async (req, res) => {
const { name, email } = req.body;
try {
const result = await pool.query(
'INSERT INTO users (name, email) VALUES ($1, $2) RETURNING *',
[name, email]
);
res.status(201).json(result.rows[0]);
} catch (err) {
res.status(500).json({ error: err.message });
}
});
app.listen(PORT, '0.0.0.0', () => {
console.log(`Backend running on port ${PORT}`);
});
backend/Dockerfile:
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY src/ ./src/
RUN addgroup -g 1001 nodejs && \
adduser -S nodejs -u 1001 && \
chown -R nodejs:nodejs /app
USER nodejs
EXPOSE 5000
CMD ["node", "src/server.js"]
Frontend (React)
frontend/Dockerfile:
# Build stage
FROM node:18-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Production stage
FROM nginx:alpine
COPY --from=build /app/build /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
frontend/nginx.conf:
server {
listen 80;
server_name localhost;
root /usr/share/nginx/html;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
location /api {
proxy_pass http://backend:5000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
Database Init Script
init-db.sql:
CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
INSERT INTO users (name, email) VALUES
('Alice', 'alice@example.com'),
('Bob', 'bob@example.com'),
('Charlie', 'charlie@example.com');
Docker Compose — Development
docker-compose.dev.yml:
version: "3.9"
services:
postgres:
image: postgres:15-alpine
container_name: postgres-dev
environment:
POSTGRES_DB: appdb
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
ports:
- "5432:5432"
volumes:
- postgres-dev-data:/var/lib/postgresql/data
- ./init-db.sql:/docker-entrypoint-initdb.d/init.sql
networks:
- dev-network
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
backend:
build:
context: ./backend
dockerfile: Dockerfile
container_name: backend-dev
command: npm run dev
ports:
- "5000:5000"
volumes:
- ./backend/src:/app/src
- /app/node_modules
environment:
- NODE_ENV=development
- DB_HOST=postgres
- DB_PORT=5432
- DB_NAME=appdb
- DB_USER=postgres
- DB_PASSWORD=postgres
depends_on:
postgres:
condition: service_healthy
networks:
- dev-network
frontend:
build:
context: ./frontend
dockerfile: Dockerfile
container_name: frontend-dev
ports:
- "3000:80"
volumes:
- ./frontend/src:/app/src
depends_on:
- backend
networks:
- dev-network
volumes:
postgres-dev-data:
networks:
dev-network:
driver: bridge
Docker Compose — Production
docker-compose.prod.yml:
version: "3.9"
services:
postgres:
image: postgres:15-alpine
container_name: postgres-prod
environment:
POSTGRES_DB: ${DB_NAME}
POSTGRES_USER: ${DB_USER}
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
volumes:
- postgres-prod-data:/var/lib/postgresql/data
- ./init-db.sql:/docker-entrypoint-initdb.d/init.sql
secrets:
- db_password
networks:
- prod-network
deploy:
resources:
limits:
cpus: '2.0'
memory: 2G
reservations:
cpus: '1.0'
memory: 1G
restart_policy:
condition: on-failure
max_attempts: 3
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${DB_USER}"]
interval: 30s
timeout: 5s
retries: 3
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
backend:
image: ${REGISTRY}/backend:${VERSION}
container_name: backend-prod
environment:
- NODE_ENV=production
- DB_HOST=postgres
- DB_PORT=5432
- DB_NAME=${DB_NAME}
- DB_USER=${DB_USER}
- DB_PASSWORD_FILE=/run/secrets/db_password
secrets:
- db_password
depends_on:
postgres:
condition: service_healthy
networks:
- prod-network
deploy:
replicas: 3
resources:
limits:
cpus: '1.0'
memory: 512M
restart_policy:
condition: on-failure
healthcheck:
test: ["CMD", "node", "-e", "require('http').get('http://localhost:5000/health')"]
interval: 30s
timeout: 3s
retries: 3
frontend:
image: ${REGISTRY}/frontend:${VERSION}
container_name: frontend-prod
ports:
- "80:80"
- "443:443"
volumes:
- ./ssl:/etc/nginx/ssl:ro
depends_on:
- backend
networks:
- prod-network
deploy:
resources:
limits:
cpus: '0.5'
memory: 256M
restart_policy:
condition: on-failure
secrets:
db_password:
external: true
volumes:
postgres-prod-data:
networks:
prod-network:
driver: bridge
Environment Variables
.env.example:
# Database
DB_NAME=appdb
DB_USER=postgres
DB_PASSWORD=changeme
# Application
NODE_ENV=production
VERSION=1.0.0
REGISTRY=registry.example.com
Dev vs Prod Farkları Özeti
| Özellik | Development | Production |
|---|---|---|
| Volumes | Source code mount | Sadece data volumes |
| Ports | Tüm servisler expose | Sadece frontend expose |
| Secrets | Plain environment vars | Docker secrets |
| Resources | Limit yok | CPU/Memory limitleri |
| Replicas | 1 | 3+ (load balancing) |
| Healthchecks | Basit veya yok | Detaylı ve frequent |
| Logging | stdout | json-file with rotation |
| Image | Local build | Registry’den pull |
| Restart | unless-stopped | on-failure with retry |
Çalıştırma
Development:
docker-compose -f docker-compose.dev.yml up -d
docker-compose -f docker-compose.dev.yml logs -f
Production:
# Secret oluştur
echo "SuperSecretPassword123" | docker secret create db_password -
# Environment variables
export DB_NAME=appdb
export DB_USER=postgres
export VERSION=1.0.0
export REGISTRY=myregistry.azurecr.io
# Deploy
docker-compose -f docker-compose.prod.yml up -d
23.4 CI/CD Pipeline Örneği — GitHub Actions ile Push & Deploy
Proje Yapısı
app/
├── .github/
│ └── workflows/
│ ├── ci.yml
│ └── cd.yml
├── src/
├── Dockerfile
├── docker-compose.yml
└── deployment/
└── docker-compose.prod.yml
GitHub Actions CI Pipeline
.github/workflows/ci.yml:
name: CI Pipeline
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '18'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run linter
run: npm run lint
- name: Run tests
run: npm test
- name: Upload coverage
uses: codecov/codecov-action@v3
with:
files: ./coverage/lcov.info
build:
needs: test
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=sha,prefix={{branch}}-
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Run Trivy security scan
uses: aquasecurity/trivy-action@master
with:
image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
format: 'sarif'
output: 'trivy-results.sarif'
- name: Upload Trivy results to GitHub Security
uses: github/codeql-action/upload-sarif@v2
if: always()
with:
sarif_file: 'trivy-results.sarif'
GitHub Actions CD Pipeline
.github/workflows/cd.yml:
name: CD Pipeline
on:
push:
tags:
- 'v*'
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
DEPLOY_USER: ${{ secrets.DEPLOY_USER }}
jobs:
deploy-staging:
runs-on: ubuntu-latest
environment: staging
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Extract version
id: version
run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT
- name: Deploy to staging
uses: appleboy/ssh-action@v1.0.0
with:
host: ${{ secrets.STAGING_HOST }}
username: ${{ secrets.DEPLOY_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
cd /opt/app
export VERSION=${{ steps.version.outputs.VERSION }}
export REGISTRY=${{ env.REGISTRY }}
export IMAGE_NAME=${{ env.IMAGE_NAME }}
# Pull latest images
echo ${{ secrets.GITHUB_TOKEN }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin
docker-compose -f docker-compose.staging.yml pull
# Deploy with zero-downtime
docker-compose -f docker-compose.staging.yml up -d
# Health check
sleep 10
curl -f http://localhost/health || exit 1
deploy-production:
needs: deploy-staging
runs-on: ubuntu-latest
environment: production
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Extract version
id: version
run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT
- name: Create deployment
id: deployment
uses: actions/github-script@v7
with:
script: |
const deployment = await github.rest.repos.createDeployment({
owner: context.repo.owner,
repo: context.repo.repo,
ref: context.ref,
environment: 'production',
auto_merge: false,
required_contexts: []
});
return deployment.data.id;
- name: Deploy to production
uses: appleboy/ssh-action@v1.0.0
with:
host: ${{ secrets.PRODUCTION_HOST }}
username: ${{ secrets.DEPLOY_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
cd /opt/app
export VERSION=${{ steps.version.outputs.VERSION }}
export REGISTRY=${{ env.REGISTRY }}
export IMAGE_NAME=${{ env.IMAGE_NAME }}
# Backup current version
docker-compose -f docker-compose.prod.yml config > backup-$(date +%Y%m%d-%H%M%S).yml
# Pull latest images
echo ${{ secrets.GITHUB_TOKEN }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin
docker-compose -f docker-compose.prod.yml pull
# Rolling update
docker-compose -f docker-compose.prod.yml up -d --no-deps --build backend
sleep 5
docker-compose -f docker-compose.prod.yml up -d --no-deps --build frontend
# Health check
for i in {1..10}; do
if curl -f http://localhost/health; then
echo "Deployment successful"
exit 0
fi
sleep 5
done
echo "Health check failed, rolling back"
docker-compose -f backup-*.yml up -d
exit 1
- name: Update deployment status (success)
if: success()
uses: actions/github-script@v7
with:
script: |
await github.rest.repos.createDeploymentStatus({
owner: context.repo.owner,
repo: context.repo.repo,
deployment_id: ${{ steps.deployment.outputs.result }},
state: 'success',
environment_url: 'https://app.example.com'
});
- name: Update deployment status (failure)
if: failure()
uses: actions/github-script@v7
with:
script: |
await github.rest.repos.createDeploymentStatus({
owner: context.repo.owner,
repo: context.repo.repo,
deployment_id: ${{ steps.deployment.outputs.result }},
state: 'failure'
});
- name: Notify Slack
if: always()
uses: slackapi/slack-github-action@v1.24.0
with:
payload: |
{
"text": "Deployment ${{ job.status }}: ${{ github.repository }} v${{ steps.version.outputs.VERSION }}",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*Deployment Status:* ${{ job.status }}\n*Version:* ${{ steps.version.outputs.VERSION }}\n*Environment:* production"
}
}
]
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
Rollback Pipeline
.github/workflows/rollback.yml:
name: Rollback
on:
workflow_dispatch:
inputs:
environment:
description: 'Environment to rollback'
required: true
type: choice
options:
- staging
- production
version:
description: 'Version to rollback to (e.g., 1.0.0)'
required: true
type: string
jobs:
rollback:
runs-on: ubuntu-latest
environment: ${{ github.event.inputs.environment }}
steps:
- name: Rollback to version
uses: appleboy/ssh-action@v1.0.0
with:
host: ${{ secrets[format('{0}_HOST', github.event.inputs.environment)] }}
username: ${{ secrets.DEPLOY_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
cd /opt/app
export VERSION=${{ github.event.inputs.version }}
# Pull specific version
docker-compose -f docker-compose.${{ github.event.inputs.environment }}.yml pull
# Deploy
docker-compose -f docker-compose.${{ github.event.inputs.environment }}.yml up -d
# Verify
sleep 10
curl -f http://localhost/health || exit 1
Secrets Configuration
GitHub Repository Settings → Secrets:
DEPLOY_HOST=production.example.com
DEPLOY_USER=deploy
SSH_PRIVATE_KEY=<private_key_content>
STAGING_HOST=staging.example.com
PRODUCTION_HOST=production.example.com
SLACK_WEBHOOK_URL=https://hooks.slack.com/services/...
24. Kaynaklar, Okuma Listesi ve CLI Cheat-Sheet (Hızlı Referans)
24.1 Önemli Dokümanlar / Resmi Git Adresleri
Resmi Dokümantasyonlar
Docker:
- Resmi Dokümantasyon: https://docs.docker.com
- GitHub: https://github.com/moby/moby
- Docker Hub: https://hub.docker.com
Dockerfile Reference:
- https://docs.docker.com/engine/reference/builder/
- Best Practices: https://docs.docker.com/develop/develop-images/dockerfile_best-practices/
Docker Compose:
- Dokümantasyon: https://docs.docker.com/compose/
- Compose File Reference: https://docs.docker.com/compose/compose-file/
- GitHub: https://github.com/docker/compose
BuildKit:
- Dokümantasyon: https://docs.docker.com/build/buildkit/
- GitHub: https://github.com/moby/buildkit
containerd:
- Resmi Site: https://containerd.io
- GitHub: https://github.com/containerd/containerd
- Dokümantasyon: https://containerd.io/docs/
Podman:
- Resmi Site: https://podman.io
- GitHub: https://github.com/containers/podman
- Dokümantasyon: https://docs.podman.io
Güvenlik ve Tarama Araçları
Trivy:
- GitHub: https://github.com/aquasecurity/trivy
- Dokümantasyon: https://aquasecurity.github.io/trivy/
Docker Bench for Security:
Cosign (Sigstore):
- GitHub: https://github.com/sigstore/cosign
- Dokümantasyon: https://docs.sigstore.dev/cosign/overview/
Notary:
Monitoring ve Logging
cAdvisor:
Prometheus:
- Resmi Site: https://prometheus.io
- Dokümantasyon: https://prometheus.io/docs/
Grafana:
- Resmi Site: https://grafana.com
- Dokümantasyon: https://grafana.com/docs/
ELK Stack:
- Elasticsearch: https://www.elastic.co/elasticsearch/
- Logstash: https://www.elastic.co/logstash/
- Kibana: https://www.elastic.co/kibana/
Fluentd:
- Resmi Site: https://www.fluentd.org
- Dokümantasyon: https://docs.fluentd.org
Orkestrasyon
Kubernetes:
- Resmi Site: https://kubernetes.io
- Dokümantasyon: https://kubernetes.io/docs/
- GitHub: https://github.com/kubernetes/kubernetes
Docker Swarm:
- Dokümantasyon: https://docs.docker.com/engine/swarm/
Nomad:
- Resmi Site: https://www.nomadproject.io
- GitHub: https://github.com/hashicorp/nomad
Registry
Harbor:
- Resmi Site: https://goharbor.io
- GitHub: https://github.com/goharbor/harbor
- Dokümantasyon: https://goharbor.io/docs/
Nexus Repository:
- Resmi Site: https://www.sonatype.com/nexus/repository-oss
Öğrenme Kaynakları
Interactive Learning:
- Play with Docker: https://labs.play-with-docker.com
- Katacoda Docker Scenarios: https://www.katacoda.com/courses/docker
Tutorials:
- Docker Labs: https://github.com/docker/labs
- Awesome Docker: https://github.com/veggiemonk/awesome-docker
Books (Ücretsiz Online):
- Docker Curriculum: https://docker-curriculum.com
- Docker for Beginners: https://docker-curriculum.com
Community
Forums:
- Docker Community Forums: https://forums.docker.com
- Stack Overflow Docker Tag: https://stackoverflow.com/questions/tagged/docker
Slack/Discord:
- Docker Community Slack: https://dockercommunity.slack.com
- Kubernetes Slack: https://kubernetes.slack.com
24.2 Hızlı Komut Listesi — Linux vs Windows
Container Yönetimi
Linux
# Container çalıştırma
docker run -d --name myapp -p 8080:80 nginx
# İnteraktif shell
docker exec -it myapp bash
# Container durdurma
docker stop myapp
# Container silme
docker rm myapp
# Tüm container'ları listeleme
docker ps -a
# Container logları
docker logs -f myapp
# Container kaynak kullanımı
docker stats myapp
# Container detayları
docker inspect myapp
# Container'ı yeniden başlatma
docker restart myapp
Windows (PowerShell)
# Container çalıştırma
docker run -d --name myapp -p 8080:80 nginx
# İnteraktif shell
docker exec -it myapp powershell
# Container durdurma
docker stop myapp
# Container silme
docker rm myapp
# Tüm container'ları listeleme
docker ps -a
# Container logları
docker logs -f myapp
# Container kaynak kullanımı
docker stats myapp
# Container detayları
docker inspect myapp
# Container'ı yeniden başlatma
docker restart myapp
Image Yönetimi
Linux
# Image build
docker build -t myapp:1.0.0 .
# Image listeleme
docker images
# Image silme
docker rmi myapp:1.0.0
# Image pull
docker pull nginx:alpine
# Image push
docker push username/myapp:1.0.0
# Image tag
docker tag myapp:1.0.0 myapp:latest
# Image history
docker history myapp:1.0.0
# Kullanılmayan image'leri temizleme
docker image prune -a
# Image export
docker save -o myapp.tar myapp:1.0.0
# Image import
docker load -i myapp.tar
Windows (PowerShell)
# Image build
docker build -t myapp:1.0.0 .
# Image listeleme
docker images
# Image silme
docker rmi myapp:1.0.0
# Image pull
docker pull mcr.microsoft.com/windows/nanoserver:ltsc2022
# Image push
docker push username/myapp:1.0.0
# Image tag
docker tag myapp:1.0.0 myapp:latest
# Image history
docker history myapp:1.0.0
# Kullanılmayan image'leri temizleme
docker image prune -a
# Image export
docker save -o myapp.tar myapp:1.0.0
# Image import
docker load -i myapp.tar
Volume Yönetimi
Linux
# Volume oluşturma
docker volume create mydata
# Volume listeleme
docker volume ls
# Volume detayları
docker volume inspect mydata
# Volume silme
docker volume rm mydata
# Volume'u mount etme
docker run -v mydata:/data nginx
# Bind mount
docker run -v /host/path:/container/path nginx
# Read-only mount
docker run -v mydata:/data:ro nginx
# Volume yedekleme
docker run --rm -v mydata:/volume -v $(pwd):/backup alpine \
tar czf /backup/mydata.tar.gz -C /volume .
# Volume geri yükleme
docker run --rm -v mydata:/volume -v $(pwd):/backup alpine \
tar xzf /backup/mydata.tar.gz -C /volume
# Kullanılmayan volume'leri temizleme
docker volume prune
Windows (PowerShell)
# Volume oluşturma
docker volume create mydata
# Volume listeleme
docker volume ls
# Volume detayları
docker volume inspect mydata
# Volume silme
docker volume rm mydata
# Volume'u mount etme
docker run -v mydata:C:\data nginx
# Bind mount
docker run -v "C:\host\path":"C:\container\path" nginx
# Volume yedekleme
docker run --rm -v mydata:C:\volume -v ${PWD}:C:\backup alpine `
tar czf C:\backup\mydata.tar.gz -C C:\volume .
# Volume geri yükleme
docker run --rm -v mydata:C:\volume -v ${PWD}:C:\backup alpine `
tar xzf C:\backup\mydata.tar.gz -C C:\volume
# Kullanılmayan volume'leri temizleme
docker volume prune
Network Yönetimi
Linux
# Network oluşturma
docker network create mynet
# Network listeleme
docker network ls
# Network detayları
docker network inspect mynet
# Network silme
docker network rm mynet
# Container'ı network'e bağlama
docker network connect mynet mycontainer
# Container'ı network'ten ayırma
docker network disconnect mynet mycontainer
# Container'ı custom network ile çalıştırma
docker run --network mynet nginx
# Host network kullanma
docker run --network host nginx
# Container'lar arası iletişim
docker run --name web --network mynet nginx
docker run --network mynet alpine ping web
Windows (PowerShell)
# Network oluşturma
docker network create mynet
# Network listeleme
docker network ls
# Network detayları
docker network inspect mynet
# Network silme
docker network rm mynet
# Container'ı network'e bağlama
docker network connect mynet mycontainer
# Container'ı network'ten ayırma
docker network disconnect mynet mycontainer
# Container'ı custom network ile çalıştırma
docker run --network mynet nginx
# Container'lar arası iletişim
docker run --name web --network mynet nginx
docker run --network mynet mcr.microsoft.com/windows/nanoserver ping web
Docker Compose
Linux
# Compose başlatma
docker-compose up -d
# Compose durdurma
docker-compose down
# Compose logları
docker-compose logs -f
# Belirli servis logları
docker-compose logs -f web
# Servis listesi
docker-compose ps
# Servis yeniden başlatma
docker-compose restart web
# Servis build etme
docker-compose build
# Build ve başlatma
docker-compose up -d --build
# Belirli compose file ile
docker-compose -f docker-compose.prod.yml up -d
# Volume'larla birlikte temizleme
docker-compose down -v
# Container içinde komut çalıştırma
docker-compose exec web bash
# Servis ölçekleme
docker-compose up -d --scale web=3
Windows (PowerShell)
# Compose başlatma
docker-compose up -d
# Compose durdurma
docker-compose down
# Compose logları
docker-compose logs -f
# Belirli servis logları
docker-compose logs -f web
# Servis listesi
docker-compose ps
# Servis yeniden başlatma
docker-compose restart web
# Servis build etme
docker-compose build
# Build ve başlatma
docker-compose up -d --build
# Belirli compose file ile
docker-compose -f docker-compose.prod.yml up -d
# Volume'larla birlikte temizleme
docker-compose down -v
# Container içinde komut çalıştırma
docker-compose exec web powershell
# Servis ölçekleme
docker-compose up -d --scale web=3
Sistem Yönetimi
Linux
# Docker versiyon
docker version
# Docker sistem bilgisi
docker info
# Disk kullanımı
docker system df
# Detaylı disk kullanımı
docker system df -v
# Sistem temizliği (tümü)
docker system prune -a --volumes
# Container prune
docker container prune
# Image prune
docker image prune -a
# Volume prune
docker volume prune
# Network prune
docker network prune
# Docker events izleme
docker events
# Container process'leri
docker top mycontainer
# Container değişiklikleri
docker diff mycontainer
# Docker daemon restart
sudo systemctl restart docker
# Docker daemon status
sudo systemctl status docker
Windows (PowerShell)
# Docker versiyon
docker version
# Docker sistem bilgisi
docker info
# Disk kullanımı
docker system df
# Detaylı disk kullanımı
docker system df -v
# Sistem temizliği (tümü)
docker system prune -a --volumes
# Container prune
docker container prune
# Image prune
docker image prune -a
# Volume prune
docker volume prune
# Network prune
docker network prune
# Docker events izleme
docker events
# Container process'leri
docker top mycontainer
# Container değişiklikleri
docker diff mycontainer
# Docker Desktop restart
Restart-Service docker
Debugging
Linux
# Container logları (son 100 satır)
docker logs --tail 100 mycontainer
# Logları timestamp ile
docker logs -t mycontainer
# Container içinde komut çalıştırma
docker exec mycontainer ls -la
# Container filesystem'i inceleme
docker exec mycontainer find / -name "*.log"
# Container network kontrolü
docker exec mycontainer netstat -tlnp
# Container process'leri
docker exec mycontainer ps aux
# Container içinde shell
docker exec -it mycontainer /bin/bash
# Container'ı root olarak
docker exec -it --user root mycontainer bash
# Container port'ları
docker port mycontainer
# Container inspect (JSON)
docker inspect mycontainer | jq '.'
# Specific field
docker inspect --format='{{.State.Status}}' mycontainer
# Container IP
docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' mycontainer
# Health check status
docker inspect --format='{{.State.Health.Status}}' mycontainer
Windows (PowerShell)
# Container logları (son 100 satır)
docker logs --tail 100 mycontainer
# Logları timestamp ile
docker logs -t mycontainer
# Container içinde komut çalıştırma
docker exec mycontainer cmd /c dir
# Container network kontrolü
docker exec mycontainer netstat -an
# Container process'leri
docker exec mycontainer powershell Get-Process
# Container içinde PowerShell
docker exec -it mycontainer powershell
# Container port'ları
docker port mycontainer
# Container inspect (JSON)
docker inspect mycontainer | ConvertFrom-Json
# Specific field
docker inspect --format='{{.State.Status}}' mycontainer
# Container IP
docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' mycontainer
# Health check status
docker inspect --format='{{.State.Health.Status}}' mycontainer
BuildKit
Linux ve Windows (aynı)
# BuildKit aktif etme
export DOCKER_BUILDKIT=1
# Build with cache
docker buildx build \
--cache-from type=registry,ref=user/app:cache \
--cache-to type=registry,ref=user/app:cache \
-t user/app:latest \
.
# Multi-platform build
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t user/app:latest \
--push \
.
# Build with secret
docker buildx build \
--secret id=mysecret,src=secret.txt \
-t myapp .
# Builder instance oluşturma
docker buildx create --name mybuilder --use
# Builder listesi
docker buildx ls
# Builder inspect
docker buildx inspect mybuilder --bootstrap
Docker Registry
Linux
# Registry'ye login
docker login
# Private registry login
docker login registry.example.com
# Registry'den pull
docker pull registry.example.com/myapp:latest
# Registry'ye push
docker push registry.example.com/myapp:latest
# Image tag (registry için)
docker tag myapp:latest registry.example.com/myapp:latest
# Logout
docker logout
Windows (PowerShell)
# Registry'ye login
docker login
# Private registry login
docker login registry.example.com
# Registry'den pull
docker pull registry.example.com/myapp:latest
# Registry'ye push
docker push registry.example.com/myapp:latest
# Image tag (registry için)
docker tag myapp:latest registry.example.com/myapp:latest
# Logout
docker logout
Shortcut Aliases (Linux için .bashrc/.zshrc)
# Bashrc/zshrc aliases
alias d='docker'
alias dc='docker-compose'
alias dps='docker ps'
alias dpsa='docker ps -a'
alias di='docker images'
alias dex='docker exec -it'
alias dlog='docker logs -f'
alias dstop='docker stop $(docker ps -q)'
alias drm='docker rm $(docker ps -aq)'
alias drmi='docker rmi $(docker images -q)'
alias dprune='docker system prune -a --volumes'
PowerShell Profile Aliases (Windows)
# $PROFILE dosyasına ekle
function d { docker $args }
function dc { docker-compose $args }
function dps { docker ps }
function dpsa { docker ps -a }
function di { docker images }
function dex { docker exec -it $args }
function dlog { docker logs -f $args }
function dstop { docker ps -q | ForEach-Object { docker stop $_ } }
function drm { docker ps -aq | ForEach-Object { docker rm $_ } }
function dprune { docker system prune -a --volumes }
Bu cheat-sheet, günlük Docker kullanımında en çok ihtiyaç duyulan komutları içermektedir. Platform-specific farklar (özellikle path ve shell komutları) belirtilmiştir. Komutları terminal veya PowerShell profilinize ekleyerek daha hızlı erişim sağlayabilirsiniz.
Son Not: Bu dokümantasyon, Docker’ın temel kavramlarından production-ready deployment’a kadar kapsamlı bir rehber sunmaktadır. Her bölüm, teorik bilgiyi pratik örneklerle pekiştirmek üzere tasarlanmıştır. Docker öğrenme yolculuğunuzda başarılar dilerim!