İçeriğe geç
Node.js

Node.js Docker: Container ile Deployment

Tarık Tunç
Node.js Docker: Container ile Deployment

Node.js Docker: Container ile Deployment

Docker, uygulamaları izole container'lar içinde çalıştırarak tutarlı deployment ortamları oluşturmanın standart yoludur. Node.js uygulamalarını Docker ile containerize etmek, geliştirme ortamından production'a sorunsuz geçiş sağlar. Bu rehberde Node.js uygulamalarını Docker ile paketleme, optimize etme ve deploy etme sürecini adım adım inceleyeceğiz.

1. Docker Temel Kavramlar

  • Image: Uygulamanın çalışması için gereken her şeyi içeren salt-okunur şablon
  • Container: Image'dan oluşturulan çalışan örnek
  • Dockerfile: Image oluşturma talimatlarını içeren dosya
  • Docker Compose: Çoklu container'ı yönetmek için araç

2. Temel Dockerfile

# Dockerfile
FROM node:20-alpine

# Çalışma dizinini ayarla
WORKDIR /app

# Bağımlılık dosyalarını kopyala
COPY package*.json ./

# Bağımlılıkları yükle
RUN npm ci --only=production

# Uygulama dosyalarını kopyala
COPY . .

# Port tanımla
EXPOSE 3000

# Uygulamayı başlat
CMD ["node", "server.js"]
# .dockerignore
node_modules
npm-debug.log
Dockerfile
docker-compose.yml
.git
.gitignore
.env
README.md
tests/
coverage/
.nyc_output

3. Optimize Edilmiş Multi-Stage Build

Multi-stage build, image boyutunu küçültmek ve güvenliği artırmak için kritik bir tekniktir.

# Dockerfile — Multi-stage build
# === Build aşaması ===
FROM node:20-alpine AS builder

WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .

# TypeScript derleme veya build adımı
RUN npm run build

# Test çalıştır (opsiyonel)
RUN npm run test

# Gereksiz devDependencies temizle
RUN npm prune --production

# === Production aşaması ===
FROM node:20-alpine AS production

# Güvenlik: non-root kullanıcı
RUN addgroup -g 1001 -S nodejs && \
    adduser -S nodeapp -u 1001

WORKDIR /app

# Sadece gerekli dosyaları kopyala
COPY --from=builder --chown=nodeapp:nodejs /app/dist ./dist
COPY --from=builder --chown=nodeapp:nodejs /app/node_modules ./node_modules
COPY --from=builder --chown=nodeapp:nodejs /app/package.json ./

# Non-root kullanıcıya geç
USER nodeapp

# Environment
ENV NODE_ENV=production
ENV PORT=3000

EXPOSE 3000

# Healthcheck
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
  CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1

CMD ["node", "dist/server.js"]
İpucu: Alpine tabanlı image'lar (node:20-alpine) standart image'lara göre 5-10 kat daha küçüktür. 900MB yerine yaklaşık 120MB boyutundadır.

4. Docker Compose ile Tam Ortam

# docker-compose.yml
version: '3.8'

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
      target: production
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=production
      - DATABASE_URL=postgresql://user:pass@db:5432/myapp
      - REDIS_URL=redis://redis:6379
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started
    restart: unless-stopped
    networks:
      - app-network

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: user
      POSTGRES_PASSWORD: pass
      POSTGRES_DB: myapp
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U user -d myapp"]
      interval: 10s
      timeout: 5s
      retries: 5
    networks:
      - app-network

  redis:
    image: redis:7-alpine
    volumes:
      - redis_data:/data
    networks:
      - app-network

  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
    depends_on:
      - app
    networks:
      - app-network

volumes:
  postgres_data:
  redis_data:

networks:
  app-network:
    driver: bridge

5. Docker Komutları

# Image oluştur
docker build -t my-node-app:1.0 .

# Container çalıştır
docker run -d --name my-app -p 3000:3000 \
  -e NODE_ENV=production \
  my-node-app:1.0

# Docker Compose ile başlat
docker compose up -d

# Logları görüntüle
docker compose logs -f app

# Container'a bağlan
docker exec -it my-app sh

# Durdur ve temizle
docker compose down
docker compose down -v  # Volume'ları da sil

6. Nginx Reverse Proxy

# nginx.conf
events {
  worker_connections 1024;
}

http {
  upstream node_app {
    server app:3000;
  }

  server {
    listen 80;
    server_name example.com;

    # Gzip sıkıştırma
    gzip on;
    gzip_types text/plain application/json application/javascript text/css;

    # Proxy ayarları
    location / {
      proxy_pass http://node_app;
      proxy_http_version 1.1;
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection 'upgrade';
      proxy_set_header Host $host;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header X-Forwarded-Proto $scheme;
      proxy_cache_bypass $http_upgrade;
    }

    # Statik dosyalar
    location /static/ {
      alias /app/public/;
      expires 30d;
      add_header Cache-Control "public, immutable";
    }
  }
}

7. Production Best Practices

# Graceful shutdown desteği
// server.js
const app = require('./app');

const server = app.listen(process.env.PORT || 3000, () => {
  console.log(`Server ${process.env.PORT || 3000} portunda çalışıyor`);
});

// Graceful shutdown
function shutdown(signal) {
  console.log(`${signal} sinyali alındı. Kapatılıyor...`);
  server.close(() => {
    console.log('HTTP sunucusu kapatıldı');
    // DB bağlantılarını kapat
    process.exit(0);
  });

  // 10 saniye içinde kapanmazsa zorla kapat
  setTimeout(() => {
    console.error('Zorla kapatılıyor');
    process.exit(1);
  }, 10000);
}

process.on('SIGTERM', () => shutdown('SIGTERM'));
process.on('SIGINT', () => shutdown('SIGINT'));
# Docker'da process yönetimi — tini kullanımı
FROM node:20-alpine

# tini — PID 1 sorunu çözümü
RUN apk add --no-cache tini
ENTRYPOINT ["/sbin/tini", "--"]

WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .

USER node
CMD ["node", "server.js"]
Uyarı: Docker container içinde npm start yerine doğrudan node server.js kullanın. npm, SIGTERM sinyalini Node.js sürecine iletmez ve graceful shutdown çalışmaz.

8. CI/CD Pipeline ile Deploy

# .github/workflows/deploy.yml
name: Deploy
on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Docker image oluştur
        run: docker build -t myapp:${{ github.sha }} .

      - name: Registry'ye push
        run: |
          echo ${{ secrets.DOCKER_PASSWORD }} | docker login -u ${{ secrets.DOCKER_USERNAME }} --password-stdin
          docker tag myapp:${{ github.sha }} myregistry/myapp:latest
          docker push myregistry/myapp:latest

Sonuç

Docker ile Node.js deployment, uygulamanızı her ortamda tutarlı şekilde çalıştırmanın en güvenilir yoludur. Multi-stage build ile image boyutunu küçültün, non-root kullanıcı ile güvenliği artırın ve Docker Compose ile tam geliştirme ortamı oluşturun. Graceful shutdown, healthcheck ve tini gibi pratiklerle production-ready container'lar hazırlayın. CI/CD pipeline'a entegre ederek otomatik deployment sürecini tamamlayın.

Kaynaklar

İlgili Yazılar