Overview #
I use the following nodes based on a Kubernetes K8s cluster with MetalLB, that is deployed bare metal on Debian 12 in this tutorial:
192.168.30.71 deb-02 # Controller / Master Node
192.168.30.72 deb-03 # Controller / Master Node
192.168.30.73 deb-04 # Worker Node
192.168.30.74 deb-05 # Worker Node
192.168.30.60 # External Resource Node
Kubernetes Services #
Overview #
-
Services expose a network port to make pods accessible from outside the Kubernetes cluster.
-
Services select Pods based on labels, a Service selector in the Service configuration is defined, that matches labels assigned to Pods.
Service Types #
-
There a three types of Kubernetes services:
ClusterIP
,NodePort
andLoadBalancer
. -
If not defined, the created service if of the type
ClusterIP
ClusterIP #
- Exposes the service on a cluster-internal IP, this makes the service only reachable from within the cluster.
NodePort #
-
used for both internal and external communication.
-
Exposes the service on each worker node IP at a static port the
NodePort
. -
The NodePort service is accessible from outside the cluster by
NodeIP:NodePort
. -
Also a
ClusterIP
is created for it and it is used for internal communication.
LoadBalancer #
-
Integrates with the cloud provider’s LoadBalancer, or for example
MetalLB
on bare-metal clusters orKlipperLB
on K3s. -
The service is externally accessible through the LoadBalancer IP.
-
Underneath the LoadBalancer a ClusterIP and NodePort are created and are used for internal and external communication.
Prerequisites #
Example Deployment #
# Create a deployment that will be exposed via the services
kubectl create deployment example-deployment --image=nginx --replicas=3
# Verify the deployment
kubectl get deployment -o wide
# Shell output:
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR
example-deployment 3/3 3 3 15s nginx nginx app=example-deployment
Create Service with Expose Command #
Example: Create ClusterIP Service #
Create ClusterIP Service #
# Create a ClusterIP service for the deployment
kubectl expose deployment example-deployment --name=example-service --type=ClusterIP --port 8080 --target-port 80
Verify the Service #
# List services
kubectl get svc
# Shell output:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
example-service ClusterIP 10.110.33.176 <none> 8080/TCP 61s
# Curl the service
curl 10.110.33.176:8080
# Shell output:
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
List Service Endpoints #
-
The endpoints of a service refer to the IP addresses and ports of the actual pods that the service routes traffic to.
-
Kubernetes automatically tracks which pods match the service’s selector. These tracked pods form the endpoints for the service.
# List endpoints of the service
kubectl get ep example-service
# Shell output:
NAME ENDPOINTS AGE
example-service 10.0.1.117:80,10.0.1.94:80,10.0.2.33:80 5m40s
# List pods
kubectl get pods -o wide
# Shell output:
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
example-deployment-fb487fc59-6rpz6 1/1 Running 0 11m 10.0.2.33 ubuntu3 <none> <none>
example-deployment-fb487fc59-dtr4f 1/1 Running 0 11m 10.0.1.94 ubuntu2 <none> <none>
example-deployment-fb487fc59-x4fcv 1/1 Running 0 11m 10.0.1.117 ubuntu2 <none> <none>
Delete the Service #
# Delete the ClusterIP service
kubectl delete service example-service
Example: Create NodePort Service #
Create NodePort Service #
# Create a NodePort service for the deployment: Random NodePort port
kubectl expose deployment example-deployment --name=example-service --type=NodePort --port 8080 --target-port 80
-
With the
expose
command it’s not possible do define a specific NodePort, it’s assigned randomly. Do assign a specific NodePort port, use a YAML configuration. -
If no service name is defined via
--name=
, the service name is the same as the name of the deployment.
Verify the Service #
# List services
kubectl get svc -o wide
# Shell output: Random NodePort port
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
example-service NodePort 10.107.139.220 <none> 8080:30432/TCP 2m22s app=example-deployment
# Curl the service: Via NodePort on Worker node
curl 192.168.30.11:30432
# Curl the service: Via ClusterIP
curl 10.107.139.220:8080
# Shell output:
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
Delete the Service #
# Delete the ClusterIP service
kubectl delete service example-service
Example: Create LoadBalancer Service #
Create LoadBalancer Service #
# Create a LoadBalancer service for the deployment:
kubectl expose deployment example-deployment --name=example-service --type=LoadBalancer --port 8080 --target-port 80
Verify the Service #
# List services
kubectl get svc -o wide
# Shell output: Random NodePort port
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
example-service LoadBalancer 10.100.100.157 192.168.30.201 8080:31163/TCP 5s app=example-deployment
# Curl the service: Via ExternalIP
curl 192.168.30.201:8080
# Shell output:
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
Delete the Service #
# Delete the ClusterIP service
kubectl delete service example-service
Service for Pod #
The expose
command can also be used to create services directly for pods:
# Run example pod
kubectl run nginx-pod --image=nginx --port=80
# Create a NodePort service for the pod
kubectl expose pod nginx-pod --name=example-service --port=8080 --target-port=80
# Create a NodePort service for a ReplicationController
kubectl expose rc rc-name --name=example-service
Service for Replication Controller #
Create Replication Controller #
vi replication-controller.yaml
apiVersion: v1
kind: ReplicationController
metadata:
name: replication-controller
labels:
app: nginx
spec:
replicas: 2
selector:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:alpine
ports:
- containerPort: 80
kubectl apply -f replication-controller.yaml
Create & Verify Service #
If no port is defined with --port=
, the ClusterIP port is the same as the port of the pod.
# Create a NodePort service for a ReplicationController
kubectl expose rc replication-controller --name=example-service
# List services
kubectl get svc
# Shell output:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
example-service ClusterIP 10.97.142.179 <none> 80/TCP 3s
# Curl the ClusterIP service
curl 10.97.142.179:80
# Shell output:
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
External-IP Option #
A floating external IP can be assigned to the services. It must be in the IP range of the LoadBalancer service.
# Create ClusterIP serivce with extneral IP
kubectl expose deployment example-deployment --name=example-service --type=ClusterIP --port 8080 --target-port 80 --external-ip="192.168.30.201"
# List services
kubectl get svc -o wide
# Shell output: ClusterIP service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
example-service ClusterIP 10.98.201.246 192.168.30.201 8080/TCP 5s
Services Example Configurations #
Example Deployment #
apiVersion: apps/v1
kind: Deployment
metadata:
name: example-deployment
spec:
selector: # Pods that belong to this deployment
matchLabels:
app: nginx # Pods that belong to this deployment
replicas: 3
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
ClusterIP Service #
apiVersion: v1
kind: Service
metadata:
name: example-service
spec:
type: ClusterIP
selector: # Must match pod selector
app: nginx # Must match pod selector
ports:
- protocol: TCP
port: 8080
targetPort: 80
NodePort Service #
apiVersion: v1
kind: Service
metadata:
name: example-service
spec:
type: NodePort
selector: # Must match pod selector
app: nginx # Must match pod selector
ports:
- protocol: TCP
port: 8080
targetPort: 80
nodePort: 30080
nodePort: 30080
If now specific NodePort is defined, a random port number gets assigned.
Verify the Serivce:
# List services
kubectl get svc -o wide
# Shell output:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
example-service NodePort 10.103.186.13 <none> 8080:30080/TCP 5s app=nginx
# Curl the service via NodePort on a Worker node
curl 192.168.30.11:30080
LoadBalancer Service #
apiVersion: v1
kind: Service
metadata:
name: example-service
spec:
type: LoadBalancer
selector: # Must match pod selector
app: nginx # Must match pod selector
ports:
- protocol: TCP
port: 8080
targetPort: 80
Verify the Serivce:
# List services
kubectl get svc
# Shell output:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
example-service LoadBalancer 10.103.75.162 192.168.30.201 8080:32433/TCP 3s
# Curl the serivce via floating IP:
curl 192.168.30.201:8080
Service / Endpoint for External Resource #
Example External Resource #
I’m deploying an Nginx container on a Ubuntu 22.04 server with the IP address 192.168.30.90
.
# Create an Nginx container on a resource outside the Kubernetes cluster
docker run -d -p 80:80 nginx
# Verify the Nginx container: Same host
curl localhost:80
# Verify the Nginx container: External host
curl 192.168.30.90:80
# Shell output:
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
Endpoint for External Resource #
Route traffic to an external resource with a Endpoint resource, that points to the external resource, and a no-selector Service that exposes the Endpoint resource. This setup is useful for integrating external services into a Kubernetes environment.
# Create a configuration for the endpoint resource
vi ext-endpoint.yaml
apiVersion: v1
kind: Endpoints
metadata:
name: service-external-resource
subsets:
- addresses:
- ip: "192.168.30.90"
ports:
- port: 80
# Create the endpoint resource
kubectl create -f ext-endpoint.yaml
Verify the Endpoint Resources #
# List endpoint resources
kubectl get endpoints
# shell output:
NAME ENDPOINTS AGE
service-external-resource 192.168.30.90:80 53s
# Verify the external resource IP
kubectl get ep service-external-resource
# Shell output:
NAME ENDPOINTS AGE
service-external-resource 192.168.30.90:80 80s
Create Service for the Endpoint #
-
Since the endpoint is not in the Kubernetes system, there is no selector defined in the configuration.
-
The service and the endpoint are linked by their name
service-external-resource
.
# Create a configuration for the service
vi service-ext-endpoint.yaml
apiVersion: v1
kind: Service
metadata:
name: service-external-resource
spec:
ports:
- protocol: TCP
port: 80
targetPort: 80
externalIPs:
- 192.168.30.201
kubectl create -f service-ext-endpoint.yaml
Verify the Service #
# List services
kubectl get svc -o wide
# Shell output:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
service-external-resource ClusterIP 10.105.191.206 192.168.30.201 80/TCP 51s <none>
# List service details
kubectl describe svc service-external-resource
# Shell output:
Name: service-external-resource
Namespace: default
Labels: <none>
Annotations: <none>
Selector: <none>
Type: ClusterIP
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.105.191.206
IPs: 10.105.191.206
External IPs: 192.168.30.201
Port: <unset> 80/TCP
TargetPort: 80/TCP
Endpoints: 192.168.30.90:80
Session Affinity: None
Events: <none>
# Curl the service / external resource
192.168.30.201:80
# Shell output:
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
Delete Resources #
# Delete the endpoint resource
kubectl delete endpoints service-external-resource
# Delete the service for the endpont
kubectl delete service service-external-resource