Menggunakan IP Yang Sama Untuk Beberapa Service TCP Berbeda
Di Kubernetes, terdapat Ingress yang dapat dipakai untuk mempublikasikan beberapa Service berbeda melalui satu IP yang sama. Akan tetapi, sintaks Ingress hanya bekerja pada lapisan HTTP(S) dimana ia melakukan pemetaan ke Service berdasarkan path di URL. Namun, ada kalanya layanan non-HTTP juga perlu dipublikasikan. Layanan TCP/UDP non-HTTP tidak mengenal konsep URL yang hanya ada di aplikasi web. Lalu, bagaimana bila ingin melakukan hal yang sama seperti di Ingress tetapi pemetaan dilakukan berdasarkan nomor port?
Untuk menunjukkan permasalahan ini secara jelas, saya akan membuat dua StatefulSet baru yang menerima koneksi dari port TCP yang berbeda. Pada percobaan sederhana ini, saya akan menggunakan nc -lp
untuk mewakili sebuah layanan yang menerima koneksi di port tertentu. Sebagai contoh, ini adalah definisi untuk layanan-xyz-port-10000
:
Dan ini adalah definisi untuk layanan-xyz-port-20000
:
Salah satu pendekatan naif untuk mempublikasikan kedua StatefulSet di atas adalah dengan menggunakan selector app=layanan-xyz
karena kedua StatefulSet di atas sama-sama memiliki label app=layanan-xyz
. Sebagai contoh, saya akan mencoba membuat sebuah
Service seperti berikut ini:
Walaupun secara sintaks, tidak ada yang salah pada manifest di atas, terdapat sebuah kesalahan logika yang mungkin saja bisa terlewatkan.
Service di atas akan menerima masukan pada port 10000
dan port 20000
lalu melewatkannya ke seluruh Pod yang memenuhi kriteria
app=layanan-xyz
(gabungan antara layanan-xyz-port-10000
dan layanan-xyz-port-20000
) secara acak. Ini berarti ada kemungkinan
request untuk port 10000
dilewatkan ke layanan-xyz-port-20000
dan juga sebaliknya. Karena layanan-xyz-port-20000
tidak menerima
koneksi di port 10000
, tentu saja ini akan menimbulkan pesan kesalahan “Connection refused”. Oleh sebab itu, bila saya mencoba melakukan
koneksi berulang kali ke port 10000
, akan ada kemungkinan saya menerima pesan kesalahan seperti berikut ini:
Tergantung pada keberuntungan apakah koneksi akan diteruskan ke Pod yang benar, beberapa penggunaka akan mendapatkan respon sukses dan pengguna lainnya akan mendapatkan pesan kesalahan. Tentu saja saya tidak ingin aplikasi yang eksekusinya bergantung pada keberuntungan! Dengan demikian, Service di atas tidak dapat dipakai untuk melewatkan layanan melalui port berbeda dengan IP yang sama!
Salah satu solusi untuk permasalahan ini adalah dengan menggunakan Ingress Controller yang memiliki kapabilitas untuk meneruskan
layanan TCP/UDP. Sebagai latihan, saya akan menggunakan ingress-nginx
yang sudah memiliki kemampuan serupa. Karena definisi Ingress
bawaan Kubernetes hanya mendukung sintaks pembagian berdasarkan path, sebagai gantinya, ingress-nginx
menggunakan ConfigMap
dengan
sintaks-nya tersendiri yang hanya dimengerti oleh Ingress Controller tersebut.
Fitur penerusan TCP/UDP di ingress-nginx
tidak diaktifkan secara default! Oleh sebab itu, saya perlu mengaktifkannya terlebih dahulu sebelum
melakukan instalasi ingress-nginx
. Sebagai contoh, saya melakukan perubahan pada file https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.3.1/deploy/static/provider/cloud/deploy.yaml dengan menambahkan baris --tcp-services-configmap
di args
untuk Deployment
dengan nama ingress-nginx-controller
:
Selain itu, saya juga perlu mempublikasikan port yang dibutuhkan dengan menambahkannya di bagian Service ingress-nginx-controller
:
Sekarang, saya siap melakukan instalasi dengan memberikan perintah kubectl apply -f deploy.yaml
dimana deploy.yaml
mewakili file manifest yang telah saya modifikasi di atas.
Berikutnya, saya perlu membuat Service lokal yang mewakili masing-masing layanan yang hendak diakses melalui ingress-nginx
. Sebagai contoh, saya membuat Service dengan isi seperti berikut ini untuk menerima pesan di port 10000
:
Saya juga melakukan hal serupa untuk membuat Service yang menerima pesan di port 20000
:
Sebagai langkah terakhir, saya perlu membuat ConfigMap dengan nama tcp-services
yang mewakili pemetaan dari port TCP ke Service yang bersangkutan dengan isi seperti berikut ini:
Pada konfigurasi di atas, default/layanan-xyz-port-10000
dan default/layanan-xyz-port-20000
adalah nama Service yang saya buat pada langkah sebelumnya. Keduanya berada di namespace default
bawaan (ini adalah namespace yang dipakai bila tidak ditentukan).
Sekarang, bila saya mengakses port 10000
dari IP milik ingress-nginx-controller
, saya akan selalu mendapatkan respon dari Service yang benar tanpa pesan kesalahan “Connection refused” lagi:
Begitu juga bila saya mengakses port 20000
dari IP tersebut, saya tidak akan menemukan pesan kesalahan lagi: