Container Teknolojisinin Temelleri

Barış Ekin Yıldırım
7 min readSep 26, 2024

--

Containerlar (konteynerler), bir uygulamayı çalıştırmak için gereken her şeyi içeren hafif, bağımsız ve çalıştırılabilir yazılım paketleridir: kod, çalışma zamanı, sistem araçları, kütüphaneler ve çeşitli ayarlardan oluşur. Bir uygulamayı ve bağımlılıklarını kapsülleyerek, farklı bilgisayar ortamlarında tutarlı bir şekilde çalışmasını sağlarlar.

Containerlar, modern DevOps uygulamalarının ve bulut bilişimin temel taşı haline gelmiştir. “Benim makinemde çalışıyor” şeklindeki eski sorunlara, geliştirmeden dağıtıma kadar tutarlı bir ortam sağlayarak çözüm sunarlar. Bu tutarlılık, verimlilikleri ve ölçeklenebilirlikleri ile birleştiğinde, containerları günümüz yazılım geliştirme ve dağıtım ortamında vazgeçilmez bir araç haline getirmiştir.

Containerlar genellikle “hafif sanal makineler” olarak tanımlansa da, bu basitleştirme onların gerçek doğasını ve yeteneklerini tam olarak yansıtmaz. Containerların gücünden tam anlamıyla yararlanmak için, onları çalıştıran temel teknolojileri ve kavramları anlamak çok önemlidir.

Container Teknolojisinin Temel Kavramları

Container teknolojisinin özünde, süreç(process) izolasyonu kavramı yatar. Containerlar, bu izolasyonu sağlamak için Linux çekirdeğinin üç temel özelliğinden yararlanır:

1. Namespaces (İsim Alanları): Containerlar için izolasyon katmanı sağlayan temel mekanizmadır. Her container için süreç kimlikleri(process ID), ağ arayüzleri, depolama bağlama noktaları ve benzerleri gibi global sistem kaynaklarının minimize edilmiş şekilleri yaratılır. Farklı namespace türleri arasında PID, NET, MNT, IPC, UTS, USER ID ve Time bulunur.

2. Control Groups (cgroups, Kontrol Grupları): Cgroups, süreç grupları için kaynak kullanımını yönetir ve sınırlar. Sistemin her containera net miktarda CPU, bellek, disk I/O ve ağ bant genişliği ayırmasını sağlar. Control Groups aynı zamanda çekirdekteki(kernel) başka bir namespace türüdür.

3. Capabilities: Capabilities, süreçlere verilen ayrıcalıklar üzerinde ayrıntılı kontrol sağlar. Bu sayede containerlar yalnızca gerekli minimum yetenek setiyle yapılandırılabilir bu da güvenliklerini artırır.

Union Filesystems (Birleşik Dosya Sistemleri)

OverlayFS gibi birleşik dosya sistemleri, containerların verimli çalışmasını sağlayan önemli bir bileşendir. Bu dosya sistemi teknolojileri, birden fazla dizinin üst üste bindirilerek tek, birleşik bir dosya sistemi olarak sunulmasına olanak tanır.

Container ortamlarında birleşik dosya sistemlerini kullanmanın temel faydaları şunlardır:

- Containerlar Arasında Paylaşılan Dosyalar: Birleşik dosya sistemleri, birden fazla containerın ortak dosya ve dizinleri paylaşmasına olanak tanır. Böylelikle aynı verilerin çoğaltılması ihtiyacını ortadan kaldırarak depolama alanından önemli ölçüde tasarruf sağlar.

- Katmanlı Dosya Sistemi İmajları: Container imajları genellikle birden fazla katmandan oluşur ve her katman bir dizi dosya sistemi değişikliğini temsil eder. Birleşik dosya sistemleri, bu katmanların verimli bir şekilde birleştirilmesini sağlayarak, her container için tek bir bütünleşik dosya sistemi oluşturur. Bu katmanlı yaklaşım, containerların daha hızlı başlatılmasına olanak tanır ve depolama gereksinimlerini azaltır.

- Copy-on-Write İşlemleri: Birleşik dosya sistemleri, dosya sistemindeki değişikliklerin hemen alttaki depolama alanına yazılmadığı bir copy-on-write stratejisi kullanır. Bunun yerine, değişiklikler ayrı bir üst katmanda saklanır. Bu, containerların ortak, salt okunur alt katmanları paylaşarak hızlı bir şekilde başlamasına ve yalnızca o containera özgü değişiklikler için yeni, yazılabilir bir katman oluşturmasına olanak tanır.

