3 poin oleh GN⁺ 2024-09-22 | 1 komentar | Bagikan ke WhatsApp
  • Kamal Proxy adalah proksi HTTP kecil yang berjalan di depan aplikasi web, dirancang untuk mengalihkan deployment ke instance baru tanpa memutus trafik yang sedang berlangsung
  • Instance baru didaftarkan dengan kamal-proxy deploy, dan proksi merutekan trafik baru ke instance tersebut hanya setelah lolos HTTP health check
  • Perintah deployment menunggu hingga semua trafik pada instance lama selesai, sehingga setelah berhasil kembali, instance lama dapat dihapus tanpa menghentikan request yang sedang berlangsung
  • Untuk menjalankan beberapa aplikasi pada satu proksi, tersedia routing berbasis host, routing berbasis path, TLS otomatis, serta kemampuan menentukan sertifikat TLS manual
  • Kamal Proxy dirancang sebagai bagian dari alat deployment Kamal, tetapi juga dapat digunakan secara mandiri atau sebagai komponen alat deployment lain

Apa yang dilakukan Kamal Proxy

  • Kamal Proxy adalah proksi HTTP kecil untuk mempermudah koordinasi deployment tanpa downtime
  • Dengan menjalankan aplikasi web di belakang Kamal Proxy, perubahan dapat dideploy tanpa menghentikan trafik yang sedang berlangsung
  • Tidak diperlukan kerja sama khusus dari sisi aplikasi untuk perilaku ini
  • Kamal Proxy dirancang untuk berjalan sebagai bagian dari Kamal, yang mencakup packaging container dan provisioning
  • Juga dapat digunakan secara mandiri atau sebagai bagian dari alat deployment lain

Alur menjalankan dan deployment

  • Instance proksi dijalankan dengan perintah kamal-proxy run
  • Tidak ada file konfigurasi; opsi dapat ditentukan ketika nilai default tidak sesuai untuk aplikasi
    • Port HTTP default adalah 80
    • Untuk menjalankan di port lain, gunakan kamal-proxy run --http-port 8080
    • Opsi lengkap dapat dilihat di kamal-proxy help run
  • Untuk mengirim trafik ke aplikasi web, instance aplikasi di-deploy ke proksi
  • Instance target deployment ditentukan dalam format hostname:port
    • Contoh: kamal-proxy deploy service1 --target web-1:3000
    • Perintah ini mendaftarkan web-1:3000 dengan nama layanan service1

Cara peralihan tanpa downtime

  • Saat instance baru didaftarkan, Kamal Proxy langsung menjalankan HTTP health check untuk memeriksa apakah instance dapat diakses dan berfungsi
  • Jika health check berhasil, proksi mulai merutekan trafik baru ke instance baru
  • Jika instance baru tidak menjadi healthy dalam waktu yang sesuai, perintah deploy menghentikan deployment dan mengembalikan kode keluar non-nol
  • Setiap deployment memindahkan seluruh trafik dari instance yang sebelumnya dideploy ke instance baru
  • Perintah deploy menunggu hingga semua trafik keluar dari instance lama, lalu kembali
    • Setelah perintah berhasil kembali, instance lama dapat dihapus tanpa memutus request yang sedang berlangsung
  • Karena instance baru hanya menerima trafik saat dalam status healthy, dan instance lama dihapus setelah sepenuhnya di-drain, deployment berjalan dengan tanpa downtime

Pengaturan health check

  • Health check default mengirim request GET ke /up sekali per detik untuk setiap layanan
  • Respons 200 dianggap sebagai status healthy
  • Untuk mengubah health check agar sesuai dengan aplikasi, gunakan flag deploy
    • --health-check-path
    • --health-check-port
    • --health-check-timeout
    • --health-check-interval
  • Contoh mengubah path health check:
    • kamal-proxy deploy service1 --target web-1:3000 --health-check-path web/index.html
  • Jika endpoint health diekspos pada port yang berbeda dari layanan utama:
    • kamal-proxy deploy service1 --target web-1:3000 --health-check-port 8080

