Mert Özen Her Satırda Daha İleri
DevOps Docker — #02

Docker Kurulumu: Docker Desktop, Linux'ta Docker Engine ve Alternatifler

Mert Özen 14 Kas 2026 12 dk 11 görüntülenme
Docker Kurulumu: Docker Desktop, Linux'ta Docker Engine ve Alternatifler

Windows'ta Docker Desktop'ı WSL 2 ile, Linux sunucuda Docker Engine'i dağıtımın paketinden değil Docker'ın resmi apt repository'sinden kurarsın. Ubuntu 24.04'te docker.io ve Compose v1 tuzağını, docker grubunun gerçekten root'a eşdeğer olduğunu, rootless mode'u, docker context ile hangi engine'e konuştuğunu görmeyi, Desktop lisansını, Podman ve WSL containers gibi alternatifleri ve amd64/arm64 emülasyon tuzağını gerçek çıktılarla inceliyoruz.

Sürüm notu: Bu yazıdaki örnekler Ubuntu 24.04 üzerinde Docker'ın resmi apt repository'sinden kurulan Docker Engine 29.9.3 (containerd 2.4, runc 1.5, Buildx 0.39, Compose 5.7) ve Docker Desktop 4.64 ile denenmiştir.

Önce Tahmin Et

Bir Ubuntu sunucusunda Ada adında bir kullanıcı açıyorsun. sudo yetkisi yok, yalnız Docker'ı şifresiz kullanabilsin diye docker grubuna eklendi. Ada root'un okuyabildiği /etc/shadow dosyasını okuyabilir mi? /root klasörüne dosya yazabilir mi? İkinci soru: aynı sunucuda sudo apt install docker.io docker-compose dersen hangi Docker ve hangi Compose sürümü kurulur? Cevaplar yazının ortasında, gerçek bir kurulumun çıktısıyla.

Windows'ta Docker Desktop ve WSL 2

