Bölüm 6 / 13
Konteyner nedir
VM'den farkı, imaj, konteyner, katman.
18 dakikalık okuma
Konteyner, "bende çalışıyor" sorununu ortadan kaldıran mekanizmadır: uygulamayı çalışması için gereken her şeyle (çalışma zamanı, kütüphaneler, dosya düzeni) tek bir imaj olarak paketler ve her ortamda aynı biçimde başlatır. Bu ders komut dersi değil kavram dersidir — komutlar 7. derste. Burada üç soruyu net cevaplarız: konteyner nedir (sanal makine değil: izole bir süreç — namespace, cgroup, katmanlı dosya sistemi), imaj ile konteyner arasındaki fark nedir ve katmanlar nasıl çalışır, hangi araç ne yapar (Docker, containerd, Podman, OCI). 1. dersteki "çekirdek paylaşılır" cümlesi burada somutlaşır; 16-01'in nesne deposu ile imaj katmanlarının şaşırtıcı benzerliği kurulur; 15-06'da Testcontainers için verilen söz de burada ödenir.
01Ne öğreneceksin
- Konteynerin çözdüğü sorun: ortam farkı, "bende çalışıyor", bağımlılık çakışması
- Konteyner = izole süreç: namespace (ne görüyor), cgroup (ne kadar kullanıyor), katmanlı dosya sistemi (ne okuyor)
- VM ↔ konteyner karşılaştırması ve "hangi durumda hangisi" kararı
- İmaj ↔ konteyner ↔ katman: salt okunur katmanlar + yazılabilir üst katman, copy-on-write, verinin neden kaybolduğu
- İçerik adresli katman ve digest: Git'in nesne deposuyla birebir koşutluk, katman paylaşımı
- OCI standardı; Docker ↔ containerd ↔ Podman; Docker Desktop ↔ Engine; Linux çekirdeği şartı
- Testcontainers'ın altındaki Docker; temel imaj seçimi (
-alpine↔-slim↔ tam) ve musl/glibc tuzağı
02Çözülen sorun: "bende çalışıyor"
Bir uygulamayı çalıştırmak, kodu kopyalamaktan fazlasıdır: doğru çalışma zamanı sürümü (.NET 8, Node 20), doğru yerel kütüphaneler, doğru dosya düzeni, doğru ortam değişkenleri ve doğru işletim sistemi paketleri gerekir. Bu kümenin adı çalışma ortamıdır ve geleneksel yayında her ortamda elle kurulur. Sonuç dört tanıdık arızadır:
- Ortam farkı. Geliştirme makinesinde .NET 8.0.8, sunucuda 8.0.3; testte çalışan sorgu üretimde başka davranır. 14-11'in "geliştirme / test / üretim ortamları ayrılır ve aynı biçimde kurulur" maddesi elle kurulumla güvenilir biçimde sağlanamaz.
- Bağımlılık çakışması. Aynı sunucuda biri Node 18, öbürü Node 22 isteyen iki uygulama; biri OpenSSL 1.1, öbürü 3.0 bekleyen iki kütüphane.
- Kurulum belgesinin bayatlaması. "Sunucu kurulum adımları" dokümanı altı ayda yalan söyler (16-11'in bayat doküman uyarısı).
- Yeni gelenin ilk günü. Projeyi çalıştırmak için PostgreSQL, Redis ve üç servis kurmak gerekiyorsa ilk gün kaybolur.
Konteyner bu sorunu paketlemeyi değiştirerek çözer: uygulama ve ihtiyaç duyduğu bütün kullanıcı alanı (çalışma zamanı, kütüphaneler, dosyalar) tek bir imaj içinde, sürümlü ve değişmez (immutable) biçimde saklanır. Aynı imaj geliştirme makinesinde, CI'da ve üretimde çalışır; aradaki tek fark ortam değişkenleri ve bağlanan verilerdir. Bu, 14-11'in ortam ayrımı maddesini mekanizmayla garanti eder ve 18. hattaki "bir kez derle, her yere dağıt" ilkesinin taşıyıcısı olur.
Dikkat edilecek nokta: konteyner çekirdeği paketlemez. Paketlenen şey kullanıcı alanıdır; çekirdek hostun çekirdeğidir (1. ders). Bu, hem konteynerin hafifliğinin hem de izolasyon sınırının nedenidir.
03Konteyner = izole süreç: namespace, cgroup, katman
Bir konteyner, hostta ps aux ile görülebilen sıradan bir süreçtir; onu
"konteyner" yapan şey çekirdeğin ona kısıtlı bir dünya göstermesidir. Üç
mekanizma:
| Mekanizma | Soru | Ne yapar |
|---|---|---|
| namespace | Süreç ne görüyor? | PID, ağ, bağlama (mount), hostname (UTS), IPC, kullanıcı ve cgroup görünümlerini ayırır |
| cgroup | Süreç ne kadar kullanabiliyor? | CPU, bellek, disk G/Ç, süreç sayısı sınırları — 4. dersteki MemoryMax ile aynı altyapı |
| katmanlı dosya sistemi | Süreç hangi dosyaları görüyor? | overlayfs ile birleştirilmiş salt okunur katmanlar + yazılabilir üst katman (05. bölüm) |
Namespace'lerin somut sonuçları, konteynerde hata ayıklarken sürekli karşına çıkar:
- PID namespace: konteynerin içinde uygulaman
PID 1'dir (1. derstekicat /proc/1/cmdlinedenemesi bunu gösterir). İki sonucu var: PID 1 sinyalleri farklı işler —SIGTERMiçin işleyici yoksa sinyal yok sayılır vedocker stopon saniye bekleyipSIGKILLatar; ayrıca PID 1 yetim süreçleri toplamakla yükümlüdür, kabuk betiğiyle başlatılan konteynerlerde zombi süreç birikmesinin nedeni budur (çözüm:--initya da doğruENTRYPOINT— 8. ders). - Ağ namespace: konteynerin kendi ağ arayüzü, kendi IP'si ve kendi
localhost'u vardır. Bu yüzden konteyner içindeki127.0.0.1hostun localhost'u değildir — 5. dersin dinleme adresi kuralı burada hayatidir. - Mount namespace: konteyner kendi kök dosya sistemini görür; hostun
/etc'si ya da ev dizini yoktur. "Sertifika/log/appsettings dosyası bulunamıyor" hatalarının temel nedeni bu ayrımdır. - User namespace: konteynerdeki UID 0 ile hostun UID 0'ı eşleştirilebilir ya da kaydırılabilir. Kaydırma yapılmıyorsa konteynerdeki root, dosya sistemi izinleri açısından hostun root'u gibi davranır — 3. dersteki bind mount ve UID eşleşmesi uyarısının kökü budur. Rootless Docker ve Podman bu kaydırmayı varsayılan yapar.
Bu mekanizmaların hepsi Linux çekirdeği özelliğidir. Docker onları icat etmedi; kullanımını kolaylaştırdı (imaj biçimi, komut satırı, registry). Bu yüzden konteyner "Docker'a özel" bir teknoloji değildir ve bu yüzden Windows ya da macOS üzerinde Linux konteyneri çalıştırmak için bir Linux çekirdeğine — yani sanal makineye (WSL2 ya da Docker Desktop'ın VM'i) ihtiyaç duyulur.
04Sanal makine ↔ konteyner
Sanal makine, donanımı taklit eder: her VM kendi çekirdeğini ve tam bir işletim sistemini çalıştırır. Konteyner ise hostun çekirdeğini paylaşır, yalnız kullanıcı alanını taşır. Bütün farklar bu tek cümleden türer.
| Ölçüt | Sanal makine | Konteyner |
|---|---|---|
| Çekirdek | Kendi çekirdeği (hipervizör üzerinde) | Hostun çekirdeğini paylaşır |
| Boyut | Birkaç GB (tam OS) | Onlarca–yüzlerce MB (yalnız kullanıcı alanı) |
| Açılış süresi | Onlarca saniye (OS açılışı) | Milisaniye–saniye (süreç başlatma) |
| Yoğunluk | Bir makinede onlarca | Bir makinede yüzlerce |
| İzolasyon gücü | Daha güçlü: donanım düzeyinde ayrım | Daha zayıf: çekirdek paylaşımı ortak saldırı yüzeyi bırakır |
| İşletim sistemi seçimi | Farklı OS/çekirdek çalıştırılabilir (Linux VM üstünde Windows) | Yalnız host çekirdeğiyle uyumlu olan (Linux'ta Linux konteyneri) |
| Yama yönetimi | Her VM'in OS'u ayrı yamalanır | İmaj yeniden derlenir (4. ders: imaj yenilenmeden yama kapanmaz) |
| Uygun yer | Farklı OS ihtiyacı, güçlü izolasyon gereği (çok kiracılı/güvenlik sınırı), eski/monolitik sistemler | Aynı OS üzerinde çok sayıda uygulama, hızlı ölçekleme, CI, tekrarlanabilir ortam |
İkisi birbirinin alternatifi değildir; pratikte iç içe kullanılır: bulut sağlayıcısında bir VM kiralar, üstünde konteyner koşturursun — izolasyonu VM sınırından, yoğunluğu ve tekrarlanabilirliği konteynerden alırsın. Kubernetes kümeleri de (12. ders) tam olarak böyle kurulur.
Kıdem düzeyinde bilinmesi gereken üç ayrıntı: (1) Güvenlik sınırı olarak konteyner
zayıftır; birbirine güvenmeyen kiracıları ayırmak için VM ya da hafif VM
(gVisor, Kata Containers, Firecracker) kullanılır. (2) Windows konteynerleri vardır
ama Windows çekirdeği gerektirir; Linux ve Windows konteynerleri aynı hostta aynı anda
koşmaz. (3) Konteynerde top ya da free çalıştırdığında
gördüğün değerler, cgroup sınırlarını değil hostun değerlerini gösterebilir —
bu yüzden .NET'in konteyner farkındalığı (cgroup limitini okuyup GC'yi ona göre
ayarlaması) önemlidir, ölçüm için docker stats kullanılır (7. ders).
05İmaj, konteyner, katman
Üç kavram sık karıştırılır; ayrımı netleştirelim:
| Kavram | Nedir | Benzetme |
|---|---|---|
| İmaj | Salt okunur, sürümlü paket: dosya sistemi katmanları + yapılandırma (giriş komutu, ortam, port) | Sınıf (tanım) |
| Konteyner | İmajdan başlatılmış çalışan örnek + üstüne eklenmiş yazılabilir katman | Nesne (örnek) |
| Katman | Dosya sistemi değişikliği paketi; imaj katmanların üst üste bindirilmesidir | Commit (16-01) |
Bir imaj, sırayla uygulanan katmanlardan oluşur. Örneğin bir ASP.NET Core imajı: temel Debian katmanı → .NET çalışma zamanı katmanı → bağımlılıklar → uygulama dosyaları. Çalıştırma anında bu katmanlar overlayfs ile tek bir dosya ağacına birleştirilir ve en üste ince, yazılabilir bir katman eklenir. Yazma davranışı copy-on-write'dır: alt katmandaki bir dosyayı değiştirdiğinde dosya önce üst katmana kopyalanır, sonra değiştirilir. Alt katmanlar hiç değişmez.
Bundan üç önemli sonuç çıkar:
- Konteyner silinince yazılan veri gider. Yazılabilir katman konteynerin ömrüne bağlıdır. Veritabanı dosyası, yüklenen belgeler ya da üretilen raporlar konteynerin içinde tutulursa ilk yeniden dağıtımda kaybolur. Volume'ün gerekçesi budur ve 7. derste kurulur.
- Büyük dosyayı değiştirmek pahalıdır. Copy-on-write, 1 GB'lık bir dosyanın tek baytını değiştirmek için onu üst katmana kopyalar; veritabanı gibi yoğun yazma yapan işler yazılabilir katmanda değil volume üzerinde çalıştırılır (performans gerekçesi).
- Silme, yer kazandırmaz. Bir katmanda eklenen dosyayı sonraki katmanda silmek onu imajdan çıkarmaz; yalnız görünmez kılar (whiteout). Bu yüzden "önce sır dosyasını kopyala, sonra sil" yaklaşımı hem imajı küçültmez hem de sırrı imaj içinde bırakır — 8. ve 9. dersin çok aşamalı derleme (multi-stage) gerekçelerinden biri.
İmajın yapılandırma tarafı da katmanlar kadar önemlidir: hangi komutla başlayacağı
(ENTRYPOINT/CMD), hangi kullanıcıyla koşacağı
(USER — 3. ders), hangi ortam değişkenlerini taşıdığı, hangi portu beyan
ettiği ve çalışma dizini. Bu bilgiler imajın manifest'inde durur ve
docker inspect ile okunur.
06İçerik adresleme: Git nesne deposuyla aynı fikir
16-01'de Git'in nesne deposunu öğrendik: içerik SHA özetiyle adreslenir, aynı içerik tek kez saklanır, bir commit'in kimliği içeriğinden türer. İmaj katmanları birebir aynı fikirle çalışır ve bu benzerlik tesadüf değildir — ikisi de değişmezlik ve tekrarlanabilirlik için aynı çözümü bulmuştur.
| Git (16-01) | Konteyner imajı |
|---|---|
| blob / tree / commit nesnesi | katman (tar arşivi) / imaj yapılandırması / manifest |
| SHA-1/SHA-256 özeti = kimlik | digest: sha256:9f2c… |
| Aynı içerik tek kez saklanır | Aynı katman birden çok imaj arasında paylaşılır |
git fetch yalnız eksik nesneyi indirir | docker pull yalnız eksik katmanı indirir ("Already exists") |
| Dal/etiket = hareketli işaretçi | Etiket (:8.0, :latest) = hareketli işaretçi |
| Commit değişmez; yeni commit yeni kimlik | Katman ve digest değişmez; yeni içerik yeni digest |
Bu koşutluğun pratik faydaları: (1) Aynı temel imajı kullanan on uygulama, o katmanı
diskte bir kez tutar — sunucuda yer ve ağda bant genişliği kazanılır.
(2) docker pull çıktısındaki "Already exists" satırları bunun görünen
yüzüdür. (3) Etiket ↔ digest ayrımı üretim güvenliğinin temelidir: etiket
taşınabilir, digest taşınamaz. nginx:1.27 etiketi yarın farklı bir imajı
gösterebilir; nginx@sha256:… her zaman tam olarak aynı imajı gösterir.
Bu yüzden üretim dağıtımlarında ve tedarik zinciri güvenliğinde digest sabitlemesi
(pinning) yapılır. Aynı sebeple :latest etiketi üretimde kullanılmaz:
"ne olduğu belirsiz, dün ile bugün farklı olabilen" bir işaretçidir ve geri alma planını
imkânsızlaştırır (10. ders, imaj registry; 18. hat, dağıtım).
07Ekosistem: OCI, Docker, containerd, Podman
"Docker" günlük dilde hem şirketi, hem ürünü, hem de konteyner kavramını anlatır; teknik olarak katmanları ayırmak gerekir. 2015'te oluşturulan OCI (Open Container Initiative) üç şeyi standartlaştırdı: imaj biçimi, çalışma zamanı ve dağıtım (registry API'si). Bunun anlamı: bir araçla ürettiğin imaj başka bir araçla çalışır.
| Katman | Örnek | İşi |
|---|---|---|
| Komut satırı / arayüz | docker CLI, nerdctl, podman | Kullanıcının konuştuğu yüz |
| Motor / API | Docker Engine (dockerd) | İmaj yönetimi, ağ, volume, REST API (soket üzerinden) |
| Yüksek seviyeli çalışma zamanı | containerd, CRI-O | İmajı çeker, konteyner ömrünü yönetir; Kubernetes bunu kullanır |
| Düşük seviyeli çalışma zamanı | runc, crun | Namespace/cgroup kurup süreci gerçekten başlatır |
| İmaj derleyici | BuildKit, buildah | Dockerfile'dan imaj üretir (8. ders) |
| Registry | Docker Hub, GitHub/GitLab registry, Harbor, ACR/ECR | İmaj deposu (10. ders) |
Bilinmesi gereken ayrımlar:
- Docker Desktop ↔ Docker Engine. Engine, Linux'ta çalışan arka plan
servisidir (
systemctl status docker— 4. ders). Docker Desktop, Windows/macOS için bir sanal makine + arayüz + Kubernetes paketidir ve ticari lisans koşullarına tabidir (belirli büyüklükteki şirketlerde ücretli). Kurumsal ortamda bu bir karar konusudur; alternatifleri Rancher Desktop, Podman Desktop ya da doğrudan WSL2 içine kurulan Engine'dir. - Podman. Docker ile komut uyumlu (
alias docker=podmançalışır), ama arka plan servisi olmadan (daemonless) ve varsayılan olarak köksüz (rootless) koşar; systemd ile iyi bütünleşir. 3. dersteki "docker grubu = root yetkisi" riskini ortadan kaldırdığı için RHEL ailesinde ve güvenlik odaklı kurumlarda tercih edilir. - Kubernetes ve Docker. Kubernetes, çalışma zamanı olarak Docker Engine'i değil containerd/CRI-O'yu kullanır ("Docker desteği kaldırıldı" haberinin anlamı budur). Ama Docker ile ürettiğin imaj OCI standardında olduğu için Kubernetes'te sorunsuz çalışır — 12. derste kavramsal olarak görülecek.
08Geliştirici için anlamı: Testcontainers senedi
Konteynerin bir geliştirici için en hızlı getirisi üretim değil geliştirme ve test tarafındadır. 7. hatta PostgreSQL kurmak, 19. hatta Redis ve RabbitMQ ayağa kaldırmak tek komuta iner (7. derste yapacağız). En önemlisi 15-06'da verilen sözün ödenmesidir.
Senet: Testcontainers (15-06). Entegrasyon testi dersinde şunu söylemiştik: gerçek veritabanına karşı test yazmanın doğru yolu, testin kendi veritabanını başlatmasıdır; sahte (in-memory) sağlayıcı SQL davranışını taklit edemez. Testcontainers'ın yaptığı iş tam olarak budur ve altında bu dersin mekanizması vardır: test başlarken bir imajdan konteyner başlatır, rastgele bir porta bağlar, hazır olmasını bekler, testler bitince konteyneri siler.
// xUnit + Testcontainers (15-06'nın devamı) — kavramsal iskelet
public class BasvuruTestleri : IAsyncLifetime
{
private readonly PostgreSqlContainer _db = new PostgreSqlBuilder()
.WithImage("postgres:16-alpine") // sürüm sabit: üretimle aynı ana sürüm
.Build();
public Task InitializeAsync() => _db.StartAsync(); // konteyner ayağa kalkar
public Task DisposeAsync() => _db.DisposeAsync().AsTask(); // ve silinir
[Fact]
public async Task Basvuru_kaydedilir()
{
var baglanti = _db.GetConnectionString(); // rastgele porta bağlı
// ... gerçek EF Core migration + gerçek sorgu
}
}
Bu desenin bu derse bağlanan dört noktası: (1) Docker gereksinimi — bu testler geliştirme makinesinde ve CI koşucusunda çalışan bir konteyner çalışma zamanı ister (18. hatta CI'da Docker erişimi bir yapılandırma maddesi olarak döner). (2) Rastgele port — paralel testlerin çakışmaması için port yayımlama dinamik yapılır (7. ders). (3) Hazır olma kontrolü — konteynerin başlaması servisin hazır olması değildir; sağlık kontrolü beklenir (7. ve 9. ders). (4) Temizlik — silinen konteynerin verisi gider; testin izole olmasını sağlayan şey de bu (05. bölüm).
Aynı mantık geliştirme ortamının tamamına genişletilir: docker compose up
ile API + veritabanı + önbellek tek komutla ayağa kalkar ve yeni gelen ilk gün kod
yazmaya başlar (9. ders). Bu, 16-11'in README "beş dakika kuralı"nı gerçekten
uygulanabilir kılan mekanizmadır.
09Temel imaj seçimi: alpine, slim, tam
Her imaj bir temel imajdan (base image) başlar ve bu seçim boyut, güvenlik ve uyumluluğu birlikte belirler. 1. dersteki glibc/musl ayrımı burada pratik karara dönüşür.
| Tür | Örnek | Boyut | Not |
|---|---|---|---|
| Tam | mcr.microsoft.com/dotnet/aspnet:8.0 (Debian) | ~220 MB | Kabuk ve araçlar var, hata ayıklamak kolay; güvenli varsayılan |
-slim | node:20-slim | Daha küçük | Aynı libc (glibc), gereksiz paketler atılmış |
-alpine | postgres:16-alpine | Çok küçük | musl: native bağımlılıklarda uyum riski; DNS ve zaman dilimi davranışında farklar |
| chiseled / distroless | dotnet/aspnet:8.0-noble-chiseled | Küçük | Kabuk yok, paket yöneticisi yok: saldırı yüzeyi en küçük, hata ayıklama en zor |
scratch | — | 0 | Tamamen boş; yalnız statik bağlı (self-contained, AOT) ikili dosyalar için |
Seçim ölçütleri: (1) Uyum önce gelir — .NET için Alpine ayrı bir çalışma kimliği
(linux-musl-x64) ister; native bağımlılığı olan kütüphaneler (görüntü işleme,
ICU, bazı veritabanı sürücüleri) Alpine'da farklı davranabilir. Yalnız boyut istiyorsan
Debian tabanlı -slim ya da chiseled imajlar daha güvenli seçimdir.
(2) Küçük imaj = küçük saldırı yüzeyi: içinde kabuk ve paket yöneticisi olmayan bir
imajda, ele geçirilen bir uygulamanın yapabilecekleri belirgin biçimde azalır
(14. hattaki sertleştirme ilkesi). (3) Hata ayıklama bedeli: kabuksuz imajda
docker exec … sh çalışmaz; teşhis için geçici bir yardımcı konteyner
(docker debug ya da aynı namespace'e bağlanan bir araç konteyneri) gerekir —
7. derste göreceğiz. (4) Mimari: Apple Silicon ya da ARM sunucularda
linux/arm64, bulutta çoğunlukla linux/amd64 gerekir; yanlış
mimarideki imaj "exec format error" verir. Çok mimarili imaj üretimi 10. dersin
konusudur.
Son bir kural ve 8. derse köprü: temel imaj sürümüyle seçilir
(:8.0 ya da digest), :latest ile değil; ve güvenlik yaması temel
imajı yenileyip yeniden derlemekle gelir (4. ders). İmaj tarama (10. ders) bu seçimi
ölçülebilir kılar: aynı uygulamanın tam ve chiseled imajları arasındaki bilinen açık sayısı
farkı, boyut tartışmasını tahminden çıkarır.
10Mini alıştırma — mekanizmayı gözle doğrula
Yaklaşık 20 dakika · Docker (ya da Podman) kurulu bir ortam
- Çekirdek paylaşımı. Hostta
uname -r, sonradocker run --rm alpine uname -rçalıştır. Çıktılar neden aynı? Aynı komutuubuntuimajıyla yinele ve sonucu yorumla. - Farklı kullanıcı alanı.
docker run --rm alpine cat /etc/os-releasevedocker run --rm ubuntu cat /etc/os-releaseçıktılarını hostunkiyle karşılaştır. Neyin değiştiğini, neyin aynı kaldığını yaz. - PID namespace.
docker run --rm alpine ps -efçalıştır: kaç süreç var, PID 1 ne? Sonra hosttaps -ef | grep -c ""ile karşılaştır. - Katman paylaşımı.
docker pull postgres:16-alpineve ardındandocker pull postgres:16çalıştır; çıktıdaki "Already exists" satırlarını say.docker image lsvedocker system dfile toplam disk kullanımının imaj boyutları toplamından küçük olduğunu gözle doğrula. - Etiket ↔ digest.
docker image inspect --format '{{index .RepoDigests 0}}' alpineile digest'i al.docker run --rm alpine@sha256:<digest> echo okile digest üzerinden çalıştır. Etiketin neden üretimde yetersiz olduğunu iki cümleyle yaz. - Yazılabilir katman ve kaybolan veri.
docker run -it --name deneme alpine shiçindeecho veri > /kok-dosya.txtyaz, çık.docker start -ai denemeile geri dön: dosya var mı? Sonradocker rm denemeyapıp aynı imajdan yeni konteyner başlat: dosya var mı? Sonucu 7. dersin volume gerekçesiyle bağla. - Katmanları oku.
docker history mcr.microsoft.com/dotnet/aspnet:8.0(ya daalpine) çıktısını incele: kaç katman var, en büyük katman hangisi? Sonradocker image inspect --format '{{len .RootFS.Layers}}' alpineile katman sayısını doğrula. - Boyut karşılaştırması.
docker pull node:20vedocker pull node:20-alpineçekipdocker image ls | grep nodeile boyutları karşılaştır. Alpine'ı seçmenin bedelini (musl) bir cümleyle yaz.
11Kendini sına
Konteynerin çözdüğü sorun nedir; konteyner teknik olarak nedir (namespace, cgroup, katmanlı dosya sistemi) ve bunların somut sonuçları nelerdir?
Çözülen sorun ortam farkıdır: uygulamanın çalışması için gereken
çalışma zamanı, kütüphaneler, dosya düzeni ve paketler her ortamda elle kurulduğunda
"bende çalışıyor", bağımlılık çakışması, bayatlayan kurulum dokümanı ve uzun ilk gün
çıkar. Konteyner uygulamayı bütün kullanıcı alanıyla birlikte sürümlü ve değişmez
bir imaj olarak paketler; aynı imaj geliştirmede, CI'da ve üretimde koşar — ortam ayrımı
mekanizmayla garanti edilir. Teknik olarak konteyner izole bir süreçtir ve üç
çekirdek mekanizmasına dayanır: namespace sürecin ne göreceğini ayırır (PID, ağ,
mount, UTS, IPC, user), cgroup ne kadar kaynak kullanacağını sınırlar (systemd'nin
MemoryMax'i ile aynı altyapı), katmanlı dosya sistemi (overlayfs)
hangi dosyaları göreceğini belirler. Somut sonuçlar: konteynerde uygulama PID 1'dir
— SIGTERM işleyicisi yoksa sinyal yok sayılır ve docker stop
zorla öldürür, ayrıca zombi süreçler birikebilir (--init); konteynerin
kendi localhost'u vardır, bu yüzden uygulama 0.0.0.0
dinlemelidir; kendi kök dosya sistemini görür, hostun /etc'si yoktur
(sertifika/yapılandırma "bulunamadı" hatalarının kaynağı); user namespace
kaydırılmadıysa konteynerdeki root dosya izinleri açısından hostun root'u gibi davranır.
Bunlar Linux çekirdeği özellikleridir; Docker onları icat etmedi, kullanımını
kolaylaştırdı — bu yüzden Windows/macOS'ta Linux konteyneri için bir sanal makine (WSL2)
gerekir.
Sanal makine ile konteyner arasındaki farklar nelerdir; hangi durumda hangisi seçilir? Konteyner güvenlik sınırı olarak yeterli midir?
Temel fark tek cümledir: VM kendi çekirdeğini ve tam bir
işletim sistemini çalıştırır, konteyner hostun çekirdeğini paylaşır ve yalnız
kullanıcı alanını taşır. Buradan türeyenler: boyut (GB ↔ MB), açılış süresi (onlarca
saniye ↔ milisaniye-saniye), yoğunluk (onlarca ↔ yüzlerce), izolasyon gücü (donanım
düzeyinde güçlü ↔ çekirdek paylaşımı nedeniyle daha zayıf), OS seçimi (farklı çekirdek
çalıştırılabilir ↔ yalnız host çekirdeğiyle uyumlu; Linux ve Windows konteynerleri aynı
hostta koşmaz) ve yama yönetimi (her VM ayrı yamalanır ↔ imaj yeniden
derlenir). Uygun yerler: VM farklı OS ihtiyacı, güçlü izolasyon gereği ve eski
monolitik sistemler için; konteyner aynı OS üzerinde çok uygulama, hızlı ölçekleme, CI ve
tekrarlanabilir ortam için. İkisi alternatif değil, iç içedir: bulutta VM kiralanır,
üstünde konteyner koşar (Kubernetes kümeleri böyle kurulur). Güvenlik sınırı olarak
konteyner zayıftır: birbirine güvenmeyen kiracıları ayırmak için VM ya da hafif VM
teknolojileri (gVisor, Kata, Firecracker) kullanılır; ayrıca konteyner içinde
top/free hostun değerlerini gösterebilir — ölçüm
docker stats ile, kaynak farkındalığı .NET'in cgroup limitini okumasıyla
sağlanır.
İmaj, konteyner ve katman arasındaki fark nedir; copy-on-write nasıl çalışır ve hangi üç sonucu doğurur?
İmaj salt okunur, sürümlü pakettir: dosya sistemi katmanları + yapılandırma (giriş komutu, kullanıcı, ortam, port). Konteyner imajdan başlatılmış çalışan örnektir ve üstüne ince bir yazılabilir katman eklenir — sınıf/nesne ilişkisi gibi. Katman bir dosya sistemi değişikliği paketidir; imaj katmanların üst üste bindirilmesidir (overlayfs ile tek ağaca birleştirilir). Copy-on-write: alt katmandaki bir dosya değiştirilmek istendiğinde önce üst yazılabilir katmana kopyalanır, sonra değiştirilir; alt katmanlar asla değişmez. Üç sonuç: (1) Konteyner silinince yazılan veri gider — yazılabilir katman konteynerin ömrüne bağlıdır; kalıcı veri için volume gerekir. (2) Büyük dosyayı değiştirmek pahalıdır — 1 GB'lık dosyanın tek baytı için tamamı kopyalanır, bu yüzden yoğun yazan veritabanı yazılabilir katmanda değil volume üzerinde çalıştırılır. (3) Silme yer kazandırmaz — sonraki katmanda silinen dosya imajdan çıkmaz, yalnız görünmez olur (whiteout); dolayısıyla "kopyala, kullan, sil" yaklaşımı hem imajı küçültmez hem de sırrı imajın içinde bırakır — çok aşamalı derlemenin gerekçelerinden biri.
İmaj katmanları ile Git nesne deposu arasındaki koşutluk nedir; etiket ↔ digest ayrımı neden önemli? OCI ekosisteminde Docker, containerd, Podman ne yapar ve Testcontainers bu dersle nasıl ilişkili?
Her iki sistem de içerik adreslemeye dayanır: Git'te blob/
tree/commit SHA özetiyle adreslenir, imajda katman/yapılandırma/manifest
sha256: digest ile; aynı içerik tek kez saklanır, bu yüzden aynı
katman birçok imaj arasında paylaşılır ve docker pull yalnız eksik katmanı
indirir ("Already exists"). Git'teki dal/etiket gibi imaj etiketi de hareketli
işaretçidir: nginx:1.27 yarın başka bir imajı gösterebilir, ama
nginx@sha256:… her zaman aynı imajı gösterir. Bu yüzden üretimde ve tedarik
zinciri güvenliğinde digest sabitlenir ve :latest kullanılmaz — geri alma
planını imkânsızlaştırır. Ekosistem katmanları: docker/podman
CLI (arayüz), Docker Engine (imaj/ağ/volume yönetimi ve API),
containerd/CRI-O (yüksek seviyeli çalışma zamanı; Kubernetes bunu kullanır),
runc (namespace/cgroup kurup süreci başlatan düşük seviyeli çalışma zamanı),
BuildKit/buildah (imaj derleme), registry (imaj deposu). OCI imaj biçimini,
çalışma zamanını ve dağıtımı standartlaştırdığı için bir araçla üretilen imaj diğeriyle
çalışır. Docker Desktop, Windows/macOS için VM + arayüz paketidir ve belirli büyüklükteki
şirketlerde ticari lisans gerektirir; Podman daemonless ve varsayılan köksüzdür, bu yüzden
"docker grubu = root yetkisi" riskini ortadan kaldırır. Testcontainers, entegrasyon
testinin kendi veritabanını başlatmasını sağlar (15-06 senedi): imajdan konteyner
başlatır, rastgele porta bağlar, hazır olmasını bekler, test sonunda siler — bu yüzden
geliştirme makinesinde ve CI koşucusunda bir konteyner çalışma zamanı gerekir, izolasyon
da silinen konteynerin verisinin gitmesinden gelir.
12Sınav köşesi
Yazılıda nasıl sorulur
(1) Konteyner teknolojisi nedir; hangi sorunu çözer? (2) Sanal makine ile konteyner
arasındaki farkları karşılaştırınız. (3) Konteynerlerin dayandığı Linux çekirdeği
özellikleri nelerdir (namespace, cgroup)? (4) İmaj ile konteyner arasındaki fark nedir?
(5) Katmanlı dosya sistemi ve copy-on-write nedir? (6) Bir konteyner silindiğinde içine
yazılan veriler ne olur; kalıcı veri nasıl sağlanır? (7) İmaj etiketi (tag) ile digest
arasındaki fark nedir; latest etiketi neden üretimde önerilmez? (8) OCI
standardı nedir; Docker, containerd ve runc'un işlevleri nelerdir? (9) Docker'ın
Windows üzerinde çalışması için neye ihtiyaç vardır? (10) Konteyner temel imajı
seçiminde hangi ölçütler dikkate alınır?
Sözlüde nasıl anlatılır — 2 dakikalık taslak
Düzen: (1) Tanım — "Konteyner sanal makine değil, izole bir süreçtir:
namespace ne göreceğini, cgroup ne kadar kaynak kullanacağını, katmanlı dosya sistemi
hangi dosyaları göreceğini belirler. Çekirdek hostla paylaşılır; bu yüzden MB
ölçeğinde ve saniyeler içinde açılır." (2) Fark — "VM kendi çekirdeğini taşır,
izolasyonu daha güçlüdür ama ağırdır. Pratikte iç içe kullanırım: bulutta VM, üstünde
konteyner. Güvenlik sınırı gereken çok kiracılı senaryoda konteynerle yetinmem."
(3) İmaj modeli — "İmaj salt okunur katmanlardan oluşur, konteyner üstüne yazılabilir
bir katman ekler; copy-on-write nedeniyle konteyner silinince yazılan veri gider — bu
yüzden kalıcı veri volume'de tutulur, veritabanı yazılabilir katmanda
çalıştırılmaz." (4) Sürüm disiplini — "Katmanlar içerik adreslidir, Git'in nesne deposu
gibi; aynı katman imajlar arasında paylaşılır. Üretimde etiket yerine digest
sabitlerim, :latest kullanmam; güvenlik yaması temel imajı yenileyip
yeniden derlemekle gelir." (5) Kıdem vuruşu — "Temel imajda uyumu boyuttan önce
tutarım: .NET'te Alpine musl uyumu risk taşır, boyut istiyorsam slim ya da chiseled
imaj seçerim — kabuksuz imaj saldırı yüzeyini küçültür ama hata ayıklamayı zorlaştırır.
Testlerde Testcontainers ile gerçek veritabanını konteynerde koştururum; bu yüzden CI
koşucusunda konteyner çalışma zamanı erişimini bir gereksinim olarak belirtirim."
