Dockerfile Anatomisi — FROM, RUN, COPY, CMD, ENTRYPOINT
Şimdiye kadar hazır image'ları kullandık — nginx, postgres, node. Başkaları bu image'ları oluşturmuş, biz sadece docker pull ile indirip kullandık. Ama gerçek dünyada kendi uygulamalarını containerize etmen gerekecek. İşte bu noktada Dockerfile devreye giriyor.
Bir IKEA mobilyasının montaj kılavuzunu düşün. Adım 1: Tabanı koy. Adım 2: Yan parçaları tak. Adım 3: Rafları yerleştir. Her adım bir öncekinin üstüne ekler. Kılavuzu farklı kişilere versen, herkes aynı mobilyayı elde eder.
Dockerfile, bir Docker image'ının montaj kılavuzu. Her satır bir talimat. Docker bu talimatları sırayla uygular ve sonunda bir image oluşturur. Aynı Dockerfile'ı kim çalıştırırsa çalıştırsın, aynı image'ı elde eder. Bu tekrarlanabilirlik, Docker'ın en güçlü özelliği.
Bu derste Dockerfile'ın tüm talimatlarını — FROM, RUN, COPY, CMD, ENTRYPOINT ve diğerlerini — detaylıca öğreneceğiz.
İlk Dockerfile — Üç Satır, Bir Image
En basit Dockerfile'ı yazalım:
FROM ubuntu:22.04
RUN apt-get update && apt-get install -y curl
CMD ["curl", "--version"]docker build -t my-curl .
docker run my-curl
# curl 7.81.0 (x86_64-pc-linux-gnu)...Üç satır. Üç katman. Bir image. Şimdi bu satırları tek tek açıklayalım ve sonra Dockerfile'ın tüm talimatlarına geçelim.
FROM — Her Şeyin Başlangıcı
Her Dockerfile mutlaka bir FROM talimatıyla başlar. Bu, base image'ı belirler — "neyin üzerine inşa edeceğiz?" sorusunun cevabı.
FROM ubuntu:22.04 # Ubuntu tabanlı
FROM alpine:3.19 # Alpine tabanlı (minimal)
FROM node:20-alpine # Node.js tabanlı
FROM python:3.12-slim # Python tabanlı
FROM scratch # Tamamen boş (sıfırdan)Bir önceki bölümde base image seçimini detaylıca konuşmuştuk. Burada sadece şunu hatırlayalım: her zaman belirli versiyon kullan (node:20-alpine, node:latest değil).
Multi-stage build'lerde birden fazla FROM olabilir — bunu Bölüm 8'de öğreneceğiz. Şimdilik tek FROM ile devam edelim.
RUN — Build Sırasında Komut Çalıştır
RUN talimatı, image build edilirken komut çalıştırır. Her RUN yeni bir katman oluşturur.
RUN apt-get update && apt-get install -y curl
RUN npm install
RUN pip install -r requirements.txtBurada kritik bir kural var: her RUN bir katman. Gereksiz katman demek gereksiz boyut demek. O yüzden ilişkili komutları birleştirmeliyiz:
# ❌ YANLIŞ: 3 ayrı katman, apt cache temizlenmiyor
RUN apt-get update
RUN apt-get install -y curl
RUN apt-get install -y wget
# ✅ DOĞRU: 1 katman, cache temizleniyor
RUN apt-get update && \
apt-get install -y --no-install-recommends curl wget && \
rm -rf /var/lib/apt/lists/*--no-install-recommends gereksiz önerilen paketleri yüklemez. rm -rf /var/lib/apt/lists/* apt cache'ini temizler. İkisi birlikte image boyutunu önemli ölçüde küçültür.
COPY — Host'tan Image'a Dosya Kopyala
COPY, build context'teki (host'taki) dosyaları image'a kopyalar:
COPY package.json /app/ # Tek dosya
COPY src/ /app/src/ # Dizin
COPY package.json package-lock.json /app/ # Birden fazla dosya
COPY . /app/ # Tüm proje
COPY --chown=node:node . /app/ # Sahiplik belirleADD vs COPY
ADD da dosya kopyalar ama ekstra özellikler var: URL'den indirebilir ve tar dosyalarını otomatik açabilir. Ama tavsiyem: her zaman COPY kullan. ADD sadece tar açma veya URL indirme gerektiğinde.
# COPY kullan (önerilen)
COPY file.txt /app/
# ADD sadece bunlar için
ADD archive.tar.gz /app/ # Tar otomatik açılır
ADD https://example.com/file /app/ # URL'den indirWORKDIR — Çalışma Dizini
Sonraki talimatlar için çalışma dizinini ayarlar. cd komutu gibi ama kalıcı:
WORKDIR /app
RUN npm install # /app dizininde çalışır
COPY . . # /app dizinine kopyalarDizin yoksa otomatik oluşturulur. RUN cd /app && npm install yerine WORKDIR /app + RUN npm install daha temiz.
ENV — Environment Variable Tanımla
ENV NODE_ENV=production
ENV PORT=3000
ENV APP_HOME=/app
WORKDIR ${APP_HOME} # Variable kullanımıENV ile tanımlanan değişkenler image'a gömülür — hem build hem runtime'da görünür. docker inspect ile okunabilir. Bu yüzden şifre gibi gizli bilgileri ENV ile koyma! Gizli bilgiler için runtime'da -e flag'i veya Docker secrets kullan.
ARG — Sadece Build Sırasında
ARG NODE_VERSION=20
FROM node:${NODE_VERSION}-alpine
ARG APP_VERSION=1.0.0
LABEL version=${APP_VERSION}docker build --build-arg NODE_VERSION=18 .ENV runtime'da da var, ARG sadece build sırasında. ARG'ı ENV'e aktarabilirsin: ENV APP_VERSION=${APP_VERSION}
EXPOSE — Port Bildirimi
EXPOSE 3000
EXPOSE 80 443⚠️ Önemli: EXPOSE portu açmaz! Sadece dokümantasyon amacıdır — "bu container 3000 portunu kullanıyor" der. Portu gerçekten açmak için docker run -p 8080:3000 gerekir.
CMD — Varsayılan Komut
Container başladığında çalışacak varsayılan komut:
CMD ["node", "server.js"]
CMD ["python", "app.py"]
CMD ["nginx", "-g", "daemon off;"]CMD override edilebilir:
docker run myapp # CMD çalışır: node server.js
docker run myapp node test.js # CMD override: node test.js
docker run myapp bash # CMD override: bashHer zaman exec form (JSON array) kullan:
# ✅ Exec form (önerilen) — sinyal doğrudan prosese gider
CMD ["node", "server.js"]
# ❌ Shell form — /bin/sh -c ile sarılır, sinyal sorunu
CMD node server.jsShell form neden sorunlu? docker stop gönderdiğinde SIGTERM sinyali sh'a gider, uygulamanıza ulaşmaz. Graceful shutdown çalışmaz.
ENTRYPOINT — Sabit Giriş Noktası
Container'ın her zaman çalıştıracağı komut:
ENTRYPOINT ["python"]
CMD ["app.py"]docker run myapp # python app.py
docker run myapp test.py # python test.py (CMD override, ENTRYPOINT sabit)
docker run myapp --version # python --versionCMD override edildiğinde ENTRYPOINT aynı kalır. Bu pattern CLI araçları için mükemmel. CMD vs ENTRYPOINT'in detaylarını bu bölümün son dersinde derinlemesine işleyeceğiz.
Diğer Talimatlar
LABEL — Metadata
LABEL maintainer="tolgahan@example.com"
LABEL version="1.0.0"
LABEL description="My awesome app"USER — Non-root Kullanıcı (Güvenlik!)
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser
CMD ["node", "server.js"]⚠️ Production'da root ile çalıştırma! Her zaman non-root user oluştur.
HEALTHCHECK — Sağlık Kontrolü
HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
CMD curl -f http://localhost:3000/health || exit 1docker ps'te (healthy) veya (unhealthy) görürsün.
VOLUME — Volume Mount Noktası
VOLUME ["/data"]Container çalıştırıldığında bu dizin için otomatik anonymous volume oluşturulur. Ama pratikte docker run -v ile named volume kullanmak daha iyi.
Tam Dockerfile Örnekleri
Node.js Express Uygulaması
FROM node:20-alpine
LABEL maintainer="tolgahan@example.com"
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --only=production && npm cache clean --force
COPY --chown=appuser:appgroup . .
USER appuser
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1
CMD ["node", "server.js"]Python Flask Uygulaması
FROM python:3.12-slim
LABEL maintainer="tolgahan@example.com"
RUN apt-get update && \
apt-get install -y --no-install-recommends gcc libpq-dev && \
rm -rf /var/lib/apt/lists/*
RUN useradd --create-home appuser
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY --chown=appuser:appuser . .
USER appuser
ENV FLASK_APP=app.py
EXPOSE 5000
CMD ["gunicorn", "--bind", "0.0.0.0:5000", "--workers", "4", "app:app"]Build Komutu
# Temel build
docker build -t myapp:v1 .
# Birden fazla tag
docker build -t myapp:v1.0.0 -t myapp:latest .
# Build argument
docker build --build-arg NODE_VERSION=18 -t myapp:v1 .
# Farklı Dockerfile
docker build -f Dockerfile.production -t myapp:prod .
# Cache kullanmadan (temiz build)
docker build --no-cache -t myapp:v1 .Build komutunun sonundaki . (nokta) build context — Docker daemon'a gönderilecek dosyaların bulunduğu dizin. Bir sonraki derste build context'i ve .dockerignore'u detaylıca işleyeceğiz.
Yaygın Hatalar
COPY — Dosya Bulunamıyor
Dosya .dockerignore'da olabilir veya build context dışında olabilir. Kontrol et.
Cache Çalışmıyor — Her Seferinde Yeniden Build
# ❌ COPY . önce — herhangi bir dosya değişince tüm bağımlılıklar yeniden yüklenir
COPY . /app/
RUN npm install
# ✅ Package dosyasını önce kopyala — sadece bağımlılıklar değişince yeniden yükle
COPY package.json package-lock.json /app/
RUN npm install
COPY . /app/Container Sinyal Almıyor
# ❌ Shell form — sinyal sh'a gider
CMD npm start
# ✅ Exec form — sinyal doğrudan node'a gider
CMD ["node", "server.js"]Bu Derste Ne Öğrendik?
Dockerfile, image build talimatlarını içeren metin dosyası. Tekrarlanabilir, otomatize edilebilir.
FROM base image, RUN komut çalıştırma, COPY dosya kopyalama, CMD varsayılan komut.
ENTRYPOINT sabit giriş noktası, CMD varsayılan argümanlar. CLI araçları için birlikte kullan.
ENV runtime'da da görünür, ARG sadece build sırasında. Gizli bilgileri ENV'e koyma.
RUN komutlarını birleştir, bağımlılıkları koddan önce kopyala (cache optimizasyonu), non-root USER kullan.
Her zaman exec form CMD kullan. Shell form sinyal sorununa yol açar.
Bir sonraki derste build context ve .dockerignore'u öğreneceğiz.
AI Asistan
Sorularını yanıtlamaya hazır