Modern yazılım geliştirmede Docker, uygulamaları izole edilmiş container’larda çalıştırarak taşınabilirlik ve tutarlılık sağlayan en önemli araçlardan biri haline geldi. Bu kapsamlı rehberde Docker’ın temel kullanımını, en yaygın komutları ve pratik örnekleri ele alacağız.

Docker Nedir ve Neden Kullanmalıyız?

Docker, uygulamaları ve tüm bağımlılıklarını hafif, taşınabilir container’larda paketlememizi sağlayan bir containerization platformudur. Geleneksel virtualization’dan farklı olarak, Docker container’ları işletim sistemi çekirdeğini paylaşarak daha az kaynak tüketir ve daha hızlı başlatılır.

Docker’ın Ana Avantajları

  • Taşınabilirlik: “Benim bilgisayarımda çalışıyor” problemini çözer
  • İzolasyon: Her container kendi ortamında çalışır
  • Kaynak Verimliliği: VM’lere göre daha az kaynak tüketir
  • Hızlı Dağıtım: Saniyeler içinde container başlatma
  • Tutarlılık: Geliştirme, test ve production ortamları arasında

Docker Kurulumu

Linux Üzerinde Kurulum

curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
sudo usermod -aG docker $USER

Docker Compose Kurulumu

sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose

Temel Docker Komutları

Image İşlemleri

docker images                    # Mevcut tüm imajları listeler
docker pull nginx:latest         # Docker Hub'dan nginx imajını indirir
docker rmi image_name            # Belirtilen imajı siler (remove image)
docker build -t my-app .         # Dockerfile'dan imaj oluşturur (-t: tag verir)
docker tag my-app:latest my-app:v1.0  # Mevcut imaja yeni tag ekler

Container İşlemleri

docker ps                        # Çalışan container'ları listeler (process status)
docker ps -a                     # Tüm container'ları listeler (-a: all)
docker run -d --name my-container nginx  # Nginx container'ını arka planda çalıştırır (-d: detached)
docker start container_name      # Durdurulmuş container'ı başlatır
docker stop container_name       # Çalışan container'ı durdurur
docker restart container_name    # Container'ı yeniden başlatır
docker rm container_name         # Container'ı siler (remove)

Container İçine Erişim

docker exec -it container_name bash  # Container içine terminal açar (-i: interactive, -t: tty)
docker logs container_name           # Container loglarını gösterir
docker logs -f container_name        # Logları canlı takip eder (-f: follow)

Dockerfile Oluşturma

Dockerfile, Docker imajlarını oluşturmak için kullanılan text dosyasıdır. İşte temel bir web uygulaması için örnek:

Node.js Uygulaması Dockerfile

Dockerfile
FROM node:18-alpine

WORKDIR /app

COPY package*.json ./ 
RUN npm install

COPY . .

EXPOSE 3000

CMD ["node", "server.js"]

Bu Dockerfile, adım adım şu işlemleri gerçekleştirir:

İlk satırda node:18-alpine imajı kullanılır. Bu, Node.js’in 18. sürümünü içeren ve oldukça hafif olan Alpine tabanlı bir resmi Docker imajıdır. Küçük boyutu sayesinde daha hızlı indirilir ve dağıtımı kolaylaştırır.

Ardından WORKDIR /app satırıyla konteyner içerisinde çalışılacak dizin /app olarak tanımlanır. Bu, sonraki tüm komutların bu dizin içinde yürütüleceği anlamına gelir.

COPY package*.json ./ satırı, proje kök dizinindeki package.json ve varsa package-lock.json dosyalarını konteynere kopyalar. Bu dosyalar, proje bağımlılıklarının tanımlı olduğu yerlerdir.

Hemen ardından gelen RUN npm install komutu, bu bağımlılıkları indirir ve kurar. Bu adımda sadece gerekli olan dosyalar konteynere kopyalandığı için Docker cache mekanizmasından faydalanarak sonraki build işlemleri hızlandırılmış olur.

Sonrasında COPY . . komutuyla projenin tüm dosyaları konteyner içine kopyalanır. Artık tüm uygulama dosyaları konteynerde yer alır.

EXPOSE 3000 satırı, uygulamanın dinleyeceği portu belirtir. Bu sadece bilgilendirici bir ifadedir, gerçek port yönlendirmesi docker run -p komutu ile yapılır.

Son olarak CMD ["node", "server.js"] komutu, konteyner başlatıldığında çalıştırılacak olan komutu tanımlar. Burada uygulamanın giriş noktası olan server.js dosyası Node.js ile çalıştırılır.

Bu yapı sayesinde uygulamanızın her ortamda aynı şekilde çalışması garanti altına alınmış olur.

Docker Compose ile Multi-Container Uygulamalar

Docker Compose, birden fazla container’ı yönetmek için kullanılır. İşte LAMP stack örneği:

