
Netlify hiçbir şeyi yanlış yapmadı. Ama gerçek Kubernetes kontrolünün nasıl hissettirdiğini bilmek istedim. 30 günlük OpenShift sandbox, daha hızlı bir site – ve neden kaldığımızın dürüst cevabı.
Neden OpenShift – ve neden geri döndük?
Netlify hiçbir şeyi yanlış yapmadı. Railway da yapmadı. Ama gerçek kontrolün nasıl hissettirdiğini bilmek istedim – sihirli deployment yok, soyutlama üstüne soyutlama yok. OpenShift Developer Sandbox ücretsiz, Kubernetes üzerinde çalışıyor ve ilk Next.js sayfasını yayına almak yarım günümü aldı. Bu makale, başlamadan önce sahip olmak istediğim şey.
Sonraki adım: Spring Boot microservisleri Railway'den OpenShift'e taşımak. Ama önce frontend çalışmalıydı.
nginx Tuzağı: OpenShift'te Root Yok
İlk deneme standart bir nginx container'ıydı. OpenShift onu hemen kapattı. Yardımcı bir hata mesajı yok, sadece CrashLoopBackOff. Neden: OpenShift varsayılan olarak root container'lara izin vermiyor. nginx port 80'de çalışıyor ve port 80 root gerektiriyor. Normal sunucularda sorun yok – OpenShift'te engelleyici.
OpenShift Security Context Constraint (SCC) Standard nginx (Port 80, root) ───────────────────────────── Container startet → OpenShift prüft SCC → User 0 (root) verboten → CrashLoopBackOff ✗ nginxinc/nginx-unprivileged (Port 8080, non-root) ────────────────────────────────────────────────── Container startet → OpenShift prüft SCC → Non-root User ✓ → Pod läuft ✓
⚠ İlk deploy'da CrashLoopBackOff
Problem: Standart nginx root olarak çalışır (port 80) – OpenShift varsayılan olarak root container'lara izin vermez.
Fix: nginxinc/nginx-unprivileged:alpine kullan. Port 8080'de çalışır, root gerekmez.
# Falsch – schlägt fehl auf OpenShift:
FROM nginx:alpine
# Richtig – rootless, Port 8080:
FROM nginxinc/nginx-unprivileged:alpineÖzel GitHub Repo Bağlamak
OpenShift özel bir GitHub reposuna doğrudan erişemiyor. Personal Access Token yeterli değil – secret'ın doğru URL ile ilişkilendirilmesi için anotasyon da gerekli.
# 1. Secret mit GitHub Personal Access Token anlegen
oc create secret generic github-secret \
--from-literal=username=<github-username> \
--from-literal=password=<github-PAT> \
--type=kubernetes.io/basic-auth
# 2. Secret annotieren – OpenShift nutzt es automatisch für die URL
oc annotate secret github-secret \
"build.openshift.io/source-secret-match-uri-1=https://github.com/<username>/*"
# 3. Builder-ServiceAccount bekommt Zugriff
oc secret link builder github-secret✓ Secret anotasyonu zorunlu
Anotasyon olmayan bir GitHub secret otomatik olarak çalışmaz. build.openshift.io/source-secret-match-uri-1 anotasyonu secret'ı doğru Git URL'sine bağlar – ancak o zaman clone başarılı olur.
Next.js için Dockerfile: Multi-Stage ve Standalone
Normal bir Dockerfile devasa bir image üretir – node_modules yüzlerce megabyte. Multi-stage build bunu çözer: build aşaması derler, runtime aşaması yalnızca sonucu kopyalar.

Anahtar, next.config.ts'deki output: "standalone" ayarı. Next.js o zaman yalnızca gerçekten import edilen bağımlılıkları .next/standalone'a paketler – final image'da node_modules gerekmez.
# next.config.ts – eine Zeile entscheidet alles:
output: "standalone"
# Das Ergebnis: .next/standalone enthält nur was wirklich gebraucht wird.
# Kein node_modules im finalen Image – spart ~800MB.✓ output: standalone ~800MB tasarruf sağlar
Standalone olmadan Dockerfile tüm node_modules dizinini final image'a kopyalar. Standalone ile yalnızca .next/standalone klasörü yeterli – Next.js gerekli bağımlılıkları zaten gömüyor.
NEXT_PUBLIC_* Tuzağı: Build Zamanı vs. Runtime
Bu, zaman açısından en pahalı hataydı. Next.js, build zamanında dondurulan değişkenler ile runtime'da değerlendirilen değişkenler arasında kesin bir ayrım yapar.
NEXT_PUBLIC_* – die Falle
Build-Zeit Runtime
────────────────────────── ──────────────────────────
NEXT_PUBLIC_SITE_URL BLOG_BASE_URL
NEXT_PUBLIC_BLOG_BASE AUTH_BASE_URL
NEXT_PUBLIC_CLOUDINARY_* CLOUDINARY_API_SECRET
NEXT_PUBLIC_GA_* CLOUDINARY_API_KEY
→ Werden in den JS-Bundle → Werden beim Start des
eingefroren (unveränderbar) Servers ausgelesen
Falsch gesetzt → leere Falsch gesetzt → localhost
Variablen im Frontend als Proxy-Ziel⚠ Frontend'de NEXT_PUBLIC_* boştu
Problem: Değişkenler runtime secret olarak ayarlandı – ama Next.js onları build zamanında inline eder. Bundle'da boş string'ler vardı.
Fix: NEXT_PUBLIC_*'ı BuildConfig'de buildArgs olarak tanımla. Sunucu tarafı secret'lar (API anahtarları) runtime secret olarak kalır.
# BuildConfig – NEXT_PUBLIC_* als buildArgs:
spec:
strategy:
type: Docker
dockerStrategy:
buildArgs:
- name: NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME
value: dein-cloud-name
- name: NEXT_PUBLIC_SITE_URL
value: https://zelkulon.com
# Runtime Vars – Server-seitige Secrets:
oc create secret generic zelkulon-secrets --from-env-file=.env.local
oc set env deployment/zelkulon-homepage --from=secret/zelkulon-secretsoc new-app Yerine BuildConfig
oc new-app --strategy=docker, OpenShift Git URL'ini doğrudan doğrulayamazsa sık sık başarısız olur. Daha temiz çözüm: BuildConfig'i manuel olarak YAML ile oluşturmak.
⚠ Build başlatmada InvalidOutputReference
Problem: oc new-app ImageStream oluşturmuyor – ImageStream olmadan build çıktısı kaydedilemiyor.
Fix: İlk build'den önce: oc create imagestream zelkulon-homepage
# ImageStream zuerst anlegen – sonst: InvalidOutputReference
oc create imagestream zelkulon-homepage
# BuildConfig per YAML
cat <<EOF | oc apply -f -
apiVersion: build.openshift.io/v1
kind: BuildConfig
metadata:
name: zelkulon-homepage
spec:
source:
type: Git
git:
uri: https://github.com/<username>/zelkulon-homepage.git
sourceSecret:
name: github-secret
strategy:
type: Docker
output:
to:
kind: ImageStreamTag
name: zelkulon-homepage:latest
EOF
# Build starten und live beobachten (dauert 5–7 Minuten)
oc start-build zelkulon-homepage --followBir build 5–7 dakika sürer – Next.js TypeCheck ve production build zaman alır. npm cache'i hızlandırır ama sandbox için yeterli.
Edge Route ile Otomatik HTTPS
Beni en çok şaşırtan şey: TLS manuel bir adım değil. OpenShift sertifika yönetimini tamamen üstleniyor – Edge Route oluştur, HTTPS hemen çalışıyor.
# App aus ImageStream deployen
oc new-app --image-stream=zelkulon-homepage:latest --name=zelkulon-homepage
# HTTPS Route mit Edge Termination – TLS übernimmt OpenShift automatisch
oc create route edge zelkulon-homepage \
--service=zelkulon-homepage \
--port=3000
# URL abrufen
oc get routes✓ OpenShift TLS'i tamamen yönetiyor
Edge Termination, TLS'in OpenShift sınırında sonlandırıldığı anlamına gelir. Sertifika satın alma, Let's Encrypt yapılandırma, manuel yenileme yok. Sadece oc create route edge ve bitti.
OpenShift vs. Netlify vs. Railway
| Özellik | OpenShift | Netlify | Railway |
|---|---|---|---|
| Altyapı kontrolü | ✅ Vollständig | ✅ Abstrakt | ✅ Abstrakt |
| Giriş engeli | ⚠ Komplex | ✅ Push & Deploy | ✅ Push & Deploy |
| HTTPS | ✅ Automatisch (Edge) | ✅ Automatisch | ✅ Automatisch |
| Root container | ❌ Kein root | ✅ Kein Problem | ✅ Kein Problem |
| Maliyet | 30 Tage Sandbox | Kostenlos (kommerziell) | $5 Starter |
| Performans | 🚀 Sehr schnell | ✅ Gut | ✅ Gut |
Tablo, 30 günlük sandbox deneyiminden sonra dürüst bir değerlendirmeyi gösteriyor. OpenShift küçük ekipler için Netlify'ın yerini almıyor.
Sonuç: Daha Hızlı Ama Herkes İçin Değil
OpenShift daha hızlı. Bu teori değil – 30 günlük sandbox'tan sonra site belirgin şekilde daha hızlı yükleniyor, görseller anında geliyor. Kubernetes zamanlaması, ayrılmış kaynaklar, soğuk başlangıç derdi yok.
Yine de Netlify ve Railway'de kalıyoruz. OpenShift daha kötü olduğu için değil, sandbox süresi dolduğu ve gerçek bir OpenShift kümesi küçük bir UG için çok pahalı olduğu için. Performans avantajı şu an maliyeti haklı kılmıyor.
- Root container çalışmaz – nginxinc/nginx-unprivileged zorunlu
- NEXT_PUBLIC_* buildArgs'a girer, runtime secret'a değil
- İlk build'den önce ImageStream oluştur
- YAML ile BuildConfig, oc new-app'dan daha güvenilir
- Performans ölçülebilir şekilde daha iyi – ama küçük UG'ler için fiyat uymuyor
Bu birinci fazı. Sonraki makalelerde OpenShift'in Spring Boot microservisleri nasıl yönettiği ele alınacak – service discovery, kalıcı veritabanlar, iç iletişim. Çabanın karşılığını verip vermeyeceği görülecek.