ders_02 / deployment
// Konteyner Teknolojileri Eğitimi

Uygulamayı
Konteynere Taşıyoruz

FastAPI · MobileNetV2 · Docker Build & Run · Ölçekleme

02 / Canlı Deployment
Kod Yaz
Dockerfile
docker build
docker run
Test Et

Bugün Ne Yapacağız?

1
Uygulamayı tanıyoruz
Görüntü sınıflandırma API — FastAPI + MobileNetV2
2
GitHub repo'yu inceliyoruz
Proje yapısı, hangi dosya ne işe yarar
3
Dockerfile adım adım yazıyoruz
Her satırın ne yaptığını açıklıyoruz
4
Build edip çalıştırıyoruz
docker builddocker run → test
5
Kriz senaryosu
500 kişi aynı anda gelirse? Ölçekleme sorunu.

Sonunda Elimizde Ne Olacak?

Tarayıcıdan resim yükle → API tahmin döndürsün:

{
  "dosya": "kedi.jpg",
  "tahminler": [
    {"sinif": "Egyptian Cat",
     "guven": 87.3},
    {"sinif": "Tabby",
     "guven": 8.1}
  ]
}

Uygulama: Görüntü Sınıflandırma

Model: MobileNetV2 — Google tarafından geliştirilmiş, mobil cihazlar için optimize edilmiş CNN. ImageNet ile önceden eğitilmiş → 1000 farklı nesneyi tanır.
>_ Kullanıcı resim yüklüyor → POST /predict
FastAPI → resmi alır, MobileNetV2'ye verir
Model → Top-3 tahmin + güven % döndürür

Stack

Web FrameworkFastAPI
ServerUvicorn
MLTensorFlow CPU
ModelMobileNetV2 (ImageNet)
Image İşlemePillow
Base Imagepython:3.11-slim
Neden MobileNetV2?
~14 MB model · Hızlı · 1000 sınıf · Çarpıcı demo sonuçları

GitHub Repo Yapısı

image-classifier-api/
│
├── app/
│   ├── __init__.py
│   ├── main.py    ← FastAPI app
│   └── model.py   ← ML tahmin
│
├── requirements.txt
├── Dockerfile
├── docker-compose.yml
└── README.md

main.py

  • GET / — Hoş geldin sayfası
  • POST /predict — Resim al, tahmin döndür
  • GET /health — Servis sağlık kontrolü
  • GET /docs — Swagger UI (otomatik)

model.py

  • MobileNetV2 yükler (ilk istek)
  • Resmi 224×224'e boyutlandırır
  • Normalize eder (preprocess_input)
  • Top-3 sonucu decode eder

app/main.py

from fastapi import FastAPI, UploadFile, File, HTTPException
from PIL import Image
import io
from app.model import predict_image

app = FastAPI(title="Görüntü Sınıflandırma API")

@app.post("/predict")
async def predict(file: UploadFile = File(...)):
    if not file.content_type.startswith("image/"):
        raise HTTPException(status_code=400, detail="Sadece resim dosyası kabul edilir")

    contents = await file.read()
    image = Image.open(io.BytesIO(contents))
    tahminler = predict_image(image)
    return {"dosya": file.filename, "tahminler": tahminler}

@app.get("/health")
async def health():
    return {"durum": "çalışıyor"}
FastAPI avantajı: /docs adresine gidince Swagger UI otomatik açılır. Frontend yazmadan API test edilebilir.

app/model.py

from tensorflow.keras.applications import MobileNetV2
from tensorflow.keras.applications.mobilenet_v2 import (
    preprocess_input, decode_predictions
)
import numpy as np

_model = None  # Singleton — bir kere yükle

def _get_model():
    global _model
    if _model is None:
        _model = MobileNetV2(weights="imagenet")
    return _model

def predict_image(image, top_k=3):
    model = _get_model()
    img = image.convert("RGB").resize((224, 224))
    arr = np.expand_dims(np.array(img, dtype=np.float32), axis=0)
    arr = preprocess_input(arr)
    preds = model.predict(arr, verbose=0)
    return [{"sinif": l, "guven": round(s*100,2)} for _,l,s in decode_predictions(preds,top_k)[0]]
Singleton neden?
Model ~14 MB, yüklenmesi birkaç saniye. Her istekte yüklesek API yavaş olur.
preprocess_input neden?
MobileNetV2, [-1, 1] aralığında piksel bekler. Fonksiyon [0,255]'i dönüştürür.

Dockerfile — Adım Adım

FROM python:3.11-slim

WORKDIR /app

