18 Kasım 2025 Salı

Docker Compose ile Traefik v3, Cloudflare ve Let's Encrypt ile Otomatik Reverse Proxy Kurulumu

Giriş

Kendi sunucunuzda birden fazla web uygulamasını alan adlarıyla yönetmek, güvenli SSL sertifikalarını otomatik yenilemek ve trafik yönlendirmelerini zahmetsizce yapmak istiyorsanız, Traefik v3 sizin için biçilmiş kaftan. Bu yazıda, Docker Compose kullanarak Traefik v3 ile Cloudflare DNS ve Let’s Encrypt entegrasyonunu adım adım kuracağız. Hedefimiz: tek bir reverse proxy arkasında otomatik HTTPS, kolay yönlendirme ve minimum bakım maliyeti.

Neden Traefik v3?

Traefik, container tabanlı ortamlarda dinamik servis keşfi, otomatik TLS ve güçlü yönlendirme kurallarıyla öne çıkıyor. v3 sürümü, performans ve güvenlik iyileştirmeleri ile birlikte yapılandırma tutarlılığı sunuyor. Docker üzerinde çalışan mikro servisleriniz için exposedByDefault=false gibi güvenli varsayılanlarla ince ayar yapmak da oldukça kolay.

Önkoşullar

- Docker ve Docker Compose kurulu bir Linux sunucu (ör. Ubuntu 22.04).

- 80 ve 443 portlarının dış dünyaya açık olması.

- Alan adınız Cloudflare üzerinde yönetiliyor olmalı.

- Cloudflare üzerinde DNS düzenleme yetkisine sahip bir API Token (Zone.DNS.Edit, Zone.Read).

- Let’s Encrypt için bir e-posta adresi.

Adım 1: Cloudflare API Token oluşturun

Cloudflare hesabınızda User Profile > API Tokens > Create Token yolunu izleyin. “Edit zone DNS” şablonunu seçip alan adınızı kısıtlayın. Token’ı güvenle saklayın; birazdan docker-compose içinde kullanacağız.

Adım 2: Proje dizinini hazırlayın

Sunucuda boş bir klasör oluşturup içine docker-compose.yml ve isteğe bağlı .env dosyası yerleştireceğiz. Aşağıdaki örnek, Traefik v3’ü, Cloudflare DNS-01 challenge ile Let’s Encrypt sertifika yenilemesi yapacak şekilde hazırlar.

version: "3.8"

services:
  traefik:
    image: traefik:v3.0
    container_name: traefik
    command:
      - --api.dashboard=true
      - --providers.docker=true
      - --providers.docker.exposedByDefault=false
      - --entrypoints.web.address=:80
      - --entrypoints.websecure.address=:443
      - [email protected]
      - --certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json
      - --certificatesresolvers.letsencrypt.acme.dnschallenge=true
      - --certificatesresolvers.letsencrypt.acme.dnschallenge.provider=cloudflare
    ports:
      - "80:80"
      - "443:443"
    environment:
      - CF_DNS_API_TOKEN=${CF_DNS_API_TOKEN}
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./letsencrypt:/letsencrypt
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.http-catchall.rule=HostRegexp(`{host:.+}`)"
      - "traefik.http.routers.http-catchall.entrypoints=web"
      - "traefik.http.routers.http-catchall.middlewares=redirect-to-https"
      - "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
      - "traefik.http.routers.traefik.rule=Host(`proxy.example.com`)"
      - "traefik.http.routers.traefik.entrypoints=websecure"
      - "traefik.http.routers.traefik.tls.certresolver=letsencrypt"
      - "traefik.http.routers.traefik.service=api@internal"
      - "traefik.http.middlewares.dashboard-auth.basicauth.users=${TRAEFIK_DASHBOARD_USER}:${TRAEFIK_DASHBOARD_HASH}"
      - "traefik.http.routers.traefik.middlewares=dashboard-auth"

  whoami:
    image: traefik/whoami:latest
    container_name: whoami
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.whoami.rule=Host(`demo.example.com`)"
      - "traefik.http.routers.whoami.entrypoints=websecure"
      - "traefik.http.routers.whoami.tls.certresolver=letsencrypt"

Yukarıdaki örnekte dashboard’ı proxy.example.com üzerinden Basic Auth ile koruyoruz. whoami servisi ise demo.example.com üzerinden otomatik TLS ile yayınlanacak. Cloudflare tarafında bu iki alt alan adını A/AAAA kaydıyla sunucunuza yönlendirin. Cloudflare “turuncu bulut” (proxy) açık olabilir; DNS-01 challenge, proxied modda da sorunsuz çalışır.

Adım 3: Ortam değişkenleri ve parola karması

.env dosyanıza aşağıdaki gibi değerleri ekleyin. Basic Auth için bir bcrypt karma gerekir. Linux’ta hızla üretmek için şu komutu kullanabilirsiniz: htpasswd -nbB admin 'Sifre123' çıktısındaki admin:$2y$... kısmını kopyalayın.

CF_DNS_API_TOKEN=cf_api_tokeniniz_buraya
TRAEFIK_DASHBOARD_USER=admin
TRAEFIK_DASHBOARD_HASH=$2y$05$XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Adım 4: Servisleri başlatın

Klasörde docker compose up -d komutunu çalıştırın. İlk kurulumda Let’s Encrypt DNS-01 challenge ile TXT kaydı oluşturup doğrulama yapar ve acme.json içine sertifikaları yazar. Ardından https://demo.example.com adresini açtığınızda whoami çıktısını, https://proxy.example.com adresini açtığınızda ise kimlik doğrulama sonrası Traefik dashboard’ını görmelisiniz.

İpuçları ve iyi uygulamalar

- letsencrypt klasörünün izinlerini 600 veya 700 seviyesinde tutun; acme.json hassas bilgiler içerir.

- production ortamında traefik konteynerinin yalnızca ihtiyaç duyduğu ağlara eriştiğinden emin olun; Docker network’leri ile segmentasyon yapın.

- Rate limitlere takılmamak için Let’s Encrypt’in staging ortamını testlerde kullanabilirsiniz: --certificatesresolvers.letsencrypt.acme.caServer=https://acme-staging-v02.api.letsencrypt.org/directory parametresini ekleyip denemeler tamamlanınca kaldırın.

- Cloudflare’de DNSSEC aktifse, zone yapılandırmanızın doğru olduğundan emin olun; yanlış DS kayıtları çözümleme sorunlarına neden olabilir.

Sık karşılaşılan hatalar ve çözümler

- Giriş başarısız: CF_DNS_API_TOKEN yetkileri eksik olabilir. Token’a Zone.DNS.Edit ve Zone.Read sağlandığından ve doğru zone’da sınırlandığından emin olun.

