APISIX: Migrasikan Operasi etcd dari HTTP ke gRPC

Zexuan Luo

Zexuan Luo

February 10, 2023

Products

Keterbatasan Operasi etcd Berbasis HTTP pada Apache APISIX

Ketika etcd masih dalam versi 2.x, antarmuka API yang diekspos adalah HTTP 1 (kita akan menyebutnya sebagai HTTP dari sekarang). Setelah etcd ditingkatkan ke versi 3.x, protokolnya diubah dari HTTP ke gRPC. Untuk pengguna yang tidak mendukung gRPC, etcd menyediakan gRPC-Gateway untuk memproksi permintaan HTTP sebagai gRPC untuk mengakses API gRPC yang baru.

Ketika APISIX mulai menggunakan etcd, API yang digunakan adalah etcd v2. Pada APISIX 2.0 (2020), kami telah memperbarui persyaratan untuk etcd dari versi 2.x ke 3.x. Kompatibilitas etcd dengan HTTP telah menghemat upaya kami untuk pembaruan versi. Kami hanya perlu memodifikasi kode pada metode pemanggilan dan pemrosesan respons. Namun, selama bertahun-tahun, kami juga menemukan beberapa masalah terkait API HTTP etcd. Masih ada beberapa perbedaan halus. Kami menyadari bahwa memiliki gRPC-gateway tidak berarti dapat mendukung akses HTTP dengan sempurna.

Berikut adalah daftar masalah terkait yang kami temui dengan etcd selama beberapa tahun terakhir:

  1. gRPC-gateway dinonaktifkan secara default. Karena kelalaian pengelola, konfigurasi default etcd tidak mengaktifkan gRPC-gateway di beberapa proyek. Jadi kami harus menambahkan instruksi dalam dokumen untuk memeriksa apakah etcd saat ini memiliki gRPC-gateway yang diaktifkan. Lihat https://github.com/apache/apisix/pull/2940.
  2. Secara default, gRPC membatasi respons hingga 4MB. etcd menghapus batasan ini di SDK yang disediakannya tetapi tidak di gRPC-gateway. Ternyata etcdctl resmi (yang dibangun di atas SDK yang disediakan) berfungsi dengan baik, tetapi APISIX tidak. Lihat https://github.com/etcd-io/etcd/issues/12576.
  3. Masalah yang sama - kali ini dengan jumlah maksimum permintaan untuk koneksi yang sama. Implementasi HTTP2 Go memiliki konfigurasi MaxConcurrentStreams yang mengontrol jumlah permintaan yang dapat dikirim oleh satu klien secara bersamaan, dengan default 250. Klien mana yang biasanya mengirim lebih dari 250 permintaan secara bersamaan? Jadi etcd selalu menggunakan konfigurasi ini. Namun, gRPC-gateway, "klien" yang memproksi semua permintaan HTTP ke antarmuka gRPC lokal mungkin melebihi batas ini. Lihat https://github.com/etcd-io/etcd/issues/14185.
  4. Setelah etcd mengaktifkan mTLS, etcd menggunakan sertifikat yang sama sebagai sertifikat server dan sertifikat klien: sertifikat server untuk gRPC-gateway dan sertifikat klien ketika gRPC-gateway mengakses antarmuka gRPC. Jika ekstensi autentikasi server diaktifkan pada sertifikat, tetapi ekstensi autentikasi klien tidak diaktifkan, akan terjadi kesalahan dalam verifikasi sertifikat. Sekali lagi, mengakses langsung dengan etcdctl berfungsi dengan baik (karena sertifikat tidak akan digunakan sebagai sertifikat klien dalam kasus ini), tetapi APISIX tidak. Lihat https://github.com/etcd-io/etcd/issues/9785.
  5. Setelah mengaktifkan mTLS, etcd memungkinkan konfigurasi kebijakan keamanan informasi pengguna sertifikat. Seperti yang disebutkan di atas, gRPC-gateway menggunakan sertifikat klien tetap saat mengakses antarmuka gRPC daripada informasi sertifikat yang digunakan untuk mengakses antarmuka HTTP di awal. Dengan demikian, fitur ini tidak akan berfungsi secara alami karena sertifikat klien tetap dan tidak akan diubah. Lihat https://github.com/apache/apisix/issues/5608.

Kami dapat merangkum masalah dalam dua poin:

  1. gRPC-gateway (dan mungkin upaya lain untuk mengonversi HTTP ke gRPC) bukanlah solusi ajaib yang memperbaiki semua masalah.
  2. Pengembang etcd tidak menekankan cukup pada metode HTTP ke gRPC. Dan pengguna terbesarnya, Kubernetes, tidak menggunakan fitur ini.