Önceki yazıda Linux container'ının Windows'ta doğrudan çalışamadığını, Docker Desktop'ın arkada bir Linux ortamı çalıştırdığını gördük. Windows'ta bu ortam varsayılan olarak WSL (Windows Subsystem for Linux; Windows içinde gerçek bir Linux kernel'i çalıştıran katman) 2. Docker'ın Windows kurulum sayfası Kasım 2026'da şu gereksinimleri listeliyor: 64 bit Windows 10 22H2 (build 19045) ya da Windows 11 23H2 (build 22631) ve üstü, WSL 2.1.5 veya daha yeni, 8 GB RAM ve bilgisayarın firmware ayarlarında açık donanım sanallaştırması.

Kurulum akışı kısa. Önce PowerShell'de WSL'i kurar ya da günceller, sürümünü kontrol edersin; sonra Docker Desktop installer'ını çalıştırıp backend olarak WSL 2'yi seçersin:

wsl --install
wsl --update
wsl --version
wsl -l -v

Docker Desktop kendi docker-desktop WSL dağıtımında çalışır ve dockerd oradadır. Kendi Ubuntu dağıtımında çalışıyorsan Settings → Resources → WSL Integration ekranından o dağıtıma Docker CLI erişimi verirsin; böylece PowerShell'den de Ubuntu terminalinden de aynı engine'e konuşursun. Installer varsayılan olarak kullanıcıya özel (per-user) kurulum yapar ve yönetici yetkisi istemez. Hyper-V backend'i yalnız tüm kullanıcılar için yapılan kurulumda seçilebilir; WSL 2 bu seride varsayılan kabul ettiğimiz yol. Bu yazıyı Windows olmayan bir makinede hazırladığım için Windows adımlarını kendim çalıştırmadım; yukarıdaki komutlar ve gereksinimler Docker'ın ve Microsoft'un dokümanından.

Lisans: Docker Desktop Herkes İçin Ücretsiz Değil

Docker Engine açık kaynak (Apache 2.0) ve Linux'ta ücretsiz. Docker Desktop ise ayrı bir ürün ve abonelik koşuluna bağlı. Docker'ın lisans sayfasına göre Desktop kişisel kullanımda, eğitimde, ticari olmayan açık kaynak projelerde ve 250'den az çalışanı ve 10 milyon dolardan az yıllık geliri olan şirketlerde ücretsiz; iki koşulun ikisi de sağlanmalı. Bunun dışındaki şirketlerde ve kamu kurumlarında profesyonel kullanım için ücretli abonelik (Pro, Team veya Business) gerekiyor. Bookstore'u geliştiren ekip büyük bir şirketin parçasıysa Docker Desktop'ı kurmadan önce bu soruyu satın alma ekibiyle konuşmak gerekir; aşağıdaki alternatifler bu yüzden önemli.

Linux'ta Docker Engine: Resmi apt Repository

Production sunucusunda Docker Desktop değil, doğrudan Docker Engine kullanırsın. Ubuntu'da bunun iki yolu var: dağıtımın kendi paketi (docker.io) ve Docker'ın resmi repository'si (docker-ce). Ubuntu 24.04'te ikisine de bakalım:

apt-cache policy docker.io docker-ce docker-compose | grep -E '^[a-z]|Candidate'
docker.io:
  Candidate: 29.1.3-0ubuntu3~24.04.2
docker-ce:
  Candidate: 5:29.9.3-1~ubuntu.24.04~noble
docker-compose:
  Candidate: 1.29.2-6ubuntu1

İkinci sorunun cevabı burada. Ubuntu'nun docker.io paketi fena eski değil, ama Docker'ın kendi yayınının sekiz minor sürüm gerisinde ve güncellemeleri Ubuntu'nun takvimine bağlı. Asıl tuzak docker-compose: bu paket Python ile yazılmış, yıllar önce desteği biten Compose v1'i (1.29.2) kurar. Eski bir blog yazısındaki apt install docker.io docker-compose satırını kopyalayan biri bugün 2021'in Compose'uyla çalışır. Docker'ın Ubuntu kurulum dokümanı bu yüzden önce çakışan paketleri kaldırmayı, sonra kendi repository'sini eklemeyi söylüyor. Aşağıdaki script dokümandaki adımların aynısı; temiz bir Ubuntu 24.04'te sudo yetkisi olan bir kullanıcıyla çalıştırılır:

#!/usr/bin/env bash
set -euo pipefail

# 1. Remove distro/unofficial packages that conflict with Docker's packages
sudo apt-get remove -y $(dpkg --get-selections docker.io docker-compose docker-compose-v2 docker-doc docker-buildx podman-docker containerd runc 2>/dev/null | cut -f1) || true

# 2. Docker's GPG key and repository (deb822 format)
sudo apt-get update
sudo apt-get install -y ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
sudo tee /etc/apt/sources.list.d/docker.sources > /dev/null <<SOURCES
Types: deb
URIs: https://download.docker.com/linux/ubuntu
Suites: $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}")
Components: stable
Architectures: $(dpkg --print-architecture)
Signed-By: /etc/apt/keyrings/docker.asc
SOURCES
sudo apt-get update

# 3. Engine, CLI, containerd, Buildx and Compose plugins
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

# 4. Check
sudo docker version
sudo docker run --rm hello-world

Script'i bir sunucuyu taklit eden temiz bir Ubuntu 24.04 container'ında çalıştırdım; orada systemd olmadığı için dockerd'yi elle başlattım, gerçek bir sunucuda paket kurulumu servisi kendisi açar. Kurulan paketler:

containerd.io 2.4.2-4~ubuntu.24.04~noble
docker-buildx-plugin 0.39.0-1~ubuntu.24.04~noble
docker-ce 5:29.9.3-1~ubuntu.24.04~noble
docker-ce-cli 5:29.9.3-1~ubuntu.24.04~noble
docker-ce-rootless-extras 5:29.9.3-1~ubuntu.24.04~noble
docker-compose-plugin 5.7.1-1~ubuntu.24.04~noble

Compose artık ayrı bir Python programı değil, docker compose (tire yok) olarak çalışan bir CLI plugin'i; docker compose version burada v5.7.1 diyor. docker-ce-rootless-extras da birazdan kullanacağımız rootless mode'un araçlarını getiriyor. Sürüm sabitlemek istersen apt list --all-versions docker-ce ile listeleyip docker-ce=5:29.9.3-1~ubuntu.24.04~noble biçiminde kurarsın; production sunucularında sürümü pipeline'dan bilinçli olarak yükseltmek, her apt upgrade'de sessizce değişmesinden iyidir.

Docker'ın dokümanı bir uyarıyı da açıkça yapıyor: ufw ya da firewalld kullanıyorsan, Docker'ın yayımladığı container port'ları bu firewall kurallarını atlar. Bunun nedenini ve güvenli varsayılanı 12. yazıda network'le birlikte göreceğiz; şimdilik "sunucuda -p ile açtığın port, ufw kapalı dese de dışarıdan erişilebilir" diye aklında tut.

docker Grubu ve Rootless Mode

docker Grubu Root'a Eşdeğerdir

Kurulumdan sonra her komutun başına sudo yazmamak için kullanıcı genellikle docker grubuna eklenir. Docker'ın post-install dokümanı bunun bedelini tek cümleyle söylüyor: "The docker group grants root-level privileges to the user." Bunu birinci soruyla deneyelim. Ada'nın sudo yetkisi yok, yalnız docker grubunda:

sudo useradd -m -s /bin/bash ada
sudo usermod -aG docker ada
su - ada
id
cat /etc/shadow
docker run --rm -v /:/host alpine:3 sh -c "echo ada-was-here > /host/root/proof.txt; head -1 /host/etc/shadow"
uid=1002(ada) gid=1002(ada) groups=1002(ada),995(docker)
cat: /etc/shadow: Permission denied
root:*:20767:0:99999:7:::

Ada /etc/shadow'u doğrudan okuyamıyor, ama Docker'dan host'un kök dizinini bir container'a bağlamasını istediğinde root'un dosyasını okuyor ve /root altına yazıyor:

-rw-r--r-- 1 root root 13 Nov 14 10:22 /root/proof.txt

Neden böyle olduğunu önceki yazıdan biliyoruz: container'ı başlatan dockerd root olarak çalışıyor ve container'daki root, user namespace kullanılmadıkça host'taki uid 0. /var/run/docker.sock'a yazabilen herkes dockerd'ye "host'un diskini bağla, privileged çalıştır" diyebilir. Sonuç: docker grubuna yalnız sunucuda zaten root olmasına razı olduğun kişileri ekle. Windows'ta Docker Desktop'ın docker-users grubu için de Docker aynı uyarıyı yapıyor: bu grup Docker socket'ine, dolayısıyla yönetici yetkisine eşdeğer erişim verir.

Rootless Mode'a Giriş

Rootless mode bu sorunu kökünden ele alır: dockerd ve container'lar root olmadan, kullanıcının kendi user namespace'inde çalışır. Container'daki root, host'ta o kullanıcının kendisine ve ona ayrılmış (/etc/subuid'deki) yardımcı uid aralığına eşlenir. Ön koşullar uidmap paketi ve kullanıcıya en az 65536 yardımcı uid ayrılmış olması; kurulum, docker-ce-rootless-extras paketiyle gelen script'le root olmayan kullanıcı tarafından yapılır:

sudo apt-get install -y uidmap
grep ^grace: /etc/subuid
dockerd-rootless-setuptool.sh install
grace:296608:65536

Script systemd'nin kullanıcı servisini kurar ve DOCKER_HOST=unix:///run/user/<uid>/docker.sock ayarını söyler. Lab container'ında systemd olmadığı için rootless daemon'ı dockerd-rootless.sh ile elle başlattım ve Grace'in engine'inde aynı saldırıyı denedim:

docker info | grep -A3 "Security Options"
docker run --rm -v /:/host alpine:3 sh -c "id; touch /host/root/x"
 Security Options:
  seccomp
   Profile: builtin
  rootless
uid=0(root) gid=0(root) groups=0(root),0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel),11(floppy),20(dialout),26(tape),27(video)
touch: /host/root/x: Permission denied

