Sıfır kesintiyle dağıtım için minimal HTTP proxy: Kamal Proxy
(github.com/basecamp)- Kamal Proxy, web uygulamalarının önünde çalışan küçük bir HTTP proxy’sidir ve devam eden trafiği kesmeden dağıtımı yeni instance’a geçirmek için tasarlanmıştır
- Yeni instance
kamal-proxy deployile kaydedilir; proxy, yalnızca HTTP health check’i geçtikten sonra yeni trafiği bu instance’a yönlendirir - Dağıtım komutu, önceki instance’taki tüm trafik boşalana kadar bekler; bu yüzden komut başarıyla döndükten sonra önceki instance, devam eden istekler kesilmeden kaldırılabilir
- Tek bir proxy üzerinde birden fazla uygulama çalıştırabilmek için host tabanlı yönlendirme, path tabanlı yönlendirme, otomatik TLS ve manuel TLS sertifikası belirtme özellikleri sunar
- Kamal Proxy, Kamal dağıtım aracının bir parçası olarak tasarlanmıştır; ancak tek başına ya da başka dağıtım araçlarının bir bileşeni olarak da kullanılabilir
Kamal Proxy ne yapar?
- Kamal Proxy, sıfır kesintili dağıtımı koordine etmeyi kolaylaştırmak için geliştirilmiş küçük bir HTTP proxy’sidir
- Web uygulamanızı Kamal Proxy’nin arkasında çalıştırırsanız, devam eden trafiği kesmeden değişiklikleri dağıtabilirsiniz
- Bunun için uygulama tarafında özel bir entegrasyon gerekmez
- Kamal Proxy, container paketleme ve provisioning de içeren Kamal sisteminin bir parçası olarak çalışacak şekilde tasarlanmıştır
- Tek başına veya başka bir dağıtım aracının parçası olarak da kullanılabilir
Çalıştırma ve dağıtım akışı
- Proxy instance’ı
kamal-proxy runkomutuyla çalıştırılır - Bir yapılandırma dosyası yoktur; varsayılanlar uygulamanıza uymuyorsa seçenekler verilebilir
- Varsayılan HTTP portu
80’dir - Farklı bir portta çalıştırmak için
kamal-proxy run --http-port 8080kullanılır - Tüm seçenekler
kamal-proxy help runile görülebilir
- Varsayılan HTTP portu
- Trafiği web uygulamasına göndermek için uygulama instance’ı proxy’ye deploy edilir
- Hedef instance
hostname:portbiçiminde belirtilir- Örnek:
kamal-proxy deploy service1 --target web-1:3000 - Bu komut
web-1:3000adresiniservice1servis adıyla kaydeder
- Örnek:
Sıfır kesintili geçiş nasıl çalışır?
- Yeni bir instance kaydedildiğinde Kamal Proxy, erişilebilir ve çalışır durumda olduğunu doğrulamak için hemen HTTP health check başlatır
- Health check başarılı olursa yeni trafik yeni instance’a yönlendirilmeye başlanır
- Yeni instance uygun süre içinde healthy durumuna gelemezse
deploykomutu dağıtımı durdurur ve sıfırdan farklı bir çıkış kodu döndürür - Her dağıtım, daha önce dağıtılmış instance’taki tüm trafiği yeni instance’a aktarır
deploykomutu, önceki instance’taki trafik tamamen boşalana kadar bekledikten sonra döner- Komut başarıyla döndükten sonra önceki instance’ı kaldırmak, devam eden istekleri kesmez
- Yeni instance yalnızca healthy durumdayken trafik alır ve önceki instance tamamen drain olduktan sonra kaldırılır; bu sayede dağıtım sıfır kesintiyle gerçekleşir
Health check yapılandırması
- Varsayılan health check, her servis için saniyede 1 kez
/upadresineGETisteği gönderir 200yanıtı healthy durum olarak kabul edilir- Health check’i uygulamanıza uyarlamak için
deploybayrakları kullanılabilir--health-check-path--health-check-port--health-check-timeout--health-check-interval
- Health check yolunu değiştirme örneği:
kamal-proxy deploy service1 --target web-1:3000 --health-check-path web/index.html
- Health endpoint’ini ana servisten farklı bir portta yayınlıyorsanız:
kamal-proxy deploy service1 --target web-1:3000 --health-check-port 8080
Yönlendirme özellikleri
-
Host tabanlı yönlendirme
- Host tabanlı yönlendirme, tek bir Kamal Proxy instance’ıyla aynı sunucudaki birden fazla uygulamaya trafik yönlendirmeyi sağlar
- Dağıtım sırasında belirli bir host belirtilebilir
kamal-proxy deploy service1 --target web-1:3000 --host app1.example.com- Bu şekilde dağıtılan instance’lar yalnızca belirtilen host için gelen trafiği alır
- Her uygulama için ayrı host tanımlarsanız, port çakışması olmadan aynı sunucuda birden fazla uygulama çalıştırabilirsiniz
- Belirli bir host aynı anda yalnızca tek bir servise yönlendirilebilir
service1zatenapp1.example.comkullanıyorsa,service2aynı host ile dağıtılmaya çalışıldığında hata dönerservice1kaldırıldıktan sonraservice2aynı hosta dağıtılabilir
-
Path tabanlı yönlendirme
- Path tabanlı yönlendirme, istek yoluna göre trafiği farklı servislere ayıran uygulamalarda kullanılır
- Servisler farklı path prefix’leri altında mount edilebilir
/apiile başlayan istekleriweb-1’e gönderme örneği:kamal-proxy deploy service1 --target web-1:3000 --path-prefix=/api- Kalan trafiği
web-2’ye gönderme örneği:kamal-proxy deploy service2 --target web-2:3000 - Varsayılan olarak path prefix, upstream’e iletilmeden önce kaldırılır
/api/users/123isteğiweb-1’e/users/123olarak iletilir- Orijinal yolu aynen iletmek için
--strip-path-prefix=falsebelirtilir
TLS desteği
- Kamal Proxy, uygulamalar için TLS sertifikalarını otomatik olarak alabilir ve yenileyebilir
- Otomatik TLS, dağıtım sırasında
--tlsbayrağı eklenerek etkinleştirilirkamal-proxy deploy service1 --target web-1:3000 --host app1.example.com --tls
- Otomatik TLS için host belirtilmesi gerekir
- Bu, rastgele host adları için kötü niyetli sertifika taleplerini engellemek amacıyla konmuş bir koşuldur
- Path tabanlı yönlendirmede TLS seçeneği root path üzerinde ayarlanmalıdır
- Aynı hostun farklı path’lerine dağıtılan servisler, root path için tanımlanmış TLS ayarını kullanır
- Manuel olarak alınmış TLS sertifikaları, kendi certificate authority’niz veya Cloudflare origin certificate kurmanız gerekiyorsa, sertifika dosyası ve private key yolunu doğrudan belirtebilirsiniz
--tls-certificate-path cert.pem--tls-private-key-path key.pem
Ortam değişkenleri ve build
- Docker container çalıştırma gibi ortamlarda
runseçenekleri ortam değişkenleri olarak verilebilir kamal-proxy run, ilgili ortam değişkeni ayarlanmışsa her seçeneğin değerini ortam değişkeninden okurkamal-proxy run --http-port 8080HTTP_PORT=8080 kamal-proxy run
- Ortam değişkeni adları mevcut ortamınızla çakışıyorsa ayırt etmek için
KAMAL_PROXY_öneki kullanılabilirKAMAL_PROXY_HTTP_PORT=8080 kamal-proxy run
- Yerel build, çalışan bir Go ortamında
makeile yapılır - Docker container olarak build almak için
make dockerkullanılır - Proxy komutlarını denemek için Docker Compose yapılandırması
exampleklasöründe bulunur
1 yorum
Hacker News yorumları
Eylem adı olarak deploy seçilmesi tuhaf
Zaten aşırı yüklenmiş bir terim; “uygulama mı dağıtıldı? Yoksa proxy’ye mi dağıtıldı?” gibi bir kafa karışıklığı yaratacak gibi
bind,intercept, hatta sadeceproxygibi kelimeler kullanılabilirdi; nedendeployolduğunu anlamıyorumBugünlerde başka sunucularla da kesintisiz dağıtım yapmak kolay; bunu ayrı bir uygulama bütününe dönüştürmeleri ilginç
Örneğin Unix socket destekleyen bir uygulama + web proxy ile sadece dosyayı taşıyarak kesintisizlik sağlanabilir
Atomiktir ve
curlile warm-up isteği de gönderebilirsinizBir kayıt sistemi bile yapmak bana abartı görünüyor
Kamal, buluttan kendi donanımına geçerken milyonlarca dolar tasarruf etmek için kullanılan bir dağıtım aracı (https://basecamp.com/cloud-exit)
Referans olabilecek bir yazı varsa daha fazla öğrenmek isterim
Bu, esas olarak Docker Swarm’da servis container’ları değiştirilirken trafiğin kesilmesiyle ilgili temel sorunu ele almaya çalışıyor gibi görünüyor
Cloud 66’da JAMStack sunucusu yaparken aynı sorunu yaşamıştık; kendi proxy’miz yerine Caddy kullanmıştık, Traefik de gayet uygun olurdu gibi geliyor
Kamal’ın k8s veya k3s yerine neden Swarm’ı seçtiğini bilmiyorum ama karmaşıklığın bir yerde durması gerekiyor ve gizlenemiyor; sonunda kendi proxy’sine gitmiş gibi
Kendim kullanmadım ama WebSockets, SSE, HTTP/3, çeşitli sıkıştırma ve şifreleme desteklerini sürekli kovalamak zorunda kalacaklar gibi göründüğü için epey şüpheliyim
Basit başlayan bir reverse proxy’nin, sonunda kaçınmaya çalıştığı karmaşıklığı sürekli içine çekmesine örnek gibi görünüyor
Geniş ve ölçeklenebilir yürütme sistemlerinin rakiplere ihtiyacı olduğunu düşünüyorum ama Kamal, kaçınmaya çalıştığı karmaşıklığa geri geri yürümek gibi duruyor
Kubernetes’in istenen durum yönetimi framework’ü olarak sahip olduğu yetenek ve esneklik daha da belirgin görünüyor
Docker kullanmaya devam ederken proxy olarak Caddy, Traefik, Envoy kullanabilirsiniz
Kubernetes de sonuçta çoğu zaman bunları ingress controller olarak kullanıyor
Görünüşe göre Swarm kullanmıyor
docker runkomutları kullanır; Swarm veya orkestrasyon yokturTemelde Capistrano’nun yerine geçen basit bir araçtır
Container orkestrasyon servislerinin temeli, bağlantıları ve blue/green dağıtımı ingress controller üzerinden ele almaktır
Daha doğrusu ingress controller, yıllardır, hatta on yıllardır bu kullanım senaryosunu ele alan sayısız reverse proxy’ye verilmiş rol adıdır
Kesintisiz dağıtım genel olarak nasıl çalışır, kısaca açıklayabilir misiniz?
Uygulamanın iki sürümünün aynı anda ayakta olduğu ve yeni trafiğin yeni sürüme yönlendirildiği bir yapı olduğunu tahmin ediyorum
Ama tek bir veritabanı kullanılıyorsa ve yeni uygulama sürümü şema değişikliği getiriyorsa, başlangıçta migration script’i şemayı değiştirecektir; eski uygulama sürümü ise eski şemayı bekler
Bunun nasıl ele alındığını merak ediyorum
Birçok framework kod dağıtımı sırasında migration çalıştırır, ama pratikte çalıştırma zamanını elle kontrol etmek daha iyidir
Her kod sürümünün hem mevcut şemada hem de gelecekteki migration sonrası şemada çalışması gerekir; bu da fiilen geriye dönük uyumlu kod demektir
Akış “mevcut/gelecek şemaların ikisinde de çalışan yeni kodu dağıt → doğrula → migration çalıştır → doğrula → artık olmayan şemaya yönelik uyumluluk kodunu temizle” şeklinde olur
Kesintisiz dağıtım veya rolling deployment yapmak istiyorsanız ödemeniz gereken ek maliyet budur; büyük şirketler genelde böyle yapar
Özellikle bölgesel kullanıcılara hizmet veriyorsanız ve dağıtım için sakin saatleri seçebiliyorsanız bu daha da geçerlidir
Uygulamanın iki sürümü aynı anda çalışabiliyor
https://xata.io/blog/multi-version-schema-migrations
Load balancer Server2’de bağlantıları kabul etmeye başlar ve Server1’de yeni bağlantı kabul etmez
Sonra Server1’deki tüm bağlantılar kapanınca Server1’i devreden çıkarır
Bunlar farklı sunucular da olabilir, tek bir sunucudaki birden fazla worker da olabilir
O süre boyunca, diğer yanıtların söylediği gibi migration’ların geriye dönük uyumlu olması gerekir
Kamal 2 şu anda RC (https://github.com/basecamp/kamal/releases) aşamasında ve otomatik SSL desteğiyle tek bir sunucuda birden fazla uygulamayı Kamal ile kolayca çalıştırmayı mümkün kılacak gibi görünüyor; heyecan verici
autocertpaketini kullanıyor; bu da asgari düzeyde bir uygulamaya yakınKırılgan ve Let's Encrypt'in sertifika verme limitleri ile eşzamanlı sertifika limitleri yüzünden proxy instance'larını yatay ölçeklendirmek zor
Caddy/Certmagic, verileri paylaşımlı depolamaya yazarak yalnızca tek bir sertifika alınmasını ve tüm instance'ların bunu depolama üzerinden yeniden kullanıp koordine etmesini sağlayarak bu sorunu çözüyor
Yayıncı alternatifi, limitlerden kaçınma veya ARI desteği de yok
Upstream hazır olana kadar istekleri bekletme işini de Caddy,
reverse_proxyüzerindetry_durationvetry_intervalayarlanınca gayet iyi hallediyorProxy header işleme de güvenilir IP'leri dikkate almıyor; etkinleştirilirse biri
X-Forwarded-Forayarlayarak IP sahteciliği yapabilirVarsayılan olarak kapalı olması iyi ama herhangi bir uyarı da yok
Amacın basitlik olduğunu anlıyorum, ancak mevcut hâliyle yeterince olgun değil ve çok fazla daha iyi seçenek olduğu için pratikte kullanmak istemem
Bunu nasıl kullanacağımı tam bilmiyorum ve bir şeyi kaçırıyor gibiyim
Örnek,
webservisinin 4 replikasını başlatıyorBunlardan
example-web-1gibi tek bir replikaya deploy edip servis oluşturabiliyorsanız, kalan 3 replikanın ne yaptığını anlamıyorumweb'i güncelleyip kesintisiz deploy yapmak içinwebservisinde build komutunu çalıştırmak, bir şekilde bu servisi başlatmak ve ardından yeni hedefe deploy çalıştırmak gerekiyor gibiAma
docker compose up --build --force-recreate webçalıştırırsanız mevcut replikalar kapanır ve her şey anlamsız hâle gelirBu proxy'nin DNS isteğinden dönen birden fazla kayda dengeleme yapıp yapmadığını kontrol etmedim, ama en azından VIP tabanlı yük dengeleme ise beklenen şekilde çalışacaktır
İkinci güncelleme kısmı daha az net
Aynı ağ içinde farklı isimli bir servis ayağa kaldırıp
kamal-proxy deployçalıştırılması bekleniyor olabilirServis adına sürüm numarası koymak gibi bir şey olabilir; hızlı geri almak istiyorsanız eski sürümü sıcak durumda tutmak da mantıklı
[0]: https://docs.docker.com/reference/compose-file/deploy/#endpo...
k8s rollout restart deploymentkullanmamak için neden olduğunu anlamıyorumYa da DNS'i veya router'ı iki backend arasında değiştirebilirsiniz
Bunun trafiği geçici durdurma desenini uygulayıp uygulamadığını merak ediyorum
Proxy'nin trafiği fiilen birkaç saniye durdurduğu bir yöntem; gelen istekler normalden birkaç saniye daha uzun sürüyor gibi görünür ama kısa bir gecikmeden sonra tamamlanır
O birkaç saniye içinde küçük bir DB migration ya da 5 saniye içinde bitirebileceğiniz biraz daha karmaşık, bloklayıcı bir altyapı değişikliği çalıştırabilirsiniz
Oldukça riskli geliyor; birkaç yıl uygulama sunucusu şirketinde çalıştım ama bu deseni ilk kez duyuyorum
Yine de Django'nun ortak yaratıcısı muhtemelen birçok deploy görmüştür, o yüzden bunu öylece geçiştirmek zor
Eskiden bu konu hakkında konuştuğumuzu hatırlıyorum
Traefik ya da kendini kanıtlamış başka araçlar kullanmak yerine kendi proxy'lerini yapmaya karar vermelerinin nedenini nerede açıkladıklarını merak ediyorum
Neden değiştirdiklerine ve kendi uygulamalarını yapmaya karar verdiklerine dair bağlam PR'da var
https://github.com/basecamp/kamal/pull/940
Ama Traefik dışındaki şeylerle resmi bir karşılaştırma yayımladıklarını sanmıyorum
Timeout ayarlamanın bir yolu olup olmadığını merak ediyorum
https://github.com/basecamp/kamal-proxy/blob/main/internal/s...
https://github.com/basecamp/kamal-proxy/blob/main/internal/s...
https://blog.cloudflare.com/exposing-go-on-the-internet/
NIH. Söylenecek başka bir şey yok