Di episode ini kita akan coba bahas Kubernetes Service, fundamental networking abstraction untuk expose application. Kita akan mempelajari Service type, bagaimana mereka enable Pod communication, dan best practice untuk service discovery.

Catatan
Untuk kalian yang ingin membaca episode sebelumnya, bisa click thumbnail episode 17 di bawah ini
Di episode sebelumnya kita sudah belajar tentang bekerja dengan multiple resource menggunakan all keyword. Selanjutnya di episode 18 kali ini, kita akan coba bahas Service, salah satu konsep paling fundamental di Kubernetes networking.
Catatan: Disini saya akan menggunakan Kubernetes Cluster yang di install melalui K3s.
Pod di Kubernetes bersifat ephemeral - mereka bisa dibuat, dihancurkan, dan dibuat ulang dengan IP address yang berbeda. Service menyediakan stable endpoint untuk access Pod, abstracting away dynamic nature dari Pod IP dan enable reliable communication antara application component.
Service di Kubernetes adalah abstraction yang define logical set dari Pod dan policy untuk access mereka. Service enable network access ke set dari Pod, menyediakan stable IP address dan DNS name meskipun Pod dibuat dan dihancurkan.
Bayangkan Service seperti load balancer dengan service discovery - dia maintain stable endpoint sambil otomatis routing traffic ke healthy Pod yang match selector nya. Saat Pod datang dan pergi, Service otomatis update list endpoint nya.
Karakteristik kunci Service:
Service solve beberapa critical networking challenge:
Tanpa Service, kalian perlu:
Kubernetes menyediakan empat Service type:
Type of Service LB L4Expose Service di cluster-internal IP. Service hanya accessible dalam cluster.
Use case: Internal communication antara microservice
Expose Service di setiap Node's IP di static port. Make Service accessible dari outside cluster.
Use case: Development, testing, atau saat LoadBalancer unavailable
Expose Service externally menggunakan cloud provider's load balancer.
Use case: Production external access di cloud environment
Map Service ke external DNS name.
Use case: Access external service dengan Kubernetes DNS
ClusterIP adalah default Service type untuk internal cluster communication.
Pertama, buat Deployment:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.25
ports:
- containerPort: 80Apply Deployment:
sudo kubectl apply -f nginx-deployment.ymlBuat ClusterIP Service:
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
type: ClusterIP
selector:
app: nginx
ports:
- protocol: TCP
port: 80
targetPort: 80Apply Service:
sudo kubectl apply -f nginx-service-clusterip.ymlVerify Service:
sudo kubectl get service nginx-serviceOutput:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx-service ClusterIP 10.43.100.50 <none> 80/TCP 30sService mendapat stable ClusterIP (10.43.100.50) yang tidak akan berubah.
Test Service dari dalam cluster:
# Buat test Pod
sudo kubectl run test-pod --image=curlimages/curl:latest --rm -it -- sh
# Di dalam Pod, test Service
curl http://nginx-service
curl http://nginx-service.default.svc.cluster.localService load balance request across semua tiga nginx Pod.
Kubernetes otomatis membuat DNS record untuk Service.
Service bisa diakses menggunakan DNS name ini:
Dalam same namespace:
<service-name>Dari different namespace:
<service-name>.<namespace>Fully qualified domain name (FQDN):
<service-name>.<namespace>.svc.cluster.localapiVersion: v1
kind: Service
metadata:
name: backend
namespace: production
spec:
selector:
app: backend
ports:
- port: 8080
targetPort: 8080Frontend Pod bisa access Service ini menggunakan:
backend (jika di same namespace)backend.production (dari different namespace)backend.production.svc.cluster.local (FQDN)NodePort expose Service di setiap Node's IP di static port (30000-32767).
apiVersion: v1
kind: Service
metadata:
name: nginx-nodeport
spec:
type: NodePort
selector:
app: nginx
ports:
- protocol: TCP
port: 80
targetPort: 80
nodePort: 30080Apply Service:
sudo kubectl apply -f nginx-service-nodeport.ymlVerify:
sudo kubectl get service nginx-nodeportOutput:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx-nodeport NodePort 10.43.100.51 <none> 80:30080/TCP 30sAccess Service dari outside cluster:
curl http://<node-ip>:30080Important
Penting: NodePort Service accessible di SEMUA node di cluster, meskipun Pod tidak running di node tersebut. Kubernetes route traffic ke appropriate node.
LoadBalancer membuat external load balancer (di supported cloud environment).
apiVersion: v1
kind: Service
metadata:
name: nginx-loadbalancer
spec:
type: LoadBalancer
selector:
app: nginx
ports:
- protocol: TCP
port: 80
targetPort: 80Apply Service:
sudo kubectl apply -f nginx-service-loadbalancer.ymlVerify:
sudo kubectl get service nginx-loadbalancerOutput (di cloud environment):
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx-loadbalancer LoadBalancer 10.43.100.52 203.0.113.10 80:31234/TCP 2mEXTERNAL-IP adalah public IP yang disediakan oleh cloud load balancer.
Note
Catatan: LoadBalancer type require cloud provider support (AWS, GCP, Azure). Di local cluster seperti Minikube atau K3s, kalian mungkin butuh MetalLB atau similar solution.
Jika kalian menggunakan K3s, secara default Service dengan tipe LoadBalancer akan otomatis dihandle oleh Klipper seperti ini
Service support flexible port mapping:
ports:
- protocol: TCP
port: 80 # Service port (yang client connect ke)
targetPort: 8080 # Pod port (dimana container listen)
nodePort: 30080 # Node port (untuk NodePort/LoadBalancer)apiVersion: v1
kind: Service
metadata:
name: api-service
spec:
selector:
app: api
ports:
- name: http
protocol: TCP
port: 80
targetPort: 8080
- name: https
protocol: TCP
port: 443
targetPort: 8443Service ini:
Define named port di Pod:
apiVersion: v1
kind: Pod
metadata:
name: web-pod
labels:
app: web
spec:
containers:
- name: web
image: nginx:1.25
ports:
- name: http
containerPort: 80
- name: metrics
containerPort: 9090Reference named port di Service:
apiVersion: v1
kind: Service
metadata:
name: web-service
spec:
selector:
app: web
ports:
- name: http
port: 80
targetPort: http
- name: metrics
port: 9090
targetPort: metricsKontrol apakah request dari same client pergi ke same Pod.
apiVersion: v1
kind: Service
metadata:
name: sticky-service
spec:
selector:
app: web
sessionAffinity: ClientIP
sessionAffinityConfig:
clientIP:
timeoutSeconds: 10800
ports:
- port: 80
targetPort: 80Dengan sessionAffinity: ClientIP, request dari same client IP pergi ke same Pod untuk specified timeout (default 10800 detik = 3 jam).
Service tanpa ClusterIP, digunakan untuk direct Pod-to-Pod communication.
apiVersion: v1
kind: Service
metadata:
name: database
spec:
clusterIP: None
selector:
app: database
ports:
- port: 5432
targetPort: 5432Dengan clusterIP: None, DNS return Pod IP directly daripada Service IP.
Use case: StatefulSet dimana setiap Pod butuh stable identity.
Buat Service yang tidak otomatis select Pod.
apiVersion: v1
kind: Service
metadata:
name: external-database
spec:
ports:
- port: 5432
targetPort: 5432Manually buat Endpoint:
apiVersion: v1
kind: Endpoints
metadata:
name: external-database
subsets:
- addresses:
- ip: 192.168.1.100
ports:
- port: 5432Use case: Access external service atau database outside Kubernetes.
Map Service ke external DNS name.
apiVersion: v1
kind: Service
metadata:
name: external-api
spec:
type: ExternalName
externalName: api.example.comPod bisa access external-api yang resolve ke api.example.com.
Use case: Abstract external service URL, making it easy untuk change them later.
Frontend, backend, dan database service:
# Frontend Service (LoadBalancer untuk external access)
apiVersion: v1
kind: Service
metadata:
name: frontend
spec:
type: LoadBalancer
selector:
app: frontend
ports:
- port: 80
targetPort: 3000
---
# Backend Service (ClusterIP untuk internal access)
apiVersion: v1
kind: Service
metadata:
name: backend
spec:
type: ClusterIP
selector:
app: backend
ports:
- port: 8080
targetPort: 8080
---
# Database Service (Headless untuk StatefulSet)
apiVersion: v1
kind: Service
metadata:
name: database
spec:
clusterIP: None
selector:
app: database
ports:
- port: 5432
targetPort: 5432Application dengan HTTP dan metrics endpoint:
apiVersion: v1
kind: Service
metadata:
name: app-service
spec:
selector:
app: myapp
ports:
- name: http
port: 80
targetPort: 8080
- name: metrics
port: 9090
targetPort: 9090
- name: health
port: 8081
targetPort: 8081Different service untuk different environment:
# Production Service
apiVersion: v1
kind: Service
metadata:
name: api
namespace: production
spec:
selector:
app: api
environment: production
ports:
- port: 80
targetPort: 8080
---
# Staging Service
apiVersion: v1
kind: Service
metadata:
name: api
namespace: staging
spec:
selector:
app: api
environment: staging
ports:
- port: 80
targetPort: 8080sudo kubectl get servicesAtau shorthand:
sudo kubectl get svcsudo kubectl describe service nginx-serviceOutput show:
Name: nginx-service
Namespace: default
Labels: <none>
Annotations: <none>
Selector: app=nginx
Type: ClusterIP
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.43.100.50
IPs: 10.43.100.50
Port: <unset> 80/TCP
TargetPort: 80/TCP
Endpoints: 10.42.0.10:80,10.42.0.11:80,10.42.0.12:80
Session Affinity: None
Events: <none>sudo kubectl get endpoints nginx-serviceOutput:
NAME ENDPOINTS AGE
nginx-service 10.42.0.10:80,10.42.0.11:80,10.42.0.12:80 5mShow actual Pod IP yang Service route ke.
Problem: Service selector tidak match Pod label.
Solusi: Ensure label match exactly:
# Pod label
labels:
app: nginx
version: v1
# Service selector harus match
selector:
app: nginx
version: v1Problem: targetPort tidak match container port.
Solusi: Verify container port:
sudo kubectl get pod <pod-name> -o jsonpath='{.spec.containers[*].ports[*].containerPort}'Problem: LoadBalancer pending di local cluster.
Solusi: Gunakan NodePort untuk local development atau install MetalLB.
Problem: Service tidak punya endpoint.
Solusi: Check apakah Pod running dan label match:
sudo kubectl get endpoints <service-name>
sudo kubectl get pods -l app=<label>Problem: Tidak bisa access Service dari different namespace.
Solusi: Gunakan full DNS name:
<service-name>.<namespace>.svc.cluster.localPilih clear, descriptive name:
# Bagus
name: user-api
name: payment-service
name: database-primary
# Hindari
name: svc1
name: service
name: appService route ke Pod, jadi ensure Pod punya resource limit:
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"Make configuration lebih clear:
ports:
- name: http
port: 80
targetPort: http
- name: metrics
port: 9090
targetPort: metricsEnsure Service hanya route ke healthy Pod:
livenessProbe:
httpGet:
path: /health
port: 8080
readinessProbe:
httpGet:
path: /ready
port: 8080Jangan expose internal service unnecessarily:
# Internal microservice
type: ClusterIP
# Hanya expose yang butuh external access
type: LoadBalancerAdd annotation documenting dependency:
metadata:
annotations:
description: "User API service"
depends-on: "database, cache"
owner: "backend-team"Pada episode 18 ini, kita telah membahas Service di Kubernetes secara mendalam. Kita sudah belajar apa itu Service, different Service type, dan cara menggunakannya untuk reliable application networking.
Key takeaway:
Service adalah fundamental untuk Kubernetes networking, enable reliable communication antara application component. Dengan memahami Service, kalian bisa build robust, scalable microservices architecture dengan proper service discovery dan load balancing.
Bagaimana, makin jelas kan tentang Service di Kubernetes? Di episode 19 berikutnya, kita akan membahas Ingress, yang menyediakan sophisticated HTTP/HTTPS routing ke Service dengan feature seperti host-based routing, path-based routing, dan TLS termination. Jadi, pastikan tetap semangat belajar dan nantikan episode selanjutnya!
Catatan
Untuk kalian yang ingin lanjut ke episode berikutnya, bisa click thumbnail episode 19 di bawah ini