Container'ın içinde hâlâ uid=0(root) yazıyor, ama bu root host'ta Grace'e eşlendiği için /root'a yazamıyor. Rootless mode'un da bedeli var: 1024'ün altındaki port'ları açmak ek ayar ister, network ve depolama tarafında bazı özellikler sınırlıdır ve her kullanıcının kendi daemon'ı, kendi image'ları olur. Ayrıntıları ve user namespace remap seçeneğini 13. yazıda güvenlikle birlikte açacağız.

Hangi Engine'e Konuşuyorsun? version, info ve context

docker komutu bir istemci olduğu için ilk soru hep "hangi daemon'a bağlıyım?" olmalı. docker version iki tarafı ayrı ayrı gösterir:

docker version
Client: Docker Engine - Community
 Version:           29.9.3
 API version:       1.56
 Go version:        go1.26.10
 Git commit:        5457199
 Built:             Tue Nov  3 21:41:21 2026
 OS/Arch:           linux/arm64
 Context:           default

Server: Docker Engine - Community
 Engine:
  Version:          29.9.3
  API version:      1.56 (minimum version 1.40)
  Go version:       go1.26.10
  Git commit:       70efc0e
  Built:            Tue Nov  3 21:41:21 2026
  OS/Arch:          linux/arm64
  Experimental:     false
 containerd:
  Version:          v2.4.2
  GitCommit:        b7a25261e029d68978bbf290df0a62fc819b3c51
 runc:
  Version:          1.5.3
  GitCommit:        v1.5.3-0-g46365c62
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0

OS/Arch satırları makinenin mimarisine bağlı: bu çıktı arm64 bir makineden, x64 bir sunucuda linux/amd64, Windows'taki Docker Desktop'ta istemci için windows/amd64 görürsün. Server tarafındaki minimum version değeri, daemon'ın kabul ettiği en eski istemci API'sini söyler; çok eski bir CI aracı ya da kütüphane bu sınırın altında kalırsa "client version is too old" hatası alır. docker info ise daemon'ın ayarlarını verir: storage driver, cgroup sürümü, default runtime, security option'lar. Hata ayıklarken bu iki komutun çıktısını paylaşmak iyi bir alışkanlık; paylaşmadan önce host adı, kernel build numarası ve kullanıcı yolları gibi makineni tanımlayan satırları çıkar.

Birden çok engine'le çalışıyorsan (Docker Desktop, uzaktaki bir test sunucusu, rootless daemon) docker context hangisine konuştuğunu açıkça seçmeni sağlar. Lab'da Linux engine'inin yanına Docker Desktop'ın socket'i için bir context ekledim:

docker context create desktop --description "Docker Desktop engine" --docker host=unix:///var/run/desktop.sock
docker context ls
docker --context default version --format "default: {{.Server.Platform.Name}} {{.Server.Version}}"
docker --context desktop version --format "desktop: {{.Server.Platform.Name}} {{.Server.Version}}"
Successfully created context "desktop"
NAME        DESCRIPTION                               DOCKER ENDPOINT                ERROR
default *   Current DOCKER_HOST based configuration   unix:///var/run/docker.sock
desktop     Docker Desktop engine                     unix:///var/run/desktop.sock
default: Docker Engine - Community 29.9.3
desktop: Docker Desktop 4.64.0 (221278) 29.2.1

Yıldız aktif context'i gösterir; docker context use desktop kalıcı olarak değiştirir, --context bayrağı ya da DOCKER_CONTEXT ortam değişkeni yalnız o komut için. Endpoint SSH (uzak sunucuya şifreli bağlantı protokolü) de olabilir (host=ssh://deploy@test-server), böylece uzak sunucudaki daemon'ı port açmadan yönetirsin. DOCKER_HOST ortam değişkeni tanımlıysa context ayarını ezer; "neden yanlış makinedeki container'ları görüyorum?" sorusunun cevabı çoğu zaman unutulmuş bir DOCKER_HOST'tur.

Alternatifler: Hepsi OCI Image Çalıştırır

Önceki yazıda gördüğümüz gibi image formatı bir standart; Docker Desktop'ın yerine geçen araçlar aynı image'ları çalıştırır. Kasım 2026'daki başlıca seçenekler:

AraçPlatformÖne çıkanNe zaman
Podman / Podman DesktopLinux, Windows, macOSDaemon yok, rootless varsayılan, docker ile uyumlu CLILisans kısıtı, rootless zorunluluğu, Red Hat ekosistemi
Rancher DesktopWindows, macOS, Linuxdockerd veya containerd seçimi, isteğe bağlı k3sÜcretsiz Desktop deneyimi ve lokal Kubernetes
WSL containers (wslc)WindowsWSL'e gömülü CLI ve API, 29 Eylül 2026'dan beri genel kullanımdaEk araç kurmadan Windows'ta container; Compose henüz yok
Colima, OrbStackmacOSHafif Linux VM ile Docker enginemacOS'ta Desktop'a alternatif; OrbStack ticari kullanımda ücretli
Apple containermacOS 26, Apple siliconHer container ayrı hafif VMDocker uyumluluğu gerekmeyen macOS iş akışları

Windows'taki okur için en yeni seçenek WSL containers. Microsoft Haziran 2026'da public preview olarak duyurduğu özelliği 29 Eylül 2026'da GA (genel kullanıma açık) ilan etti: wsl --update ile gelen wslc.exe (takma adı container.exe) wslc run, wslc network, wslc events gibi Docker'a benzeyen komutlar sunuyor ve Visual Studio Code'un dev container'ları onu driver olarak kullanabiliyor. Microsoft'un kendi duyurusuna göre Compose desteği bir sonraki hedef; bu yüzden birden çok container'ı birlikte yöneteceğin Docker Compose serisi için bugün hâlâ Docker Desktop veya Podman gerekiyor. .NET tarafında da bir etkisi var: .NET 11 SDK'nın lokal container runtime'ını seçme davranışındaki değişikliği 15. yazıda göreceğiz. Bu seride örnekleri Docker ile anlatıyoruz; komutların çoğu Podman'da docker yerine podman yazarak aynen çalışır.

Mimari Tuzağı: amd64 ve arm64

Image'lar işlemci mimarisine özeldir. Docker Hub ve MCR'deki (Microsoft Container Registry; Microsoft'un image'larını dağıttığı registry) resmi image'ların çoğu birden çok mimari için yayımlanır ve Docker makinene uygun olanı seçer; --platform ile başka bir mimariyi isteyebilirsin:

docker run --rm --platform linux/amd64 alpine:3 uname -m
docker run --rm --platform linux/arm64 alpine:3 uname -m
x86_64
aarch64

İki komut da çalıştı, çünkü Docker Desktop makinenin mimarisinden farklı olan image'ı emülasyonla çalıştırabiliyor. x64 bir Windows makinesinde linux/arm64 container'ı, x86 komutlarına çevrilerek koşar. Tek mimarili bir image'da Docker bunu bir uyarıyla bildirir. Aşağıdaki çıktı arm64 bir makinede yalnız amd64 için yayımlanan bir .NET SDK tag'inden; x64 bir makinede aynı uyarıyı yalnız arm64 olan bir image'la, ters yönde görürsün:

docker run --rm mcr.microsoft.com/dotnet/sdk:10.0-noble-amd64 sh -c 'uname -m; dotnet --version'
WARNING: The requested image's platform (linux/amd64) does not match the detected host platform (linux/arm64/v8) and no specific platform was requested
x86_64
10.0.404

