Service tipe ClusterIP memberikan satu alamat IP virtual yang stabil untuk sekelompok Pod di dalam kluster. Pod dapat dibuat dan dihapus kapan saja, sedangkan alamat IP Pod berubah setiap kali itu terjadi, sehingga klien memerlukan titik akses yang tidak bergeser. Topik ini membahas masalah yang dipecahkan oleh ClusterIP, struktur manifest Service berbasis selector, cara objek Endpoints terbentuk, serta simulasi distribusi trafik yang dapat dijalankan di sandbox. Setelah membacanya, pembaca memahami alasan ClusterIP menjadi dasar service discovery di dalam kluster Kubernetes.
IP Pod yang tidak stabil#
Setiap Pod memperoleh alamat IP dari rentang alamat Pod yang dikonfigurasi pada kluster. Alamat tersebut hanya hidup selama Pod itu ada. Apabila Pod dihapus karena replika diskalakan turun, Node mengalami masalah, atau Deployment melakukan rollout baru, Pod penggantinya hampir pasti memperoleh alamat yang berbeda.
Keadaan ini tidak mengganggu komunikasi searah seperti pengiriman log, tetapi menjadi masalah serius bagi klien yang memanggil satu layanan secara berulang. Klien yang menyimpan alamat Pod di konfigurasinya akan gagal setiap kali alamat berubah. Dengan tiga replika, klien juga perlu memilih satu dari tiga alamat, sedangkan daftar alamat tersebut terus berubah mengikuti penjadwalan.
ClusterIP menyelesaikan kedua persoalan itu sekaligus. Service menyediakan satu alamat virtual yang tidak berubah selama Service ada, dan Service itulah yang mengikuti daftar Pod terkini yang cocok dengan selector-nya. Klien cukup mengetahui satu alamat dan satu port, tanpa perlu memantau kehidupan Pod satu per satu.
Manifest Service dan selector#
Manifest Service berbasis ClusterIP hanya memerlukan selector dan satu daftar port. Bidang spec.selector berisi label yang dicocokkan terhadap label Pod, sedangkan setiap entri pada spec.ports mendeklarasikan port yang diekspos Service dan targetPort yang dituju di dalam Pod. Port Service inilah yang dipanggil klien, sedangkan targetPort adalah port proses di dalam container.
Bidang metadata.name menentukan identitas Service di seluruh kluster dan menjadi dasar nama DNS-nya. Nama ini harus unik di dalam satu namespace, sehingga konvensi penamaan yang konsisten memudahkan pemanggilan dari komponen lain. Bidang labels pada metadata tidak wajib, tetapi membantu pengelompokan saat jumlah objek di kluster bertambah.
Berkas service.yaml di bawah ini mendefinisikan Service bernama service-aplikasi yang memilih Pod berlabel app: aplikasi-catatan dan mengekspos port 80 ke arah port 8080 pada Pod.
apiVersion: v1
kind: Service
metadata:
name: service-aplikasi
labels:
app: aplikasi-catatan
spec:
type: ClusterIP
selector:
app: aplikasi-catatan
ports:
- name: http
port: 80
targetPort: 8080Perintah berikut menerapkan manifest tersebut lalu menampilkan Service yang terbentuk bersama objek Endpoints-nya.
# tidak-dijalankan
# terapkan manifest Service ke kluster
kubectl apply -f service.yaml
# tampilkan Service dan Endpoints yang terbentuk
kubectl get service,endpointsNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service-aplikasi ClusterIP 10.96.42.17 <none> 80/TCP 5s
NAME ENDPOINTS AGE
service-aplikasi 10.244.1.5:8080,10.244.2.11:8080,10.244.3.7:8080 5sCara kerja Endpoints dan kube-proxy#
Service tidak menyimpan daftar Pod secara langsung. Controller pada control plane terus memantau Pod yang cocok dengan selector, lalu mengisi objek Endpoints dengan pasangan alamat IP dan port dari Pod yang siap menerima trafik. Apabila satu Pod mati dan Pod baru muncul, isi objek Endpoints berubah otomatis, sedangkan alamat virtual Service tetap.
Pada setiap Node, kube-proxy menerjemahkan alamat virtual Service menjadi aturan penerusan paket. Mekanismenya bergantung pada implementasi, umumnya berupa aturan iptables atau program eBPF yang memilih salah satu endpoint untuk setiap koneksi baru. Alamat virtual tidak pernah dipasang pada antarmuka jaringan mana pun, sehingga alamat tersebut hanya bermakna di dalam jaringan kluster.
Diagram berikut merangkum alur permintaan dari Pod klien menuju salah satu endpoint.
Perintah describe memperlihatkan selector, alamat virtual, dan daftar endpoint yang terbentuk untuk service-aplikasi.
# tidak-dijalankan
# tampilkan detail Service beserta endpoint yang ditemukan selector
kubectl describe service service-aplikasiName: service-aplikasi
Namespace: default
Selector: app=aplikasi-catatan
Type: ClusterIP
IP: 10.96.42.17
Port: http 80/TCP
TargetPort: 8080/TCP
Endpoints: 10.244.1.5:8080,10.244.2.11:8080,10.244.3.7:8080Simulasi distribusi trafik#
Service meneruskan setiap koneksi baru ke salah satu endpoint yang tersedia, dan kube-proxy umumnya memilih endpoint secara bergilir dengan pola round-robin. Skrip berikut membaca service.yaml menggunakan PyYAML, mencetak selector dan port yang terdaftar, lalu mensimulasikan sepuluh permintaan yang dibagikan ke tiga endpoint secara deterministik melalui pembagian berputar berbasis modulo. Pembagian berbasis modulo dipilih agar hasilnya dapat dihitung ulang persis, berbeda dari pemilihan endpoint pada kube-proxy yang dapat bergantung pada detail implementasinya.
"""Simulasi distribusi trafik Service ClusterIP secara round-robin deterministik."""
import yaml
with open("service.yaml", encoding="utf-8") as berkas:
manifest = yaml.safe_load(berkas)
spec = manifest["spec"]
# daftar endpoint yang dilaporkan objek Endpoints untuk selector di atas
endpoints = ["10.244.1.5", "10.244.2.11", "10.244.3.7"]
print(f"Nama Service : {manifest['metadata']['name']}")
print(f"Tipe : {spec['type']}")
print(f"Selector : {spec['selector']}")
for port in spec["ports"]:
print(f"Port : {port['port']} -> targetPort {port['targetPort']}")
print()
print(f"Distribusi 10 permintaan ke {len(endpoints)} endpoint (pembagian berputar):")
rekap = {ep: 0 for ep in endpoints}
for urutan in range(10):
tujuan = endpoints[urutan % len(endpoints)]
rekap[tujuan] += 1
print(f" permintaan {urutan + 1:2d} -> {tujuan}")
print()
print("Rekapitulasi:")
for ep, jumlah in rekap.items():
print(f" {ep} : {jumlah} permintaan")Nama Service : service-aplikasi
Tipe : ClusterIP
Selector : {'app': 'aplikasi-catatan'}
Port : 80 -> targetPort 8080
Distribusi 10 permintaan ke 3 endpoint (pembagian berputar):
permintaan 1 -> 10.244.1.5
permintaan 2 -> 10.244.2.11
permintaan 3 -> 10.244.3.7
permintaan 4 -> 10.244.1.5
permintaan 5 -> 10.244.2.11
permintaan 6 -> 10.244.3.7
permintaan 7 -> 10.244.1.5
permintaan 8 -> 10.244.2.11
permintaan 9 -> 10.244.3.7
permintaan 10 -> 10.244.1.5
Rekapitulasi:
10.244.1.5 : 4 permintaan
10.244.2.11 : 3 permintaan
10.244.3.7 : 3 permintaanSepuluh permintaan menghasilkan pembagian 4, 3, dan 3. Pembagian tidak sepenuhnya rata apabila jumlah permintaan tidak habis dibagi banyaknya endpoint, dan permintaan ke-10 kembali ke endpoint pertama. Prinsip yang sama berlaku pada Service asli, yaitu setiap endpoint menerima bagian yang hampir merata dalam jangka panjang. Nilai modulo pada skrip memilih endpoint dari sisa pembagian nomor permintaan, sehingga urutan pemilihan berputar penuh sebelum kembali ke awal. Pada kluster nyata, kube-proxy bekerja per koneksi, bukan per permintaan, sehingga koneksi HTTP yang panjang tetap terikat pada satu endpoint sampai koneksi ditutup.
Kesalahan umum#
Kesalahan konfigurasi ClusterIP hampir selalu bermanifestasi sebagai Endpoints yang kosong atau koneksi yang ditolak. Tiga kasus berikut paling sering ditemui.
- Selector tidak cocok dengan Pod mana pun. Penyebabnya paling sering salah ketik pada nilai label, misalnya
app: aplikasiCatatanpadahal Pod berlabelaplikasi-catatan. Service tetap dibuat, tetapi Endpoints kosong dan permintaan ditolak. Perbaikannya adalah menyamakan nilai label pada Pod dan selector, lalu memeriksakubectl get endpoints. targetPorttidak sesuai port yang mendengarkan di dalam container. Service meneruskan paket ke port yang dituju, tetapi tidak ada proses yang mendengarkan di sana, sehingga koneksi ditolak. Perbaikannya adalah menyamakantargetPortdengan port yang dideklarasikan padaspec.containers[].portsPod.- ClusterIP diakses dari luar kluster. Alamat virtual hanya berlaku di dalam jaringan kluster, sehingga panggilan dari komputer di luar kluster menuju alamat tersebut tidak berfungsi. Apabila akses eksternal diperlukan, gunakan NodePort atau LoadBalancer.
Ketiga kasus ini berbagi satu pola perbaikan, yaitu mengamati hasil nyata sebelum mengubah manifest. Perintah kubectl get endpoints menampilkan hasil pencocokan selector pada objek Endpoints, sedangkan kubectl describe memperlihatkan target port yang terdaftar, sehingga kedua perintah tersebut menutup sebagian besar diagnosis ClusterIP. Dengan urutan itu, kebanyakan kasus ClusterIP selesai tanpa menelusuri komponen lain.
Ringkasan#
- ClusterIP memberikan satu IP virtual yang stabil sepanjang hidup Service, sedangkan alamat Pod berubah setiap kali Pod dibuat ulang.
- Controller mengisi objek Endpoints dari Pod yang cocok dengan selector, dan kube-proxy menerjemahkan alamat virtual menjadi aturan penerusan paket pada setiap Node.
- Kesalahan selector atau
targetPortbermanifestasi sebagai Endpoints kosong atau koneksi yang ditolak, sehinggakubectl get endpointsmenjadi pemeriksaan pertama yang paling berguna.
Referensi#
- Service — Dokumentasi Kubernetes — Konsep Service, tipe ClusterIP, objek Endpoints, dan pemetaan port.
- Service v1 — Dokumentasi Kubernetes
— Referensi API untuk bidang
specService beserta selector dan ports.