- Sertifika alınamıyor: Alan adınız doğru IP’ye işaret ediyor mu? DNS yayılımı tamamlandı mı? Ayrıca Cloudflare’de CNAME flattening ayarları bazen beklenmedik davranışlar gösterebilir; A kaydıyla test etmek daha sağlıklıdır.

- 404 veya yönlendirme döngüsü: http-catchall ve redirect-to-https etiketlerinin çakışmadığından emin olun. Aynı ana bilgisayar için hem http hem https router tanımlarını kontrol edin.

Sonuç

Bu kurulumla Docker tabanlı servisleriniz için modern, güvenli ve otomatik bir reverse proxy katmanı elde ettiniz. Traefik v3, Cloudflare DNS-01 ve Let’s Encrypt birleşimi; mikro servislerinizi hızlıca yayına almanızı, sertifikaları otomatik yenilemeyi ve tek noktadan görünürlük sağlamayı mümkün kılıyor. Bundan sonra yeni bir servis eklemek yalnızca birkaç label yazmak kadar basit. Üstelik, aynı mimariyi staging ve production çevrelerinde tekrar kullanarak sürdürülebilir bir dağıtım modeli kurabilirsiniz.

17 Kasım 2025 Pazartesi

Next.js ile Passkey (WebAuthn) Entegrasyonu: Adım Adım Parolasız Giriş Rehberi

Giriş

Parolasız giriş, kullanıcı deneyimini iyileştirirken güvenlik risklerini ciddi şekilde azaltan modern bir yaklaşım. Passkey teknolojisi, FIDO2/WebAuthn standartları üzerine kurulu olup, kimlik doğrulamayı biyometri (Face ID, Touch ID, Windows Hello) veya cihaz PIN’i gibi yerel yöntemlere devrediyor. Bu rehberde, Next.js tabanlı bir projeye Passkey (WebAuthn) eklemenin pratik bir yolunu adım adım anlatıyorum. Amacımız, hızlıca çalışan bir kayıt (registration) ve giriş (authentication) akışı kurmak.

Neden Passkey?

Parola sızıntıları, kimlik avı (phishing) ve zayıf şifreler artık klasik güvenlik açıkları. Passkey, özel anahtarın cihazda güvenli biçimde saklanması ve sitenizin alan adına (RP ID) bağlanması nedeniyle phishing’e karşı dayanıklıdır. Kullanıcılar şifre hatırlamak zorunda kalmaz; cihazlarının biyometrik sensörleri ile tek dokunuşta oturum açabilirler.

Ön Koşullar

- Next.js 13+ (App Router önerilir), Node.js 18+
- HTTPS ortamı (yerelde localhost istisnası)
- Modern bir tarayıcı (Chrome, Edge, Safari, Firefox; mobil platformlarda da destek artıyor)
- Temel bir veritabanı (PostgreSQL, MySQL veya Prisma ile soyutlama)

Gerekli Paketler

WebAuthn işlemlerini kolaylaştırmak için yaygın olarak kullanılan @simplewebauthn paketlerini kullanacağız. Terminalde aşağıdaki komutu çalıştırın:

npm i @simplewebauthn/server @simplewebauthn/browser zod

Mimariyi Anlamak

Kayıt akışında sunucu, kullanıcı için bir “challenge” üretir ve istemci bu challenge’ı cihazdaki güvenlik anahtarında imzalayıp geri gönderir. Sunucu, gelen yanıtı doğrular ve kimlik bilgilerini (credential) veritabanına yazar. Giriş akışı benzer şekilde çalışır ancak var olan credential ile imzalama yapılır. Tüm süreçte origin (https://alanadiniz.com) ve RP ID (alanadiniz.com) uyumu kritik önemdedir.

Adım 1: Kayıt (Registration) API’si

App Router kullandığınızı varsayalım. Kayıt başlatma için “/api/webauthn/register/options” ve doğrulama için “/api/webauthn/register/verify” uç noktaları oluşturalım.

// app/api/webauthn/register/options/route.ts
import { NextResponse } from 'next/server';
import { generateRegistrationOptions } from '@simplewebauthn/server';

export async function POST() {
  const rpName = 'Uygulama Adı';
  const rpID = process.env.RP_ID || 'localhost'; // üretimde alanadiniz.com
  const user = { id: 'user-123', name: '[email protected]', displayName: 'Ali' };

  const options = await generateRegistrationOptions({
    rpName,
    rpID,
    userID: user.id,
    userName: user.name,
    timeout: 60000,
    attestationType: 'none',
    authenticatorSelection: {
      residentKey: 'preferred',
      userVerification: 'preferred',
    },
  });

  // challenge'ı oturum/cookie/cache'te saklayın
  // ör: await saveChallenge(user.id, options.challenge)

  return NextResponse.json(options);
}

Kullanıcı, tarayıcıda bu seçeneklerle passkey kaydı başlatır. Ardından istemciden dönen yanıtı doğrulamak için verify uç noktası:

// app/api/webauthn/register/verify/route.ts
import { NextResponse } from 'next/server';
import {
  verifyRegistrationResponse,
} from '@simplewebauthn/server';

export async function POST(req: Request) {
  const body = await req.json();
  const rpID = process.env.RP_ID || 'localhost';
  const expectedOrigin = process.env.EXPECTED_ORIGIN || 'http://localhost:3000';

  // const expectedChallenge = await getChallenge(userId)

  const verification = await verifyRegistrationResponse({
    response: body,
    expectedChallenge: '...challenge...',
    expectedOrigin,
    expectedRPID: rpID,
  });

  const { verified, registrationInfo } = verification;

  if (verified && registrationInfo) {
    const {
      credentialPublicKey,
      credentialID,
      counter,
      credentialBackedUp,
      credentialDeviceType,
    } = registrationInfo;

    // Veritabanına kaydedin:
    // credentialID (base64url), publicKey (Buffer), counter, userId vb.

    return NextResponse.json({ ok: true });
  }

  return NextResponse.json({ ok: false }, { status: 400 });
}

Adım 2: İstemci (Kayıt)

İstemci tarafında @simplewebauthn/browser paketini kullanın. Aşağıda basit bir örnek var:

import {
  startRegistration,
} from '@simplewebauthn/browser';

async function registerPasskey() {
  const optionsRes = await fetch('/api/webauthn/register/options', { method: 'POST' });
  const options = await optionsRes.json();

  const attResp = await startRegistration(options);
  const verifyRes = await fetch('/api/webauthn/register/verify', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(attResp),
  });

  if (verifyRes.ok) {
    alert('Passkey kaydedildi!');
  }
}

Adım 3: Giriş (Authentication) API’si

Girişte de benzer iki uç nokta gerekir: “/api/webauthn/auth/options” ve “/api/webauthn/auth/verify”.

// app/api/webauthn/auth/options/route.ts
import { NextResponse } from 'next/server';
import { generateAuthenticationOptions } from '@simplewebauthn/server';

