Chapter 4: Service Discovery and Load Balancing

Haiyue
21min

Chapter 4: Service Discovery and Load Balancing

Learning Objectives
  • Master the four types of Services and their use cases
  • Understand Kubernetes DNS and service discovery mechanisms
  • Learn to configure Ingress for Layer 7 load balancing
  • Proficiently configure network communication between services

Key Concepts

Why Services are Needed

In Kubernetes, Pods are dynamic and can be created or destroyed at any time. Each Pod has its own IP address, but these IP addresses are not fixed. Services provide a stable access endpoint for a group of Pods.

🔄 正在渲染 Mermaid 图表...

How Services Work

Services select backend Pods through Label Selectors, and kube-proxy forwards traffic to the correct Pods.

🔄 正在渲染 Mermaid 图表...

Four Service Types

🔄 正在渲染 Mermaid 图表...

ClusterIP Service

ClusterIP is the default Service type, accessible only within the cluster.

Creating ClusterIP Service

# web-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
      - name: nginx
        image: nginx:1.20
        ports:
        - containerPort: 80
---
# web-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: web-service
spec:
  type: ClusterIP  # Default type, can be omitted
  selector:
    app: web       # Select Pods with label app=web
  ports:
  - name: http
    port: 80       # Service port
    targetPort: 80 # Pod port
    protocol: TCP
# Create Deployment and Service
kubectl apply -f web-deployment.yaml
kubectl apply -f web-service.yaml

# View Service
kubectl get svc web-service
# NAME          TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
# web-service   ClusterIP   10.96.123.45    <none>        80/TCP    5s

# View Service details
kubectl describe svc web-service

# View Endpoints (backend Pod list)
kubectl get endpoints web-service
# NAME          ENDPOINTS                                      AGE
# web-service   10.244.1.5:80,10.244.2.6:80,10.244.3.7:80     5s

# Test access within cluster
kubectl run test-pod --image=busybox --rm -it --restart=Never -- wget -qO- http://web-service

Multi-Port Service

apiVersion: v1
kind: Service
metadata:
  name: multi-port-service
spec:
  selector:
    app: myapp
  ports:
  - name: http
    port: 80
    targetPort: 8080
  - name: https
    port: 443
    targetPort: 8443
  - name: metrics
    port: 9090
    targetPort: 9090

Headless Service

Headless Service does not assign a ClusterIP and directly returns the list of backend Pod IPs, commonly used with StatefulSet.

apiVersion: v1
kind: Service
metadata:
  name: headless-service
spec:
  clusterIP: None  # Key: set to None
  selector:
    app: database
  ports:
  - port: 3306
    targetPort: 3306
# View Headless Service
kubectl get svc headless-service
# NAME               TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)    AGE
# headless-service   ClusterIP   None         <none>        3306/TCP   5s

# DNS query returns all Pod IPs
kubectl run test --image=busybox --rm -it --restart=Never -- nslookup headless-service
# Server:    10.96.0.10
# Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local
#
# Name:      headless-service
# Address 1: 10.244.1.10 pod-0.headless-service.default.svc.cluster.local
# Address 2: 10.244.2.11 pod-1.headless-service.default.svc.cluster.local
# Address 3: 10.244.3.12 pod-2.headless-service.default.svc.cluster.local

NodePort Service

NodePort opens a port on each node, allowing external cluster access to the Service.

🔄 正在渲染 Mermaid 图表...

Creating NodePort Service

apiVersion: v1
kind: Service
metadata:
  name: nodeport-service
spec:
  type: NodePort
  selector:
    app: web
  ports:
  - port: 80          # Service port (internal cluster access)
    targetPort: 80    # Pod port
    nodePort: 30080   # Node port (optional, auto-assigned if not specified)
# Apply configuration
kubectl apply -f nodeport-service.yaml

# View Service
kubectl get svc nodeport-service
# NAME               TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
# nodeport-service   NodePort   10.96.200.100   <none>        80:30080/TCP   5s

# Get node IP
kubectl get nodes -o wide

# Access from external (using any node IP)
curl http://<NodeIP>:30080

NodePort Configuration Options

apiVersion: v1
kind: Service
metadata:
  name: nodeport-advanced
spec:
  type: NodePort
  selector:
    app: web
  ports:
  - port: 80
    targetPort: 80
    nodePort: 30080
  # Traffic policy control
  externalTrafficPolicy: Local  # Local: forward only to Pods on this node, Cluster: can forward across nodes
Notes
  • NodePort port range defaults to 30000-32767
  • externalTrafficPolicy: Local can preserve client source IP but may cause load imbalance
  • Production environments recommend using LoadBalancer or Ingress instead of NodePort

LoadBalancer Service

LoadBalancer type automatically creates a cloud provider’s load balancer in cloud environments.

🔄 正在渲染 Mermaid 图表...

Creating LoadBalancer Service