docker-compose.yml

Dockerfile
version: '3.8'

services:
  app:
    build: .
    ports:
      - "8000:80"
    volumes:
      - ./src:/var/www/html
    depends_on:
      - database
    environment:
      - DB_HOST=database
      - DB_NAME=myapp
      - DB_USER=root
      - DB_PASS=secret

  database:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: secret
      MYSQL_DATABASE: myapp
    volumes:
      - db_data:/var/lib/mysql
    ports:
      - "3306:3306"

  phpmyadmin:
    image: phpmyadmin/phpmyadmin
    ports:
      - "8080:80"
    environment:
      PMA_HOST: database
    depends_on:
      - database

volumes:
  db_data:

Docker Compose Komutları

docker-compose up -d                    # Tüm servisleri arka planda başlatır (-d: detached)
docker-compose down                     # Tüm servisleri durdurur ve kaldırır
docker-compose logs -f service_name     # Belirtilen servisin loglarını canlı takip eder
docker-compose exec service_name bash   # Belirtilen servis içine terminal açar
docker-compose build                    # Tüm servislerin imajlarını yeniden oluşturur
docker-compose restart service_name     # Belirtilen servisi yeniden başlatır

Pratik Örnekler

1. Nginx Web Sunucusu

docker run -d \                        # Arka planda çalıştır (detached)
  --name my-nginx \                     # Container'a isim ver
  -p 8080:80 \                         # Port yönlendirme (host:container)
  -v $(pwd)/html:/usr/share/nginx/html \ # Volume mount (yerel:container)
  nginx:alpine                          # Kullanılacak imaj

2. MySQL Veritabanı

docker run -d \                        # Arka planda çalıştır
  --name mysql-db \                     # Container ismi
  -e MYSQL_ROOT_PASSWORD=password \     # Environment variable (-e)
  -e MYSQL_DATABASE=myapp \             # Oluşturulacak veritabanı
  -p 3306:3306 \                       # MySQL portu
  -v mysql_data:/var/lib/mysql \        # Volume ile veri kalıcılığı
  mysql:8.0                             # MySQL 8.0 imajı

3. Redis Cache

docker run -d \                        # Arka planda çalıştır
  --name redis-cache \                  # Container ismi
  -p 6379:6379 \                       # Redis portu
  redis:alpine                          # Hafif Redis imajı

Volume ve Network Yönetimi

Volume İşlemleri

docker volume create my-volume      # Yeni volume oluşturur
docker volume ls                    # Mevcut volume'ları listeler (list)
docker volume inspect my-volume     # Volume detaylarını gösterir
docker volume rm my-volume          # Volume'u siler (remove)

Network İşlemleri

docker network create my-network    # Yeni network oluşturur
docker network ls                   # Mevcut network'leri listeler
docker network inspect my-network   # Network detaylarını gösterir
docker run --network=my-network nginx  # Container'ı belirtilen network'e bağlar

En İyi Pratikler

1. Multi-Stage Build Kullanımı

Modern uygulamalarda, özellikle frontend projelerinde, build işlemi zaman alabilir ve fazladan dosyalar üretebilir. Bu noktada Multi-Stage Build yöntemi devreye girer. Aşağıdaki Dockerfile örneğinde, bir Node.js uygulaması önce derleniyor, ardından sadece gerekli olan build çıktısı daha küçük bir imajda çalıştırılıyor.

Dockerfile
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build

FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html

Bu yapı iki aşamadan oluşur:

İlk aşama (builder):
İlk satırda node:18-alpine imajı kullanılır ve bu aşama "builder" olarak adlandırılır. Çalışma dizini /app olarak belirlenir. Ardından package.json ve varsa package-lock.json dosyaları kopyalanır ve npm install komutuyla bağımlılıklar kurulur. Sonrasında tüm proje dosyaları kopyalanır ve npm run build komutu çalıştırılarak projeyi derleyen bir dist klasörü üretilir.

İkinci aşama (final imaj):
İkinci aşamada, küçük ve hızlı bir web sunucusu olan nginx:alpine imajı temel alınır. İlk aşamadaki build çıktısı olan /app/dist dizini, Nginx’in servis verdiği /usr/share/nginx/html dizinine kopyalanır.

Sonuç olarak, yalnızca build sonucu olan statik dosyaları içeren, küçük boyutlu, güvenli ve production için ideal bir imaj elde edilir. Geliştirme araçları ve kaynak dosyalar son imajda yer almadığı için performans ve güvenlik açısından büyük avantaj sağlar.

2. .dockerignore Dosyası

Docker imajı oluşturulurken, proje klasöründeki tüm dosyalar konteynerin içine kopyalanır. Ancak bazı dosyaların veya klasörlerin imajın içine dahil edilmesi gereksizdir ve build süresini uzatabilir, imaj boyutunu şişirebilir. İşte bu noktada .dockerignore dosyası devreye girer.

