Değişmez İşletim Sistemi Ne Kazandırır, Ne Kaybettirir
Kök dosya sistemini salt okunur tutan, güncellemeleri imaj değişimi olarak gönderen işletim sistemlerinin üç farklı yaklaşımı, gerçek kazançları ve gözden kaçan maliyetleri.
Bir sunucuyu iki yıl açık tutun, üzerinde apt veya yum ile paket kurup silin, config dosyalarını elle düzenleyin, birkaç kez "geçici" diye çalıştırdığınız script'i unutun. İki yıl sonra o makinenin durumunu başka bir makinede birebir yeniden üretmek neredeyse imkansızdır. Bu soruna "config drift" denir ve klasik, değiştirilebilir (mutable) Linux dağıtımlarının doğasında vardır: paket yöneticisi sisteme aşama aşama, geri dönüşü zor değişiklikler biriktirir.
Değişmez işletim sistemi bu birikimi engellemenin bir yoludur. Kök dosya sistemi salt okunur tutulur, güncellemeler tek tek paket yükseltmesi olarak değil bütün bir imaj değişimi olarak gelir ve sistemin çalışır durumunu değiştiren her şey ya bir konteynerde ya da açıkça ayrılmış birkaç kalıcı dizinde (genellikle /etc ve /var) yaşar.
Üç farklı yaklaşım
"Değişmez işletim sistemi" tek bir teknoloji değil, en az üç farklı yöntemin ortak adıdır.
İmaj tabanlı, atomik güncellemeli dağıtımlar bunlardan biri. Fedora CoreOS ve Fedora Silverblue rpm-ostree kullanır: kök dosya sistemi git benzeri bir nesne deposundan (ostree) oluşur, güncelleme yeni bir "commit" çeker ve bir sonraki açılışta o commit'e geçilir. Eski durum diskte durduğu için rpm-ostree rollback ile bir önceki imaja anında dönülebilir. openSUSE MicroOS aynı fikri btrfs anlık görüntüleriyle uygular: transactional-update bir güncellemeyi ayrı bir anlık görüntü üzerinde uygular, sistem yeniden başlayınca o görüntüye geçer, sorun çıkarsa GRUB menüsünden bir önceki görüntü seçilir.
Konteyner çalıştırmaya özelleşmiş, minimal dağıtımlar ikinci yaklaşım. Bottlerocket ve Flatcar Container Linux üzerine genel amaçlı paket kurulmasına hiç izin vermez. Bottlerocket'ta kabuk erişimi bile yoktur; sorun giderme için ayrı bir "admin container" veya "control container" API üzerinden açılır. Buradaki varsayım nettir: bu makine yalnızca bir konteyner çalıştırma ajanı ve kubelet barındırır, başka hiçbir şey.
Bildirimsel (declarative), tam sistem tanımlı dağıtımlar üçüncü yol. NixOS kök dosya sistemini salt okunur tutmaz ama benzer bir garantiyi başka yoldan verir: sistemin tamamı tek bir yapılandırma dosyasından türetilir, her yükseltme yeni bir "generation" olarak eklenir, eskisi silinmeden durur. Sonuç, klasik anlamda "immutable" olmasa da reprodüksiyonu garanti edilmiş bir sistemdir.
Üçü de aynı sorunu çözer: sistemin şu anki hâli, o hâli üreten tanımdan tahmin edilebilir olsun.
Ne kazanılır
Rollback'in maliyeti sıfıra yakındır. Klasik bir dağıtımda başarısız bir güncellemeden geri dönmek ya yedekten geri yükleme ya da elle paket downgrade etmek demektir; imaj tabanlı bir sistemde bu tek komuttur çünkü önceki imaj zaten diskte durur.
Fleet yönetimi basitleşir. Yüz sunucuyu aynı imajla ayağa kaldırdığınızda hepsi bit bit aynıdır; "bu sunucuda neden farklı davranıyor" sorusu neredeyse ortadan kalkar çünkü sunucular arasında birikmiş, kayıt altına alınmamış fark yoktur.
Saldırı yüzeyi küçülür. Kabuk yok, genel amaçlı paket yöneticisi yok, SSH bazı dağıtımlarda hiç açık değil. Bir saldırganın sisteme kalıcı bir arka kapı bırakması, kök dosya sistemi salt okunur olduğu ve her yeniden başlatmada temiz imaja döndüğü için çok daha zordur.
Test edilebilirlik artar. Bir imajı CI'da üretip aynı imajı hem test hem üretim ortamına dağıtabilirsiniz; test ettiğiniz bit dizisiyle üretimde çalışan bit dizisi birebir aynıdır.
Ne kaybedilir
Ad-hoc hata ayıklama alışkanlığı çalışmaz. "SSH'la gir, bir araç kur, bak" refleksi olan bir ekip için ilk hafta zorludur; paket kurulamadığından debug aracı bir konteynerde (toolbox, rpm-ostree'nin geçici katmanı ya da Bottlerocket'ın admin container'ı) çalıştırılmalıdır ve bu ekstra bir adımdır.
Bazı ajanlar ve kernel modülleri sorun çıkarır. Kernel modülü yükleyen bir güvenlik ajanı, host'un paket yöneticisine bağımlı bir monitoring aracı ya da özel bir dosya sistemi sürücüsü, "her makineye kur" varsayımıyla yazılmıştır. Bunları immutable bir host'a taşımak ya konteynerleştirmeyi ya da o aracı tamamen değiştirmeyi gerektirir; bu her zaman mümkün değildir.
Katman biriktirme (package layering) yanlış kullanılırsa aynı drift sorununu yeniden üretir. rpm-ostree ve transactional-update ikisi de host'a doğrudan paket eklemeye izin verir. Bu özellik pratik ama kötüye kullanılırsa "her makine kendi katmanını biriktirir, hiçbiri birbirine benzemez" durumuna geri döner. Katman eklemek istisna olmalı, kural değil; kalıcı ihtiyaçlar imaja gömülmeli.
Öğrenme eğrisi gerçek bir maliyettir. Ignition/Butane, ostree, btrfs anlık görüntüleri, imaj pipeline'ı kurmak, klasik bir dağıtımı elle yönetmekten daha fazla ön yatırım ister. Küçük bir ekip için bu yatırımın karşılığını alması zaman alır.
/etc'nin özel muamelesi kafa karıştırır. Kök dosya sistemi salt okunur olsa da /etc genelde yazılabilir kalır, çünkü yerel yapılandırma orada tutulur. rpm-ostree bunu üç yönlü birleştirme ile çözer: eski varsayılan /etc, yeni varsayılan /etc ve sizin yerel değişiklikleriniz karşılaştırılır. Bir dosyayı siz hiç değiştirmediyseniz güncelleme onu sessizce yeni varsayılana çeker; değiştirdiyseniz sizin sürümünüz korunur ama yeni sürümdeki bir güvenlik düzeltmesini de kaçırmış olursunuz. İkisi de doğru davranış gibi görünür, ikisi de birbirinden habersiz sürpriz üretebilir; bu yüzden /etc'te elle tutulan her dosya, o dosyanın neden özel olduğunu açıklayan bir notla birlikte tutulmalıdır.
Ne zaman seçilmemeli
Geliştirici iş istasyonları ve deneme yanılma gerektiren makineler için immutable bir taban genelde zarar verir; burada esneklik hıza doğrudan katkı yapar. Az sayıda, birbirinden çok farklı görev yapan sunucular için de imaj pipeline'ı kurmanın maliyeti kazancını geçebilir; üç farklı görevi olan beş sunucu için ayrı ayrı imaj bakımı, elle yönetimden daha pahalı olabilir. Kısacası fayda, aynı imajı çok sayıda makinede tekrar tekrar kullandığınızda büyür; tekil, özel makineler için küçülür.
Pratik bir başlangıç
Fedora CoreOS'ta ilk kurulum bir Butane (YAML) dosyasından Ignition (JSON) config üretmekle başlar:
variant: fcos
version: 1.5.0
passwd:
users:
- name: core
ssh_authorized_keys:
- ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA... kullanici@ornek
systemd:
units:
- name: podman-example.service
enabled: true
contents: |
[Unit]
Description=Ornek konteyner
After=network-online.target
[Service]
ExecStart=/usr/bin/podman run --rm --name example nginx:alpine
[Install]
WantedBy=multi-user.target
Bu dosya butane aracıyla derlenir, çıkan Ignition config imaj ilk açılışta bir kez uygulanır; sonraki her değişiklik ya yeni bir imaj olarak ya da açıkça yönetilen bir birim dosyası olarak gelmelidir, elle /etc düzenlemek üretim akışının dışına çıkmak anlamına gelir.
Karar basit bir soruya iner: makinenizin durumunu yarın sıfırdan yeniden üretebilir misiniz, sadece tuttuğunuz tanım dosyalarından? Cevap hayırsa ve bu sizi rahatsız ediyorsa, immutable bir taban o rahatsızlığı yapısal olarak çözer. Cevap "zaten önemli değil, tek makine bu" ise, o karmaşıklığı almaya değmez.