apiVersion: v1
kind: Service
metadata:
  name: loadbalancer-service
  annotations:
    # Cloud provider specific annotations (AWS example)
    service.beta.kubernetes.io/aws-load-balancer-type: "nlb"
    service.beta.kubernetes.io/aws-load-balancer-internal: "false"
spec:
  type: LoadBalancer
  selector:
    app: web
  ports:
  - port: 80
    targetPort: 80
  # Optional: specify load balancer IP (requires cloud provider support)
  # loadBalancerIP: 203.0.113.10
# Apply configuration
kubectl apply -f loadbalancer-service.yaml

# View Service (wait for EXTERNAL-IP assignment)
kubectl get svc loadbalancer-service -w
# NAME                   TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)        AGE
# loadbalancer-service   LoadBalancer   10.96.100.50    <pending>       80:31234/TCP   5s
# loadbalancer-service   LoadBalancer   10.96.100.50    203.0.113.10    80:31234/TCP   30s

# Access via external IP
curl http://203.0.113.10

Local LoadBalancer Testing (MetalLB)

For local Kubernetes clusters (like Minikube, Kind), MetalLB can provide LoadBalancer functionality.

# Install MetalLB
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.13.7/config/manifests/metallb-native.yaml

# Wait for MetalLB to be ready
kubectl wait --namespace metallb-system \
  --for=condition=ready pod \
  --selector=app=metallb \
  --timeout=90s
# metallb-config.yaml
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: default-pool
  namespace: metallb-system
spec:
  addresses:
  - 192.168.1.240-192.168.1.250  # Available IP range
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: default
  namespace: metallb-system
spec:
  ipAddressPools:
  - default-pool

ExternalName Service

ExternalName maps a Service to an external DNS name, commonly used to access external cluster services.

apiVersion: v1
kind: Service
metadata:
  name: external-database
spec:
  type: ExternalName
  externalName: database.example.com  # External service DNS name
# Pods within cluster can access external database via external-database
kubectl run test --image=busybox --rm -it --restart=Never -- nslookup external-database
# Name:      external-database
# Address 1: <IP address of database.example.com>

Accessing External Fixed IP Services

If the external service is a fixed IP rather than a domain name, you need to manually create Endpoints.

# Service without Selector
apiVersion: v1
kind: Service
metadata:
  name: external-service
spec:
  ports:
  - port: 3306
    targetPort: 3306
---
# Manually create Endpoints
apiVersion: v1
kind: Endpoints
metadata:
  name: external-service  # Must match Service name
subsets:
- addresses:
  - ip: 192.168.100.10  # External service IP
  - ip: 192.168.100.11
  ports:
  - port: 3306

Kubernetes DNS

CoreDNS is Kubernetes’ DNS server, providing DNS resolution for Services and Pods.

DNS Resolution Rules

🔄 正在渲染 Mermaid 图表...

DNS Hands-on Testing

# Create test Service and Deployment
kubectl create deployment nginx --image=nginx
kubectl expose deployment nginx --port=80

# Create test Pod for DNS queries
kubectl run dns-test --image=busybox:1.28 --rm -it --restart=Never -- sh

# Execute following commands in container
# View DNS configuration
cat /etc/resolv.conf
# nameserver 10.96.0.10
# search default.svc.cluster.local svc.cluster.local cluster.local
# options ndots:5

# Resolve Service
nslookup nginx
nslookup nginx.default
nslookup nginx.default.svc.cluster.local

# Resolve kubernetes API Service
nslookup kubernetes
nslookup kubernetes.default.svc.cluster.local

Cross-Namespace Access

# Create Service in production namespace
apiVersion: v1
kind: Service
metadata:
  name: backend-api
  namespace: production
spec:
  selector:
    app: backend
  ports:
  - port: 8080
# Access production namespace Service from default namespace Pod
kubectl run test --image=busybox --rm -it --restart=Never -- wget -qO- http://backend-api.production:8080

Custom DNS Configuration

apiVersion: v1
kind: Pod
metadata:
  name: custom-dns-pod
spec:
  containers:
  - name: app
    image: nginx
  dnsPolicy: "None"  # Fully custom DNS
  dnsConfig:
    nameservers:
    - 8.8.8.8
    - 8.8.4.4
    searches:
    - my-domain.com
    options:
    - name: ndots
      value: "2"

Ingress Controller

Ingress provides Layer 7 (HTTP/HTTPS) load balancing, supporting domain name and path-based routing.

Ingress Architecture

🔄 正在渲染 Mermaid 图表...

Installing Nginx Ingress Controller

# Install using Helm
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update
helm install ingress-nginx ingress-nginx/ingress-nginx \
  --namespace ingress-nginx \
  --create-namespace

# Or install using kubectl
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.8.1/deploy/static/provider/cloud/deploy.yaml

# Verify installation
kubectl get pods -n ingress-nginx
kubectl get svc -n ingress-nginx

Creating Ingress Rules

# First create backend services
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
spec:
  replicas: 2
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: web-service
spec:
  selector:
    app: web
  ports:
  - port: 80
    targetPort: 80