.dockerignore, .gitignore dosyasına benzer şekilde çalışır ve hangi dosya veya klasörlerin Docker context’e dahil edilmeyeceğini belirtir.

Örnek bir .dockerignore dosyası şu şekilde olabilir:

node_modules
npm-debug.log
.git
.DS_Store
*.md
.env

Bu dosyada yer alan satırların anlamı:

  • node_modules: Lokal geliştirme sırasında oluşturulan bağımlılık klasörü, genellikle büyük boyutludur ve imaj içine dahil edilmesine gerek yoktur.
  • npm-debug.log: Hata ayıklama günlük dosyalarıdır, gereksizdir.
  • .git: Versiyon kontrol klasörü, sadece geliştirme ortamında anlamlıdır.
  • .DS_Store: macOS sistemlerinin oluşturduğu gereksiz dosyalar.
  • *.md: Belgeler (örneğin README.md) genellikle çalışma zamanında gerekli değildir.
  • .env: Ortam değişkenleri içeren dosya, güvenlik nedeniyle imaj içine alınmamalıdır.

Bu dosya sayesinde Docker imajı daha temiz, küçük ve hızlı hale gelir. Ayrıca hassas bilgilerin yanlışlıkla konteynerin içine dahil edilmesi de engellenmiş olur.

3. Güvenlik İpuçları

  • Root kullanıcı yerine non-root kullanıcı kullanın
  • Minimal base imajlar tercih edin (alpine, scratch)
  • Secrets’ları environment variable olarak geçmeyin
  • Regular security updates yapın

4. Performans Optimizasyonu

  • Layer caching’den faydalanın
  • Multi-stage build kullanın
  • Gereksiz dosyaları .dockerignore ile hariç tutun
  • Minimal imaj boyutu için alpine veya distroless imajlar kullanın

Container Monitoring ve Debugging

Resource Kullanımı İzleme

docker stats                        # Tüm container'ların kaynak kullanımını gösterir
docker stats container_name         # Belirtilen container'ın istatistiklerini gösterir
docker system df                     # Docker'ın disk kullanımını gösterir (disk free)
docker system prune                  # Kullanılmayan imaj, container ve network'leri temizler

Log Yönetimi

docker logs --tail 100 container_name     # Son 100 log satırını gösterir
docker logs --since 1h container_name     # Son 1 saatteki logları gösterir
docker logs -f --timestamps container_name # Logları zaman damgasıyla canlı takip eder

Health Check Ekleme

HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD curl -f http://localhost:8080/health || exit 1

Production Ortamı İçin İpuçları

1. Resource Limits

docker run -d \
  --memory="512m" \                    # Maksimum 512MB RAM kullanımı
  --cpus="0.5" \                      # Maksimum 0.5 CPU core kullanımı
  --name limited-container \
  nginx:alpine

2. Restart Policies

docker run -d \
  --restart=unless-stopped \           # Manuel durdurulana kadar sürekli yeniden başlat
  --name always-running \
  nginx:alpine

3. Environment Variables

docker run -d \
  --env-file .env \                   # Environment variable'ları dosyadan oku
  --name configured-app \
  my-application

Sorun Giderme

Yaygın Problemler ve Çözümleri

  1. Port conflict: docker ps ile kullanılan portları kontrol edin
  2. Disk Alanı: docker system prune -a ile temizlik yapın
  3. Permission Denied: Kullanıcıyı docker grubuna ekleyin
  4. Container Başlamıyor: docker logs container_name ile logları kontrol edin

Debug Komutları

docker inspect container_name       # Container'ın tüm detaylarını JSON formatında gösterir
docker exec -it container_name sh   # Container içine shell erişimi (-it: interactive terminal)
docker run --rm -it alpine sh       # Geçici alpine container'ında shell açar (--rm: çıkışta sil)

Uzun lafın kısası

Docker, modern yazılım geliştirmede vazgeçilmez bir araç haline gelmiştir. Container’lar sayesinde uygulamalarımızı tutarlı, taşınabilir ve ölçeklenebilir şekilde dağıtabiliyoruz. Bu rehberde ele aldığımız temel kavramlar ve komutlar, Docker yolculuğunuzda sağlam bir temel oluşturacaktır.

Docker’ı etkili kullanmak için sürekli pratik yapmak ve topluluktan öğrenmeye devam etmek önemlidir. Her proje farklı gereksinimler getirebilir, ancak burada öğrendiğiniz temel prensipleri uygulayarak başarılı containerization stratejileri geliştirebilirsiniz.

İlerleyen yazılarda Docker Swarm, Kubernetes entegrasyonu ve advanced networking konularını da ele alacağız. Docker journey’niz için bu temel rehberin faydalı olmasını umuyorum!

Kategoriler:

DevOps,