Beginner

Services in Kubernetes

Create and manage services, service types, kube-proxy, internal DNS, EndpointSlices and Ingress.

2 modules · ~25 minutes


Table of Contents

  1. Overview
  2. Module 1 — Creating Kubernetes Services
  3. Module 2 — Managing Kubernetes Services
  4. Service Types — Reference
  5. Kubernetes Networking — Key Concepts
  6. kube-proxy
  7. Kubernetes Internal DNS
  8. EndpointSlices
  9. Ingress
  10. kubectl Commands — Quick Reference
  11. Diagrams

Overview

A Kubernetes Service is a network abstraction that defines:

  • how external clients access Pods
  • how internal Pods discover each other

Containers are ephemeral — they appear, complete a task, and disappear. Without a Service, clients can never reliably find the Pods they need. Services solve this by providing a stable IP address and DNS name that remain constant regardless of the underlying Pod lifecycle.

Services also provide:

  • Load balancing — traffic distribution among replicas
  • Session affinity — persistent client-pod relationships
  • Health checks — exclusion of failing Pods
  • Service discovery via DNS — resolution by service name

Module 1 — Creating Kubernetes Services

Understanding the Role of Kubernetes Services

The Resource Discovery Problem

Kubernetes Pods are designed to be ephemeral: they appear, complete a task, then disappear — sometimes in just seconds. This creates two fundamental problems:

  1. How do external clients find Pods? Pod IP addresses change on every restart.
  2. How do you control access? You need to ensure only legitimate clients access the right resources.

The Default Service — kubernetes.default.svc

When any Kubernetes cluster is created, a ClusterIP Service is automatically created. This service enables connections between cluster components and the Kubernetes API server. The DNS name of this service is:

kubernetes.default.svc

DNS name breakdown:

  • kubernetes — Service name
  • default — Namespace it resides in
  • svc — suffix indicating it’s a Service

All kubectl commands use this service under the hood.

Namespaces

kubectl get namespaces

Namespaces allow organizing and isolating deployments and resources, and applying granular access permissions. A new cluster includes several predefined Namespaces:

NamespaceUsage
defaultDefault namespace for user resources
kube-systemInternal Kubernetes components (kube-proxy, CoreDNS, etc.)
kube-publicPublic resources accessible to everyone
kube-node-leaseLease objects for nodes

Defining Services Using kubectl

Creating a Deployment and Exposing as NodePort

# 1. Create an nginx Deployment
kubectl create deployment my-nginx --image=nginx:latest --port=80

# 2. Expose as NodePort Service
kubectl expose deployment my-nginx \
  --type=NodePort \
  --port=80 \
  --target-port=80 \
  --name=my-nginx-service

# 3. List Services
kubectl get services

# 4. Get node IP
kubectl get nodes -o wide

# 5. Test access
curl http://<NODE_IP>:<NODE_PORT>

Default Values of kubectl expose

ParameterDefaultOverride
--protocolTCP--protocol=UDP
--typeClusterIP--type=NodePort or --type=LoadBalancer
--portAuto-detected from Pod exposed portspecify explicitly
--target-portSame as --portspecify if different

Note: The kubectl expose command doesn’t support the --node-port argument for manually assigning a NodePort. Use a YAML manifest for that.


Defining Services Using YAML Manifests

Example Architecture

Two deployments communicate via a Service:

  • backend: hashicorp/http-echo server that responds with simple text
  • frontend: curlimages/curl container that calls the backend in a loop

The frontend uses the DNS name backend-service:80 — this works only if the Kubernetes Service creates a functioning DNS environment in the cluster.

Manifest — Backend Deployment

# backend-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend
spec:
  replicas: 1
  selector:
    matchLabels:
      app: backend
  template:
    metadata:
      labels:
        app: backend
    spec:
      containers:
      - name: http-echo
        image: hashicorp/http-echo:0.2.3
        args:
          - "-listen=:8080"
          - "-text=Hello from the Backend!"
        ports:
        - containerPort: 8080

Manifest — ClusterIP Service

# backend-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: backend-service
spec:
  type: ClusterIP   # Default type
  selector:
    app: backend    # Matches the backend Pod label
  ports:
    - port: 80          # Service port (used by internal clients)
      targetPort: 8080  # Container port in the backend Pod

Key mechanism: The selector field (app: backend) is the only link between the Service and the Pods. The Service dynamically watches all Pods with this label.

Manifest — Frontend Deployment