Linux Namespace’lere Genel Bir Bakış

Namespace, Linux çekirdeğinin bir özelliğidir ve çekirdek kaynaklarını böler, böylece bir grup süreç bir dizi kaynağı görürken, başka bir grup süreç farklı bir kaynak setini görür. Linux çekirdeği birkaç tür namespace sağlar:

- PID Namespace: Süreç kimliklerinin izole bir görünümünü sağlar. Bir PID namespace’indeki ilk süreç PID 1 alır ve sonraki süreçler, global PID’lerinden bağımsız olarak artan PID’ler alır.

- NET Namespace: Ağ arayüzlerini, yönlendirme tablolarını ve güvenlik duvarı kurallarını izole eder. Bu, her containerın kendi ağ yığınına(network stack) sahip olmasına olanak tanır.

- MNT Namespace: Dosya sistemi hiyerarşisinin izole bir görünümünü sağlar, containerların kendi kök dosya sistemine sahip olmasına izin verir.

- IPC Namespace: System V IPC ve POSIX mesaj kuyrukları gibi süreçler arası iletişim kaynaklarını izole eder.

- UTS Namespace: Her containerın kendi ana bilgisayar adına ve etki alanı adına sahip olmasına izin verir.

- USER ID Namespace: Container içindeki kullanıcı ve grup kimliklerini dışarıdaki farklı kullanıcı ve grup kimliklerine eşler, böylelikle güvenliği artırır.

- Time Namespace: Sistem saati ve zamanının izole bir görünümünü sağlar. Bu, her containerın kendi saat dilimi ve saat ayarlarına sahip olmasına olanak tanır.

Control Groups (cgroups)

Control Groups veya cgroups, bir süreç koleksiyonunun kaynak kullanımını (CPU, bellek, disk I/O, ağ vb.) sınırlayan, hesaplayan ve izole eden bir Linux çekirdeği özelliğidir.

Cgroups, container kaynak yönetiminde çok önemli bir rol oynar:

- Aynı ana makinedeki containerlar arasında adil kaynak dağılımı sağlarlar. Bu, tek bir “açgözlü” containerın mevcut tüm kaynakları sömürmesini önler.

- Tek bir containerın mevcut tüm kaynakları tüketmesini ve diğer containerların veya ana sistemin performansını etkilemesini önlerler. Bu, bir düzeyde kaynak izolasyonu ve öngörülebilirlik sağlar.

- Kaynak tahsisi üzerinde ayrıntılı kontrol sağlayarak donanım kaynaklarının verimli kullanımına olanak tanırlar. Yöneticiler, her containerın kaynak kullanımı için kesin sınırlar ve paylar belirleyebilir.

Örneğin, cgroups kullanarak bir containerı yalnızca bir CPU çekirdeğinin %50'sini ve 512MB RAM kullanacak şekilde sınırlayabilirsiniz. Bu, containerın mevcut kaynakların çok büyük bir kısmını tüketmemesini ve diğer containerların veya ana sistemin performansını etkilememesini sağlar.

Cgroups, container tabanlı ortamların etkili ve verimli çalışması için çok önemli olan kaynak yönetimi ve izolasyon yeteneklerini sağlayan temel bir mekanizmadır. Kaynak tahsisi üzerinde kontrol sağlayarak ve kaynak açlığını önleyerek, cgroups containerlaştırılmış uygulamaların öngörülebilir ve güvenilir performans göstermesine yardımcı olur.

Capabilities

Linux capabilities, geleneksel olarak root kullanıcısı ile ilişkilendirilen ayrıcalıkları ayrı birimlere ayırır. Bu, bir sürecin hangi ayrıcalıklı işlemleri gerçekleştirebileceği üzerinde daha ayrıntılı bir kontrol sağlar.

Capabilities, container ortamları için ayrıntılı bir güvenlik modeli sağlar. Containerlar genellikle azaltılmış bir yetenek setiyle çalışır, en az ayrıcalık ilkesini takip ederek güvenliği artırır. Örneğin, bir containera tam root erişimi verilmeden ayrıcalıklı portlara bağlanma yeteneği (CAP_NET_BIND_SERVICE) verilebilir.

Bu seçici verilmiş yetkiler, bir container içindeki saldırı yüzeyini ve ayrıcalık yükseltme potansiyelini azaltmaya yardımcı olur. Bir containerın çalışması için gereken minimum yetenek setini sağlayarak, ele geçirilmiş bir containerın yetkisiz ayrıcalıklı işlemler gerçekleştirme riski önemli ölçüde azaltılır.

Container güvenliği, containerları üretim ortamlarında kullanırken oldukça kritik bir konudur. İzolasyonu artırmak ve potansiyel güvenlik açıklarını azaltmak için containerlarla birlikte yaygın olarak kullanılan birkaç ek güvenlik özelliği vardır:

- Bir containerın kullanabileceği sistem çağrılarını kısıtlamak için Seccomp (Secure Computing) profilleri kullanılabilir.

- Bir containerın sistem kaynaklarına erişimini daha da kısıtlamak için AppArmor veya SELinux politikaları kullanılabilir.

- Root ayrıcalıkları olmadan çalışan rootless containerlar yaratılabilir.

- Köken ve bütünlüğü sağlamak için imzalanmış container görüntüleri oluşturulabilir.

Capabilities’i bu diğer güvenlik mekanizmalarıyla birlikte kullanarak, container dağıtımları sıkılaştırılabilir ve potansiyel saldırılara veya yanlış kullanıma karşı daha dirençli hale getirilebilir. Container güvenliğine öncelik vermek, containerlaştırılmış altyapı kullanarak kritik uygulamaları production ortamlarında çalıştırmak için çok önemlidir.

Terminalden Container Benzeri Yapılar Oluşturma

Namespace Kullanarak Ortam İzolasyonu

Linux’un yerel araçlarını kullanarak elimizle container benzeri bir ortam nasıl oluşturulacağını keşfedelim. Bu, containerların temel düzeyde nasıl çalıştığını daha derinden anlamamızı sağlayacaktır.

Basit bir Container Benzeri Ortam Oluşturma

Yeni namespace’ler oluşturmak ve süreç izolasyonunu göstermek için unshare komutunu kullanacağız:

Bu örnekte, kendi PID namespace’ine sahip yeni bir süreç oluşturduk. Süreç kimliğinin 1 olduğunu göreceksiniz, bu da namespace’deki ilk süreç olduğunu gösterir. Ayrıca, containerın kendi ana bilgisayar adına sahip olmasına izin veren UTS namespace’ini ve containerın dosya sisteminin kendi görünümüne sahip olmasını sağlayan mount namespace’ini de kullandık.

Cgroups Kullanarak Kaynakları Sınırlama

Cgroups’un nasıl kurulacağını ve kullanılacağını göstermek için, özel bir cgroup oluşturalım ve kaynak sınırlarını yapılandıralım.

Bu örnekte, container benzeri ortamımızda kaynak kullanımını yönetmek ve sınırlamak için cgroups’un nasıl kullanılacağını gösteriyoruz. Süreçlere özel kaynak sınırları uygulamak için oom-space adında özel bir cgroup oluşturduk.

İlk olarak, cgroup için 100MB’lık bir bellek sınırı belirledik. Bu işlem, elimizdeki cgroup’a atanan herhangi bir sürecin bu bellek tahsisini aşamayacağını garanti eder. Eğer aşmaya çalışırsa, çekirdek otomatik olarak bu süreci sonlandıracak, böylece kaynak çekişmesini önleyecek ve nihai olarak sistem stabilitesini koruyacaktır.

Ardından, bir CPU sınırı belirledik ve oom-space cgroup’undaki süreçlerin her 100.000 mikrosaniyelik aralıkta maksimum 50.000 mikrosaniye CPU süresi tüketmesine izin verdik. Bu yapılandırma, herhangi bir tek sürecin CPU kaynaklarını tekelleştirmesini önler ve sistem genelinde adil kaynak paylaşımını teşvik eder.

Son olarak, mevcut kabuk(shell) sürecini oom-space cgroup’una atadık, böylece tanımlanan tüm kaynak sınırlarının buna uygulanmasını sağladık. Bellek yoğun bir süreç(memory intensive) çalıştırarak, bu cgroup sınırlarının kaynak tüketimini nasıl etkili bir şekilde kontrol ettiğini gözlemledik. Süreç belirlenen bellek sınırını aştığı için sonlandırıldı, böylece cgroups’un koruyucu yetenekleride incelenmiş oldu.

