Node.js İle Docker Üzerinde Konteynerize Edilmiş Uygulama Nasıl Yapılır?

Node.js İle Docker Üzerinde Konteynerize Edilmiş Uygulama Nasıl Yapılır?
Node.js İle Docker Üzerinde Konteynerize Edilmiş Uygulama Nasıl Yapılır?

Gereksinimler ve Ön Hazırlık

Projeye başlamadan önce sisteminizde aşağıdaki araçların güncel sürümlerinin kurulu olduğundan emin olmalısınız. 2026 yılı itibarıyla Node.js tarafında LTS (Long Term Support) sürümlerini kullanmak güvenlik ve performans açısından kritik öneme sahiptir.

  • Node.js (v22+): Uygulama mantığını çalıştırmak için gerekli çalışma zamanı ortamı.
  • Docker Desktop veya Docker Engine: Konteynerleri oluşturmak ve yönetmek için temel araç.
  • npm veya pnpm: Paket yönetimi için. Bu rehberde hız ve güvenlik avantajları nedeniyle npm kullanılacaktır.
  • Kod Editörü: VS Code gibi Docker eklentisi destekleyen bir editör işinizi kolaylaştıracaktır.

Adım 1: Basit Bir Node.js Uygulaması Oluşturma

İlk adım olarak, konteynerize edeceğimiz temel bir Node.js uygulaması hazırlayalım. Uygulamamız, Express kütüphanesini kullanan basit bir HTTP sunucusu olacak. Proje klasörünüzü oluşturun ve gerekli bağımlılıkları yükleyin.

mkdir node-docker-app
cd node-docker-app
npm init -y
npm install express

Bu komutlar, projenizin temel yapılandırmasını oluşturur ve Express kütüphanesini projenize dahil eder. Şimdi, uygulamanın giriş noktası olacak app.js dosyasını oluşturalım.

// app.js dosyası
const express = require('express');
const app = express();
const PORT = process.env.PORT || 3000;

app.get('/', (req, res) => {
  res.json({ message: 'Docker üzerinde Node.js uygulaması başarıyla çalışıyor!' });
});

app.listen(PORT, () => {
  console.log(`Uygulama http://localhost:${PORT} adresinde çalışıyor.`);
});

Bu kod, 3000 numaralı portu dinleyen ve gelen isteklere JSON formatında yanıt veren temel bir sunucudur. process.env.PORT kullanımı, uygulamanızın farklı ortamlarda (örneğin bulut sunucularında) dinamik port atamalarına uyum sağlamasına olanak tanır.

Adım 2: Dockerfile Oluşturma ve Yapılandırma

Docker'ın uygulamanızı nasıl paketleyeceğini anlaması için bir Dockerfile dosyasına ihtiyacı vardır. Bu dosya, uygulamanın çalışması için gerekli olan işletim sistemi katmanlarını ve komutları tanımlar.

# Dockerfile
FROM node:22-alpine

# Uygulama dizinini oluştur
WORKDIR /usr/src/app

# Bağımlılıkları kopyala
COPY package*.json ./

# Sadece üretim bağımlılıklarını yükle
RUN npm ci --only=production

# Uygulama kaynak kodunu kopyala
COPY . .

# Uygulamanın çalıştığı portu belirt
EXPOSE 3000

# Uygulamayı başlat
CMD ["node", "app.js"]

Burada node:22-alpine kullanmamızın sebebi, Alpine Linux tabanlı imajların çok küçük boyutlu olması ve güvenlik açıklarını minimize etmesidir. npm ci komutu, package-lock.json dosyasındaki tam sürümleri yükleyerek tutarlı bir ortam sağlar.

Kritik Güvenlik Uyarısı: Docker imajlarınızda asla root kullanıcısı ile uygulama çalıştırmayın. Üretim ortamında, Dockerfile içerisinde USER node komutunu kullanarak yetkisiz bir kullanıcı oluşturmak, olası bir sızıntıda sistemin tamamının ele geçirilmesini zorlaştırır.

Adım 3: .dockerignore Dosyası ile Gereksiz Dosyaları Eleme

Docker imajı oluşturulurken node_modules klasörünün veya yerel geliştirme ortamındaki gizli dosyaların (.git, .env) imaja dahil edilmemesi gerekir. Bu hem imaj boyutunu küçültür hem de güvenlik sağlar.

