Başta kapasitesini çok ciddiye almadığınız ufak siteler büyür ya da sayıları artarsa, bir gün illaki buna benzer bir disk uyarısı alırsınız.

Kobilere uygulama geliştiriyor olsanız bile ürün görseli için karadüzen upload tercih ediliyorsa o biriktirilenler er ya da geç diskte gereksiz bir yük haline geliyor. Ayrıca her görsel isteğinin uygulama sunucusunun bulunduğu network'ten servis edilmesi de zaten iyi bir mimari değil.

"Bizde disk alanı çok, dışarda değil kendi mahallemizde depolansın" diyen için de geçerli bu durum. Tercih böyle bile olsa on-prem'den cloud'a geçiş açısından depolama işi bir standarda tâbi olmalı. Onun için de S3 uyumlu Minio veya benzeri paketlere sırt dönmemek lazım.

Ben bu yazıda Cloudflare R2 aracılığıyla bu işi handle etmek adına size iki ayrı örnek paslıyorum. Tarafımızda 0 disk alanı hedefimize dair örnek domain adları;

siteadresi.com      → falanca bir e-ticaret sitesine dair domain bu olsun
cdn.siteadresi.com  → ürün görsel url'leri için subdomain de bu olsun

Nihai hedefimiz şunu edinebilmek:

<img src="https://cdn.siteadresi.com/products/abc-123/urun-fotografi-a1b2c3d4e5f6.jpg" alt="Ürün fotoğrafı">

Cloudflare R2 Neden Uygun?

Hem S3 uyumlu bir depolama çözümü, hem de 10GB-month seviyesine kadar ücretsiz bir sağlayıcı. Backend tarafında tercihin hangisi ise (Python, PHP, ..) standart S3-client mantığıyla dosya yükleyebildiğiniz için mevcut uygulama yapınıza entegre etmesi oldukça pratik.

Direkt kitabın ortasından konuşmaya devam ediyoruz. Bir görseli R2 bucket içine şu object key ile yüklediğimizi düşünelim:

products/abc-123/urun-fotografi-a1b2c3d4e5f6.jpg

"bucket" de nedir? gibi bir soru söz konusu ise Cloudflare hesabınla R2 hizmetine tıkladığında lamba yanacak, orayı bi kurcala. ilk bucket'ini oluşturmada arayüz seni yönlendiriyor olacak

Genel olarak akış şöyle :

1. R2 bucket oluşturulur.
2. Bucket için API token üretilir.
3. `cdn.siteadresi.com` custom domain olarak bucket'a bağlanır.
4. Cloudflare DNS tarafında ilgili kayıt aktif hale getirilir.
5. Backend upload işlemini R2 API'ye yapar.
6. Frontend görseli doğrudan `cdn.siteadresi.com` üzerinden çağırır.

CLI üzerinden olsun, tercihim "Python" diyenler için örnek:

# ilişkili paket:
python -m pip install boto3

Örnek Python script:

Kullanım:

python cf-r2_draft.py \
  --file ./urun-fotografi.jpg \
  --product-code ABC-123 \
  --account-id CLOUDFLARE_ACCOUNT_ID \
  --access-key R2_ACCESS_KEY_ID \
  --secret-key R2_SECRET_ACCESS_KEY \
  --bucket siteadresi-products \
  --cdn-base-url https://cdn.siteadresi.com

Yukarıdaki örnek taslak seviyede bir kod. Asenkron yürümesini istediğim faaliyetlerde kullandığım, olası talepleri güzel sırtlayan concurrent.futures kütüphanesini kullanmanızı öneriyorum. Ve -varsa- CF taraflı limitlere de göz atmanızda fayda olabilir. Aşırı eş zamanlı isteklerde 429/5XX gibi cevaplarla karşılaşabileceğinizi hesaba katmak gerekir. Bendeki portföyün hareketliliği şimdiye kadar bu tarz bir kısıtlılık yaşatmadı. Kaldı ki zaten fazla gıdıkladığın takdirde kendi barındırman da "hoop bi dk" diyor :)

PHP sdk ile yürümek isteyen için bir örnek :

Benim gibi php script dili ile duygusal bağı olan biriyseniz bu örnek işinizi görecektir. ( pek tabi ki php ile de CLI'dan aşağıdaki işlemi yapabilir, bu prosesi geçerli http runtime'ınızdan hariç tutabilirsiniz )

# ilişkili paket:
composer require aws/aws-sdk-php

İki örnek için de çıktı şöyle:

https://cdn.siteadresi.com/products/abc-123/urun-fotografi-a1b2c3d4e5f6.jpg

Elde ettiğin bu bilgiyi tarafında tutarsın kendi veritabanı sürücün her ne ise artık. O iş sende.

Dikkat edilecek diğer mevzular:

  • Güvenlik Konusu: R2 access key ve secret key hiçbir zaman frontend tarafına verilmemeli veya JS içine gömülmemeli.
  • Dosya İsimleri Neden Benzersiz Olmalı? Çünkü ürün görselleri genellikle cache'lenebilir dosyalar. Bu yüzden aynı URL'nin içeriğini sürekli değiştirmek yerine, görsel değiştiğinde yeni bir URL üretmek daha temiz olur. Uygulama tarafında da görsel değişimi daha net takip edilir. Misal:
# ilk yükleme böyle:
products/abc-123/urun-fotografi-a1b2c3d4e5f6.jpg

# görsel güncellendiğinde:
products/abc-123/urun-fotografi-f9e8d7c6b5a4.jpg
  • Cache-Control Tercihi: Ürün görselleri için upload sırasında aşağıdaki başlık tercih edilebilir:
Cache-Control: public, max-age=31536000, immutable

"""
Bu, tarayıcıya ve ara cache katmanlarına şu mesajı verir:
Bu dosya uzun süre değişmeyecek. Aynı URL tekrar istenirse cache kullanılabilir.
Ama bu kararın doğru çalışması için URL bazlı düşünmek gerekir.
Yani dosya değiştiyse aynı URL'yi ezmek yerine yeni dosya adı üretmek daha sağlıklı olur.
"""

Son not: R2 taraflı ayarda custom-domain bilgisini o istediğin subdomain yaptıktan sonra DNS kayıtlarına bir göz at. Şöyle bir bilgi ile karşılaşıyorsan her şey yolunda demektir:

Type: CNAME
Name: cdn
Target: Cloudflare'ın R2 custom domain adımında verilen hedef
Status: Proxied

"Object storage" ve de CDN kültürüne ilk adımı attın. Hayırlara vesile olsun.