# frontend-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend
spec:
  replicas: 1
  selector:
    matchLabels:
      app: frontend
  template:
    metadata:
      labels:
        app: frontend
    spec:
      containers:
      - name: curl-loop
        image: curlimages/curl:7.86.0
        command: ["/bin/sh"]
        args:
          - "-c"
          - |
            while true; do
              echo "=== Calling backend-service:80 ==="
              curl -s backend-service:80
              echo
              sleep 5
            done

Deployment and Verification

kubectl apply -f backend-deployment.yaml
kubectl apply -f frontend-deployment.yaml
kubectl apply -f backend-service.yaml

# Check Pods
kubectl get pods

# Follow frontend logs (before Service: failure)
kubectl logs -f <frontend-pod-name>

# After Service deployment: success
# Output: "Hello from the Backend!"

Module 2 — Managing Kubernetes Services

Preparing Your Cluster for a LoadBalancer Service

Why Preparation Is Needed

LoadBalancer Services work natively with cloud providers (AWS, Azure, GCP) because they directly integrate external load balancer provisioning.

In bare metal or local VM environments, a LoadBalancer remains in Pending state indefinitely without additional infrastructure.

MetalLB — Load Balancer for Bare Metal Environments

MetalLB is an open source project that provides a LoadBalancer implementation for Kubernetes clusters running outside the cloud.

# 1. Apply MetalLB manifest
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/main/config/manifests/metallb-native.yaml

# 2. Verify MetalLB Pods
kubectl get pods -n metallb-system

Manifest — IP Address Pool (MetalLB)

# ip-pool.yaml
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: first-pool
  namespace: metallb-system
spec:
  addresses:
  - 192.168.1.240-192.168.1.250

Manifest — L2Advertisement (MetalLB)

# l2advertisement.yaml
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: example
  namespace: metallb-system
kubectl apply -f ip-pool.yaml
kubectl apply -f l2advertisement.yaml

MetalLB announces service IPs via the Layer 2 (ARP/NDP) protocol, making the load balancer accessible from the local network.


Launching a LoadBalancer Service

Deployment with Liveness and Readiness Probes

# nginx-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        ports:
        - containerPort: 80
        livenessProbe:
          httpGet:
            path: /
            port: 80
          initialDelaySeconds: 15
          periodSeconds: 20
        readinessProbe:
          httpGet:
            path: /
            port: 80
          initialDelaySeconds: 5
          periodSeconds: 5
ProbeRoleOn Failure
livenessProbeEnsures container is aliveKubernetes restarts the container
readinessProbeEnsures Pod is ready to receive trafficPod is removed from load balancing

Important: Without readinessProbe, a LoadBalancer has no way to know which Pods are actually available.

Manifest — LoadBalancer Service

# nginx-loadbalancer-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: nginx-loadbalancer-service
spec:
  type: LoadBalancer
  selector:
    app: nginx
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80
kubectl apply -f nginx-deployment.yaml
kubectl apply -f nginx-loadbalancer-service.yaml

# Verify (wait for EXTERNAL-IP)
kubectl get svc nginx-loadbalancer-service

# Test access
curl http://<EXTERNAL-IP>

Adding Session Affinity to a Kubernetes LoadBalancer

Session Affinity (or “sticky sessions”) ensures a client is always redirected to the same Pod. This is essential for:

  • Maintaining connection sessions (logins)
  • Improving performance through caching
  • Facilitating debugging

Manifest — LoadBalancer with Session Affinity

# nginx-loadbalancer-affinity-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: nginx-loadbalancer-service
spec:
  type: LoadBalancer
  selector:
    app: nginx
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80
  sessionAffinity: ClientIP
  sessionAffinityConfig:
    clientIP:
      timeoutSeconds: 10800   # 3 hours of persistence
kubectl apply -f nginx-loadbalancer-affinity-service.yaml

Applying a Kubernetes LoadBalancer to the Cloud

On cloud providers (AKS, EKS, GKE), simply using type: LoadBalancer automatically provisions a cloud load balancer. No MetalLB needed.

Azure AKS example:

# The cloud provider auto-provisions an Azure Load Balancer
kubectl apply -f nginx-loadbalancer-service.yaml
kubectl get svc nginx-loadbalancer-service
# EXTERNAL-IP will be assigned automatically (~30 seconds)

Service Types — Reference

