Canlı bir IoT platformunu Kubernetes'ten taşımak - tek bir zorunlu uygulama güncellemesi olmadan

19 Temmuz 2026· Hüseyin Çınar


Ocak 2024’te, görme engelliler için akıllı baston platformu WeWALK’un backend’ini devraldım. Kod tabanı 2020’den kalmaydı ve benden önce birkaç elden geçmişti - altında da gerçek kullanıcıların her gün güvendiği canlı bir production sistemi vardı.

Tam olarak 27.000 kayıtlı kullanıcı - ve bu sayı hakkında dürüst olayım: etkileyici değil. İnsanlar milyonlarca kullanıcılı veritabanlarını taşıyor ve hakkında tek satır yazmıyor. Bu taşımayı ilginç yapan kullanıcı sayısı değildi. Üç başka şeydi:

  1. Kullanıcılar kesintiye uğratılamaz. Birçoğu için uygulama, şehirde dolaşabilmenin bir parçası. “Devam etmek için güncelleyin” bir yemek uygulamasında küçük bir rahatsızlıktır; bir erişilebilirlik ürününde, birinin bastonunun sokak ortasında telefonuyla konuşmayı kesmesi demek olabilir. O yüzden planlamadan önce tek kural sabitlendi: kimse güncellemeye zorlanmayacak. Sahadaki her istemci binary’si çalışmaya devam edecek.
  2. Veri hiç durmuyor. Bunlar günde iki kez uygulama açan kullanıcılar değil. Aktif her baston, yaklaşık on saniyede bir 102 baytlık bir tanı anlık görüntüsünü bölümlenmiş bir zaman serisi tablosuna gönderiyor. Bunu sahadaki cihaz filosuyla çarpın: hiç duraklamayan bir yazma akışı elde edersiniz - taşımanız sırasında da dahil. “Kullanıcılar” sokaktaki bastonlarsa bakım penceresi diye bir şey yok.
  3. Veri çeşitliydi. Eski kümede MongoDB ve PostgreSQL, yenisinde devreye giren Firestore; üstüne IAP faturalama olayları, kural tabanlı bir yetkilendirme motoru ve JSONB öncesi/sonrası diff’leri tutan bir denetim izi. Bu, kopyalanacak tek büyük bir tablo değildi. Farklı tutarlılık ihtiyaçları olan birkaç tür verinin aynı anda taşınmasıydı.

Ha, bir de şu vardı: eski cluster fiilen bir kara kutuydu.

Yedeklerden başlamak

Sistem, kendi sunucularında koşan bir Rancher/Kubernetes kümesindeydi. Taşıma başladığında o ortama çalışır erişim artık pratik değildi - yılların ekip ve tedarikçi geçişleri içinde doğal olarak yaşanan, sektörde kimsenin itiraf etmeyi sevmediği kadar yaygın bir durum. Elimizde güvenilir olarak olan şey yedeklerdi.

Plan da buna dönüştü: sistemin gerçekte ne olduğunu yedeklerinden çıkar; eski makineleri canlandırmaya çalışmak yerine onu Google Cloud Run, Cloud SQL ve Firestore üzerinde yeniden kur.

İnceleyemediğiniz bir sistemi hiç yeniden inşa etmediyseniz, eğitici bir deneyim olarak tavsiye ederim; başka türlü asla. Konfigürasyonun hangi kısmının yazıya döküldüğünü, hangi kısmının hiçbir yerde belgelenmemiş olduğunu tek tek öğreniyorsunuz. Ayağa kalkan her servis; yedeğin söylediği, kodun beklediği ve production trafiğinin kanıtladığı şeyler arasında küçük bir pazarlıktı.

Eski adresler, yeni bulut

Platformun bir kısmı AWS’deydi ve mobil uygulamalar oradaki WebSocket endpoint’leriyle konuşuyordu. O adresler sahadaki istemci binary’lerine gömülü. GCP’ye normal yoldan geçmek yeni adres demek, yeni adres zorunlu güncelleme demek - en baştan yasakladığımız şey.

Bu yüzden eski adresler hiç ölmedi. Mevcut AWS WebSocket adreslerini canlı tutup her şeyi yeni altyapıya ileten bir Lambda→Cloud Run proxy’si yazdım. Sahadaki en eski istemci sürümü bugün de hep konuştuğu adresle konuşuyor; cevap veren şeyin taşındığından haberi bile yok. Kimse bir şey güncellemedi. Taşımadan kaynaklı destek kaydı: bildiğim kadarıyla sıfır.

En değerli araç bir diff’ti

Sonra dokuz servisi bölgeler arasında taşıdık: europe-west3’ten europe-west1’e. Bizi kurtaran şey zekice bir şey değildi. Geçişten önce her servisin konfigürasyonunu kaynak ile hedef arasında karşılaştırdım - her ortam değişkeni, her ayar, servis servis.

O diff dört eksik ortam değişkeni yakaladı. Dört. Herhangi biri, olabilecek en kötü anda fark edilen bir production olayı olurdu. Yıllarca hem Kubernetes kümesi hem serverless platform işlettikten sonra dürüst kanaatim şu: taşımalar nadiren ilginç şekillerde başarısız olur. Bir env değişkeni yanınızda gelmediği için başarısız olurlar. Önce diff’leyin. Sıkıcıdır ve işe yarar.

Yerinde durmayan veriyi taşımak

Veri katmanının kendi taşıması vardı: Firestore’dan PostgreSQL’e - telemetri akışı yazmaya devam ederken. Bunu çift-yazma (dual-write) olarak yaptık: iki depoya da yaz, okumaya eskisinden devam et, okuma geçişini bir ortam bayrağının arkasına koy. Yeni depo kendini canlı yazma yükü altında kanıtlayınca okumayı çevirdik. Eski yol, bir şeyler ters giderse diye zarif bozulmayla birlikte kaçış kapısı olarak yerinde kaldı.

Zaman serisi tarafı ayrıca özen istiyordu: sürekli akan cihaz anlık görüntüleri “dünyayı durdur, kopyala, yeniden başlat” yaklaşımını kaldırmaz - tablonun bölümlenmiş olmasının ve geçişin toplu bir iş yerine bayrak çevirme olmak zorunda olmasının nedeni tam da bu.

O şema o günden beri 174 migration ile 34 tablodan yaklaşık 95 tabloya büyüdü. 2024 başında yedeklerinden yeniden inşa edilmek zorunda kalan sistem, bugün benim sevdiğim türden bir production sistemi: sıkıcı, gözlemlenebilir, istendiğinde yeniden deploy edilebilir.

Aklımda kalan

“Zorunlu güncelleme yok” kısıtı önce yük gibi hissettirdi, sonra filtre olduğu anlaşıldı. Tembel seçeneklerin hepsini daha en başta eledi; geriye kalan, gerçekten daha iyi mimariydi. Ve bütün projenin en gösterişsiz iki ürünü - bir yığın yedek ve bir konfigürasyon diff’i - o kullanıcılar için yazdığım her zekice koddan daha fazlasını yaptı.

Dokümantasyonu olmayan bir production sistemi devralıyorsanız: yedeklerinizden yeniden kurabildiğinizi ihtiyacınız olmadan önce test edin ve her geçişten önce konfigürasyonu diff’leyin. Yazının özü bu kadar aslında.

← Tüm yazılar