# Sistem paketleri (Pillow için)
RUN apt-get update && apt-get install -y \
    libglib2.0-0 libgl1-mesa-glx \
    && rm -rf /var/lib/apt/lists/*

# Önce bağımlılıklar (cache!)
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Model ağırlıklarını build'de indir
RUN python -c "from tensorflow.keras.applications \
    import MobileNetV2; MobileNetV2(weights='imagenet')"

# Uygulama kodu (en sona — sık değişir)
COPY app/ app/

EXPOSE 8000
CMD ["uvicorn", "app.main:app",
     "--host", "0.0.0.0", "--port", "8000"]

Sıralama neden önemli?

  • apt-get — OS paketleri (hiç değişmez)
  • COPY requirementspip install — yavaş ama nadir değişir
  • Model indirme — build'de bir kere
  • COPY app/ — hızlı, sık değişir → sona
Kod güncellediğinizde sadece son katman rebuild edilir.
--host 0.0.0.0:
127.0.0.1 container içine kapanır. 0.0.0.0 port mapping ile dışarıya açar.

Dockerfile vs Docker Compose

Dockerfile → tek servis, tek image. docker-compose.yml → birden fazla servis, tek komutla hepsi ayağa kalkar. Bizim projemizde API + ölçekleme için Compose kullanacağız.
Dockerfile vs Docker Compose

docker build & docker run

1. Image İnşa Et

docker build -t image-classifier:v1 .

# İlk build: ~15-20 dk (TF indirme)
# Sonraki build: ~30 saniye (cache)

# Boyutu kontrol et
docker images image-classifier
-t → isim:tag ver
. → Dockerfile bu dizinde

2. Çalıştır & Test Et

docker run -d \
  -p 8000:8000 \
  --name classifier \
  image-classifier:v1

# Log takibi
docker logs -f classifier

# Tarayıcıda aç
# http://localhost:8000/docs

# Curl ile test
curl -X POST http://localhost:8000/predict \
  -F "[email protected]"

Container Komutları

Durum & İzleme

# Çalışan containerlar
docker ps

# Tüm containerlar (durmuş dahil)
docker ps -a

# Canlı CPU / RAM kullanımı
docker stats classifier

# Log takibi
docker logs -f classifier

İçine Gir & İncele

# Container içine gir
docker exec -it classifier bash

# İçeride
ls /app          # dosyaları gör
python --version # Python'u doğrula
exit

# Tam metadata (IP, portlar, mount)
docker inspect classifier

Durdur & Temizle

# Durdur
docker stop classifier

# Sil
docker rm classifier

# Tüm çalışanları durdur
docker stop $(docker ps -q)

# Tüm durmuşları sil
docker rm $(docker ps -aq)

# Kullanılmayan her şeyi temizle
docker system prune

docker ps çıktısını oku

CONTAINER ID — kısa hash
IMAGE — hangi image'dan
STATUS — Up / Exited / Paused
PORTS0.0.0.0:8000→8000/tcp
NAMES — classifier

Swagger UI ile Test

Adımlar

1. http://localhost:8000/docs
2. POST /predict → Try it out
3. Resim dosyası seç
4. Execute
5. JSON yanıtı gör
Health check: http://localhost:8000/health
Anında çalıştığını doğrular.

Beklenen Yanıt

{
  "dosya": "kedi.jpg",
  "tahminler": [
    {
      "sinif": "Egyptian Cat",
      "guven_yuzdesi": 87.34
    },
    {
      "sinif": "Tabby",
      "guven_yuzdesi": 8.12
    },
    {
      "sinif": "Tiger Cat",
      "guven_yuzdesi": 4.54
    }
  ]
}

Kriz Senaryosu

[ ! ] ALERT
Uygulamayı paylaştınız. 500 kişi aynı anda istek attı.

Tek Container'da Sorun

  • Model sıralı işler
  • 500. kullanıcı dakikalarca bekler
  • CPU %100'e çıkar
  • Timeout hataları başlar
  • Container çöker → herkes erişemez

İlk Akla Gelen

docker run -d -p 8001:8000 image-classifier:v1
docker run -d -p 8002:8000 image-classifier:v1
docker run -d -p 8003:8000 image-classifier:v1

Yeni Sorunlar

  • Kullanıcı hangi porta gidecek?
  • Yük nasıl dağıtılacak?
  • Container çökerse ne olur?

docker run vs docker compose

docker run

Tek bir container'ı tek seferlik başlatırsın. Her parametre — port, env, volume, network — komut satırına yazılır. Tekrar çalıştırmak istersen aynı komutu hatırlamak zorundasın. İkinci bir container eklemek istersen ikinci bir komut daha.

docker compose

Tüm yapıyı bir YAML dosyasında tanımlarsın. Kim, hangi port, hangi env, hangi volume, hangi sırayla ayağa kalkar — hepsi dosyada. Sonra sadece docker compose up yazarsın.

docker run docker compose
Tekrar kullanım Komutu ezberle / kopyala up yaz, bitti
Çoklu container 3 ayrı komut tek up
Ekip paylaşımı "şu komutu çalıştır" dosyayı git'e koy
Silme her birini tek tek down
Versiyonlama yok yml dosyası git'te

Docker Compose ile Ölçekleme

services:
  api:
    build: .
    expose:
      - "8000"   # ports değil expose

  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
    depends_on:
      - api

# 3 API instance:
docker compose up --scale api=3 -d
Kullanıcı → :80
Nginx Load Balancer — yükü dengeli dağıtır
API 1
API 2
API 3
Bu lokal için iyi. Sunucu çökerse? Farklı sunuculara yayılmak gerekirse?
→ AWS ECS bunu otomatik yapar.

3. Derse Köprü

Lokalde çalışıyor. Ölçekleme sorununu gördük.
Bunu gerçek dünyaya taşıyalım.
✓ done

Yaptıklarımız

  • FastAPI app yazdık
  • Dockerfile oluşturduk
  • Build ettik, test ettik
? issue

Sorunlar

  • Sadece lokalde çalışıyor
  • Ölçekleme manuel
  • Sunucu çökerse biter
→ next

AWS ile Çözüm

  • ECR → Image deposu
  • ECS/Fargate → Otomatik
  • ALB → Yük dağıtımı
Sonraki ders: Bu container'ı AWS'e taşıyoruz. Siz telefonunuzdan test ediyorsunuz.