Untuk mengatasi masalah ini, kami perlu menggunakan etcd langsung melalui gRPC, sehingga kami tidak perlu melalui jalur HTTP gRPC-Gateway yang disediakan untuk kompatibilitas.

Mengatasi Tantangan Migrasi ke gRPC

Bug di lua-protobuf

Masalah pertama kami selama proses migrasi adalah bug tak terduga di pustaka pihak ketiga. Seperti kebanyakan aplikasi OpenResty, kami menggunakan lua-protobuf untuk mendekode/mengode protobuf.

Setelah mengintegrasikan file proto etcd, kami menemukan bahwa terkadang terjadi crash di kode Lua, melaporkan kesalahan "table overflow". Karena crash ini tidak dapat direproduksi dengan andal, insting pertama kami adalah mencari contoh minimal yang dapat direproduksi. Menariknya, jika Anda menggunakan file proto etcd saja, Anda tidak dapat mereproduksi masalah sama sekali. Crash ini tampaknya hanya terjadi ketika APISIX sedang berjalan.

Setelah beberapa debugging, saya menemukan masalah di lua-protobuf saat mengurai bidang oneof dari file proto. lua-protobuf mencoba mengalokasikan ukuran tabel sebelumnya saat mengurai, dan ukuran yang dialokasikan dihitung sesuai dengan nilai tertentu. Ada kemungkinan tertentu bahwa nilai ini akan menjadi angka negatif. Kemudian LuaJIT akan mengonversi angka ini menjadi angka positif besar saat mengalokasikan, menghasilkan kesalahan "table overflow". Saya melaporkan masalah ini kepada penulis dan kami mempertahankan fork dengan solusi sementara secara internal.

Penulis lua-protobuf sangat responsif, memberikan perbaikan keesokan harinya dan merilis versi baru beberapa hari kemudian. Ternyata ketika lua-protobuf membersihkan file proto yang tidak lagi digunakan, ia melewatkan beberapa bidang, menghasilkan angka negatif yang tidak masuk akal ketika oneof diproses kemudian. Masalah ini hanya terjadi sesekali, dan mengapa tidak dapat direproduksi ketika menggunakan file proto etcd saja karena melewatkan langkah-langkah membersihkan bidang-bidang ini.

Menyelaraskan dengan Perilaku HTTP

Selama proses migrasi, saya menemukan bahwa API yang ada tidak mengembalikan hasil eksekusi secara tepat tetapi respons HTTP dengan status respons dan badan. Kemudian, pemanggil perlu memproses respons HTTP sendiri.

Jika respons dalam gRPC, mereka perlu dibungkus dengan cangkang respons HTTP untuk menyelaraskan dengan logika pemrosesan. Jika tidak, pemanggil perlu memodifikasi kode di beberapa tempat untuk menyesuaikan dengan format respons baru (gRPC). Terutama mengingat bahwa operasi etcd berbasis HTTP lama juga perlu didukung secara bersamaan.

Meskipun menambahkan lapisan tambahan untuk kompatibilitas dengan respons HTTP tidak diinginkan, kami harus mengatasinya. Selain itu, kami juga perlu melakukan beberapa pemrosesan pada respons gRPC. Misalnya, ketika tidak ada data yang sesuai, HTTP tidak mengembalikan data apa pun, tetapi gRPC mengembalikan tabel kosong. Ini juga perlu disesuaikan untuk menyelaraskan dengan perilaku HTTP.

Dari Koneksi Pendek ke Koneksi Panjang

Dalam operasi etcd berbasis HTTP, APISIX menggunakan koneksi pendek, sehingga tidak perlu mempertimbangkan manajemen koneksi. Yang perlu kami lakukan hanyalah memulai koneksi baru setiap kali kami membutuhkannya dan menutupnya setelah selesai.

Tetapi gRPC tidak dapat melakukan ini. Salah satu tujuan utama migrasi ke gRPC adalah untuk mencapai multiplexing, yang tidak dapat dicapai jika koneksi gRPC baru dibuat untuk setiap operasi. Di sini kami perlu berterima kasih kepada gRPC-go, karena kemampuannya dalam manajemen koneksi bawaan, yang dapat secara otomatis menyambung kembali begitu koneksi terputus. Jadi kami dapat menggunakan gRPC-go untuk menggunakan kembali koneksi. Dan hanya kebutuhan bisnis yang perlu dipertimbangkan di tingkat APISIX.