Bununla birlikte leaker uygulamasının kaynak kodunu aşağıda bulabilirsiniz:

# Bu kod "https://github.com/saschagrunert/demystifying-containers" reposundan alınmıştır. 
# Containerlar hakkında daha derin bilgi edinmek için bu depoyu incelemenizi şiddetle tavsiye ederim.

pub fn main() {
let mut vec = vec![];
loop {
vec.extend_from_slice(&[1u8; 10_000_000]);
println!("{}0 MB", vec.len() / 10_000_000);
}
}

Farklı Linux çekirdeği özelliklerini elle sıfırdan oluşturarak, container teknolojisine olanak sağlayan temeller hakkında daha derin bir anlayış kazanmış olduk. Namespaces izolasyon katmanını sağlarken, cgroups kaynak kullanımını yönetir ve sınırlar, böylece containerlaştırılmış uygulamaların verimli ve güvenli çalışması için temel oluştururlar.

İleri Seviye Uygulamalar ve Sınırlamalar

Container teknolojisi, temel izolasyon ve kaynak yönetiminin ötesine uzanır:

- Orchestration (Orkestrasyon): Kubernetes gibi araçlar, container kümelerini yönetmeye olanak tanır ve yük dengeleme, ölçekleme ve kendi kendini iyileştirme gibi özellikler sağlar. Orkestratörler sadece containerları değil, aynı zamanda altyapıyı da yöneterek uygulamaların dirençli ve yüksek kullanılabilirlikte kalmasını sağlar. Docker, containerd veya CRI-O gibi runtime’ları kullanarak Kubernetes, geniş bağlamda yük dengeleme, ölçekleme ve kendi kendini iyileştirmeyi otomatikleştirir.

- Networking (Ağ): Calico ve Cilium gibi ağ çözümleri gelişmiş yetenekler sunar. Calico, sağlam ağ politikalarıyla basit, ölçeklenebilir Layer 3 ağ oluşturmaya odaklanırken, Cilium daha derin güvenlik, gözlemlenebilirlik ve ağ performansı optimizasyonları için eBPF’i kullanır.

- Storage (Depolama): Docker volume eklentileri ve Kubernetes’teki CSI (Container Storage Interface) gibi kalıcı depolama çözümleri, uygulama gereksinimlerine bağlı olarak blok, dosya veya nesne depolama olsun, çeşitli depolama backend’leri ile esnek entegrasyona olanak tanır.

Containerların Sınırlamaları

Containerlar birçok fayda sunsa da, her senaryo için uygun değildir:

- Containerlar ana makine çekirdeğini paylaşır, bu da çoklu kullanıcılı ortamlarda güvenlik riskleri oluşturur. Ancak gVisor, Kata Containers veya Firecracker gibi teknolojiler, bu endişeleri gidermek için ek izolasyon sunarak containerları hassas iş yükleri için daha güvenli hale getirir.

- Neredeyse birebir performans sunarlar, ancak I/O yoğun iş yüklerinde veya belirli CPU bağımlı görevlerde ek yük olabilir. Aşırı performans gereksinimleri için doğrudan donanım erişimi veya bare-metal yaklaşım sizin için hala gerekli olabilir.

- Containerlar, doğrudan donanım erişimi gerektiren uygulamalar için uygun olmayabilir. Ancak NVIDIA’nın containerlarda GPU desteği gibi gelişmeler, özellikle AI/ML iş yükleri için bu alanda ilerleme kaydediyor.

- Containerlar, özelleşmiş donanım erişimi gerektiren uygulamalar için uygun olmayabilir.

Sonuç

Namespaces, cgroups ve capabilities’i anlamak, containerları etkili bir şekilde kullanmak için çok önemlidir. Bu özelliklere ek olarak, güvenlik açığı taraması yapmak, en az ayrıcalık ilkelerine uymak ve uygun ağ segmentasyonunu sağlamak gibi en iyi uygulamalara bağlı kalmak, containerlaştırılmış ortamların güvenliğini ve güvenilirliğini daha da artırabilir.

--

--