export async function POST() {
  const rpID = process.env.RP_ID || 'localhost';
  // kullanıcıyı e-posta ile tespit ettiğinizi varsayın ve onun credentialID'lerini çekin
  // const allowCredentials = [...] // veritabanından
  const options = await generateAuthenticationOptions({
    rpID,
    timeout: 60000,
    userVerification: 'preferred',
    // allowCredentials,
  });

  // challenge saklanır: await saveAuthChallenge(userId, options.challenge)
  return NextResponse.json(options);
}
// app/api/webauthn/auth/verify/route.ts
import { NextResponse } from 'next/server';
import { verifyAuthenticationResponse } from '@simplewebauthn/server';

export async function POST(req: Request) {
  const body = await req.json();
  const rpID = process.env.RP_ID || 'localhost';
  const expectedOrigin = process.env.EXPECTED_ORIGIN || 'http://localhost:3000';

  // const expectedChallenge = await getAuthChallenge(userId)
  // const authenticator = await getAuthenticator(credentialID)

  const verification = await verifyAuthenticationResponse({
    response: body,
    expectedChallenge: '...challenge...',
    expectedOrigin,
    expectedRPID: rpID,
    authenticator: {
      credentialPublicKey: Buffer.from('...'),
      credentialID: Buffer.from('...'),
      counter: 0,
      transports: ['internal', 'hybrid'],
    },
  });

  const { verified, authenticationInfo } = verification;

  if (verified && authenticationInfo) {
    const { newCounter } = authenticationInfo;
    // counter'ı güncelleyin ve oturum açın (JWT/Session)
    return NextResponse.json({ ok: true });
  }

  return NextResponse.json({ ok: false }, { status: 401 });
}

Adım 4: İstemci (Giriş)

İstemci tarafında startAuthentication ile challenge’ı imzalatıp doğrulamaya gönderin.

import {
  startAuthentication,
} from '@simplewebauthn/browser';

async function loginWithPasskey() {
  const optionsRes = await fetch('/api/webauthn/auth/options', { method: 'POST' });
  const options = await optionsRes.json();

  const asseResp = await startAuthentication(options);
  const verifyRes = await fetch('/api/webauthn/auth/verify', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(asseResp),
  });

  if (verifyRes.ok) {
    // yönlendirme veya token işlemleri
  }
}

Veritabanı ve Güvenlik Notları

- RP ID üretimde çıplak alan adınız olmalı (ör. alanadiniz.com). Subdomain kullanıyorsanız buna dikkat edin.
- expectedOrigin tam şema ile eşleşmeli (https://alanadiniz.com). HTTP yerine HTTPS zorunludur (localhost hariç).
- Veritabanında şu alanlar saklanır: userId, credentialID (base64url), credentialPublicKey (Buffer), counter, transports, deviceType/backup bilgisi.
- Aynı kullanıcı için birden fazla credential destekleyin; kullanıcı yeni cihaz ekleyebilir.
- 0-RTT veya platform senkronizasyonu (iCloud Anahtar Zinciri, Google Password Manager) sayesinde cihazlar arası passkey geçişi mümkün hale gelir.

Sorun Giderme

- NotAllowedError: Genellikle origin veya user gesture eksikliği. Butona tıklama gibi bir kullanıcı etkileşimiyle çağırın.
- SecurityError: RP ID ile origin uyuşmuyor. Ortam değişkenlerini (RP_ID, EXPECTED_ORIGIN) kontrol edin.
- Unknown or unsupported transport: Esnek olun; transports alanını istemci döndürdüğü şekilde saklayın.
- Gömülü tarayıcılar: Bazı uygulama içi web görünümleri WebAuthn’ı kısıtlayabilir; harici tarayıcı önerin.

Sonuç

Passkey ile parolasız giriş, hem kullanıcılar hem de geliştiriciler için büyük bir kazanım. Next.js ve @simplewebauthn ile birkaç uç nokta ve doğru yapılandırma sayesinde modern, phishing’e dayanıklı ve hızlı bir kimlik doğrulama deneyimi sunabilirsiniz. Üretime geçmeden önce HTTPS, alan adı uyumu ve veritabanı bütünlüğü konularını titizlikle test etmeyi unutmayın.

16 Kasım 2025 Pazar

Docker Buildx ile Çok Mimarili İmaj Oluşturma ve GitHub Actions ile Otomatik Yayınlama

Apple Silicon (ARM64) dizüstüler, bulut tarafında ise yaygın AMD64 sunucular derken, tek bir Docker imajıyla her yerde çalışmak artık bir zorunluluk. Bu yazıda, Docker Buildx kullanarak çok mimarili (multi-arch) imaj oluşturmayı ve GitHub Actions ile her push sonrasında otomatik olarak Docker Hub’a yayınlamayı adım adım anlatıyorum.

Amaç net: Tek bir tag altında linux/amd64 ve linux/arm64 manifest’leri içeren, hafif ve güvenilir bir imaj üretmek. Böylece ister ARM tabanlı bir Raspberry Pi, ister AMD64 bir Kubernetes node’u olsun, aynı etiketi çekip sorunsuz çalıştırabileceksiniz.

Gereksinimler ve Temel Kavramlar

Buildx, Docker’ın gelişmiş build sürücüsüdür. Çok mimarili build, cache yönetimi, manifest listeleri ve daha fazlasını destekler. QEMU ise farklı mimariler için kullanıcı modunda emülasyon sağlayarak tek bir makinede çoklu platform derleme yapmaya imkan verir. Yayınladığınız imajlar tek bir tag altında toplanır; client çektiğinde mimarisine uygun katmanı indirir.

Yerelde Buildx Kurulumu ve Hızlı Test

Önce Buildx’in etkin olduğundan emin olun. Modern Docker Desktop sürümlerinde varsayılan olarak geliyor. CLI ile yeni bir builder yaratıp kullanabilirsiniz:

docker buildx create --name multiarch --use
docker buildx inspect --bootstrap

Basit bir test için iki platforma birden build alıp registry’ye push edelim. Docker Hub’a giriş yapmayı unutmayın:

docker login
docker buildx build \
  --platform linux/amd64,linux/arm64 \
  -t KULLANICI_ADI/uygulama:deneme \
  --push .

Bu komut, yerel makinenizde emülasyon üzerinden derleyerek manifest listesi içeren bir imaj yayınlar. İndirme sırasında doğru mimari otomatik seçilir.

Dockerfile İçin Pratik İpuçları

Çok mimarili build’lerde Dockerfile’ınızı platform farkındalığı ile yazmak önemlidir. Multi-stage kullanın ve base imajları mümkün olduğunca alpine veya distroless tercih edin. Ayrıca Buildx, bazı değişkenleri otomatik sağlar: TARGETOS, TARGETARCH, TARGETPLATFORM.

Örneğin Go tabanlı bir uygulama için minimal bir Dockerfile şöyle olabilir:

# syntax=docker/dockerfile:1.7
FROM --platform=$BUILDPLATFORM golang:1.22-alpine AS builder
ARG TARGETOS TARGETARCH
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=$TARGETARCH go build -o /bin/app ./cmd/server

FROM gcr.io/distroless/static:nonroot
COPY --from=builder /bin/app /app
USER nonroot:nonroot
ENTRYPOINT ["/app"]

Burada GOOS/GOARCH değerlerini Buildx sağlıyor. Böylece tek seferde ARM64 ve AMD64 çıktıları üretiliyor. Node.js benzeri yorumlanan dillerde genellikle ekstra işlem gerekmese de, mimariye bağlı paketler (ör. native addon’lar) varsa platforma özel kurulum adımları eklemelisiniz.

GitHub Actions ile Otomatik Yayınlama

Her etiket veya ana dala push sonrası otomatik olarak çok mimarili imaj yayınlamak için aşağıdaki workflow’u kullanabilirsiniz. Repository > Settings > Secrets bölümünde DOCKERHUB_USERNAME ve DOCKERHUB_TOKEN sırlarını oluşturmayı unutmayın.

name: docker-multiarch
on:
  push:
    branches: [ "main" ]
    tags: [ "v*" ]

jobs:
  build-and-push:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Set up QEMU
        uses: docker/setup-qemu-action@v3

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Login to Docker Hub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}

      - name: Extract tags and labels
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ secrets.DOCKERHUB_USERNAME }}/uygulama
          tags: |
            type=ref,event=branch
            type=semver,pattern={{version}}
            type=sha

      - name: Build and push
        uses: docker/build-push-action@v6
        with:
          context: .
          platforms: linux/amd64,linux/arm64
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=gha
          cache-to: type=gha,mode=max
          provenance: true

