Menerapkan Isolasi Aplikasi Untuk Tenant Dengan Namespace Di Kubernetes
Pada suatu hari, anggap saja ada dua perusahaan fiktif dengan nama perusahaan1
dan perusahaan2
yang ingin memakai aplikasi latihan-k8s. Mereka selanjutnya akan disebut sebagai tenant. Setiap tenant akan mendaftarkan beberapa user dengan hak akses berbeda. Tentu saja mereka juga tidak ingin data mereka diakses oleh tenant lain. Salah satu solusi yang umum ditempuh untuk hal ini adalah dengan menerapkan multitenancy pada aplikasi. Ini bisa jadi membutuhkan perubahan cukup besar pada kode program bila tidak didukung dari awal pada saat dirancang. Selain itu, bila masing-masing tenant menginginkan fitur yang bertolak belakang di kemudian hari, akan cukup sulit mengelolanya di satu program yang sama. Sebagai alternatif multitenancy, saya akan mencoba menggunakan fitur namespace di Kubernetes untuk men-deploy aplikasi yang sama dengan URL berbeda yang sama sekali tidak berhubungan satu sama lainnya. Penerapan GitOps akan membantu mempermudah proses inisialisasi namespace untuk tenant.
Fitur namespace di Kubernetes memungkinkan saya untuk men-deploy resource dengan nama yang sama tanpa takut terjadi konflik. Walaupun demikian, tidak seluruh resource Kubernetes dipisahkan oleh namespace. Sebagai contoh, PersistentVolume berlaku secara global di cluster sementara PersistentVolumeClaim bersifat unik di setiap namespace. Bila ingin mengelola PersistentVolume secara GitOps, saya perlu membuat repository Git baru terpisah untuk mewakili PersistentVolume tersebut. Namun, sebagai latihan, saya cukup menghapus seluruh deklarasi PersistentVolume yang ada. Hal ini karena Kubernetes akan mencoba membuat PersistentVolume baru untuk memenuhi apa yang dibutuhkan oleh PersistenceVolumeClaim.
Untuk mengakses aplikasi, idealnya setiap tenant dapat menggunakan subdomain seperti https://web.perusahaan1.latihan.jocki.me, https://web.perusahaan2.latihan.jocki.me, dan seterusnya. Namun ini berarti saya perlu melakukan provisioning sertifikat TLS baru setiap kali membuat tenant baru. Agar lebih sederhana, sebagai latihan, saya akan menggunakan URL seperti https://web-perusahaan1.latihan.jocki.me, https://web-perusahaan2.latihan.jocki.me, dan seterusnya. Untuk itu, saya bisa menambahkan setter Kpt dengan nama tenant
untuk setiap URL yang ada di file konfigurasi, seperti yang terlihat pada contoh berikut ini:
Selain itu, akan lebih baik bila saya melakukan instalasi Kong Ingress Controller pada namespace tersendiri (terpisah dari aplikasi). Karena manifest instalasi Kong Ingress Controller secara default akan membuat namespace dengan kong
dan melakukan instalasi ke namespace tersebut, saya cukup memberikan perintah seperti berikut ini:
Agar lebih mudah dalam pembuatan dan inisialisasi tenant baru, saya akan membuat sebuah Bash script dengan nama create-tenant.sh
dengan isi seperti berikut ini:
Sekarang, untuk menyiapkan aplikasi untuk tenant baru, saya cukup mengerjakan script di atas seperti pada contoh berikut ini:
Script di atas akan membuat dua folder baru, tenant-perusahaan1
dan tenant-perusahaan2
, dimana masing-masing merupakan repository Git yang mewakili isi namespace tenant-perusahaan1
dan tenant-perusahaan2
:
Directorydeployment
Directorytenant-perusahaan1
Directory.git
- …
Directorykubernetes
- … (manifest k8s)
Directorytenant-perusahaan2
Directory.git
- …
Directorykubernetes
- … (manifest k8s)
- create-tenant.sh
Bagian yang paling penting dari script di atas adalah kpt live apply
yang akan melakukan sinkronisasi manifest Kubernetes di folder milik tenant dengan resources Kubernetes yang ada di namespace untuk tenant tersebut. Untuk kasus yang lebih realistis, script ini dapat dipicu oleh sebuah halaman registrasi dimana setelah tenant mendaftarkan dirinya, ia akan memperoleh sebuah URL untuk mengakses aplikasi.
Bila saya membuka halaman https://web-perusahaan1.latihan.jocki.me dan https://web-perusahaan2.latihan.jocki.me, saya akan menemukan bahwa walaupun mereka dibuat berdasarkan kode program yang sama, isi database-nya berbeda sehingga perubahan pada halaman yang satu tidak akan mempengaruhi halaman lainnya, seperti yang diperlihatkan pada gambar berikut ini:
Bukan hanya itu, karena Keycloak dijalankan secara terpisah di masing-masing namespace milik tenant, kedua tenant juga dapat mendaftarkan pengguna mereka tanpa mempengaruhi pengguna milik tenant lainnya seperti yang diperlihatkan pada gambar berikut ini:
Bagaimana bila ada perubahan terbaru dari aplikasi? Sebagai contoh, anggap saja v0.0.5 dari aplikasi sudah diluncurkan. Cara yang paling aman adalah melakukan update secara manual, misalnya melakukan canary release dengan memilih tenant secara acak yang mendapatkan update. Kelebihannya adalah bila terjadi kesalahan fatal pada versi terbaru, hal tersebut tidak akan langsung mempengaruhi seluruh tenant. Untuk men-update versi aplikasi yang dipakai tenant tertentu, saya dapat memberikan perintah seperti berikut ini:
Di GitOps, setiap perubahan pada infrastruktur harus diwakili oleh sebuah commit sehingga perubahan selalu terdokumentasikan di riwayat Git. Oleh sebab itu, saya kemudian memberikan perintah berikut ini:
Merasa perubahan secara manual satu per satu terlalu repot dan ingin langsung men-update seluruh tenant yang ada sekaligus? Saya bisa membuat script untuk mengotomatisasikan proses di atas ke seluruh folder tenant yang ada seperti pada contoh berikut ini:
Untuk melihat perubahan terakhir pada infrastruktur tenant, saya dapat menggunakan git log
seperti pada contoh berikut ini:
Bila suatu hari nanti tenant perusahaan1
memiliki lonjakan jumlah pengguna dan bersedia mengambil paket dengan kinerja tinggi, saya dapat meningkatkan jumlah pod untuk tenant tersebut dengan perintah seperti:
Perubahan di atas hanya berlaku untuk tenant1 dan tidak akan mempengaruhi tenant lainnya. Untuk membuktikannya, saya dapat memberikan perintah berikut ini untuk melihat jumlah pod di setiap namespace yang ada: