Docker'ın laptoptan buluta aynı uygulamayı çalıştırmayı nasıl kolaylaştırdığını, dağıtımları nasıl basitleştirdiğini, taşınabilirliği nasıl artırdığını ve ortam sorunlarını nasıl azalttığını öğrenin.

Çoğu bulut dağıtım sıkıntısı tanıdık bir sürprizle başlar: uygulama dizüstü bilgisayarda çalışıyor, ama bulut sunucusuna geçince bozuluyor. Belki sunucuda farklı bir Python veya Node sürümü vardır, eksik bir sistem kütüphanesi vardır, yapılandırma dosyası biraz farklıdır veya arka planda çalışması gereken bir servis çalışmıyordur. Bu küçük farklar birikir ve ekipler ürünü geliştirmek yerine ortamı hata ayıklamakla uğraşır.
Docker, uygulamanızı çalıştırmak için gereken runtime ve bağımlılıkları birlikte paketleyerek yardımcı olur. “X sürümünü yükle, sonra Y kütüphanesini ekle, sonra bu konfigürasyonu ayarla” gibi adımlar göndermek yerine, bu parçaları zaten içeren bir container image gönderirsiniz.
Kullanışlı bir zihinsel model:
Aynı imajı bulutta çalıştırdığınızda, yerelde test ettiğinizle aynı şeyi çalıştırmış olursunuz ve “ama sunucum farklı” problemleri büyük ölçüde azalır.
Docker farklı rollere farklı nedenlerle yardımcı olur:
Docker son derece yardımcıdır, ancak ihtiyaç duyacağınız tek araç değildir. Konfigürasyonu, gizli bilgileri, veri depolamayı, ağ yapılandırmasını, izlemeyi ve ölçeklemeyi yine yönetmeniz gerekir. Birçok ekip için Docker, yerel iş akışlar için Docker Compose ve üretim için orkestrasyon platformlarıyla birlikte çalışan bir yapı taşıdır.
Docker'ı uygulamanız için bir nakliye konteyneri olarak düşünün: teslimatı öngörülebilir hale getirir. Limanda (bulut kurulumu ve runtime) ne olduğu hâlâ önemlidir—ama her gönderim aynı şekilde paketlendiğinde iş çok daha kolaylaşır.
Docker yeni bir sözlüğe sahipmiş gibi gelebilir, ama temel fikir basittir: uygulamanızı her yerde aynı şekilde çalıştırılacak biçimde paketleyin.
Bir sanal makine, tam bir misafir işletim sistemi ve uygulamanızı birlikte paketler. Bu esnek ama çalıştırması daha ağır ve başlatması daha yavaştır.
Bir konteyner uygulamanızı ve bağımlılıklarını paketler, ancak host makinenin OS çekirdeğini paylaşır; tam bir işletim sistemi taşımak yerine. Bu yüzden konteynerler genellikle daha hafif olur, saniyeler içinde başlar ve aynı sunucuda daha fazla çalıştırılabilir.
Image: Uygulamanız için salt okunur bir şablon. Kodunuzu, runtime'ı, sistem kütüphanelerini ve varsayılan ayarları içeren paketlenmiş bir artefakt gibi düşünün.
Container: Bir image'ın çalışan örneği. Eğer image bir plan ise, container şu anda yaşadığınız ev gibidir.
Dockerfile: Bir image oluşturmak için Docker'ın kullandığı adım adım talimatlar (bağımlılık yükleme, dosya kopyalama, başlatma komutunu ayarlama).
Registry: Image'lerin saklandığı ve dağıtıldığı servis. Image'leri bir registry'e "push" eder ve sunucularda daha sonra "pull" edersiniz (halka açık registry'ler veya şirket içi özel registry'ler).
Uygulamanız Dockerfile'dan oluşturulmuş bir image olarak tanımlandığında, teslim için standart bir birim elde edersiniz. Bu standartlaşma sürümleri tekrarlanabilir kılar: yerelde test ettiğiniz aynı image'i dağıtırsınız.
Ayrıca teslimleri devretmeyi basitleştirir. "Benim makinemde çalışıyor" demek yerine, registry'deki belirli bir image versiyonunu gösterip: bu container'ı şu ortam değişkenleriyle, şu portta çalıştırın diyebilirsiniz. Bu, geliştirme ve üretim ortamlarının tutarlılığı için temel oluşturur.
Docker'ın bulut dağıtımlarında önem kazanmasının en büyük nedeni tutarlılıktır. Bir Dockerfile içinde ortamı bir kez tanımlayıp aşamalar arasında tekrar kullanırsınız; böylece dizüstü bilgisayarınızda, CI runner'da veya bulut VM'de ne yüklü olduğuna bağlı kalmazsınız.
Pratikte tutarlılık şunlar olarak görünür:
Bu tutarlılık hızlıca fayda sağlar. Üretimde görülen bir hatayı, aynı image tag'ini yerelde çalıştırarak yeniden üretebilirsiniz. Eksik bir kütüphane nedeniyle başarısız olan bir dağıtım da artık daha az olasıdır çünkü test container'ınızda da o kütüphane eksik olurdu.
Ekipler genellikle kurulum dokümanları veya sunucu yapılandırma script'leriyle standartlaşmaya çalışır. Sorun, zaman içinde makinelerin yamalar ve paket güncellemeleriyle değişmesi ve farkların yavaşça birikmesidir.
Docker ile ortam bir artefakt olarak ele alınır. Güncellemeniz gerektiğinde yeni bir image oluşturur ve onu dağıtırsınız—değişiklikler açık ve incelenebilir olur. Eğer güncelleme sorun yaratırsa, rollback genellikle önceki bilinen iyi tag'i dağıtmak kadar basittir.
Docker'ın bir diğer büyük kazanımı taşınabilirliktir. Bir container image uygulamanızı taşınabilir bir artefakta dönüştürür: bir kez oluşturun, sonra uyumlu bir container runtime olan her yerde çalıştırın.
Bir Docker image uygulama kodunuzu ve runtime bağımlılıklarını (ör. Node.js, Python paketleri, sistem kütüphaneleri) paketler. Bu, laptopunuzda çalıştırdığınız bir imajın aynı zamanda şunlarda da çalışabileceği anlamına gelir:
Bu, uygulama runtime düzeyinde tedarikçi kilitlenmesini azaltır. Bulut-native servisleri (veritabanları, kuyruklar, depolama) kullanmaya devam edebilirsiniz, ama çekirdek uygulamanız host değişti diye yeniden inşa edilmek zorunda kalmaz.
Taşınabilirlik, image'lerin bir registry'de saklandığında ve sürümlendiğinde en iyi şekilde çalışır—halka açık veya özel. Tipik bir iş akışı şöyledir:
myapp:1.4.2).Registry'ler ayrıca dağıtımları yeniden üretmeyi ve denetlemeyi kolaylaştırır: üretim 1.4.2 çalıştırıyorsa, aynı artefaktı daha sonra çekip tam olarak aynı bitlere erişebilirsiniz.
Host taşıma: Bir VM sağlayıcısından diğerine geçerseniz, yığın yeniden kurulmaz. Yeni sunucuyu registry'e yönlendirirsiniz, image'i pull eder ve aynı konfigürasyonla container'ı başlatırsınız.
Ölçeklendirme: Daha fazla kapasiteye mi ihtiyacınız var? Aynı image'den ek containerlar başlatın. Her örnek özdeş olduğu için ölçekleme tekrarlanabilir bir işlem olur, elle kurulum gerekmez.
İyi bir Docker image yalnızca "çalışan bir şey" değildir. Daha sonra yeniden oluşturabileceğiniz ve güvenebileceğiniz paketlenmiş, sürümlenmiş bir artefakttır. Bulut dağıtımlarını öngörülebilir kılan bu güvenilirdir.
Bir Dockerfile, uygulama imajınızı adım adım nasıl monte edeceğinizi tanımlar—tam olarak hangi başlangıç imajı, hangi bağımlılıklar, hangi dosyaların kopyalanacağı ve uygulamayı hangi komutun başlatacağı gibi. Bu dosyanın temiz ve niyetli tutulması, imajın hata ayıklanmasını, incelenmesini ve bakımını kolaylaştırır.
Küçük image'ler daha hızlı çekilir, daha hızlı başlar ve kırılma ya da güvenlik açıkları içerebilecek fazla "şey" barındırmaz.
alpine veya slim varyantları).Birçok uygulama derleme araçlarına ihtiyaç duyar ama çalışmak için bunlara gerekmez. Multi-stage build'ler bir aşamada derleyip ikinci, minimal aşamada üretim için küçük bir imaj oluşturmanıza izin verir.
# build stage
FROM node:20 AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# runtime stage
FROM nginx:1.27-alpine
COPY --from=build /app/dist /usr/share/nginx/html
Sonuç, daha az yaması gereken daha küçük bir üretim imajıdır.
Tag'ler neyi dağıttığınızı tam olarak belirlemenin yoludur.
latest'e güvenmeyin; belirsizdir.1.4.2)1.4.2-<sha> veya sadece <sha>) ki bir imajı üreten koda her zaman izleyebilesiniz.Bu, temiz geri almaları ve değişiklikler olduğunda net denetimleri destekler.
Gerçek bir bulut uygulaması genellikle tek bir süreç değildir. Küçük bir sistemdir: bir web ön yüzü, bir API, belki bir arka plan worker ve bir veritabanı veya cache. Docker hem basit hem çok servisli düzenleri destekler—sadece container'ların nasıl iletişim kurduğunu, konfigürasyonun nerede durduğunu ve verinin nasıl yeniden başlatmalarda korunduğunu anlamanız gerekir.
Tek container'lı bir uygulama statik site veya bağımlılığı olmayan tek bir API olabilir. Tek bir portu açarsınız (ör. 8080) ve çalıştırırsınız.
Çok servisli uygulamalar daha yaygındır: web api'ye bağlıdır, api db'ye bağlıdır ve bir worker kuyruktan işler tüketir. IP adreslerini sabitlemek yerine, container'lar genellikle paylaşılan bir ağda servis adıyla iletişim kurar (ör. db:5432).
Docker Compose, tüm yığını bir komutla başlatmanızı sağladığı için yerel geliştirme ve staging için pratiktir. Ayrıca uygulamanızın "şekil"ini (servisler, portlar, bağımlılıklar) ekipçe paylaşılabilecek bir dosyada belgeler.
Tipik ilerleyiş:
Image'ler yeniden kullanılabilir ve paylaşılması güvenli olmalıdır. Ortama özgü ayarları image dışında tutun:
Bunları ortam değişkenleri, bir .env dosyası (dikkat: commit etmeyin) veya bulutunuzun secrets manager'ı aracılığıyla iletin.
Container'lar geçicidir; veriniz olmamalı. Yeniden başlatmalarda kalması gereken her şey için volume kullanın:
Bulut dağıtımlarında eşdeğer şey yönetilen depolamadır (managed DB, network diskleri, object storage). Önemli fikir: container'lar uygulamayı çalıştırır; kalıcı depolama durumu korur.
Sağlıklı bir Docker dağıtım iş akışı kasıtlı olarak basittir: bir image oluşturun ve aynı image'i her yerde çalıştırın. Dosyaları sunuculara kopyalamak veya kurucuları yeniden çalıştırmak yerine dağıtımı tekrarlanabilir bir rutine çevirirsiniz: image'i pull et, container'ı çalıştır.
Çoğu ekip şu pipeline'ı takip eder:
myapp:1.8.3).Son adım Docker'ı iyi anlamlı bir şekilde "sıkıcı" yapar:
# build locally or in CI
docker build -t registry.example.com/myapp:1.8.3 .
docker push registry.example.com/myapp:1.8.3
# on the server / cloud runner
docker pull registry.example.com/myapp:1.8.3
docker run -d --name myapp -p 80:8080 registry.example.com/myapp:1.8.3
İki yaygın yol:
Sürümler sırasında aksaklıkları azaltmak için üretimde genellikle üç yapı taşı kullanılır:
Registry sadece depolama değildir—ortamları tutarlı tutmanın yoludur. Yaygın bir uygulama aynı image'i dev → staging → prod arasında promote etmek (genellikle yeniden tag'leyerek) ve her seferinde yeniden inşa etmemektir. Böylece üretim, test ettiğiniz tam artefaktı çalıştırır ve "staging'te çalışıyordu" sürprizleri azalır.
CI/CD temel olarak yazılımı gönderme hattıdır. Docker, her adımın bilinen bir ortamda çalışmasını sağladığından bu hattı daha öngörülebilir kılar.
Docker dostu bir pipeline genellikle üç aşama içerir:
myapp:1.8.3).Bu akış teknik olmayan paydaşlara da kolayca anlatılabilir: "Bir kutuyu kapatıyoruz, kutuyu test ediyoruz, sonra aynı kutuyu her ortama gönderiyoruz."
Testler genellikle yerelde geçer ama üretimde başarısız olur çünkü runtime farklıdır, sistem kütüphaneleri eksiktir veya farklı ortam değişkenleri vardır. Testleri container içinde çalıştırmak bu boşlukları azaltır. CI runner'ınızın dikkatlice ayarlanmış bir makineye ihtiyacı yok—sadece Docker olması yeterlidir.
Docker "promote, yeniden inşa etme" modelini destekler. İşlem şöyledir:
myapp:1.8.3'ü bir kez oluşturun ve test edin.Ortamlar arasında sadece konfigürasyon değişir (URL'ler veya kimlik bilgileri gibi), uygulama artefaktı değişmez. Bu, sürüm günü belirsizliklerini azaltır ve geri almayı basitleştirir: önceki image tag'i tekrar dağıtılır.
Hızlı hareket ediyorsanız ve Docker faydalarını günlerce alt yapı kurmadan almak istiyorsanız, Koder.ai sohbet odaklı bir iş akışından üretime uygun bir uygulama oluşturup onu temizce konteynerleştirmede yardımcı olabilir.
Örneğin ekipler sıklıkla Koder.ai'ı şunlar için kullanır:
docker-compose.yml eklemek (böylece dev ve prod davranışı uyumlu kalır),Temel avantaj, Docker'ın dağıtım ilkesinin korunmasıdır; Koder.ai fikirden konteyner-e hazır kod tabanına giden yolu hızlandırır.
Docker bir servisi tek bir makinada paketlemeyi ve çalıştırmayı kolaylaştırır. Ancak birden fazla servis, her servisten birden fazla kopya ve birden fazla sunucu olduğunda her şeyi koordine eden bir sisteme ihtiyacınız olur. Orkestrasyon, container'ların nerede çalışacağını belirleyen, onları sağlıklı tutan ve talep değiştikçe kapasiteyi ayarlayan yazılımdır.
Birkaç container ile işleri elle yönetebilirsiniz; ancak ölçek büyüdüğünde bu yaklaşım hızla yetersiz kalır:
Kubernetes (kısaca K8s) en yaygın orkestratördür. Basit bir zihinsel model:
Kubernetes image oluşturmaz; onları çalıştırır. Siz hâlâ bir Docker image oluşturur, registry'e push edersiniz; Kubernetes o image'i node'lara çeker ve container'ları başlatır. Image hâlâ her yerde kullanılan taşınabilir, sürümlenmiş artefakttır.
Eğer tek bir sunucuda birkaç servis varsa, Docker Compose çoğu durumda yeterli olabilir. Orkestrasyon, yüksek erişilebilirlik, sık dağıtımlar, otomatik ölçekleme veya kapasite ve dayanıklılık için birden fazla sunucu gerektiğinde değer kazanmaya başlar.
Konteynerler uygulamayı sihirli şekilde güvenli hale getirmez—ancak standartlaştırma ve otomasyon yoluyla zaten yapılması gereken güvenlik çalışmalarını uygulamayı kolaylaştırır. Artısı, Docker size denetçiler ve güvenlik ekiplerinin önem verdiği kontrolleri ekleyebileceğiniz tekrarlanabilir noktalar verir.
Bir container image uygulamanızla birlikte bağımlılıkları da paketlediği için güvenlik açıkları genellikle temel imajlardan veya sizin yazmadığınız sistem paketlerinden gelir. Image taraması, üretime göndermeden önce bilinen CVE'leri kontrol eder.
Taramayı pipeline'da bir kapı haline getirin: kritik bir açıklık bulunursa build'i başarısız kılın ve yamalı bir temel imajla yeniden oluşturun. Tarama sonuçlarını artefakt olarak saklayın ki denetimlerde ne gönderildiğini gösterebilesiniz.
Mümkün olduğunda non-root kullanıcı ile çalıştırın. Birçok saldırı, container içinde root erişimi üzerinden kaçma veya dosya sistemi üzerinde değişiklik yapma üzerine kuruludur.
Ayrıca konteyner için salt okunur dosya sistemi ve sadece belirli yazılabilir yolları mount etmeyi düşünün (loglar veya yüklemeler için). Bu, bir saldırgan içeri girerse değiştirebileceklerini sınırlar.
Asla API anahtarlarını, parolaları veya özel sertifikaları Docker image'inize kopyalamayın veya Git'e commit etmeyin. Image'ler önbelleğe alınır, paylaşılır ve registry'lere push edilir—gizli bilgiler genişçe sızabilir.
Bunun yerine gizli bilgileri runtime'ta platformunuzun secret store'u ile enjekte edin (ör. Kubernetes Secrets veya bulut sağlayıcınızın secrets manager'ı) ve yalnızca gerekli servislere erişim izni verin.
Geleneksel sunucuların aksine container'lar çalışırken kendilerini yamalamaz. Standart yaklaşım: bağımlılıkları güncellenmiş bir image oluşturup yeniden dağıtmak.
Bir takvim belirleyin (haftalık veya aylık) even app kodu değişmemiş olsa bile image'leri yeniden build edin; yüksek önem derecesi taşıyan CVE'ler temel imajı etkilediğinde derhal yeniden build edin. Bu alışkanlık dağıtımları denetlemeyi ve riski azaltmayı kolaylaştırır.
Docker "kullanılıyor" olsa bile birkaç alışkanlık problemlere yol açabilir. İşte en çok ağrı veren hatalar ve pratik önlemler.
Yaygın bir anti-pattern, sunucuya SSH ile girip bir şeyi elden düzeltmek veya çalışan container içine exec ile girip konfigürasyonu anında değiştirmektir. Bir kerede işe yarar, sonra tekrar bozulur çünkü o tam durumu kimse yeniden üretemez.
Bunun yerine container'ları sığır gibi davranın: geçici ve yerine konulabilir. Her değişikliği image build ve dağıtım pipeline'ı üzerinden yapın. Debug gerekiyorsa geçici bir ortamda yapın ve sonra düzeltmeyi Dockerfile, konfigürasyon veya altyapı ayarlarına kodlayın.
Devasa image'ler CI/CD'yi yavaşlatır, depolama maliyetlerini artırır ve güvenlik yüzeyini genişletir.
Bunu önlemek için Dockerfile yapınızı sıkılaştırın:
.dockerignore ekleyin ki node_modules, build artefaktları veya yerel gizlilikler image'e dahil olmasın.Amaç, temiz ve hızlı yeniden üretilebilir bir build sağlamak.
Konteynerler uygulamanın ne yaptığını anlamayı gereksiz kılmaz. Loglar, metrikler ve izler yoksa sorunları sadece kullanıcı şikayetleriyle fark edersiniz.
En azından uygulamanızın stdout/stderr'e log yazdığından (yerel dosyalara değil), temel sağlık endpoint'leri olduğundan ve birkaç anahtar metriği (hata oranı, gecikme, kuyruk derinliği) yayınladığından emin olun. Sonra bu sinyalleri bulut stack'inizin izleme araçlarına bağlayın.
Durumsuz container'lar kolayca değiştirilir; durumlu veri ise değil. Ekipler genellikle veritabanını container içinde çalıştırmanın "iyi çalıştığını" görürler ama bir yeniden başlatma verileri sildiğinde sorunla karşılaşırlar.
Başından itibaren durumu nerede tutacağınıza karar verin:
Docker uygulamaları paketlemek için mükemmeldir—ama güvenilirlik, bu container'ların nasıl oluşturulduğu, gözlemlendiği ve kalıcı veriye nasıl bağlandığı konusunda kasıtlı olmaktan gelir.
Docker'a yeniyseniz, değeri hızlı almak için tek bir gerçek servisi uçtan uca konteynerleştirin: build edin, yerelde çalıştırın, registry'e push edin ve deploy edin. Kapsamı küçük tutmak ve sonuçları kullanılabilir yapmak için bu kontrol listesini kullanın.
İlk olarak tek, durumsuz bir servis seçin (bir API, bir worker veya basit bir web uygulaması). Başlaması için ne gerektiğini tanımlayın: dinlediği port, gerekli ortam değişkenleri ve harici bağımlılıklar (ör. ayrı çalıştırabileceğiniz bir veritabanı).
Hedefi net tutun: "Aynı image ile yerelde ve bulutta aynı uygulamayı çalıştırabilmek."
Uygulamanızı güvenilir şekilde build edip çalıştırabilecek en küçük Dockerfile'ı yazın. Tercihler:
Sonra yerel geliştirme için hizmetleri ve ortam değişkenlerini bağlayan bir docker-compose.yml ekleyin (laptopa Docker dışında bir şey kurmadan). İleride daha derin yerel kurulum isterseniz genişletebilirsiniz—önce basit başlayın.
Image'lerin nerede duracağını seçin (Docker Hub, GHCR, ECR, GCR vb.). Sonra dağıtımları öngörülebilir kılacak tag'ler benimseyin:
:dev yerel test için (opsiyonel):git-sha (değiştirilemez, dağıtımlar için en iyisi):v1.2.3 sürümler içinÜretimde :latest kullanmaktan kaçının.
Her merge olduğunda image'i build edip registry'e push eden bir CI kurun. Pipeline'ın şu adımları içermesi iyi:
Bunlar çalışınca, yayımlanan image'i bulut deploy adımına bağlayıp oradan yinelemeye başlayabilirsiniz.
Docker, uygulamanızı çalışması için gereken runtime ve bağımlılıklarla birlikte paketleyerek “makinemde çalışıyor” sorunlarını azaltır. Aynı görüntüyü (image) yerelde, CI’da ve bulutta çalıştırırsınız; böylece işletim sistemi paketleri, dil sürümleri ve yüklü kütüphanelerdeki farklar davranışı gizlice değiştirmez.
Genellikle bir image bir kez (ör. myapp:1.8.3) üretilir ve farklı ortamlarda bu image'dan birçok container çalıştırılır.
Bir sanal makine (VM) tam bir misafir işletim sistemi ile birlikte gelir; bu yüzden daha ağırdır ve başlatması genellikle daha yavaştır. Bir konteyner ise host makinenin çekirdeğini paylaşır ve yalnızca uygulamanın ihtiyaç duyduğu runtime ve kütüphaneleri taşır. Bu nedenle konteynerler genellikle:
Registry, image'lerin saklandığı ve sürümlendiği yerdir; diğer makinelerin bu image'leri çekip çalıştırabilmesi için gereklidir.
Yaygın bir iş akışı:
docker build -t myapp:1.8.3 .docker push <registry>/myapp:1.8.3Bu yaklaşım geri almayı da kolaylaştırır: önceki bir tag'i yeniden dağıtabilirsiniz.
Üretimde ne çalıştığını her zaman tanımlayabilmek için değiştirilemez, izlenebilir tag'ler kullanın.
Pratik yaklaşım:
:1.8.3:<git-sha>:latest kullanmaktan kaçının (belirsizdir)Bu, temiz geri almaları ve denetimleri destekler.
Ortama özgü ayarları image'in dışına koyun. API anahtarlarını, parolaları veya özel sertifikaları Dockerfile içine kopyalamayın.
Bunun yerine:
.env dosyalarını Git'e eklemeyinBöylece image'ler yeniden kullanılabilir ve bilgilerin sızması azalır.
Konteynerler geçicidir; dosya sistemi yeniden başlatıldığında veya container yeniden oluşturulduğunda değişebilir. Kalıcı olması gereken veriler için:
Kural: uygulamaları konteynerlerde çalıştırın, durumu amaçlanmış depolamada tutun.
Compose, yerel geliştirme veya tek host için çok uygundur:
db:5432)Çok sunuculu, yüksek erişilebilirlik ve otomatik ölçekleme gereken üretim ortamları için genellikle bir orkestratör (çoğunlukla Kubernetes) kullanılır.
Pratik bir pipeline: build → test → publish → deploy
“Promote, rebuild yapma” yaklaşımını tercih edin (aynı artifact'i dev → staging → prod'a ilerletin).
Sık görülen nedenler:
-p 80:8080).Hata ayıklamak için prod tag'ini yerelde çalıştırıp konfigürasyonu karşılaştırmak iyi bir ilk adımdır.