Bu iş akışı; QEMU ve Buildx’i hazırlar, Docker Hub’a giriş yapar, branch/semver/commit’e göre etiketler üretir, iki platform için build alır ve imajı push eder. cache-to/cache-from ile katmanlar GitHub’ın cache’inde saklandığı için tekrar derlemeler belirgin şekilde hızlanır.

Yayın Sonrası Doğrulama

Manifest’i kontrol etmek için şu komutu çalıştırın:

docker buildx imagetools inspect KULLANICI_ADI/uygulama:latest

Çıktıda hem linux/amd64 hem de linux/arm64 listeleniyorsa her şey yolunda demektir. Ayrıca bir ARM makinede docker run ile test etmek, gerçek koşulları görmek açısından faydalıdır.

İpuçları ve Sık Görülen Hatalar

- Native bağımlılıklar: OpenSSL, libc farkları gibi platforma duyarlı paketler kullanıyorsanız, base imajlarınızı ve paket yöneticinizi tutarlı seçin. Alpine ile glibc tabanlı dağıtımların farklarını göz önünde bulundurun.

- Boyut optimizasyonu: Çok aşamalı build, .dockerignore ve distroless tabanlarını kullanın. Gerekirse --target ile üretim aşamasını ayrılayın.

- Güvenlik ve tedarik zinciri: provenance: true ile SBOM/attestation üretimini açtınız; ayrıca imajları imzalamak için cosign gibi araçları değerlendirin.

- Versiyonlama: latest etiketine ek olarak semver ile tag’lemek, geriye dönüşleri kolaylaştırır. metadata-action bu süreci oldukça pratik hale getiriyor.

Sonuç olarak, Docker Buildx ve GitHub Actions birlikte kullanıldığında hem geliştirici deneyimini iyileştiriyor hem de kullanıcılarınızın farklı donanımlarda aynı imajı sorunsuzca çalıştırmasını sağlıyor. Kurulumu bir kez yaptıktan sonra, çok mimarili yayın şirketinizin CI/CD zincirinin doğal bir parçası haline gelir.

14 Kasım 2025 Cuma

WebGPU ile Tarayıcıda Yapay Zeka: Transformers.js Kullanarak Yerel Metin Sınıflandırıcı Kurulumu

Özet

Tarayıcıda çalışan yapay zeka uygulamaları, veriyi cihazdan çıkarmadan işlemek ve sunucu maliyetlerini azaltmak için güçlü bir çözüm haline geldi. WebGPU sayesinde artık yalnızca WebAssembly (WASM) ile sınırlı değiliz; modern ekran kartlarının gücünü doğrudan JavaScript ile kullanabiliyoruz. Bu rehberde, Transformers.js kütüphanesi ile tek sayfalık bir uygulama geliştirerek WebGPU üzerinde yerel metin sınıflandırma (pozitif/negatif duygu analizi) kuracağız. Kod, hiçbir sunucu tarafı model barındırma olmadan çalışacak ve tarayıcı, modeli indirip IndexedDB üzerinde önbellekleyecek.

Neden WebGPU?

WebGPU, grafik ve hesaplama iş yüklerinde tarayıcıya modern bir düşük seviye API sunar. WASM ile CPU üzerinde çalışan modeller, bazen yeterli performansı yakalayamazken, WebGPU destekli ONNX Runtime Web ve Transformers.js kombinasyonu, önemli ölçüde hız kazandırır. Sonuç: daha düşük gecikme, daha akıcı kullanıcı deneyimi ve ölçeklenmesi kolay bir mimari.

Önkoşullar

- Güncel bir Chromium tabanlı tarayıcı (Chrome/Edge 113+). Çoğu sistemde WebGPU varsayılan olarak açıktır. Eğer kapalıysa chrome://flags üzerinden “Enable WebGPU” etkinleştirilebilir.

- Modern bir GPU ve güncel sürücüler önerilir. GPU yoksa, uygulama otomatik olarak WASM’e düşer (daha yavaş olabilir).

- Basit bir yerel sunucu. Örnek olarak python -m http.server 8000 veya npx serve . kullanabilirsiniz.

Proje Yapısı ve Temel Sayfa

Bir klasör oluşturun: webgpu-ai-demo/. İçine index.html adında bir dosya ekleyin ve aşağıdaki iskeleti yerleştirin. COOP/COEP başlıkları, çok iş parçacıklı WASM için önerilir; yerel geliştirmede meta etiketleriyle eşdeğeri sağlanabilir. Üretimde ise HTTP yanıt başlıklarını sunucuda ayarlayın.

Aşağıdaki kodu tek bir HTML dosyasında kullanabilirsiniz:

<!doctype html>
<html lang="tr">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>WebGPU ile Metin Sınıflandırma</title>
  <meta http-equiv="Cross-Origin-Opener-Policy" content="same-origin">
  <meta http-equiv="Cross-Origin-Embedder-Policy" content="require-corp">
  <style>body{font-family:system-ui, Arial, sans-serif;max-width:800px;margin:40px auto;padding:0 16px}</style>