Operasi etcd APISIX dapat dibagi menjadi dua kategori, satu adalah operasi CRUD (tambah, hapus, ubah, query) pada data etcd; yang lainnya adalah menyinkronkan konfigurasi dari control plane. Secara teori, kedua operasi etcd ini dapat berbagi koneksi gRPC yang sama, tetapi kami memutuskan untuk memisahkannya menjadi dua koneksi demi pemisahan tanggung jawab. Untuk koneksi operasi CRUD, karena APISIX perlu diperlakukan secara terpisah saat startup dan setelah startup, pernyataan if ditambahkan saat mendapatkan koneksi baru. Jika ada ketidakcocokan (yaitu koneksi saat ini dibuat saat startup sementara kami membutuhkan koneksi setelah startup), maka kami akan menutup koneksi saat ini dan membuat yang baru. Saya telah mengembangkan metode sinkronisasi baru untuk sinkronisasi konfigurasi, sehingga setiap sumber daya menggunakan stream di bawah koneksi yang ada untuk memantau etcd.

Manfaat Migrasi ke gRPC

Satu manfaat yang jelas setelah migrasi ke gRPC adalah jumlah koneksi yang diperlukan untuk mengoperasikan etcd sangat berkurang. Saat mengoperasikan etcd melalui HTTP, APISIX hanya dapat menggunakan koneksi pendek. Dan saat menyinkronkan konfigurasi, setiap sumber daya akan memiliki koneksi terpisah.

Setelah beralih ke gRPC, kami dapat menggunakan fungsi multiplexing gRPC, dan setiap sumber daya hanya menggunakan satu stream alih-alih koneksi lengkap. Dengan cara ini, jumlah koneksi tidak lagi meningkat dengan jumlah sumber daya. Mengingat bahwa pengembangan APISIX selanjutnya akan memperkenalkan lebih banyak jenis sumber daya, misalnya, versi terbaru 3.1 telah menambahkan secrets, pengurangan jumlah koneksi dengan menggunakan gRPC akan lebih signifikan.

Saat menggunakan gRPC untuk sinkronisasi, setiap proses hanya memiliki satu (dua, jika subsistem stream diaktifkan) koneksi untuk sinkronisasi konfigurasi. Pada gambar di bawah, kita dapat melihat bahwa dua proses memiliki empat koneksi, dua di antaranya untuk sinkronisasi konfigurasi, Admin API menggunakan satu koneksi, dan koneksi yang tersisa adalah untuk agen istimewa untuk melaporkan info server.

gRPC menggunakan lebih sedikit koneksi

Sebagai perbandingan, gambar di bawah menunjukkan 22 koneksi yang diperlukan untuk menggunakan metode sinkronisasi konfigurasi asli sambil mempertahankan parameter lain tetap tidak berubah. Selain itu, koneksi ini adalah koneksi pendek.

terlalu banyak koneksi

Satu-satunya perbedaan antara kedua konfigurasi ini adalah apakah gRPC diaktifkan untuk operasi etcd:

etcd: use_grpc: true host: - "http://127.0.0.1:2379" prefix: "/apisix" ...

Selain mengurangi jumlah koneksi, menggunakan gRPC untuk mengakses etcd langsung alih-alih gRPC-gateway dapat menyelesaikan serangkaian masalah yang dibatasi secara arsitektur seperti autentikasi mTLS yang disebutkan di awal artikel. Akan ada lebih sedikit masalah setelah menggunakan gRPC, karena Kubernetes menggunakan gRPC untuk mengoperasikan etcd. Jika ada masalah, itu akan ditemukan oleh komunitas Kubernetes.

Tentu saja, karena metode gRPC masih relatif baru, APISIX pasti akan memiliki beberapa masalah baru saat mengoperasikan etcd melalui gRPC. Saat ini, default masih menggunakan metode berbasis HTTP asli untuk mengoperasikan etcd secara default. Pengguna memiliki opsi untuk mengonfigurasi use_grpc di bawah etcd ke true di config.yaml sendiri. Anda dapat mencoba apakah metode gRPC lebih baik. Kami juga akan terus mengumpulkan umpan balik dari berbagai sumber untuk meningkatkan operasi etcd berbasis gRPC. Ketika kami menemukan pendekatan gRPC cukup matang, kami akan menjadikannya pendekatan default.

Untuk Memaksimalkan APISIX, Anda Membutuhkan API7

Anda menyukai kinerja Apache APISIX, bukan beban mengelolanya. Anda dapat fokus pada bisnis inti Anda tanpa khawatir tentang konfigurasi, pemeliharaan, dan pembaruan.

Tim kami terdiri dari pencipta dan kontributor Apache APISIX, pengelola inti OpenResty dan NGINX, anggota Kubernetes, dan ahli industri di bidang infrastruktur cloud. Anda mendapatkan orang-orang terbaik di balik layar.

Apakah Anda ingin mempercepat pengembangan Anda dengan percaya diri? Untuk memaksimalkan dukungan APISIX, Anda membutuhkan API7. Kami menyediakan dukungan mendalam untuk APISIX dan solusi manajemen API berdasarkan kebutuhan Anda!

Hubungi kami sekarang: https://api7.ai/contact.

Tags: