Hizmetler Hosting & Sunucu Araçlar Blog Ara Kurumsal EnglishEN
Teklif Alın

Dockerfile Nedir, Neden Önemlidir?

Bir uygulamayı sunucuya taşırken en sık karşılaşılan sorunlardan biri, geliştirme ortamında sorunsuz çalışan bir uygulamanın üretim (production) ortamında farklı davranmasıdır: Node.js sürümü farklıdır, sistem kütüphaneleri eksiktir, bağımlılıklar başka bir sırayla kurulmuştur. Docker bu sorunu, uygulamayı işletim sistemi, çalışma zamanı ve bağımlılıklarıyla birlikte tek bir imaj (image) içine paketleyerek çözer. Bu imajın nasıl inşa edileceğini tanımlayan dosya ise Dockerfile'dır: satır satır işlenen, her satırın ayrı bir katman oluşturduğu basit bir talimat listesidir.

İyi yazılmış bir Dockerfile yalnızca çalışır durumda bir imaj üretmekle kalmaz; build'i hızlı tutar, nihai imajı gereksiz dosyalardan arındırır ve üretim ortamında güvenlik açısından makul bir yüzey bırakır. Bu rehberde temel talimatları, katman önbellekleme mantığını, multi-stage build tekniğini ve üç farklı proje tipi için örnek Dockerfile'ları ele alıyoruz.

Temel Dockerfile Talimatları

Karmaşık görünse de bir Dockerfile, birkaç temel talimatın farklı kombinasyonlarından oluşur. Aşağıdaki tablo en sık kullanılanları özetler:

TalimatGörevi
FROMİmajın temelini belirler (örn. node:20-alpine, python:3.12-slim). Her Dockerfile en az bir FROM ile başlar.
WORKDIRKonteyner içindeki çalışma dizinini ayarlar; sonraki COPY, RUN ve CMD talimatları bu dizine göre çalışır.
COPYBuild context'teki (genelde proje klasörü) dosyaları imaj içine kopyalar.
RUNBuild sırasında bir kabuk komutu çalıştırır, örneğin bağımlılıkları kurar. Her RUN yeni bir katman oluşturur.
EXPOSEKonteynerin hangi portu dinlediğini belgeler; port yönlendirmesini kendisi yapmaz, bu docker run -p ile ayrıca eşlenir.
CMD / ENTRYPOINTKonteyner başlatıldığında çalışacak varsayılan komutu tanımlar. CMD genelde tek başına, ENTRYPOINT ise sabit bir komut ile değişken argümanlar için kullanılır.

Katman Önbellekleme: Build Hızını Belirleyen Detay

Docker bir imajı satır satır inşa eder; her RUN ve COPY talimatı ayrı bir katman oluşturur ve Docker, önceki build'den değişmeyen katmanları önbellekten kullanır. Bu mekanizma, Dockerfile'ın yazım sırasını build hızı açısından kritik hale getirir. Bir Node.js projesinde önce package.json ve package-lock.json dosyalarını kopyalayıp npm ci çalıştırmak, ardından geri kalan kaynak kodu kopyalamak bu yüzden önerilir: bağımlılık kurulum katmanı yalnızca package.json gerçekten değiştiğinde yeniden çalışır. Kodda küçük bir değişiklik yapıp yeniden build aldığınızda bağımlılık kurulum adımı önbellekten gelir ve build saniyeler içinde tamamlanır; aynı mantık Python'da requirements.txt için de geçerlidir.

Bunun tersi de doğrudur: COPY . . talimatı bağımlılık kurulumundan önce yazılırsa, kaynak kodda yapılan her ufak değişiklik önbelleği geçersiz kılar ve bağımlılıkların tamamı sıfırdan yeniden kurulur. Küçük bir projede bu birkaç saniyelik bir kayıp gibi görünse de, sık build alan bir CI/CD pipeline'ında dakikalarca sürebilen gereksiz bir bekleme haline gelir.