# .dockerignore
node_modules
npm-debug.log
.git
.env
Dockerfile
.dockerignore

Bu dosya, Docker'ın imajı oluştururken bu klasörleri ve dosyaları yok saymasını sağlar. Özellikle node_modules klasörünü dışarıda bırakmak, imaj oluşturma sürenizi ciddi oranda kısaltacaktır.

Adım 4: Docker İmajını Oluşturma ve Çalıştırma

Artık her şey hazır. Şimdi terminal üzerinden Docker imajımızı inşa edelim ve çalıştıralım. Aşağıdaki komut, node-app adında bir imaj oluşturacaktır.

# İmajı oluştur
docker build -t node-app .

# Konteyneri çalıştır
docker run -p 3000:3000 node-app

-p 3000:3000 parametresi, yerel makinenizdeki 3000 numaralı portu, konteynerin içindeki 3000 numaralı porta yönlendirir. Tarayıcınızdan http://localhost:3000 adresine giderek çıktıyı görebilirsiniz.

Konteynerizasyon Yöntemlerinin Karşılaştırılması

Yöntem Avantajı Dezavantajı
Alpine İmaj Çok düşük boyut, yüksek güvenlik Bazı kütüphaneler için derleme gerektirebilir
Debian tabanlı Yüksek uyumluluk İmaj boyutu daha büyüktür
Multi-stage Build En temiz üretim imajı Dockerfile karmaşıklığı artar

Adım 5: Multi-Stage Build ile İmajı Optimize Etme

Üretim ortamında daha güvenli ve hafif imajlar için "Multi-stage build" (çok aşamalı yapılandırma) kullanmalısınız. Bu yöntem, uygulamanın derlenmesi için gerekli olan araçları ayrı bir aşamada tutar ve nihai imaja sadece çalışan dosyaları aktarır.

# Multi-stage Dockerfile örneği
FROM node:22-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .

FROM node:22-alpine
WORKDIR /app
COPY --from=builder /app .
EXPOSE 3000
USER node
CMD ["node", "app.js"]

Bu yapı sayesinde, geliştirme aşamasındaki gereksiz dosyalar ve araçlar nihai imajın içerisine dahil edilmez, böylece saldırı yüzeyi daraltılmış olur.

Sorumluluk Reddi: Bu rehberde paylaşılan kod örnekleri genel eğitim amaçlıdır. Üretim ortamında kullanacağınız uygulamalarda SQL injection, XSS ve diğer güvenlik tehditlerine karşı girdi doğrulama (input validation) ve şifreleme kütüphaneleri (bcrypt, helmet vb.) kullanmanız zorunludur.

Sıkça Sorulan Sorular

Docker konteynerim çalışıyor ancak portlara erişemiyorum, ne yapmalıyım?

Konteynerin çalıştığından docker ps komutuyla emin olun. Eğer port yönlendirmesi (-p parametresi) eksikse veya Node.js uygulamanız 0.0.0.0 yerine 127.0.0.1 adresini dinliyorsa dışarıdan erişemezsiniz. Uygulamanızın her arayüzü dinlediğinden emin olun.

Neden npm install yerine npm ci kullanmalıyım?

npm ci, package-lock.json dosyasını baz alır ve tam olarak kilitlenmiş sürümleri yükler. Bu, Docker imajınızın her seferinde aynı bağımlılıklarla kurulmasını garanti eder ve üretim ortamında sürpriz hataları önler.

İmaj boyutunu nasıl daha fazla küçültebilirim?

.dockerignore dosyanızı kontrol edin. Geliştirme bağımlılıklarını (devDependencies) kurmadığınızdan emin olun. Ayrıca, mümkünse distroless imajları kullanarak işletim sistemi araçlarını tamamen kaldırabilirsiniz.

Docker konteynerleri güncellendiğinde verilerim silinir mi?

Evet, konteynerler geçicidir (ephemeral). Verilerin kalıcı olması için Docker Volume kullanmanız gerekir. docker run -v /yerel/yol:/konteyner/yol komutuyla verilerinizi dışarıda saklayabilirsiniz.

Node.js uygulamasını Docker'da hata ayıklama (debug) nasıl yaparım?