---
# Create Ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: web-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx  # Specify Ingress Controller
  rules:
  - host: web.example.com  # Domain name
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: web-service
            port:
              number: 80
# Apply configuration
kubectl apply -f web-ingress.yaml

# View Ingress
kubectl get ingress
# NAME          CLASS   HOSTS             ADDRESS         PORTS   AGE
# web-ingress   nginx   web.example.com   192.168.1.240   80      5s

# Local testing (add hosts entry)
echo "192.168.1.240 web.example.com" | sudo tee -a /etc/hosts

# Access test
curl http://web.example.com

Multi-Domain and Path Routing

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: multi-host-ingress
spec:
  ingressClassName: nginx
  rules:
  # First domain
  - host: api.example.com
    http:
      paths:
      - path: /v1
        pathType: Prefix
        backend:
          service:
            name: api-v1-service
            port:
              number: 80
      - path: /v2
        pathType: Prefix
        backend:
          service:
            name: api-v2-service
            port:
              number: 80
  # Second domain
  - host: admin.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: admin-service
            port:
              number: 80

HTTPS Configuration

# Generate self-signed certificate (production environments recommend using cert-manager)
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
  -keyout tls.key -out tls.crt \
  -subj "/CN=web.example.com"

# Create Secret
kubectl create secret tls web-tls-secret \
  --cert=tls.crt \
  --key=tls.key
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: https-ingress
  annotations:
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - web.example.com
    secretName: web-tls-secret  # TLS certificate Secret
  rules:
  - host: web.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: web-service
            port:
              number: 80

Advanced Ingress Configuration

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: advanced-ingress
  annotations:
    # Path rewrite
    nginx.ingress.kubernetes.io/rewrite-target: /$2
    # Rate limiting
    nginx.ingress.kubernetes.io/limit-rps: "10"
    nginx.ingress.kubernetes.io/limit-connections: "5"
    # Timeout configuration
    nginx.ingress.kubernetes.io/proxy-connect-timeout: "30"
    nginx.ingress.kubernetes.io/proxy-read-timeout: "60"
    # Request body size limit
    nginx.ingress.kubernetes.io/proxy-body-size: "10m"
    # Enable CORS
    nginx.ingress.kubernetes.io/enable-cors: "true"
    nginx.ingress.kubernetes.io/cors-allow-origin: "*"
spec:
  ingressClassName: nginx
  rules:
  - host: api.example.com
    http:
      paths:
      - path: /api(/|$)(.*)
        pathType: Prefix
        backend:
          service:
            name: api-service
            port:
              number: 80

NetworkPolicy

NetworkPolicy controls network traffic between Pods, implementing network isolation.

NetworkPolicy Working Principle

🔄 正在渲染 Mermaid 图表...

Creating NetworkPolicy

# Deny all ingress traffic (default isolation)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: deny-all-ingress
  namespace: production
spec:
  podSelector: {}  # Select all Pods
  policyTypes:
  - Ingress
  # No ingress rules means deny all ingress

---
# Allow specific traffic
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: backend-policy
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: backend
  policyTypes:
  - Ingress
  - Egress
  ingress:
  # Allow traffic from frontend
  - from:
    - podSelector:
        matchLabels:
          app: frontend
    ports:
    - protocol: TCP
      port: 8080
  egress:
  # Allow access to database
  - to:
    - podSelector:
        matchLabels:
          app: database
    ports:
    - protocol: TCP
      port: 5432
  # Allow DNS queries
  - to:
    - namespaceSelector: {}
      podSelector:
        matchLabels:
          k8s-app: kube-dns
    ports:
    - protocol: UDP
      port: 53

Common NetworkPolicy Scenarios

# Scenario 1: Only allow Pods in same namespace
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: same-namespace-only
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector: {}  # All Pods in same namespace

---
# Scenario 2: Allow access from specific namespace
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-from-monitoring
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: web
  policyTypes:
  - Ingress
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          name: monitoring
    ports:
    - protocol: TCP
      port: 9090

---
# Scenario 3: Allow external IP access
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-external-ip
spec:
  podSelector:
    matchLabels:
      app: public-api
  policyTypes:
  - Ingress
  ingress:
  - from:
    - ipBlock:
        cidr: 203.0.113.0/24
        except:
        - 203.0.113.100/32
    ports:
    - protocol: TCP
      port: 443

Summary

Through this chapter, you should have mastered:

  • Service Types: ClusterIP (internal cluster), NodePort (node port), LoadBalancer (cloud load balancer), ExternalName (external mapping)
  • Service Discovery: Understanding how Kubernetes DNS works and resolution rules
  • Ingress: Configuring Layer 7 load balancing for domain and path routing
  • Network Policies: Using NetworkPolicy to control network access between Pods
  • Best Practices: Network design and security isolation for microservice architectures

In the next chapter, we will learn about configuration management and storage, including the use of ConfigMap, Secret, and persistent storage.