Çok Aşamalı (Multi-Stage) Build ile Küçük İmajlar

Bir uygulamayı build etmek için gereken araçlar (derleyiciler, build araçları, geliştirme bağımlılıkları) çoğunlukla çalışma zamanında hiç gerekmez. Multi-stage build, bir Dockerfile içinde birden fazla FROM talimatı kullanarak bu ikisini birbirinden ayırmayı sağlar. İlk aşama (örneğin FROM node:20-alpine AS build) tüm bağımlılıkları kurar ve projeyi derler. İkinci aşama sıfırdan, minimal bir imajdan (örn. nginx:alpine) başlar ve COPY --from=build talimatıyla yalnızca ilk aşamanın ürettiği çıktıyı (derlenmiş dosyalar, dist/ klasörü gibi) kopyalar. Sonuç olarak nihai imajda build araçları, kaynak kod veya node_modules gibi geliştirme bağımlılıkları bulunmaz; yalnızca çalışma zamanı için gerekli minimum dosyalar vardır. Bu hem imaj boyutunu ciddi ölçüde küçültür hem de üretim imajının saldırı yüzeyini azaltır.

Base imaj seçimi de boyutu doğrudan etkiler. alpine etiketli imajlar (Alpine Linux tabanlı) ve slim etiketli imajlar (minimal Debian tabanlı), tam sürümlere göre çok daha küçüktür ve daha hızlı indirilip push edilir. Alpine, glibc yerine musl libc kullandığı için bazı native bağımlılıklarla nadiren uyumsuzluk çıkarabilir; böyle bir durumda -slim veya tam imaj makul bir alternatiftir.

Node.js, Python ve Statik Site İçin Örnekler

Aşağıdaki üç örnek, yukarıdaki prensiplerin farklı proje tiplerinde nasıl uygulandığını gösterir.

Node.js

FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]

Python

FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["python", "app.py"]

Statik Site (Nginx ile Multi-Stage Build)

FROM node:20-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM nginx:alpine
COPY --from=build /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

Üç örnekte de ortak desen aynıdır: önce bağımlılık dosyaları kopyalanır ve kurulum yapılır, ardından kaynak kod eklenir. Statik site örneğinde ayrıca build aşaması ile çalışma zamanı aşaması tamamen birbirinden ayrılmıştır; nihai imaj yalnızca Nginx ve derlenmiş dosyaları içerir, Node.js çalışma zamanını değil.

Sık Yapılan Hatalar

  • COPY . . talimatını bağımlılık kurulumundan önce yazmak: Bu, katman önbelleğini işe yaramaz hale getirir ve her kod değişikliğinde tüm bağımlılıkların yeniden kurulmasına neden olur.
  • .dockerignore dosyası kullanmamak: node_modules, .git ve .env gibi dosya ve klasörler build context'e dahil olur; bu hem build'i yavaşlatır hem de hassas dosyaların yanlışlıkla imaja kopyalanma riskini doğurur.
  • Gereksiz yere büyük base imaj kullanmak: node:20 yerine node:20-alpine, python:3.12 yerine python:3.12-slim tercih edilebiliyorsa imaj boyutu ve saldırı yüzeyi ciddi biçimde küçülür.
  • Konteyneri root kullanıcıyla çalıştırmak: Birçok resmi imaj varsayılan olarak root kullanıcıyla çalışır; üretimde yetkisiz bir kullanıcıya geçmek (örn. USER node), konteyner içinde oluşabilecek bir güvenlik açığının etkisini sınırlar.
  • Multi-stage build kullanmadan build araçlarını nihai imajda bırakmak: Derleyiciler, geliştirme bağımlılıkları ve kaynak kod, üretim imajında hem yer kaplar hem de gereksiz bir saldırı yüzeyi oluşturur.

Bu prensipleri her projede elle sıfırdan uygulamak yerine, doğru sıralamayı ve uygun base imajı otomatik üreten bir başlangıç noktasından ilerlemek daha pratiktir.

WhatsApp