Endpoints and Service Discovery
CKA Domain 3 — Kubernetes Endpoints/EndpointSlice resource management and service discovery mechanisms
Overview
Endpoints is a Kubernetes resource that tracks the IP addresses of Service backend Pods. When a Service has a Selector, K8s automatically creates and updates Endpoints. EndpointSlice is the next-generation replacement (K8s v1.21+), solving the performance issues of a single Endpoints resource in large-scale clusters.
1. Endpoints Resource
Automatic Management
When a Service defines a Selector, K8s automatically creates and maintains Endpoints:
apiVersion: v1
kind: Service
metadata:
name: my-svc
spec:
selector:
app: nginx
ports:
- port: 80
targetPort: 80
---
# Endpoints automatically created by K8s
apiVersion: v1
kind: Endpoints
metadata:
name: my-svc # Name must match the Service name
subsets:
- addresses:
- ip: 10.244.1.5
nodeName: node-1
targetRef:
kind: Pod
name: nginx-pod-1
namespace: default
- ip: 10.244.2.7
nodeName: node-2
targetRef:
kind: Pod
name: nginx-pod-2
namespace: default
ports:
- port: 80
protocol: TCP
# View Endpoints
kubectl get endpoints
kubectl get endpoints my-svc
kubectl describe endpoints my-svc
# Endpoints name has a one-to-one mapping with Service name
# If a Service has no Endpoints, it means no Pods matched
kubectl get svc,ep
Manually Creating Endpoints (Pointing to External Services)
When you need a Service to point to an external service (such as an external database), you can create a Service without a Selector and manually bind Endpoints.
# 1. Create a Service without a Selector
apiVersion: v1
kind: Service
metadata:
name: external-mysql
spec:
ports:
- port: 3306
targetPort: 3306
---
# 2. Manually create Endpoints with the same name
apiVersion: v1
kind: Endpoints
metadata:
name: external-mysql # Name must match the Service name
subsets:
- addresses:
- ip: 192.168.1.100 # External database IP
- ip: 192.168.1.101
ports:
- port: 3306
# Verify
kubectl get svc external-mysql
kubectl get endpoints external-mysql
# Test from a Pod
kubectl run test-db --image=busybox:1.28 --rm -it --restart=Never -- telnet external-mysql 3306
2. EndpointSlice (K8s v1.21+)
EndpointSlice is the replacement for Endpoints, solving the performance bottleneck of a single Endpoints object in large clusters.
Core Features
- Automatic sharding: Each EndpointSlice contains at most 100 endpoints by default
- Address types: Supports IPv4, IPv6, FQDN
- More efficient updates: Only changed shards are updated rather than the entire Endpoints
# View EndpointSlices
kubectl get endpointslices
kubectl get endpointslices -o yaml
# View association with Service
kubectl describe endpointslices <slice-name>
EndpointSlice YAML Example
apiVersion: discovery.k8s.io/v1
kind: EndpointSlice
metadata:
name: my-svc-abc123
labels:
kubernetes.io/service-name: my-svc # Required label identifying the owning Service
addressType: IPv4
endpoints:
- addresses:
- 10.244.1.5
conditions:
ready: true
serving: true
terminating: false
nodeName: node-1
targetRef:
kind: Pod
name: nginx-pod-1
namespace: default
- addresses:
- 10.244.2.7
conditions:
ready: true
serving: true
terminating: false
nodeName: node-2
- addresses:
- 10.244.3.9
conditions:
ready: false # Pod not ready
serving: false
terminating: false
ports:
- name: http
protocol: TCP
port: 80
Endpoints vs EndpointSlice
| Feature | Endpoints | EndpointSlice |
|---|---|---|
| API version | v1 | discovery.k8s.io/v1 |
| GA stage | GA from the start | K8s v1.21+ GA |
| Single object capacity | Unlimited (single large object) | Max 100 endpoints per slice |
| Update efficiency | Full update | Incremental update |
| Multiple address types | IPv4 only | IPv4, IPv6, FQDN |
| Large-scale clusters | Performance degradation | Recommended |
3. Service Types Without a Selector
In addition to manually binding Endpoints, there are the following Selector-less Service variants:
1. ExternalName Service
Maps a Service to an external domain name via DNS CNAME.
apiVersion: v1
kind: Service
metadata:
name: external-api
spec:
type: ExternalName
externalName: api.example.com
# No selector or ports needed
kubectl run test --image=busybox:1.28 --rm -it --restart=Never -- nslookup external-api
# Returns a CNAME record: api.example.com
2. Service Pointing to External IPs (No Selector + Manual Endpoints)
apiVersion: v1
kind: Service
metadata:
name: external-ip-svc
spec:
ports:
- name: http
port: 80
---
apiVersion: v1
kind: Endpoints
metadata:
name: external-ip-svc
subsets:
- addresses:
- ip: 10.0.0.1
- ip: 10.0.0.2
ports:
- port: 80
3. Verifying a Selector-less Service
# A Service without a Selector does not automatically create Endpoints
kubectl describe svc external-ip-svc
# Note the output has no Endpoints line (unless manually created)
4. Service Discovery
Kubernetes provides two service discovery mechanisms: Environment Variables and DNS.
1. Environment Variables
K8s injects Service environment variables when a Pod starts.
# View environment variables in a Pod
kubectl exec <pod-name> -- env
# Output contains (example):
# MY_SVC_SERVICE_HOST=10.96.0.20
# MY_SVC_SERVICE_PORT=80
# MY_SVC_PORT=tcp://10.96.0.20:80
# MY_SVC_PORT_80_TCP=tcp://10.96.0.20:80
# MY_SVC_PORT_80_TCP_PROTO=tcp
# MY_SVC_PORT_80_TCP_PORT=80
# MY_SVC_PORT_80_TCP_ADDR=10.96.0.20
Environment variable naming convention:
<SERVICE_NAME>_SERVICE_HOST
<SERVICE_NAME>_SERVICE_PORT
# Verify environment variables
kubectl run env-test --image=busybox:1.28 --rm -it --restart=Never -- sh
# Inside the container
env | grep -i kubernetes
# KUBERNETES_SERVICE_HOST=10.96.0.1
# KUBERNETES_SERVICE_PORT=443
Limitations:
- Only injected at Pod creation time; Services created after the Pod starts will not appear
- Depends on the creation order of Pod and Service (DNS is recommended)
2. DNS Service Discovery (Recommended)
Resolve Service domain names through CoreDNS.
# Access within the same namespace
kubectl run dns-test --image=busybox:1.28 --rm -it --restart=Never -- nslookup my-svc
# Access across namespaces
kubectl run dns-test --image=busybox:1.28 --rm -it --restart=Never -- nslookup my-svc.other-ns
# Full FQDN
kubectl run dns-test --image=busybox:1.28 --rm -it --restart=Never -- nslookup my-svc.default.svc.cluster.local
3. Environment Variables vs DNS Comparison
| Feature | Environment Variables | DNS |
|---|---|---|
| Creation order dependency | Yes (Service must exist before Pod) | No |
| Dynamic updates | No (requires Pod recreation) | Yes |
| Cross-namespace | Not supported | Supported |
| Recommended | Not recommended | Recommended |
5. Endpoints-Related Troubleshooting
# Debug: Service has no Endpoints
kubectl describe svc my-svc
# See <none> in the Endpoints field
# Check if Selector matches Pods
kubectl get pods -l app=my-app
kubectl get svc my-svc -o yaml | grep selector
# Check if Pods are ready
kubectl get pods -l app=my-app
# Ensure STATUS is Running and READY column is 1/1
# Manually test Endpoints connectivity
kubectl run test --image=busybox:1.28 --rm -it --restart=Never -- wget -qO- http://<endpoint-ip>:80
# View EndpointSlice details
kubectl get endpointslices -l kubernetes.io/service-name=my-svc -o yaml
🧪 Complete Hands-on Example: Creating Manual Endpoints Pointing to an External Service
Scenario Description
Create a Service without a Selector, manually configure Endpoints pointing to a simulated external database IP, then verify that Pods can access the "external database" via the Service name.
Prerequisites
- Cluster is running normally
- kubectl is configured for cluster access
- Prepare a reachable external IP for testing (can use a cluster node IP or any reachable address)
Steps
Step 1: Create a Service without a Selector
# Create a Service named external-db without specifying a selector
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Service
metadata:
name: external-db
spec:
ports:
- protocol: TCP
port: 3306
targetPort: 3306
EOF
# Expected output: service/external-db created
# Verify the Service has no Selector and no Endpoints
kubectl get svc external-db
# NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
# external-db ClusterIP 10.96.200.10 <none> 3306/TCP 5s
kubectl describe svc external-db
# ... Note the Endpoints field is <none> and there is no Selector line in the output
Step 2: Manually create Endpoints with the same name pointing to an external IP
# Manually create Endpoints; the name must exactly match the Service
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Endpoints
metadata:
name: external-db # Name must match the Service
subsets:
- addresses:
- ip: 192.168.1.100 # Simulated external database IP
ports:
- port: 3306
EOF
# Expected output: endpoints/external-db created
# Verify Endpoints are bound
kubectl get endpoints external-db
# NAME ENDPOINTS AGE
# external-db 192.168.1.100:3306 5s
kubectl describe svc external-db
# ... Endpoints field now shows 192.168.1.100:3306
Step 3: Create a test Pod to verify service discovery
# Create a test Pod
kubectl run test-app --image=busybox:1.28 --rm -it --restart=Never -- sh
# Note: Execute the following commands inside the container
# Inside the test Pod's shell:
# 1. Check DNS resolution
nslookup external-db
# Expected output:
# Server: 10.96.0.10
# Address: 10.96.0.10:53
# Name: external-db.default.svc.cluster.local
# Address: 10.96.200.10 # Service's ClusterIP
# 2. Connect to the external service via Service name
# (If there is an actual service on 192.168.1.100:3306, use the following commands to test)
# nc -zv external-db 3306
# telnet external-db 3306
# 3. View environment variables
env | grep EXTERNAL
# Expected output:
# EXTERNAL_DB_SERVICE_HOST=10.96.200.10
# EXTERNAL_DB_SERVICE_PORT=3306
# EXTERNAL_DB_PORT=tcp://10.96.200.10:3306
# ...
# 4. View /etc/resolv.conf
cat /etc/resolv.conf
# Expected output:
# nameserver 10.96.0.10
# search default.svc.cluster.local svc.cluster.local cluster.local
# options ndots:5
# Press Ctrl+D to exit the container
Step 4: Compare with automatic Endpoints (create a Service with a Selector)
# Create automatic Endpoints for comparison
kubectl create deployment test-nginx --image=nginx:1.25 --replicas=2
kubectl expose deployment test-nginx --name=auto-svc --port=80 --target-port=80
# View the automatically created Endpoints
kubectl get endpoints auto-svc
# NAME ENDPOINTS AGE
# auto-svc 10.244.1.5:80,10.244.2.7:80 10s
kubectl describe endpoints auto-svc
# Output includes targetRef, referencing specific Pod names
# Compare with manually created Endpoints
kubectl describe endpoints external-db
# Output has no targetRef because it points to an external address rather than a Pod
Step 5: Add multiple external addresses (high availability scenario)
# Update Endpoints to add multiple external addresses for simple load balancing
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Endpoints
metadata:
name: external-db
subsets:
- addresses:
- ip: 192.168.1.100 # Primary database
- ip: 192.168.1.101 # Standby database
ports:
- port: 3306
EOF
# Expected output: endpoints/external-db configured
# Verify multiple addresses
kubectl get endpoints external-db
# NAME ENDPOINTS AGE
# external-db 192.168.1.100:3306,192.168.1.101:3306 1m
# Verify from a Pod that DNS still resolves to the Service ClusterIP
kubectl run test-app2 --image=busybox:1.28 --rm -it --restart=Never -- nslookup external-db
# Expected output: DNS still returns the Service ClusterIP; kube-proxy handles load balancing between the two backends
Verification
# View all Services and their Endpoints
kubectl get svc,ep
# Check the full status of the Selector-less Service
kubectl describe svc external-db
# Confirm the output includes:
# Type: ClusterIP
# IP: 10.96.200.10
# Port: <unset> 3306/TCP
# Endpoints: 192.168.1.100:3306,192.168.1.101:3306
# Session Affinity: None
# Note: No Selector field is displayed
# Clean up resources
kubectl delete svc external-db auto-svc
kubectl delete endpoints external-db
kubectl delete deployment test-nginx
Exam Tips
-
Services with manual Endpoints must not have a Selector, otherwise the Endpoints will be overwritten by automatic management
-
The Endpoints name must exactly match the Service name (including namespace)
-
The
targetReffield in manual Endpoints is optional (not needed when pointing to external IPs) -
Common CKA exam scenario: cluster Pods access an external database (e.g., AWS RDS, Azure SQL) via Service name
-
EndpointSlice is the replacement for Endpoints (K8s v1.21+ GA), recommended for large-scale clusters
-
Note that the
kubectl create servicecommand cannot create a Service without a Selector; you must use YAML -
Use
nc -zvortelnetto test port connectivity rather thancurl, which is better suited for non-HTTP services (e.g., databases) -
https://kubernetes.io/docs/concepts/services-networking/service/#services-without-selectors
-
https://kubernetes.io/docs/concepts/services-networking/endpoint-slices/
-
https://kubernetes.io/docs/tasks/administer-cluster/enabling-endpoint-slices/
-
https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/
-
https://kubernetes.io/docs/concepts/services-networking/service/#environment-variables