Fitur routing

  • Routing berbasis host

    • Routing berbasis host memungkinkan satu instance Kamal Proxy merutekan trafik ke beberapa aplikasi pada server yang sama
    • Host tertentu dapat ditentukan saat deployment
    • kamal-proxy deploy service1 --target web-1:3000 --host app1.example.com
    • Instance yang dideploy dengan cara ini hanya menerima trafik untuk host yang ditentukan
    • Dengan menentukan host terpisah untuk setiap aplikasi, beberapa aplikasi dapat berjalan di server yang sama tanpa konflik port
    • Satu host tertentu hanya dapat dirutekan ke satu layanan pada satu waktu
    • Jika service1 sudah menggunakan app1.example.com, error akan dikembalikan saat service2 dideploy dengan host yang sama
    • Setelah service1 dihapus, service2 dapat dideploy ke host yang sama
  • Routing berbasis path

    • Routing berbasis path digunakan untuk aplikasi yang membagi trafik ke layanan berbeda berdasarkan path request
    • Layanan dapat di-mount di bawah prefiks path yang berbeda
    • Contoh mengirim request yang dimulai dengan /api ke web-1: kamal-proxy deploy service1 --target web-1:3000 --path-prefix=/api
    • Contoh mengirim sisanya ke web-2: kamal-proxy deploy service2 --target web-2:3000
    • Secara default, prefiks path dihapus sebelum diteruskan ke upstream
    • Request /api/users/123 diteruskan ke web-1 sebagai /users/123
    • Untuk meneruskan path asli apa adanya, tentukan --strip-path-prefix=false

Dukungan TLS

  • Kamal Proxy dapat memperoleh dan memperbarui sertifikat TLS untuk aplikasi secara otomatis
  • TLS otomatis diaktifkan dengan menambahkan flag --tls saat deployment
    • kamal-proxy deploy service1 --target web-1:3000 --host app1.example.com --tls
  • TLS otomatis memerlukan host yang ditentukan
    • Ini adalah syarat untuk mencegah permintaan sertifikat secara berbahaya untuk nama host sembarang
  • Pada routing berbasis path, opsi TLS harus diatur pada path root
    • Layanan yang dideploy pada path lain di host yang sama menggunakan pengaturan TLS yang ditentukan pada path root
  • Jika perlu memasang sertifikat TLS yang diperoleh secara manual, certificate authority sendiri, atau Cloudflare origin certificate, path file sertifikat dan private key dapat ditentukan langsung
    • --tls-certificate-path cert.pem
    • --tls-private-key-path key.pem

Variabel lingkungan dan build

  • Dalam lingkungan seperti menjalankan container Docker, opsi run dapat ditentukan sebagai variabel lingkungan
  • kamal-proxy run membaca nilai setiap opsi dari variabel lingkungan jika variabel tersebut disetel
    • kamal-proxy run --http-port 8080
    • HTTP_PORT=8080 kamal-proxy run
  • Jika nama variabel lingkungan bertabrakan dengan lingkungan yang sudah ada, prefiks KAMAL_PROXY_ dapat ditambahkan untuk membedakannya
    • KAMAL_PROXY_HTTP_PORT=8080 kamal-proxy run
  • Build lokal dilakukan dengan make pada lingkungan Go yang berfungsi
  • Untuk build dengan container Docker, gunakan make docker
  • Konfigurasi Docker Compose untuk mencoba perintah proksi tersedia di folder example