Konteyneri --inspect bayrağı ile başlatarak Chrome DevTools üzerinden uzaktan hata ayıklama yapabilirsiniz. Ancak bu yöntemi sadece geliştirme ortamında kullanın.

Docker Compose ile Mikroservis Mimarisine Geçiş

Tek bir konteyner yönetmek genellikle yeterli olsa da, gerçek dünya projelerinde Node.js uygulamanızın yanında bir veritabanı (MongoDB, PostgreSQL veya Redis) çalıştırmanız gerekecektir. Docker Compose, çoklu konteyner uygulamalarını tek bir docker-compose.yml dosyası üzerinden tanımlamanıza ve yönetmenize olanak tanır.

Aşağıdaki örnek, Node.js uygulamanızın bir Redis veritabanı ile nasıl birlikte çalışacağını gösterir:

version: '3.8'
services:
  app:
    build: .
    ports:
      - "3000:3000"
    environment:
      - REDIS_HOST=redis
    depends_on:
      - redis
  redis:
    image: redis:alpine
    ports:
      - "6379:6379"

Bu yapılandırma ile docker-compose up komutunu çalıştırdığınızda, Docker her iki servisi de aynı sanal ağ üzerinde ayağa kaldırır. Uygulamanız, Redis'e bağlanmak için localhost yerine redis host adını kullanabilir.

Docker Konteynerlerinde Performans İzleme ve Log Yönetimi

Üretim ortamında çalışan bir Node.js uygulamasının sağlığını takip etmek kritik öneme sahiptir. Konteynerler izole oldukları için, geleneksel yöntemlerle log takibi yapmak zorlaşabilir. Docker, logları standart çıktı (stdout) üzerinden toplar.

Logları Görüntüleme

Konteyneriniz çalışırken logları anlık takip etmek için aşağıdaki komutu kullanabilirsiniz:

docker logs -f [konteyner_id_veya_ismi]

Performans Analizi

Konteynerin kaynak tüketimini (CPU ve RAM kullanımı) anlık olarak görmek için docker stats komutunu kullanın. Bu komut, özellikle Node.js'in olay döngüsünü (event loop) tıkayan süreçleri tespit etmenize yardımcı olur:

docker stats --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}"
İpucu: Eğer uygulamanız yüksek trafik alıyorsa, Node.js'in tek iş parçacıklı yapısını desteklemek için pm2 gibi bir süreç yöneticisini Docker imajınızın içine dahil etmeniz, konteyner içindeki hataları yönetmek ve uygulamayı yeniden başlatmak için en iyi pratiktir.

Güvenlik: Root Olmayan Kullanıcı ile Çalıştırma

Varsayılan olarak Docker konteynerleri root yetkisi ile çalışır. Bu, güvenlik açığı durumunda saldırganın tüm ana makineye erişebilmesi riskini doğurur. Uygulamanızı daha güvenli hale getirmek için Dockerfile içerisinde düşük yetkili bir kullanıcı oluşturun:

# Dockerfile içerisine ekleyin
RUN addgroup -S nodejs && adduser -S nodejs -G nodejs
USER nodejs

# Uygulama dosyalarının sahipliğini değiştirin
COPY --chown=nodejs:nodejs . .

Bu basit değişiklik, uygulamanızın konteyner içerisinde sınırlı yetkilerle çalışmasını sağlar ve olası bir "container breakout" (konteynerden dışarı sızma) saldırısının etkisini ciddi oranda azaltır.

Sonuç

Node.js uygulamalarını Docker ile konteynerize etmek, modern yazılım geliştirme döngüsünün temel taşıdır. Bu rehberde, basit bir uygulamadan başlayarak güvenli, optimize edilmiş ve üretim ortamına hazır bir Docker imajı oluşturma sürecini tamamladınız. Bir sonraki adım olarak, çoklu konteyner yönetimi için Docker Compose kullanımını ve CI/CD süreçlerine Docker entegrasyonunu araştırmanızı öneririm.

Bu yazıya tepkinizi paylaşın:
Deniz Aydın

On yıllık yaşam tarzı editörlüğü deneyimimle karmaşık süreçleri herkesin anlayabileceği basit adımlara dönüştürüyorum. Okuyucuların günlük yaşam kalitesini artıracak uygulanabilir çözümler üretmeye odaklanıyorum.

Yorumlar (0)

Yorum Yaz