Docker Temelleri: Temelden Gerçek Proje Kullanımına
Mustafa Kürşad BAŞER
Docker Temelleri: Temelden Gerçek Proje Kullanımına
Docker’ı öğrenirken ilk eşik kolay: image çek, container çalıştır, port aç, bitti. Asıl değer, aynı projeyi her seferinde aynı şekilde ayağa kaldırabildiğinde ortaya çıkıyor. Tek makinede değil, ekipte. Sadece local’de değil, prod’a yakın davranışlarla.
Bu yazı, Docker’ın temel yapı taşlarını netleştiriyor ve sonra Dockerfile ile docker compose üzerinden gerçekçi bir akış kuruyor. Araya debug rutini, sık yapılan hatalar ve iki tane kontrol listesi de ekledim. Amaç komut ezberi değil, pratik refleks.
Problem: Teoride biliyorum, pratikte takılıyorum
Docker tarafında en çok zaman yediren şey genelde şu oluyor: Kavramlar tam oturmadan kurulum büyüyor. Sonra bir yerde port çakışıyor, DB bağlantısı kopuyor, veri kayboluyor ve debug uzuyor.
- “Bende çalışıyor” ama ekipte aynı şekilde kalkmıyor
- DB datası uçuyor, container restart sonrası her şey sıfırlanıyor
- Servisler konuşmuyor, localhost yüzünden yanlış yere bağlanılıyor
- Build yavaş, her değişiklikte cache bozuluyor
- Prod’a yakın davranış yok, restart ve sağlık yaklaşımı düşünülmemiş
Yaklaşım: Kavramları netleştir, sonra kurulum akışını standartlaştır
Docker’ı pratikte anlaşılır kılan şey, birkaç kavramı doğru ayırmak. Sonra da bu kavramları Compose ile tek bir tarifte birleştirmek. Aşağıdaki parçalar netleşince “Docker karmaşık” hissi ciddi azalıyor.
Docker Engine, Docker Daemon ve Docker CLI
Neden önemli? Docker komutlarını yazan taraf CLI. Asıl işi yapan taraf ise arka planda çalışan daemon. Bunların hepsi Engine üzerinden container, image, network ve volume operasyonlarını yürütüyor. Komut çalışmıyorsa bazen sorun Dockerfile değil, daemon veya Engine tarafındaki durum olabiliyor.
Pratik ipucu
- Sorun anında önce şuna bakmak çoğu şeyi hızlandırır: Docker çalışıyor mu, container’lar ayakta mı, log ne diyor
- “Komut bende çalışmıyor” durumlarında daemon tarafı da ihtimal dahilinde
Docker Image ve Docker Container
Neden önemli? Image paket. Container çalışan instance. Container’ı silmek uygulamayı “yok etmez”, sadece o instance gider. Kalıcı veri gerekiyorsa çözüm container değil volume olur.
Yaygın hata
- Image ile container’ı aynı şey sanmak ve veri kaybını container üzerinden çözmeye çalışmak
Dockerfile: Image’ı üreten tarif
Neden önemli? Dockerfile iyi değilse image şişer, build uzar, cache çalışmaz. Bu da günlük geliştirmede sürekli zaman kaybı demek.
Pratik ipucu
- Bağımlılık dosyalarını önce kopyalayıp yüklemek, cache tarafında en büyük kazanç
- .dockerignore yoksa image gereksiz büyür ve build süresi uzar
Docker Hub ve Registry mantığı
Neden önemli? Docker Hub bir registry. Postgres, Redis gibi hazır image’ların geldiği yer. Aynı mantıkla kendi image’larını da push edersin. Registry tarafı, özellikle prod’da versiyonlama ve geri dönüş planı için kritik.
Yaygın hata
- latest kullanmak ve “dün çalışan bugün bozuldu” sürprizini davet etmek
Docker Compose: Çoklu servisi tek komutla kaldırmak
Neden önemli? Gerçek hayatta uygulama tek servis değil. Web, DB, cache, worker gibi parçalar var. Compose, bu parçaları tek dosyada tarifleyip tek komutla çalıştırır. Ekip içinde tutarlı ortam kurmanın en pratik yolu burada.
Yaygın hata
- Compose ortamında servisler arası bağlantıda localhost kullanmak. Container içindeki localhost, o container’ın kendisi
Volume ve Network: Veri kalıcılığı ve servis iletişimi
Neden önemli? DB verisi gibi kalıcı şeyleri volume ile yönetmek gerekir. Network tarafında ise Compose, servisleri aynı ağda konuşturur ve servis adları DNS gibi çalışır.
Pratik ipucu
- DB datası için named volume kullanmak, ekipte taşınabilirliği artırır
- Bağlantı string’inde host olarak servis adını kullanmak çoğu bağlantı sorununu çözer
Hızlı Özet
- Dockerfile image üretir
- Image’dan container çalışır
- Container geçicidir, kalıcı veri için volume gerekir
- Compose servisleri aynı network’e koyar, servis adı DNS gibi çalışır
- CLI komut gönderir, daemon işi yapar
Uygulama: Dockerfile + Compose ile gerçekçi bir kurulum
Basit ama gerçekçi bir örnek üzerinden gidelim: web uygulaması ve Postgres. Amaç şu: tek komutla ortam kalksın, DB datası kaybolmasın, servisler doğru konuşsun.
Mini örnek Dockerfile
1FROM node:20-alpine
2WORKDIR /app
3
4# Cache için önce bağımlılıklar
5COPY package*.json ./
6RUN npm ci --omit=dev
7
8# Sonra kaynak kod
9COPY . .
10
11EXPOSE 3000
12CMD ["node", "server.js"]docker-compose.yml örneği
1services:
2 api:
3 build: .
4 ports:
5 - "3000:3000"
6 environment:
7 - DATABASE_URL=postgres://app:app@postgres:5432/appdb
8 depends_on:
9 - postgres
10
11 postgres:
12 image: postgres:16
13 environment:
14 - POSTGRES_USER=app
15 - POSTGRES_PASSWORD=app
16 - POSTGRES_DB=appdb
17 volumes:
18 - pgdata:/var/lib/postgresql/data
19 ports:
20 - "5432:5432"
21
22volumes:
23 pgdata:Neden önemli?
- DATABASE_URL içinde host postgres. Çünkü servis adı bu
- pgdata named volume. Container silinse bile data kalır
- Port çakışırsa host portu değiştirilebilir, container portunu sabit tutmak daha az karıştırır
Komutlar
1docker compose up -d
2docker compose logs -f api
3docker compose exec api sh
4docker compose downYaygın hatalar ve anti-pattern’ler
“Her şeyi tek container yapayım”
- Belirti: Debug zorlaşır, sorumluluklar karışır, küçük bir değişiklik büyük etki yaratır
- Çözüm: Servisleri ayırmak. API ayrı, DB ayrı, gerekiyorsa worker ayrı
latest kullanmak
- Belirti: Dün çalışan bugün bozulur, geri dönüş zorlaşır
- Çözüm: Versiyon pinlemek. Örnek: postgres:16, node:20-alpine
Secret’ı image içine gömmek
- Belirti: Registry’ye push sonrası geri dönüş yok, sızıntı riski büyür
- Çözüm: Secret runtime’da verilir. Repo’ya yazılmaz, image içine gömülmez
“DB datası nereye gitti”
- Belirti: Container yeniden başlayınca tablo yok, data yok
- Çözüm: Named volume kullanmak. DB için bu genelde en temiz yol
Ne zaman Docker Run, ne zaman Docker Compose
| İhtiyaç | Docker run | Docker Compose |
|---|---|---|
| Tek servis, hızlı deneme | Uygun | Fazla gelebilir |
| API + DB + cache gibi çoklu servis | Zorlaşır | Uygun |
| Ekipte ortak kurulum | Tutarsız olur | Uygun |
| Ortamı dokümante etmek | Komutlar dağılır | YAML net olur |
| Tek komutla kaldır indir | Zayıf | Güçlü |
Debug rutini: ps → logs → exec → inspect
Sorun çıktığında çoğu kez yeniden build almak yerine şu sırayı izlemek daha hızlı sonuç veriyor:
1. Container ayakta mı
1docker ps2. Log ne diyor
1docker logs -f <container_name>3. İçeriden kontrol
1docker exec -it <container_name> sh4. Detaylı teşhis
1docker inspect <container_name>Bu akış genelde port, env, host adı, volume ve network kaynaklı problemleri hızlı yakalatıyor.
Checklist: Minimum standart
Local geliştirme için
- docker compose up ile tüm servisler tek komutla kalkıyor
- DB için named volume var, restart sonrası data duruyor
- Servis bağlantıları localhost değil servis adıyla çalışıyor
- .dockerignore var, image gereksiz şişmiyor
- Log takibi net: docker compose logs -f
Prod yaklaşımı
- Image tag’leri pinli, latest yok
- Secret’lar repo ve image içinde değil
- Servis sınırları net, web ayrı db ayrı
- Basit bir debug rutini dokümante edilmiş
Kısa sonuç / takeaway
- Dockerfile image üretir, image’dan container çalışır
- Container geçicidir, kalıcı veri volume ile çözülür
- Compose ekip için standart ortam sağlar
- Compose network’ünde servis adı DNS gibidir, localhost tuzağına düşme
- Debug sırası zaman kazandırır: ps → logs → exec → inspect
Sonuç
Docker’ın pratikte fark yarattığı yer, tek seferlik çalıştırma değil; aynı kurulumu tekrar tekrar, farklı makinelerde, aynı sonuçla üretebilmek. Kavramlar net olunca iş basitleşiyor: Dockerfile image üretir, image container olarak çalışır, veri kalıcı olacaksa volume’a taşınır, birden fazla servis varsa Compose düzeni kurar. Bu temel oturduktan sonra geriye iki şey kalıyor: versiyonları sürpriz çıkarmayacak şekilde yönetmek ve sorun anında kısa bir debug akışıyla hızlı teşhis yapmak. Local’de başlayan bu disiplin, prod’a yaklaşırken de en çok yükü alan kısım oluyor.

Software Engineer
A passionate software engineer who enjoys creating elegant solutions to complex problems. Beyond coding, I am deeply interested in exploring the intersections of technology, art, and human consciousness.
Similar Posts

Node.js’te EventEmitter ile Olay Odaklı Programlama Rehberi
Node.js’in olay odaklı yapısını keşfedin! EventEmitter sınıfıyla asenkron olayları nasıl yöneteceğinizi adım adım öğrenin.

Retrofit Rehberi: Avantajları ve Android’de Retrofit ile RESTful API Etkileşimi
Merhaba, bu yazımda, Android uygulama geliştirme sürecinde sıklıkla kullanılan ve HTTP isteklerini yönetmek, RESTful API’larla etkileşim kurmak için ideal bir çözüm sunan Retrofit kütüphanesine odaklanacağız.