Pods come and go. Their IP addresses change. Services provide stable endpoints that persist beyond pod lifetimes. Ingress routes external traffic to services. In this lesson, you'll master the networking layer that makes Kubernetes applications accessible and resilient.
1. Learning Objectives
By the end of this lesson, you will be able to:
- Create and use different Service types (ClusterIP, NodePort, LoadBalancer)
- Understand Service discovery and DNS in Kubernetes
- Configure Ingress for HTTP/HTTPS routing
- Implement Network Policies for security
- Use Headless Services for StatefulSets
- Troubleshoot network connectivity issues
2. Why This Matters
Real-world scenario: Your backend pods restart, getting new IP addresses. Your frontend needs to find them without hardcoding IPs. Services solve this with stable DNS names and load balancing across pods.
3. Core Concepts
Service Types Overview
Service Types:
1. ClusterIP (default) - Internal only, accessible within cluster
2. NodePort - Exposes on each node's IP at high port (30000-32767)
3. LoadBalancer - Creates cloud load balancer (AWS ELB, Azure LB, GCP LB)
4. ExternalName - Maps to external DNS name
5. Headless - No cluster IP, direct pod DNS (for StatefulSets)
Service types summary
ClusterIP (Internal Service)
# clusterip-service.yaml
apiVersion: v1
kind: Service
metadata:
name: backend-service
spec:
type: ClusterIP
selector:
app: backend
tier: api
ports:
- port: 8080
targetPort: 80
protocol: TCP
sessionAffinity: ClientIP
# Access within cluster:
# curl http://backend-service:8080
# curl http://backend-service.default.svc.cluster.local:8080
kubectl apply -f clusterip-service.yaml
kubectl get svc
kubectl describe svc backend-service
kubectl get endpoints backend-service
ClusterIP service for internal communication
NodePort (External Access via Node IP)
# nodeport-service.yaml
apiVersion: v1
kind: Service
metadata:
name: web-nodeport
spec:
type: NodePort
selector:
app: web
ports:
- port: 80
targetPort: 8080
nodePort: 30080 # Optional, Kubernetes assigns if omitted
protocol: TCP
# Access: http://<node-ip>:30080
# For minikube: minikube service web-nodeport
kubectl get svc web-nodeport
# Output: web-nodeport NodePort 10.96.0.1 <none> 80:30080/TCP
NodePort service
LoadBalancer (Cloud Load Balancer)
# loadbalancer-service.yaml
apiVersion: v1
kind: Service
metadata:
name: web-loadbalancer
annotations:
# AWS specific
service.beta.kubernetes.io/aws-load-balancer-type: "nlb"
service.beta.kubernetes.io/aws-load-balancer-internal: "false"
# Azure specific
service.beta.kubernetes.io/azure-load-balancer-internal: "false"
# GCP specific
cloud.google.com/load-balancer-type: "External"
spec:
type: LoadBalancer
selector:
app: web
ports:
- port: 80
targetPort: 8080
externalTrafficPolicy: Local
kubectl apply -f loadbalancer-service.yaml
kubectl get svc web-loadbalancer -w
# Wait for EXTERNAL-IP to be assigned
# For minikube (tunnel required):
# minikube tunnel
# kubectl get svc web-loadbalancer
LoadBalancer service for cloud deployments
Service Discovery (DNS)
# Kubernetes DNS format:
# <service-name>.<namespace>.svc.cluster.local
# Example DNS names:
# backend-service.default.svc.cluster.local
# frontend-service.production.svc.cluster.local
# Test DNS resolution from a pod
kubectl run dns-test --image=busybox -it --rm --restart=Never -- nslookup kubernetes.default
# Access service by short name (same namespace)
kubectl run curl --image=curlimages/curl -it --rm --restart=Never -- curl http://backend-service:8080
# Access service from different namespace
kubectl run curl --image=curlimages/curl -it --rm --restart=Never -- curl http://backend-service.production:8080
# Environment variables (old style, still works)
kubectl exec backend-pod -- env | grep SERVICE
# BACKEND_SERVICE_SERVICE_HOST=10.96.0.1
# BACKEND_SERVICE_SERVICE_PORT=8080
Service discovery and DNS
Ingress (HTTP/HTTPS Routing)
# ingress.yaml - HTTP/HTTPS routing
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: main-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/ssl-redirect: "true"
cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
ingressClassName: nginx
tls:
- hosts:
- app.example.com
secretName: app-tls
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: frontend-service
port:
number: 80
- path: /api
pathType: Prefix
backend:
service:
name: backend-service
port:
number: 8080
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: backend-service
port:
number: 8080
# Install Ingress Controller (nginx)
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.9.0/deploy/static/provider/cloud/deploy.yaml
# For minikube
minikube addons enable ingress
# Apply ingress
kubectl apply -f ingress.yaml
kubectl get ingress
kubectl describe ingress main-ingress
Ingress for HTTP/HTTPS routing
Network Policies (Security)
# network-policy.yaml - Restrict traffic
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: backend-network-policy
spec:
podSelector:
matchLabels:
app: backend
policyTypes:
- Ingress
- Egress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 8080
egress:
- to:
- podSelector:
matchLabels:
app: database
ports:
- protocol: TCP
port: 5432
- to:
- namespaceSelector: {}
podSelector:
matchLabels:
app: dns
ports:
- protocol: UDP
port: 53
# Deny all traffic to pods in default namespace
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-all
spec:
podSelector: {}
policyTypes:
- Ingress
# Allow all traffic from specific namespace
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-from-monitoring
spec:
podSelector: {}
ingress:
- from:
- namespaceSelector:
matchLabels:
name: monitoring
kubectl apply -f network-policy.yaml
kubectl get networkpolicies
Network Policies for security
4. Complete Project: Production Ingress Setup
# complete-ingress.yaml
---
# Certificate Issuer (Let's Encrypt)
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: admin@example.com
privateKeySecretRef:
name: letsencrypt-prod
solvers:
- http01:
ingress:
class: nginx
---
# Main Ingress with TLS
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: production-ingress
annotations:
nginx.ingress.kubernetes.io/rate-limit: "10r/s"
nginx.ingress.kubernetes.io/proxy-body-size: "10m"
nginx.ingress.kubernetes.io/proxy-read-timeout: "60"
nginx.ingress.kubernetes.io/enable-cors: "true"
nginx.ingress.kubernetes.io/cors-allow-origin: "https://example.com"
cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
ingressClassName: nginx
tls:
- hosts:
- app.example.com
- api.example.com
secretName: example-com-tls
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: frontend-service
port:
number: 80
- path: /api
pathType: Prefix
backend:
service:
name: backend-service
port:
number: 8080
- path: /socket.io
pathType: Prefix
backend:
service:
name: websocket-service
port:
number: 3000
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: backend-service
port:
number: 8080
---
# Internal services (not exposed externally)
apiVersion: v1
kind: Service
metadata:
name: backend-service
spec:
type: ClusterIP
selector:
app: backend
ports:
- port: 8080
targetPort: 8080
---
# Network Policy: Only allow ingress traffic
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: backend-allow-ingress
spec:
podSelector:
matchLabels:
app: backend
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector: {}
podSelector:
matchLabels:
app: nginx-ingress
ports:
- protocol: TCP
port: 8080
# Apply all resources
kubectl apply -f complete-ingress.yaml
# Wait for certificate
kubectl get certificate -w
# Test ingress
curl https://app.example.com
curl https://api.example.com/health
Production ingress with TLS and security
5. Common Errors & Solutions
6. Summary Checklist
7. Next Steps
Next lesson: Kubernetes Lesson 6.4: ConfigMaps & Secrets
Comments (0)
This is exactly what I needed! The initContainer approach solved our migration issues completely. Thanks for the detailed guide!
ReplyLeave a Comment