Target Audience: Backend developers who want to configure network access in Kubernetes Prerequisites: Pod, Deployment concepts After reading this: You will understand how to reliably access Pods with Services

TL;DR
  • Service provides a stable network endpoint for a set of Pods
  • Pod IPs change but Service IP (ClusterIP) is fixed
  • Three types exist: ClusterIP, NodePort, LoadBalancer

Why Service is Needed#

Pod IPs change whenever Pods are recreated. How can we communicate reliably in this situation?

flowchart LR
    subgraph Before[Before restart]
        P1[Pod<br>10.244.1.5]
    end
    subgraph After[After restart]
        P2[Pod<br>10.244.1.9]
    end
    Before -->|IP changed!| After

Using Service maintains a consistent access point even when Pod IPs change.

ProblemService Solution
Pod IP changesProvide fixed Service IP
Need traffic distribution to multiple PodsAutomatic load balancing
Difficult to access by Pod nameProvide DNS name
Need external access methodProvide NodePort, LoadBalancer

How Service Works#

Service finds Pods using label selectors and distributes traffic to those Pods.

flowchart LR
    Client[Client] -->|my-service:80| SVC[Service<br>10.96.100.5]
    SVC -->|load balancing| P1[Pod 1<br>10.244.1.5]
    SVC --> P2[Pod 2<br>10.244.1.6]
    SVC --> P3[Pod 3<br>10.244.2.3]

Service and Pod connection process:

StepDescription
1. Pod creationPods created with labels
2. Service creationDefine target Pods with selector
3. Endpoints creationService automatically manages Pod IP list
4. Traffic routingkube-proxy forwards traffic to Pods

Service Types#

Kubernetes provides three Service types.

ClusterIP (Default)#

Service accessible only within the cluster.

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  type: ClusterIP  # Default value, can be omitted
  selector:
    app: my-app
  ports:
  - port: 80        # Service port
    targetPort: 8080 # Pod port
flowchart LR
    subgraph Cluster
        Pod1[Other Pod] -->|my-service:80| SVC[Service<br>ClusterIP]
        SVC --> Target[Target Pod]
    end
    External[External] -.-x|No access| SVC

ClusterIP characteristics:

CharacteristicDescription
Access scopeInternal cluster only
Use caseInternal microservice communication
DNS<service-name>.<namespace>.svc.cluster.local

NodePort#

Allows external access through specific port on all nodes.

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  type: NodePort
  selector:
    app: my-app
  ports:
  - port: 80
    targetPort: 8080
    nodePort: 30080  # Range 30000-32767
flowchart LR
    External[External] -->|Node IP:30080| Node[Node]
    Node --> SVC[Service]
    SVC --> Pod[Pod:8080]

NodePort characteristics:

CharacteristicDescription
Access scopeExternal (node IP + port)
Port range30000-32767
Use caseDev/test, simple external exposure
LimitationOnly one service per port

LoadBalancer#

Provisions cloud provider’s load balancer.

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  type: LoadBalancer
  selector:
    app: my-app
  ports:
  - port: 80
    targetPort: 8080
flowchart LR
    External[External] -->|LB IP:80| LB[Cloud LB]
    LB --> Node1[Node 1]
    LB --> Node2[Node 2]
    Node1 --> Pod1[Pod]
    Node2 --> Pod2[Pod]

LoadBalancer characteristics:

CharacteristicDescription
Access scopeExternal (fixed IP)
RequirementsCloud environment (AWS, GCP, Azure)
Use caseProduction external service
CostCloud LB cost incurred

Type Comparison Summary#

TypeAccess ScopeUse CaseCost
ClusterIPInternalInter-microservice communicationNone
NodePortExternal (node IP)Dev/testNone
LoadBalancerExternal (LB IP)ProductionYes

Service DNS#

Kubernetes automatically assigns DNS names to Services.

DNS Name Format#

<service-name>.<namespace>.svc.cluster.local

DNS name examples:

