"Docker'ı kaldırdım, makinem bir daha açılmadı" demiş arkadaşımıza şu soruyu soralım: Konteynerin ne yaptığını biliyor musun? Çünkü Docker'ın yaptığı işin yüzde doksanı, çekirdeğin (kernel) onlarca yıldır süren üç eski özelliğinin üstüne konmuş pırıltılı bir paket. Bugün o özellikleri elle kullanacağız.
Chroot: 1979'dan Kalma Zaman Bölücüsü
En eski usül chroot: sürecin kök dizinini (/) başka bir klasörle değiştirmek. Süreç, kendi görüşünde gerçekten / klasöründe yaşar ve üst dizine çıkamaz. PostgreSQL'in ilk geliştiricileri bile bu yöntemi kullanıyordu:
mkdir -p /jail/{bin,lib,lib64,etc}
cp /bin/bash /jail/bin/
ldd /bin/bash | grep "=> /" | awk '{print $3}' | xargs -I{} cp --parents {} /jail/
chroot /jail /bin/bash
Dört satırda çalışan ama kendi kütüphanelerine mahkûm bir mini sistem. Ne yazık ki chroot tek başına hapis değil: sürecin root kullanıcı kalması yeterli, dışarı çıkma yolları biliniyor. Saf sanal alan (jail) olarak değil, dosya sistemi hizalama aracı olarak düşünün. BSD tarafındaki jail() ve Solaris'teki zones aynı fikrin olgunlaşmış halleriydi; Linux'un cevabı biraz sonra gelecekti.
Namespaces: Çekirdek Görüşü Kesmek
Gerçek izolasyon Linux 2.6.24 sonrası gelen namespaces ile başlar. Her namespace, çekirdeğin belirli bir kaynak görüşünü sürece özelleştirir: PID (kendi süreç numaraları), NET (kendi arayüz ve portları), MNT (kendi mount noktaları), UTS (kendi hostname'i), IPC ve USER. Aynı anda altı ayrı evrende yaşayan bir süreç düşünün:
unshare(CLONE_NEWNS | CLONE_NEWPID |
CLONE_NEWNET | CLONE_NEWUTS);
Bu tek satır, sürecin kendi dosya sistemi tablosuna, kendi PID uzayına ve kendi sanal ağına sahip olmasını sağlar. İçeriden bakınca PID 1 olursunuz; dışarıdan bakıldığında sadece sıradan bir 4563. sürecinizesiniz. Docker'ın "konteyner içinde 8080 dinleyebilirim, dışarıyla çakışmam" demesinin karşılığı budur.
Cgroups ve Elle Yazılmış Mini Docker
Namespaces görüşü kısıtlar; cgroups ise harcamayı. memory.max=256M, cpu.max=50000 100000 gibi limitlerle bir sürecin RAM'i şişirip sistemi kilitlemesi engellenir. Üçünü birleştirdiğinizde Docker'ın çekirdek kısmını elle yazmış olursunuz: chroot ile dosya sistemi, namespaces ile süreç ağı, cgroups ile kaynak sınırı. Eksik kalan tek şey katman yönetimi: imaj saklama, ağ kurma, versiyonlama. Docker'ın asıl katkısı da çekirdek özelliklerinde değil, bu mühendislik katmanındadır.
Bunu fark eden ekibin iki yolu var: ya uzun uzun sistem çağrıları yazmak, ya da bu parçaları bir araya getirmiş araçları kullanmak. systemd-nspawn ilkini değil ama ikincisini yapar:
systemd-nspawn -D /jail --machine=deneme \
--private-network --memory-max=512M
Sonuç. Docker'ı sihir gibi görmek, yanlış yönlere savrulmanın reçetesi. Oysa katmanlarını bilen mühendis, konteyner yavaşladığında docker exec yerine nsenter ile sürecin namespace'lerine iner; güvenlik denetiminde "root olamaz" demeden önce hangi capabilities'in düştüğünü sorar. Eski usüllerin dönüşü bu: 1979'da doğan fikirler, modern araçların altında hâlâ aynı yerde duruyor.
Okur Yorumları (0)