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

  1. Yavaş okuyun. Özellikle 10. bölümden sonra teknik detaylar artıyor. Acele ederseniz önemli noktalari kaçırabilirsiniz.

  2. 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.

  3. 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.

  4. 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.

Docker CLI-Daemon Communication

Ö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 build

Dockerfile’dan imaj oluşturma:

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 --help veya docker <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 belirler
  • WORKDIR → çalışma dizinini ayarlar
  • COPY / ADD → dosya kopyalama
  • RUN → imaj inşa sürecinde komut çalıştırma
  • CMD → 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

  1. 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/mysecret
    

    Build komutu:

    DOCKER_BUILDKIT=1 docker build --secret id=mysecret,src=secret.txt .
    
  2. Cache Yönetimi --cache-from ile önceden build edilmiş imajlardan cache kullanabilirsin.

    docker build --cache-from=myapp:cache -t myapp:latest .
    
  3. Parallel Build Bağımsız katmanlar aynı anda build edilir, build süresi kısalır.

  4. 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 down ile 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 Registry Architecture

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

  1. İmaj oluştur (docker build)
  2. İmaj etiketle (docker tag)
  3. Registry’ye yükle (docker push)
  4. 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:

  1. Manager Node

    • Cluster yönetimini üstlenir.

    • Servis dağıtımı, cluster durum kontrolü ve yük dengeleme işlemlerini yapar.

  2. 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 Architecture Components

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:

  1. Paket deposu ekleme → Docker’ın resmi paket deposunu sisteme ekleriz.
  2. GPG anahtarı ekleme → Paket bütünlüğünü doğrulamak için gerekli.
  3. Docker ve containerd kurulumu → Docker Engine’i ve container runtime’ını kurarız.
  4. Docker servisini aktif etme → Sistem başlatıldığında Docker’ın çalışmasını sağlar.
  5. 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çin sudo gerekmez (çı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.
  • .dockerignore ile 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*.jsonRUN npm ciCOPY . . 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ı

  • COPY komutlarını minimumda tutun.
  • Ağ üzerinden dosya indirmeleri için RUN curl/wget yerine 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-cache seç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:

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

  • .dockerignore dosyası.gitignore gibi ç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çmealpine, busybox gibi 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 create komutu volume oluşturur.
  • /app/data iç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/tmp geç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.gz formatı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

host modu Linux’ta çalışır ancak Windows/macOS üzerinde Docker Desktop’ta desteklenmez.

Güvenlik açısından host modu 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:

  • deploy bölümü eklendi (replicas, update_config, placement)
  • depends_on kaldırıldı (Swarm’da çalışmaz)
  • secrets kullanıldı
  • Network driver overlay olarak 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 logs komutuyla 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 logs komutu ç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 etiket
  • syslog-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 adresi
  • tag: Log etiketi
  • gelf-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ış

Docker Monitoring Stack

Akış:

  1. cAdvisor her konteynerin CPU, memory, network, disk metriklerini toplar
  2. Prometheus cAdvisor’dan metriklerini düzenli aralıklarla çeker (scraping)
  3. 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üresi
  • container_memory_usage_bytes: Bellek kullanımı
  • container_network_receive_bytes_total: Alınan ağ trafiği
  • container_network_transmit_bytes_total: Gönderilen ağ trafiği
  • container_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

  1. Grafana’ya giriş yapın (http://localhost:3000, admin/admin)
  2. Configuration > Data Sources > Add data source
  3. Prometheus seçin
  4. URL: http://prometheus:9090
  5. Save & Test

Dashboard ekleme:

  1. Dashboards > Import
  2. Dashboard ID: 193 (Docker and System Monitoring)
  3. Load > Import

Artık tüm konteynerlerinizin metriklerini görselleştirebilirsiniz.

Custom panel oluşturma:

  1. Create > Dashboard > Add new panel
  2. Query: rate(container_cpu_usage_seconds_total{name="mycontainer"}[5m])
  3. Visualization: Graph
  4. 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

ELK Stack for Docker Logs

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

  1. Kibana’ya giriş yapın (http://localhost:5601)
  2. Management > Index Patterns > Create index pattern
  3. Pattern: docker-*
  4. Next step > Time field: @timestamp
  5. Create index pattern
  6. 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 hata
  • 137: SIGKILL (OOM Killer tarafından öldürülmüş olabilir)
  • 139: Segmentation fault
  • 143: 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 dosya
  • C (Changed): Değiştirilen dosya
  • D (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ıyor
  • Permission denied: Dosya izin sorunu
  • Connection refused: Hedef servis çalışmıyor
  • No 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 :Z etiketi 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

  1. docker ps -a — Konteyner durumu ve exit code
  2. docker logs mycontainer — Hata mesajları
  3. docker inspect mycontainer — Detaylı konfigürasyon
  4. docker run -it --entrypoint /bin/sh myimage — Manuel test
  5. Volume ve network ayarlarını kontrol et
  6. Resource limitleri kontrol et
  7. 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:

  1. docker ps -a ile durum kontrolü
  2. docker logs ile hata mesajlarını oku
  3. docker inspect ile detaylı konfigürasyonu incele
  4. docker exec ile konteyner içine gir ve manuel test et
  5. Network bağlantılarını test et (ping, curl, tcpdump)
  6. Resource limitlerini kontrol et
  7. 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:

  1. Build: Dockerfile’dan image oluşturma
  2. Test: Image’i test etme (unit test, integration test)
  3. Scan: Güvenlik açıklarını tarama
  4. Push: Registry’ye yükleme
  5. 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ı)
  • TARGETOS ve TARGETARCH: 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 buildx kullanı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)
  • latest tag’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-dir ile 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-recommends ile 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 history ile görülebilir
  • docker inspect ile 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 inspect ile 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-recommends kullanı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)
  • .dockerignore ile 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):

  1. Administration > Users > New User
  2. Username, Email, Password
  3. Projects > myapp > Members > Add
  4. Kullanıcı seç, role ata

Robot accounts (CI/CD için):

Harbor, programmatic access için robot account’lar sunar.

  1. Projects > myapp > Robot Accounts > New Robot Account
  2. Name: cicd-bot
  3. Expiration: 30 days
  4. Permissions: Push, Pull
  5. 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:

  1. Harbor UI > Replication
  2. New Replication Rule
  3. Source registry: harbor-us-east
  4. Destination registry: harbor-eu-west
  5. Trigger: Event based (her push’ta)
  6. 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üreleri
  • go_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 hali
  • DCT_ROOT_KEY_PASSPHRASE: Root key şifresi
  • DCT_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 logs her zaman ilk adım
  • docker inspect detaylı 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:

  1. PID Namespace (Process ID)
  2. NET Namespace (Network)
  3. MNT Namespace (Mount)
  4. UTS Namespace (Hostname)
  5. IPC Namespace (Inter-Process Communication)
  6. USER Namespace (User ID)
  7. 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 limitleri
  • blkio: Disk I/O
  • devices: Device erişimi
  • pids: Process sayısı limiti
  • cpuset: 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 ediliyor
  • Permissive: 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 user
  • system_r: SELinux role
  • svirt_lxc_net_t: SELinux type (container process’ler için)
  • s0: Sensitivity level
  • c123,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 :z kullanın
  • Database gibi özel veriler için :Z kullanın
  • Gerektiğinde manuel relabel yapın (restorecon)

Production:

  • SELinux’u enforcing modda tutun
  • label=disable kullanmayı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_t tipi
  • Volume mount için :z (shared) veya :Z (private)
  • Production’da enforcing modda tutun
  • Denial’ları ausearch ve audit2why ile 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 dizini
  • tar 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

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 save ile 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 path
  • dm.use_deferred_removal: Lazy device removal (performans)
  • dm.use_deferred_deletion: Background deletion
  • dm.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: false yapı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 stats ile 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:

Dockerfile Reference:

Docker Compose:

BuildKit:

containerd:

Podman:

Güvenlik ve Tarama Araçları

Trivy:

Docker Bench for Security:

Cosign (Sigstore):

Notary:

Monitoring ve Logging

cAdvisor:

Prometheus:

Grafana:

ELK Stack:

Fluentd:

Orkestrasyon

Kubernetes:

Docker Swarm:

Nomad:

Registry

Harbor:

Nexus Repository:

Öğrenme Kaynakları

Interactive Learning:

Tutorials:

Books (Ücretsiz Online):

Community

Forums:

Slack/Discord:

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!