ExampleDescription
my-serviceAccess from same namespace
my-service.defaultService in default namespace
my-service.production.svc.cluster.localFull FQDN

DNS Usage#

# Using Service DNS in application
spring:
  datasource:
    url: jdbc:postgresql://db-service:5432/mydb

When accessing other Services from within a Pod, use DNS names instead of IPs.

Endpoints#

Service manages Pod IP list through Endpoints resource.

# Check Endpoints
kubectl get endpoints my-service

Expected output:

NAME         ENDPOINTS                                   AGE
my-service   10.244.1.5:8080,10.244.1.6:8080,10.244.2.3:8080   1h

If Endpoints is empty, check:

Check ItemCommand
Pod existencekubectl get pods -l <selector>
Pod Ready stateCheck READY in kubectl get pods
Label matchCompare Service selector with Pod labels

Session Affinity#

Use when you want to send requests from same client to same Pod.

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app: my-app
  sessionAffinity: ClientIP
  sessionAffinityConfig:
    clientIP:
      timeoutSeconds: 3600  # 1 hour
  ports:
  - port: 80
    targetPort: 8080

Comparing session affinity options:

OptionDescription
NoneDefault, round-robin distribution
ClientIPFixed based on client IP
Cookie-based Session Affinity
Kubernetes Service doesn’t support cookie-based session affinity. Use Ingress Controller if needed.

Connecting External Services#

You can abstract external services (e.g., external DB) as a Service.

ExternalName#

Maps external DNS name to a Service.

apiVersion: v1
kind: Service
metadata:
  name: external-db
spec:
  type: ExternalName
  externalName: database.example.com

Accessing external-db from Pod connects to database.example.com.

Service Without Selector#

Directly specify external IP.

apiVersion: v1
kind: Service
metadata:
  name: external-service
spec:
  ports:
  - port: 80
    targetPort: 80
---
apiVersion: v1
kind: Endpoints
metadata:
  name: external-service  # Same name as Service
subsets:
- addresses:
  - ip: 203.0.113.10
  ports:
  - port: 80

Practice: Creating and Testing Service#

Create ClusterIP Service#

# Create Deployment (skip if already exists)
kubectl create deployment nginx --image=nginx:1.25 --replicas=3

# Create Service
kubectl expose deployment nginx --port=80 --target-port=80

# Or create with YAML
kubectl apply -f service.yaml

Check Service#

# List Services
kubectl get services

# Detailed information
kubectl describe service nginx

# Check Endpoints
kubectl get endpoints nginx

Test from Inside Cluster#

# Create test Pod
kubectl run test --image=busybox:1.36 --rm -it -- sh

# Access via Service DNS
wget -qO- http://nginx
wget -qO- http://nginx.default.svc.cluster.local

# Make multiple requests to see distribution to different Pods
for i in 1 2 3 4 5; do wget -qO- http://nginx | head -1; done

Test NodePort#

# Create NodePort Service
kubectl expose deployment nginx --type=NodePort --port=80 --target-port=80

# Check assigned NodePort
kubectl get service nginx

# Access in Minikube
minikube service nginx --url

Troubleshooting#

Cannot Connect to Service#

  1. Check Endpoints:

    kubectl get endpoints <service-name>

    If empty, check selector and Pod labels.

  2. Check Pod Ready state:

    kubectl get pods -l <selector>

    If Pod is Running but not Ready, check readinessProbe.

  3. Check ports:

    kubectl describe service <service-name>

    Verify targetPort matches Pod’s containerPort.

DNS Resolution Failure#

# Check CoreDNS status
kubectl get pods -n kube-system -l k8s-app=kube-dns

# Test DNS
kubectl run test --image=busybox:1.36 --rm -it -- nslookup <service-name>

Next Steps#

Once you understand Services, proceed to the next steps:

GoalRecommended Doc
External HTTP routingNetworking
Separate configurationConfigMap and Secret
Actual deployment practiceSpring Boot Deployment