</head>
<body>
  <h1>WebGPU ile Tarayıcıda Duygu Analizi</h1>
  <textarea id="input" rows="6" style="width:100%" placeholder="Değerlendirilecek metni yazın..."></textarea>
  <br><br>
  <button id="analyze">Analiz Et</button>
  <pre id="log" style="background:#f6f8fa;padding:12px;white-space:pre-wrap">Hazırlanıyor...</pre>
  <script type="module">
    import { pipeline, env } from 'https://cdn.jsdelivr.net/npm/@xenova/transformers/dist/transformers.min.js';
    // WebGPU varsa kullan, yoksa WASM'e düş.
    const preferGPU = 'gpu' in navigator;  // navigator.gpu modern tarayıcılarda mevcuttur.
    const device = preferGPU ? 'webgpu' : 'wasm';
    // WASM fallback için iş parçacığı sayısı (COOP/COEP ile etkin çoklu iş parçacığı).
    env.backends.onnx.wasm.numThreads = Math.max(1, Math.min(4, (navigator.hardwareConcurrency||4)-1));
    const logEl = document.getElementById('log');
    const btn = document.getElementById('analyze');
    const input = document.getElementById('input');

    log('Model indiriliyor ve hazırlanıyor...');
    const classifier = await pipeline('text-classification', 'Xenova/distilbert-base-uncased-finetuned-sst-2-english', {
      device, quantized: true,
      progress_callback: x => log('İndiriliyor: ' + Math.round((x.progress||0)*100) + '%')
    });
    log('Hazır. Bir metin yazıp "Analiz Et"e basın.');

    btn.addEventListener('click', async () => {
      btn.disabled = true;
      const text = input.value.trim();
      if (!text) { log('Lütfen bir metin girin.'); btn.disabled = false; return; }
      log('Analiz ediliyor...');
      const output = await classifier(text, { topk: 2 });
      log(JSON.stringify(output, null, 2));
      btn.disabled = false;
    });

    function log(msg){ logEl.textContent = msg; }
  </script>
</body>
</html>

Çalıştırma

Proje klasörüne geçip basit bir sunucu açın. Örneğin Python kullanıyorsanız: python -m http.server 8000. Ardından tarayıcıdan http://localhost:8000 adresine gidin. İlk çalıştırmada model ağırlıkları indirileceği için bir miktar bekleme olabilir. Bu dosyalar IndexedDB üzerinde önbelleklenecek; bir sonraki açılışta çok daha hızlı başlayacaktır.

Performans İpuçları

- WebGPU: Kodda device: 'webgpu' geçiliyorsa GPU kullanılacaktır. Tarayıcınız ve ekran kartınız destekliyorsa belirgin hız kazanırsınız.

- Kuantizasyon: quantized: true seçeneğiyle 8-bit ağırlıklar kullanılır; indirme boyutu ve bellek tüketimi düşer, hız genellikle artar.

- Isınma (warm-up): Uygulama açıldığında boş bir metinle hızlı bir “ısınma” çağrısı yapmak, ilk gerçek isteğin gecikmesini azaltır.

- Çok iş parçacığı: WASM’e düşmeniz halinde COOP/COEP etkinse env.backends.onnx.wasm.numThreads ile çekirdek sayınıza göre değer belirleyin.

- Küçük modeller: Tarayıcı için küçük/orta boy modeller tercih edin. Daha büyük LLM’ler (ör. 7B) tarayıcıda pratik olmayabilir; sunucuya veya yerel native uygulamalara yönelin.

Hata Giderme

- “navigator.gpu tanımsız” uyarısı alıyorsanız tarayıcınızı güncelleyin veya WebGPU’yu flags üzerinden etkinleştirin. Bazı kurumsal politikalar WebGPU’yu devre dışı bırakabilir.

- “CORS/COEP/COOP” ile ilgili hatalarda sayfayı mutlaka bir HTTP sunucusundan servis edin. Üretimde, meta etiket yerine gerçek yanıt başlıklarını (COOP/COEP) sunucuda ayarlayın.

- Model indirme yavaşsa farklı bir ağa geçmeyi deneyin. İlk indirme bir kez yapılır; sonraki açılışlarda IndexedDB önbelleği devreye girer.

- Sonuçlar beklediğiniz gibi değilse Türkçe metinler için uygun bir modele geçebilirsiniz; Transformers.js ile Türkçe duygu analizi veya çok dilli modelleri deneyin.

Kısa Değerlendirme

Bu yaklaşım, uçtan uca gizlilik (metin cihazdan çıkmaz), anında ölçeklenebilirlik (sunucu GPU’su gerekmez) ve düşük gecikme gibi avantajlar sunuyor. Chrome ve Edge üzerinde WebGPU ile duyduğum hız artışı belirgin oldu; Safari ve Firefox tarafında ise deneysel destekler hızla olgunlaşıyor. Projenizde basit bir duygu analiziyle başlayıp, soru-cevap ya da özetleme gibi diğer görevler için hafif modellerle devam edebilirsiniz.

Sonuç

WebGPU ve Transformers.js, tarayıcıda çalışan modern yapay zeka uygulamalarını herkes için erişilebilir kılıyor. Bu yazıdaki örneği temel alarak kendi metin moderasyonu, kullanıcı geri bildirimi analizi veya içerik sınıflandırma araçlarınızı birkaç yüz satırlık kodla hayata geçirebilirsiniz. Performansı artırmak için kuantizasyon, uygun model seçimi ve önbellekleme stratejilerinden yararlanmayı unutmayın.

13 Kasım 2025 Perşembe

GitHub Actions ile AWS’e Şifresiz Dağıtım (OIDC) Nasıl Kurulur? Adım Adım Rehber

Modern CI/CD süreçlerinde uzun ömürlü AWS erişim anahtarlarını projelerde saklamak hem riskli hem de yönetimi zahmetli. OpenID Connect (OIDC) sayesinde GitHub Actions, AWS’e şifresiz ve kısa ömürlü kimlik doğrulama ile bağlanabiliyor. Bu yazıda, GitHub Actions’ı kullanarak AWS’e OIDC tabanlı, güvenli ve pratik bir dağıtım hattını nasıl kuracağınızı adım adım anlatıyorum.

Özet: AWS tarafında GitHub’ı güvenilen kimlik sağlayıcısı olarak tanımlayacağız, belirli bir depo/branch için kısıtlı yetkili bir IAM rolü oluşturacağız ve GitHub Actions üzerinde bu rolü geçici olarak üstlenerek dağıtım yapacağız. Anahtar saklamaya gerek yok.

OIDC ile neden şifresiz dağıtım?

Klasik yaklaşımda, GitHub Secrets içine AWS_ACCESS_KEY_ID ve AWS_SECRET_ACCESS_KEY koyarız. Bu yöntem, anahtar sızıntısı, rotasyon zorluğu ve fazladan yetkiler gibi sorunlar yaratır. OIDC ile GitHub, çalıştırdığı iş akışı (workflow) için imzalı bir kimlik belirteci üretir; AWS bu belirteci doğrular ve yalnızca o an, o koşullarda geçerli kısa ömürlü kimlik bilgileri verir. Sonuç: Daha az gizli bilgi, daha sıkı yetkilendirme ve otomatik süre sonu.

Önkoşullar

- Bir AWS hesabı ve IAM üzerinde rol oluşturma yetkisi
- GitHub’da bir depo (public ya da private)
- Dağıtım hedefi: Örneğin S3 statik site, ECR + ECS/Fargate, ya da CloudFormation/SAM ile altyapı

AWS tarafı: IAM rolü ve güven ilişkisi (trust policy)

1) AWS IAM’de Identity providers bölümünden OpenID Connect sağlayıcısı olarak token.actions.githubusercontent.com ekleyin. Audience olarak sts.amazonaws.com kullanın.
2) Yeni bir IAM rolü oluşturun ve “Web identity” seçeneğiyle az önceki sağlayıcıyı seçin.
3) Aşağıdaki gibi bir trust policy tanımlayın. Bu örnek sadece belirli bir repo ve branch için yetki veriyor:

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": { "Federated": "arn:aws:iam::HESAP_IDNIZ:oidc-provider/token.actions.githubusercontent.com" },
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
},
"StringLike": {
"token.actions.githubusercontent.com:sub": "repo:ORGANIZASYON/DEPO_ADI:ref:refs/heads/main"
}
}
}
]
}

4) Role bağlayacağınız izinleri minimum ilke (least privilege) ile ayarlayın. Örneğin S3’e sadece belirli bir bucket’a yazma yetkisi veya ECR push izinleri. Örnek bir S3 dağıtım politikası (özet):

{
"Version": "2012-10-17",
"Statement": [
{ "Effect": "Allow", "Action": ["s3:PutObject","s3:DeleteObject"], "Resource": ["arn:aws:s3:::hedef-bucket/*"] },
{ "Effect": "Allow", "Action": ["s3:ListBucket"], "Resource": ["arn:aws:s3:::hedef-bucket"] }
]
}

GitHub tarafı: Workflow ile rolü üstlenmek

GitHub Actions, OIDC token’ı otomatik üretir. AWS ile konuşmak için aws-actions/configure-aws-credentials eylemini (action) kullanacağız ve IAM rolümüzü geçici olarak üstleneceğiz. Basit bir S3 dağıtımı örneği:

name: Deploy to S3 (OIDC)
on:
push:
branches: [ "main" ]
permissions:
id-token: write # OIDC token üretimi için şart
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Kodu çek
uses: actions/checkout@v4

- name: AWS kimlik bilgilerini yapılandır
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::HESAP_IDNIZ:role/GITHUB_OIDC_DEPLOY_ROLE
aws-region: eu-central-1

- name: Statik dosyaları derle
run: |
npm ci
npm run build

- name: S3'e senkronize et
run: |
aws s3 sync ./build s3://hedef-bucket --delete

Dikkat edilmesi gereken en kritik kısım permissions altında id-token: write yetkisinin verilmesi. Bu izin olmadan GitHub, OIDC belirteci oluşturmaz ve AWS rolünü üstlenemezsiniz.

Güvenlik ve en iyi uygulamalar

- Least privilege: Role bağlanan politikalar sadece gereken servis ve kaynaklara izin versin. “*” yerine spesifik ARN kullanın.
- Branch kısıtları: Trust policy içinde sub koşulunu belirli branch’lerle sınırlandırın (ör. refs/heads/main ve refs/tags/v*).
- Ortam ayrımı: Prod/staging için farklı rol ve politikalar tanımlayın; workflow’larda çevresel değişkenlerle hedefleri ayırın.
- Geçici kimlik: Varsayılan token süreleri kısadır. Bu, çalınsa bile etkisini sınırlar. Gereksiz session duration artırımı yapmayın.

Sık karşılaşılan hatalar ve çözümü

- AccessDenied: Not authorized to perform sts:AssumeRoleWithWebIdentity: Trust policy’de OIDC provider ARN’i, audience ve subject desenini kontrol edin.
- Invalid identity token: GitHub OIDC domain’i ve thumbprint otomasyonlarını doğru oluşturduğunuzdan emin olun; provider’ı tekrar eklemek çoğu kez sorunu çözer.
- Missing id-token permission: Workflow permissions kısmında id-token: write yoksa ekleyin.
- Bucket veya ECR erişim hataları: IAM rolüne eklediğiniz izin politikasının kaynak ARN’lerini ve region bilgisini doğrulayın.

Gelişmiş kullanım: ECR + ECS/Fargate dağıtımı

S3 yerine konteyner dağıtıyorsanız, aynı rol ile ecr:BatchCheckLayerAvailability, ecr:PutImage, ecr:GetAuthorizationToken gibi izinleri verip Docker imajını ECR’a push edebilir, ardından aws ecs update-service ile hizmeti yeni imaja yönlendirebilirsiniz. Tüm akış yine şifresiz ve OIDC tabanlı kalır.

Sonuç olarak, GitHub Actions + OIDC yaklaşımı, güvenliği artırırken bakım yükünü azaltır. API anahtarlarıyla uğraşmadan, denetlenebilir ve politikalarla sıkı şekilde sınırlandırılmış bir dağıtım boru hattı elde edersiniz. Yeni projelerde varsayılan tercih olarak OIDC’yi değerlendirmenizi öneririm.

12 Kasım 2025 Çarşamba

Tarayıcıda WebGPU ile Makine Öğrenmesi: ONNX Runtime Web ve TensorFlow.js Rehberi

WebGPU, tarayıcıda donanım hızlandırmalı hesaplamayı modern grafik API’lerine (Metal, Direct3D 12, Vulkan) yakın bir modelle sunarak WebGL’in ötesine geçen yeni standart. Bu sayede makine öğrenmesi (ML) çıkarımını doğrudan kullanıcının cihazında, ek sunucu maliyeti olmadan ve gizliliği koruyarak çalıştırmak mümkün oluyor. Bu yazıda, WebGPU ile tarayıcıda ML modelini çalıştırmak için iki pratik yaklaşımı adım adım göstereceğim: ONNX Runtime Web ve TensorFlow.js. İkisini de basit örneklerle kurup, performans ipuçları ve sık karşılaşılan sorunlarla birlikte ele alacağız.

WebGPU hazır mı? Uyum ve kontrol

Güncel Chrome/Edge sürümlerinde WebGPU varsayılan olarak etkin. Safari’de kademeli destek ilerliyor; Firefox cephesinde Nightly kanalıyla deneysel destek mevcut. Hızlı bir kontrol için konsolda if (navigator.gpu) { console.log("WebGPU hazir"); } çalıştırabilirsiniz. Üretimde en sağlıklı deneyimi almak için HTTPS üzerinde barındırma yapın; bazı gelişmiş optimizasyonlar ve çoklu iş parçacığı (WASM tarafı) için COOP/COEP başlıklarını ayarlamak da gerekebilir.

