Docker nedir? Container'lar ve neden önemli oldukları
“Benim makinemde çalışıyor” bir zamanlar o kadar yaygındı ki espri malzemesi olmuştu: Bir geliştiricide sorunsuz çalışan, ama farklı bir sunucuya, farklı bir işletim sistemi sürümüne ya da bir iş arkadaşının dizüstü bilgisayarına geçtiği anda bozulan bir uygulama. Docker, bir uygulamayı gerçekten çalışması için gereken her şeyle birlikte paketleyerek bu bahaneyi tarihe karıştırmak için var.
Container gerçekte nedir?
Bir Docker container'ı, uygulama kodunuzu çalışma zamanı (runtime), kütüphaneleri, yapılandırması ve sistem bağımlılıklarıyla birlikte imaj (image) adı verilen tek bir birimde toplar. Bu imaj sizin dizüstü bilgisayarınızda, bir ekip arkadaşınızın makinesinde, bir staging sunucusunda ya da canlı ortamda birebir aynı şekilde çalışır; çünkü ana makinede o an kurulu olan şeylere dayanmaz. Kendi ortamını yanında getirir.
İnsanlar genellikle container'ın hafif bir sanal makine olduğunu varsayar, ancak bu karşılaştırma aradaki farkı küçümser. Sanal makine, kendi çekirdeği (kernel) de dâhil olmak üzere bütün bir bilgisayarı sanallaştırır; bu yüzden bir VM'in açılması bir dakika sürebilir ve uygulamanız daha başlamadan kolayca gigabaytlarca bellek tüketebilir. Container ise ana makinenin çekirdeğini paylaşır ve yalnızca uygulama katmanını izole eder; bu sayede bir iki saniyede başlar ve belleğin çok küçük bir kısmını kullanır. Birkaç tam VM'i zor kaldıracak bir donanımda onlarca container'ı rahatça çalıştırabilirsiniz.
Bu, gündelik işte neden gerçekten önemli?
- Ortamlar arasında tutarlılık: CI'da testlerden geçen container, canlı ortamda çalışanla birebir aynı çıktıdır (artifact); farklı olabilecek yeniden bir derleme değil.
- Hızlı ve ucuz izolasyon: Bir veritabanını, bir önbelleği ve uygulamanızı aynı makinede, bağımlılık sürümleri yüzünden birbirleriyle çatışmadan ayrı container'lar olarak çalıştırabilirsiniz.
- Tekrarlanabilir işe alıştırma: Yeni bir mühendis, yarısı güncelliğini yitirmiş kurulum adımlarıyla dolu bir wiki sayfası yerine tek bir komut çalıştırır ve tüm sistem yerelde ayağa kalkar.
- Basit yatay ölçeklendirme: Uygulamanızın üç kopyasının bir yük dengeleyicinin arkasında çalışması mı gerekiyor? Bu, senkronize tutulması gereken üç farklı kurulum değil, üç kez başlatılan aynı imajdır.
Asıl sözleşme Dockerfile'dır
Her imaj bir Dockerfile'dan oluşturulur. Dockerfile, ortamı kurmak için gereken adımları tek tek listeleyen düz bir metin dosyasıdır: hangi temel imajdan başlanacağı, nelerin kurulacağı, nelerin kopyalanacağı, uygulamayı hangi komutun başlatacağı. Bu dosya kodla birlikte sürüm kontrolüne eklenir; yani ortamın kendisi de sürümlenir ve incelenebilir. Birinin kafasında yaşayan ya da güncelliğini yitirmiş bir README'de duran kulaktan kulağa bilgi olmaktan çıkar.
İnsanların takıldığı noktalar
Başlangıçta en sık yapılan hata, container'a SSH ile bağlanıp elle düzenlenen hafif bir VM gibi davranmaktır: Gerekli adımları Dockerfile'a yazmak yerine, container çalıştıktan sonra bir şeyleri elle kurmak. Bu, bütün amacı boşa çıkarır: Container yeniden başladığı anda bu manuel değişiklikler kaybolur ve yeniden “Beş dakika önce çalışıyordu” noktasına dönersiniz. Bir container'ın ihtiyaç duyduğu her şey imajın kendisine işlenmiş olmalıdır.
Diğer yaygın tuzak, gereğinden çok daha büyük imajlardır; çünkü derleme sırasında içeri çekilen her gereksiz paket nihai imajda kalır. Derlemenin bir aşamada yapıldığı ve yalnızca bitmiş çıktının sade bir nihai imaja kopyalandığı çok aşamalı derlemeler (multi-stage builds), imaj boyutunu genellikle büyük ölçüde küçültür. Bu da hem dağıtım hızı hem de saldırı yüzeyiniz açısından önemlidir.
Docker tek başına her şeyi çözmez. Tek bir container çalıştırmak basittir; ancak onlarcasını ağ, ölçeklendirme ve yedeğe geçiş (failover) ile birlikte güvenilir biçimde çalıştırmak başka bir problemdir ve genellikle üzerine eklenen Kubernetes gibi bir orkestrasyon aracıyla çözülür. Yine de bir container'ın gerçekte ne olduğunu ve bir VM'den neden temelden farklı olduğunu anlamak, modern altyapıdaki diğer her şeyin üzerine inşa edildiği temeldir.
Bir uygulamayı doğru biçimde container'a almak (sade bir imaj, mantıklı bir derleme süreci ve onu yayına alan CI/CD hattı), Burncode'da altyapı ve DevOps işlerini üstlendiğimizde ilk kurduğumuz şeylerden biridir; çünkü güvenilir bir dağıtımdaki diğer her şey bu parçaya dayanır.