Emülasyon geliştirme makinesinde "çalışıyor gibi" görünür ama iki sorunu var. Birincisi hız: CPU'nun her talimatı çevrildiği için derleme, sıkıştırma ve JIT (kodu çalışma anında makine koduna çeviren derleme) gibi işler belirgin biçimde yavaşlar. İkincisi destek: Docker'ın kullandığı QEMU (başka mimarideki programları çeviren açık kaynak emülatör) için .NET ekibinin dotnet-docker dokümanı açıkça ".NET doesn't support being run in QEMU" diyor; emülasyon altında .NET'in tuhaf hatalar vermesi beklenen bir durum. Bu yüzden .NET image'larını hedef mimaride çalıştırır, farklı mimari için build ederken SDK'yı yerel mimaride tutarız; bunun Dockerfile tarafını 6. ve 9. yazıda göreceğiz.

Üretimde Dikkat

Production sunucusunda Docker Desktop değil Docker Engine çalışır ve onu dağıtımın paketinden değil Docker'ın repository'sinden kurarsın. Sürümü sabitle, yükseltmeyi bilinçli yap ve Engine 29'un eski API istemcilerini reddetmesi gibi değişiklikleri release notes'tan oku; Kasım 2026 itibarıyla güncel patch 29.9.3.

Sunucuya erişimi docker grubu üzerinden değil, root erişimi olarak düşün ve öyle yönet. Ekipte herkesin docker grubunda olduğu bir sunucuda aslında herkes root'tur. Uzaktan yönetim için daemon'ı TCP'ye açmak yerine SSH context'i kullan; TLS (trafiği şifreleyen protokol) olmadan açılmış bir 2375 port'u, internete açık bir root shell'i demektir.

Lisansı kurulumdan önce netleştir. Docker Desktop'ın ücretli olduğu bir şirkette geliştiricilerin habersizce kurduğu Desktop'lar sonradan bir denetim konusu olabilir; Podman Desktop, Rancher Desktop ya da WSL containers bu durumda gerçek alternatiflerdir.

Sık Düşülen Tuzaklar

Eski bir rehberden apt install docker.io docker-compose kopyalamak. Ubuntu 24.04'te bu, Docker'ın gerisinde bir Engine ve desteği çoktan bitmiş Compose v1 (1.29.2) demek. Resmi repository'yi kullan ve Compose'u docker compose olarak çağır.

Her şeyi sudo docker ile çalıştırmak. Bir süre sonra ~/.docker altında root'a ait dosyalar oluşur ve normal kullanıcıyla verilen komutlar permission hatası verir. Ya docker grubunu bilinçli olarak kullan ya da rootless mode'a geç.

Container port'unu açıp ufw'nin koruduğunu sanmak. Docker'ın kendi kuralları ufw'nin önünde çalışır; 12. yazıda bunu test edip doğru varsayılanı kuracağız.

Yanlış mimaride image çalıştırıp "yavaş ama çalışıyor" demek. Emülasyonda geçen bir test, hedef sunucuda aynı davranışı garanti etmez; docker image inspect --format '{{.Architecture}}' ile çalıştırdığın image'ın mimarisine bak.

Küçük Bir Deneme

★☆☆ Kendi makinende docker version ve docker context ls çalıştır. Client ve Server sürümlerini, Server'ın minimum version değerini ve aktif context'in endpoint'ini not et.

★★☆ Bir Ubuntu 24.04 VM'inde (ya da WSL'deki Ubuntu dağıtımında) önce apt-cache policy docker.io docker-compose docker-ce çıktısına bak, sonra yazıdaki script ile Docker Engine'i kur. docker compose version ve docker buildx version ne diyor?

★★★ Aynı VM'de root olmayan bir kullanıcı için rootless mode'u kur. Rootful ve rootless engine için birer context oluştur, ikisinde de docker run --rm -v /:/host alpine:3 touch /host/root/x dene ve farkı açıkla.

Docker kurulu ve hangi engine'e konuştuğumuzu biliyoruz. Bir sonraki yazıda container'ın yaşam döngüsüne geçiyoruz: docker run bayrakları, ps, logs, exec, stop ve kill farkı, restart policy'leri ve bir .NET uygulamasının docker stop'ta neden bazen 10 saniye bekleyip öldürüldüğü.