TypeAccessibilityUse CaseEXTERNAL-IP
ClusterIPInside cluster onlyInter-service communicationNone
NodePortVia NodeIP:NodePortDev/testing external accessNone (uses Node IP)
LoadBalancerVia external load balancerProduction on cloudYes (provisioned by cloud)
ExternalNameVia CNAMEAccess to external servicesNone

ClusterIP — Default Service

apiVersion: v1
kind: Service
metadata:
  name: my-clusterip-svc
spec:
  type: ClusterIP       # Default, can be omitted
  selector:
    app: my-app
  ports:
  - port: 80
    targetPort: 8080

NodePort Service

apiVersion: v1
kind: Service
metadata:
  name: my-nodeport-svc
spec:
  type: NodePort
  selector:
    app: my-app
  ports:
  - port: 80
    targetPort: 8080
    nodePort: 31569     # Optional: specific port 30000-32767

Access: http://<NODE_IP>:31569

ExternalName Service

apiVersion: v1
kind: Service
metadata:
  name: externalname-svc
spec:
  type: ExternalName
  externalName: db.example.com    # Returns CNAME pointing to this name

Kubernetes Networking — Key Concepts

CNI (Container Network Interface)

The CNI is the networking interface between Kubernetes and the network plugins responsible for assigning IP addresses to Pods and managing routing.

Popular CNI plugins:

PluginFeatures
CalicoNetwork policies, BGP routing, high performance
FlannelSimple, overlay network
Weave NetSimple, encryption
CiliumeBPF-based, advanced observability

Network Policies

NetworkPolicy objects control network traffic between Pods and namespaces:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: deny-all-ingress
spec:
  podSelector: {}       # Applies to all pods in namespace
  policyTypes:
  - Ingress             # Block all incoming traffic

kube-proxy

kube-proxy runs on each node and manages iptables/ipvs rules to route traffic to the correct Pod.

When you create a Service, kube-proxy:

  1. Detects the new Service via the API server
  2. Creates appropriate iptables rules on the node
  3. These rules transparently redirect traffic to one of the available Pods

Kubernetes Internal DNS

CoreDNS is the default DNS server in Kubernetes. It runs as Pods in kube-system and provides internal name resolution.

DNS format:

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

Examples:

# Full DNS name
backend-service.default.svc.cluster.local

# Short form (same namespace)
backend-service

# With explicit namespace
backend-service.production

Testing DNS resolution:

# From inside a Pod
kubectl exec -it <pod-name> -- nslookup backend-service
kubectl exec -it <pod-name> -- curl backend-service:80

EndpointSlices

EndpointSlices are the mechanism Kubernetes uses to track which Pod IPs are currently available for a Service.

# View EndpointSlices for a service
kubectl get endpointslices -l kubernetes.io/service-name=my-service
kubectl describe endpointslice <name>

The Service controller automatically creates and updates EndpointSlices when Pods are added, removed, or change state.


Ingress

Ingress provides HTTP/HTTPS routing at Layer 7 (application layer), allowing multiple services to be exposed through a single external IP.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx
  rules:
  - host: myapp.example.com
    http:
      paths:
      - path: /api
        pathType: Prefix
        backend:
          service:
            name: api-service
            port:
              number: 80
      - path: /
        pathType: Prefix
        backend:
          service:
            name: frontend-service
            port:
              number: 80

Difference between Service and Ingress:

AspectServiceIngress
LayerL4 (TCP/UDP)L7 (HTTP/HTTPS)
External IPOne per LoadBalancerOne for all services
SSL/TLSManualCentralized (via cert-manager)
Path routingNoYes
Host routingNoYes

kubectl Commands — Quick Reference

# List services
kubectl get services
kubectl get svc                     # Abbreviated
kubectl get svc -A                  # All namespaces
kubectl get svc -o wide             # With additional info

# Describe a service
kubectl describe svc <name>

# Create a service from manifest
kubectl apply -f service.yaml

# Expose a deployment
kubectl expose deployment <name> --type=NodePort --port=80

# Delete a service
kubectl delete svc <name>
kubectl delete -f service.yaml

# View endpoints
kubectl get endpoints
kubectl get endpointslices

# Test access
kubectl port-forward svc/<name> 8080:80
curl http://localhost:8080

Search Terms

services · kubernetes · containers · service · manifest · loadbalancer · deployment · default · kubectl · metallb · affinity · clusterip · defining · network · nodeport · reference · session

Interested in this course?

Contact us to book it or get a custom training plan for your team.