ONNX Runtime Web ile WebGPU: Dönüştür, yükle, çalıştır

ONNX formatı; PyTorch, TensorFlow, scikit-learn ve daha fazlasından dönüştürülebilen evrensel bir model temsili sunar. Tarayıcıda ONNX modellerini çalıştırmak için onnxruntime-web paketinin WebGPU varyantını kullanacağız.

Kurulum (npm):
npm i onnxruntime-web

Basit örnek (WebGPU yüklü sürümle import):
import * as ort from 'onnxruntime-web/webgpu';

// 1) Oturumu aç
const session = await ort.InferenceSession.create('/models/model.onnx');

// 2) Girdi hazırlama (ör. 1x3x224x224 float32 tensör)
const data = new Float32Array(1 * 3 * 224 * 224);
// ... preprocess ile veriyi doldurun
const feeds = {
  'input': new ort.Tensor('float32', data, [1, 3, 224, 224])
};

// 3) Çıkarım
const results = await session.run(feeds);
const output = results['output'] || Object.values(results)[0];
console.log('Çıkış boyutu:', output.dims);

Önemli nokta: webgpu varyantını import ettiğinizde yürütücü bu backend’i tercih eder. WebGPU destekli değilse otomatik olarak WASM’e düşebilir; ancak tutarlı performans için destekli tarayıcı hedefleyin. Büyük modelleri CDN üzerinden getirirken HTTP/2 veya HTTP/3 tercih edin; tek büyük dosya yerine parçalara bölünmüş ağırlıklar ilk etkileşimi hızlandırabilir.

TensorFlow.js + WebGPU: Hızlı başlangıç

TensorFlow ekosisteminde çalışıyorsanız veya TFJS’nin zengin yüksek seviyeli API’lerinden faydalanmak istiyorsanız WebGPU backend’i iyi bir seçenek. Aşağıdaki adımlarla bir GraphModel’i WebGPU üzerinde çalıştırabiliriz.

Kurulum (npm):
npm i @tensorflow/tfjs @tensorflow/tfjs-backend-webgpu

Örnek kullanım:
import * as tf from '@tensorflow/tfjs';
import '@tensorflow/tfjs-backend-webgpu';

await tf.setBackend('webgpu');
await tf.ready();

// Modeli yükle (GraphModel tercih)
const model = await tf.loadGraphModel('/models/model.json');

// Girdi oluştur (NHWC: [1, 224, 224, 3])
const input = tf.randomNormal([1, 224, 224, 3]);
const warmup = model.predict(input);
await warmup.data(); // sıcak başlatma
warmup.dispose();

const output = model.predict(input);
const result = await output.data();
console.log('İlk 5 skor:', Array.from(result).slice(0, 5));
input.dispose();
output.dispose();

TFJS tarafında model.predict zincirindeki tensörleri zamanında dispose() etmek ve tahmin öncesi bir warmup çağrısı yapmak kare sürelerini belirgin biçimde iyileştirir. Özellikle görüntü tabanlı modellerde tf.image.resizeBilinear gibi GPU dostu operasyonları kullanmak CPU-GPU senkronizasyonunu azaltır.

Performans ipuçları: FP16, quantization ve bellek

- FP16 desteği: Cihazınız destekliyse (çoğu modern GPU’da var), FP16 ile bant genişliği ve bellek kullanımını düşürürsünüz. ONNX tarafında dönüştürme sırasında, TFJS tarafında ise uygun model varyantını tercih edin.
- INT8 quantization: Görsel sınıflandırma ve bazı NLP modellerinde int8 kuantizasyon ciddi hız ve boyut kazancı sağlar. Kalibrasyon verisiyle kuantize edilmiş ONNX modelleri WebGPU’da iyi sonuç verir.
- Graf optimizasyonu: ONNX için optimize edilmiş grafik ve sabit katman birleştirme (fusion) seçenekleri; TFJS için GraphModel tercih edin ve gereksiz düğümleri taşımayın.
- Veri aktarımını minimize edin: GPU’dan CPU’ya data() çağrılarını sadece gerekli olduğunda yapın. Akış içinde tensörleri GPU’da tutmak darboğazları azaltır.
- Pipeline hazırlığı: Tek seferlik session ve model nesnelerini uygulama ömrü boyunca yeniden kullanın; her tahminde yeniden oluşturmayın.

Hata ayıklama ve yaygın sorunlar

- navigator.gpu undefined: Tarayıcı desteklemiyor ya da ortam güvenli değil. Güncel sürüm kullanın ve sayfayı HTTPS üzerinde servis edin.
- CORS/COEP/COOP: Büyük modelleri farklı bir origin’den çekiyorsanız CORS başlıkları ve gerekiyorsa COOP/COEP ayarları doğru olmalı. Aksi halde iş parçacığı/optimizasyon kısıtları veya yükleme hataları görebilirsiniz.
- Çıktı isimleri: ONNX modellerinde çıktı adları farklı olabilir. Object.keys(results) ile kontrol edip doğru düğümü okuyun.
- Bellek sızıntısı: TFJS ve ORT’ta tek kullanımlık tensörleri mutlaka dispose() edin. Uzun oturumlarda aksi halde GPU belleği dolar.
- Mobil cihazlar: Termal kısıtlar nedeniyle uzun süreli çıkarımda hız düşebilir. Toplu işler için aralıklı işlem veya daha küçük/kuantize model seçin.

Hangi yolu seçmeli?

- ONNX Runtime Web: Farklı çerçevelerden gelen modelleri tek formatta toplamak, üretim odaklı sabitlenmiş bir çalışma zamanı kullanmak ve WebGPU/WASM arasında esnek geçiş yapmak istediğiniz projeler için ideal.
- TensorFlow.js: TF ekosistemine aşinalığınız varsa, tarayıcıya özel yüksek seviyeli API’lerle hızlı prototipleme ve zengin yardımcı fonksiyonlar arıyorsanız tercih edin.

Sonuç olarak WebGPU, tarayıcı tarafı makine öğrenmesinde yeni bir sayfa açıyor. Doğru model boyutu, kuantizasyon stratejisi ve bellek yönetimiyle, gerçek zamanlı veya etkileşimli deneyimleri tamamen istemci tarafında, gizliliğe saygılı ve düşük gecikmeli şekilde sunmak artık mümkün. Hem ONNX Runtime Web hem de TensorFlow.js, geliştirici deneyimini olgunlaştıracak seviyede; projenizin kökenine ve ekosistem tercihlerinize göre seçim yapıp hızla üretime geçebilirsiniz.

11 Kasım 2025 Salı

Docker Buildx ile Çok Mimarili (Multi-Arch) Konteyner İmajı Oluşturma Rehberi (2025)

Giriş

Farklı mimarilerde (ARM64, AMD64 gibi) çalışan sunucuların ve geliştirici makinelerinin arttığı bir dönemde, tek bir Docker imajını her yerde sorunsuz çalıştırmak kritik hale geldi. Apple Silicon (M1/M2/M3) kullanan geliştiriciler, AMD64 tabanlı üretim sunucularına dağıtım yaparken uyumluluk sorunları yaşayabiliyor. Bu rehberde, Docker Buildx ve BuildKit kullanarak çok mimarili (multi-arch) Docker imajı oluşturmayı, imajı bir container kayıt deposuna itip SBOM/provenance gibi modern güvenlik özelliklerini eklemeyi adım adım anlatıyorum.

Neden Çok Mimarili İmaj?

Çok mimarili imajlar, tek bir etiket altında birden fazla işlemci mimarisini içeren bir manifest listesi barındırır. Böylece docker pull çalıştığında istemcinin mimarisine uygun katmanlar otomatik olarak indirilir. Sonuç: tek etiket, tek dağıtım akışı ve daha az sürpriz. Ayrıca CI/CD hatlarınız basitleşir ve hem ARM64 hem de AMD64 için ayrı imaj yönetme yükünüz azalır.

Gereksinimler ve Kurulum

- Docker 24+ ve Buildx etkin olmalı (Docker Desktop ile varsayılan gelir). Linux sunucularda buildx plugin’inin kurulu olduğundan emin olun.

- Farklı mimariler için yerel derleme yapmıyorsanız, emülasyon için QEMU/binfmt gerekir. Docker Desktop bunu sağlar; çıplak Linux’ta bir defaya mahsus şu komutla etkinleştirebilirsiniz: docker run --privileged --rm tonistiigi/binfmt --install all

- Kayıt deposu erişimi (Docker Hub, GHCR, GitLab Registry vb.) ve push yetkisi.

Adım 1: Buildx Builder Oluşturma

Yeni bir builder örneği, BuildKit özelliklerini (cache, çoklu platform, SBOM) etkin kullanmanızı sağlar. Aşağıdaki komut genel bir başlangıçtır: docker buildx create --name multi --use --bootstrap. Bu, “multi” adında bir builder oluşturur ve aktif hale getirir.

Adım 2: Dockerfile’ı Multi-Arch Uyumlu Yazma

Temel imaj olarak multi-arch sağlayan resmi imajları seçin (örneğin node:20-alpine, python:3.12-slim, golang:1.22-alpine). Derleme sırasında mimari fark yaratabilecek bağımlılıklar (ör. native modüller, OS paketleri) için hedef platforma duyarlı bayrakları düşünün. Multi-stage derleme (builder + runtime) hem boyutu küçültür hem de taşınabilirliği artırır.

Adım 3: Çok Mimarili İmajı İnşa Etme ve Gönderme

Örnek bir komut: docker buildx build --platform linux/amd64,linux/arm64 -t ghcr.io/kullanici/uygulama:1.0 --push . Bu komut iki mimari için derler ve manifest listesi ile birlikte etikete gönderir. Yerelde test etmek isterseniz --load kullanabilirsiniz; ancak --load tek mimari için çalışır. Multi-arch için en iyi pratik --push kullanmaktır.

Adım 4: SBOM ve Provenance Eklemek

SBOM (Software Bill of Materials) ve provenance, tedarik zinciri güvenliği için giderek zorunlu hale geliyor. BuildKit ile şu bayrakları ekleyebilirsiniz: --sbom=true --provenance=true. Tam örnek: docker buildx build --platform linux/amd64,linux/arm64 -t ghcr.io/kullanici/uygulama:1.0 --sbom=true --provenance=true --push . Çoğu kayıt deposu bu metadataları saklayıp sonradan denetlenmesine izin verir.

Adım 5: Cache Yapılandırması ile Hız Kazanın

Buildx, uzak kayıt deposu tabanlı cache’i destekler. İlk derlemede --cache-to type=registry,ref=ghcr.io/kullanici/uygulama:buildcache,mode=max; sonraki derlemelerde --cache-from type=registry,ref=ghcr.io/kullanici/uygulama:buildcache kullanın. Bu sayede farklı CI runner’ları arasında katmanlar paylaşılarak derleme süresi ciddi şekilde kısalır.

Gizli Anahtarlar ve Çok Aşamalı Derlemeler

Derleme esnasında gizli anahtar (örneğin NPM_TOKEN) kullanacaksanız, komutta --secret id=NPM_TOKEN,env=NPM_TOKEN bayrağını, Dockerfile içinde de RUN --mount=type=secret,id=NPM_TOKEN kullanın. Bu yaklaşım sırları imaj katmanlarına sızdırmadan bağımlılık yüklemeyi mümkün kılar.

Doğrulama ve Hata Ayıklama

İmajınızın gerçekten çok mimarili olup olmadığını kontrol etmek için docker buildx imagetools inspect ghcr.io/kullanici/uygulama:1.0 çalıştırın. Manifest listesinde linux/amd64 ve linux/arm64 girdiğini görmelisiniz. Yerel test için Apple Silicon’da docker run --platform linux/amd64 ile x86_64 çalıştırıp davranışı karşılaştırabilirsiniz.

CI/CD Entegrasyonu (GitHub Actions Örneği)

GitHub Actions’ta tipik adımlar: QEMU kurulumu, Buildx kurulumu, kayıt deposuna login, cache ayarları, ardından çok mimarili build ve push. Resmi docker/setup-qemu-action ve docker/setup-buildx-action aksiyonlarını kullanın. Workflow’da gizli anahtarları secrets ile yönetin, etiketleri semantik sürümleme veya git SHA ile otomatik üretin.

Performans ve Maliyet İpuçları

- Mümkünse her mimari için yerel runner kullanın; emülasyon (QEMU) doğru ama yavaştır. Büyük derlemelerde süreyi belirgin etkiler.

- Minimal taban imajları (alpine, distroless) boyutu küçültür ve indirme süresini hızlandırır. Ağ maliyeti ve soğuk başlangıç süreleri düşer.

- Çok aşamalı derlemeyle derleme araçlarını final imajdan uzak tutun; güvenlik taramalarında daha az yüzey alanı elde edersiniz.

Sonuç

Docker Buildx ile çok mimarili imaj üretmek, bugün heterojen altyapılarda sürdürülebilir bir dağıtım stratejisinin bel kemiği. Doğru taban imajları, cache ve güvenlik metadatalarıyla desteklendiğinde, tek etiketle tüm platformlara güvenle dağıtım yapabilirsiniz. Üstelik CI/CD hatlarınıza entegre edilmesi kolay ve uzun vadede bakım yükünü ciddi biçimde azaltıyor. Bir kez kurduktan sonra, “nerede çalışacak?” sorusu gündemden düşüyor; geriye sadece uygulamanızın değer üretmesi kalıyor.