1 komentar

 
GN⁺ 2024-09-22
Komentar Hacker News
  • Agak aneh memilih deploy sebagai nama operasinya
    Itu sudah istilah yang terlalu sarat makna, jadi terasa bisa menimbulkan kebingungan seperti “aplikasinya yang di-deploy? atau ke proxy yang di-deploy?”
    Padahal bisa saja memakai kata seperti bind, intercept, atau cukup proxy, jadi kurang paham kenapa harus deploy

    • “Di-deploy” bisa dipahami sebagai berjalan dan terdaftar di proxy
    • Jika trafik masuk berasal dari proxy, saya tidak tahu deploy bisa berarti apa lagi selain “trafik proxy mulai mengalir ke instance aplikasi yang baru”
  • Sekarang ini zero-downtime deployment juga bisa dilakukan dengan mudah lewat server lain, jadi menarik bahwa ini dijadikan satu aplikasi terpisah
    Misalnya, aplikasi + web proxy yang mendukung Unix socket bisa melakukan ini tanpa downtime hanya dengan memindahkan file
    Sifatnya atomik, dan permintaan pemanasan juga bisa dikirim dengan curl
    Membangun sampai sistem registrasi terasa berlebihan

    • Ini hanyalah bagian yang sangat kecil dari Kamal(https://kamal-deploy.org)
      Kamal adalah alat deployment yang dipakai untuk pindah dari cloud ke hardware sendiri dan menghemat jutaan dolar(https://basecamp.com/cloud-exit)
    • Akan menarik kalau ada yang bisa menjelaskan pendekatan ini lebih lanjut
      Kalau ada tulisan rujukan, saya ingin mempelajarinya lebih jauh
  • Ini tampaknya terutama berusaha menangani masalah mendasar putusnya trafik saat mengganti container layanan di Docker Swarm
    Kami mengalami masalah yang sama saat membuat server JAMStack di Cloud 66, dan memakai Caddy alih-alih proxy buatan sendiri; Traefik juga sepertinya akan cukup cocok
    Saya tidak tahu kenapa Kamal memilih Swarm alih-alih k8s atau k3s, tetapi kompleksitas pasti harus ditempatkan di suatu tempat dan tidak bisa disembunyikan, jadi pada akhirnya tampaknya mereka memilih proxy buatan sendiri
    Saya belum mencobanya sendiri, tetapi cukup skeptis karena rasanya akan terus dikejar-kejar dukungan untuk WebSockets, SSE, HTTP/3, berbagai kompresi, dan enkripsi

    • Kamal terasa dibangun di atas asumsi bahwa “Kubernetes terlalu rumit”, lalu dari pembenaran itu malah membangun ulang cukup banyak hal yang dilakukan Kubernetes
      Ini terlihat seperti contoh reverse proxy yang awalnya sederhana tetapi akhirnya terus menyerap kompleksitas yang tadinya ingin dihindari
      Saya pikir sistem eksekusi yang luas dan dapat diperluas memang perlu pesaing, tetapi Kamal tampak seperti pekerjaan mundur ke arah kompleksitas yang justru ingin dihindarinya
      Itu malah semakin menonjolkan kemampuan dan fleksibilitas Kubernetes sebagai kerangka kerja manajemen desired state
    • Sepertinya Anda mencampuradukkan orkestrasi dan proxy
      Anda tetap bisa terus memakai Docker sambil menggunakan Caddy, Traefik, atau Envoy sebagai proxy
      Kubernetes pada akhirnya juga sering memakai mereka sebagai ingress controller
    • Saya tidak tahu Anda melihat dari mana bahwa Kamal memakai Docker Swarm
      Dari yang saya lihat, sepertinya tidak memakai Swarm
    • Kamal memakai perintah docker run biasa, dan tidak ada Swarm maupun orkestrasi
      Pada dasarnya ini alat sederhana pengganti Capistrano
    • Kalau itu alasannya, rasanya seperti belum melakukan riset yang sangat dasar tentang topik ini
      Dasar dari layanan orkestrasi container adalah menangani koneksi dan deployment biru/hijau lewat ingress controller
      Lebih tepatnya, nama peran yang diberikan kepada banyak reverse proxy yang sudah menangani kasus penggunaan ini selama bertahun-tahun, bahkan puluhan tahun, adalah ingress controller
  • Akan bagus kalau ada yang bisa menjelaskan secara singkat bagaimana zero-downtime deployment biasanya bekerja
    Saya menduga strukturnya adalah dua versi aplikasi berjalan bersamaan dan trafik baru diarahkan ke versi baru
    Tetapi jika memakai satu database yang sama dan versi aplikasi baru membawa perubahan skema, skrip migrasi saat startup akan mengubah skema itu sementara versi aplikasi lama masih mengharapkan skema lama
    Saya penasaran bagaimana ini biasanya ditangani

    • Langkah pertama adalah memisahkan migrasi dan deployment
      Banyak framework menjalankan migrasi saat deployment kode, tetapi dalam praktiknya lebih baik mengendalikan waktunya secara manual
      Setiap versi kode harus bisa berjalan baik dengan skema saat ini maupun skema setelah migrasi di masa depan, sehingga pada dasarnya menjadi kode yang kompatibel ke belakang
      Alurnya menjadi “deploy kode baru yang bekerja dengan skema saat ini/masa depan → verifikasi → jalankan migrasi → verifikasi → bersihkan kode yang menangani skema yang sudah hilang”
    • Migrasi harus kompatibel ke belakang, sehingga skema database harus bisa mendukung dua versi aplikasi sekaligus
      Ini biaya tambahan yang harus dibayar untuk zero-downtime deployment atau rolling deployment, dan perusahaan besar umumnya memang melakukannya seperti ini
    • Cara ketika benar-benar membutuhkan zero downtime sudah dijelaskan oleh jawaban lain, tetapi untuk sebagian besar organisasi dan sebagian besar migrasi, waktu henti karena migrasi DB pada praktiknya nyaris tak terbedakan dari nol
      Apalagi jika penggunanya berada dalam satu wilayah tertentu dan Anda bisa memilih jam yang sepi untuk deployment
    • Saya belum memakainya sendiri, tetapi tampaknya Xata setidaknya untuk Postgres telah membuat solusi yang cukup rapi untuk migrasi DB
      Dua versi aplikasi bisa berjalan bersamaan
      https://xata.io/blog/multi-version-schema-migrations
    • Benar, pada suatu titik kedua versi memang harus sama-sama berjalan
      Load balancer mulai menerima koneksi di Server2 dan berhenti menerima koneksi baru di Server1
      Setelah semua koneksi di Server1 tertutup, barulah Server1 diputus
      Ini bisa berupa server yang berbeda, atau beberapa worker dalam satu server
      Selama periode itu, seperti jawaban-jawaban lain jelaskan, migrasi harus kompatibel ke belakang
  • Kamal 2 saat ini masih RC(https://github.com/basecamp/kamal/releases), dan tampaknya akan mendukung SSL otomatis, jadi saya menantikan kemudahan menjalankan beberapa aplikasi dengan Kamal di satu server

    • Menggunakan paket autocert, tetapi ini terasa seperti implementasi yang sangat minimal
      Rentan, dan karena batas penerbitan Let's Encrypt serta batas sertifikat bersamaan, sulit untuk melakukan scale-out horizontal pada instance proxy
      Caddy/Certmagic menyelesaikan masalah ini dengan menulis data ke penyimpanan bersama, sehingga hanya satu sertifikat yang diterbitkan dan semua instance dapat menggunakan ulang serta berkoordinasi lewat penyimpanan itu
      Tidak ada pengganti issuer, penghindaran batas penerbitan, atau dukungan ARI
      Soal menahan request sampai upstream siap juga ditangani dengan baik oleh Caddy jika mengatur try_duration dan try_interval pada reverse_proxy
      Penanganan header proxy juga tidak mempertimbangkan IP tepercaya, jadi jika diaktifkan, seseorang bisa memalsukan IP dengan menetapkan X-Forwarded-For
      Untungnya default-nya nonaktif, tetapi juga tidak ada peringatannya
      Saya paham tujuannya adalah kesederhanaan, tetapi dalam kondisi sekarang rasanya belum matang, dan ada terlalu banyak pilihan yang lebih baik sehingga saya tidak ingin memakainya di dunia nyata
  • Saya tidak begitu paham cara memakai ini dan rasanya ada yang saya lewatkan
    Contohnya memulai 4 replika layanan web
    Jika kita bisa membuat layanan dengan men-deploy ke satu replika seperti example-web-1, saya tidak paham tiga replika lainnya dipakai untuk apa
    Untuk memperbarui web agar deployment tanpa downtime bisa dilakukan, sepertinya kita harus menjalankan perintah build pada layanan web, lalu menyalakan layanan ini dengan suatu cara, kemudian menjalankan deploy terhadap target baru
    Tetapi jika menjalankan docker compose up --build --force-recreate web, replika lama akan dimatikan sehingga semuanya jadi tidak berarti

    • Replika lain pada pertanyaan pertama akan di-load-balance oleh Docker, baik lewat VIP maupun dengan mengembalikan beberapa IP untuk request DNS[0]
      Saya belum mengecek apakah proxy ini menyeimbangkan beban ke beberapa record yang dikembalikan dari request DNS, tetapi setidaknya kalau load balancing berbasis VIP, seharusnya bekerja seperti yang diharapkan
      Bagian pembaruan kedua kurang jelas
      Mungkin yang diharapkan adalah menjalankan layanan dengan nama berbeda di jaringan yang sama lalu melakukan kamal-proxy deploy
      Bisa saja nama layanannya menyertakan nomor versi, dan jika ingin rollback cepat, masuk akal juga menjaga versi sebelumnya tetap hangat
      [0]: https://docs.docker.com/reference/compose-file/deploy/#endpo...
    • Saya benar-benar tidak mengerti alasan untuk tidak langsung memakai k8s rollout restart deployment
      Atau cukup alihkan DNS atau router antara dua backend
  • Saya penasaran apakah ini mengimplementasikan pola traffic pausing
    Proxy pada dasarnya menghentikan lalu lintas selama beberapa detik, sehingga request yang masuk terlihat memakan waktu beberapa detik lebih lama dari biasanya, tetapi selesai setelah jeda singkat
    Dalam beberapa detik itu, Anda bisa menjalankan migrasi DB kecil atau perubahan infrastruktur yang lebih rumit namun bersifat blocking selama masih bisa selesai dalam 5 detik

    • Saya penasaran apakah ada yang pernah melakukan ini di lingkungan produksi sungguhan
      Terdengar cukup berisiko, dan walau saya sudah beberapa tahun bekerja di perusahaan app server, ini pertama kalinya saya mendengar pola ini
      Meski begitu, kalau ini datang dari salah satu pendiri Django, sulit juga untuk langsung mengabaikannya
    • Sejujurnya ini terdengar seperti pola hidup berbahaya
    • Caddy memang melakukan ini
      Saya ingat pernah membicarakan topik ini sebelumnya
  • Saya penasaran di mana dijelaskan alasan memilih membuat proxy sendiri alih-alih memakai Traefik atau alat lain yang sudah terbukti

  • Saya penasaran apakah ada cara untuk mengatur timeout
    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/

  • Ini NIH. Tidak ada lagi yang perlu ditambahkan