Skip to main content

Kubernetes Services: Example ClusterIP, NodePort & LoadBalancer Services with Expose-Command and YAML Configuration; Service for External Endpoint

1585 words·
Kubernetes Kubectl
Table of Contents
Kubernetes-Components - This article is part of a series.
Part 6: This Article

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 and LoadBalancer.

  • 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 or KlipperLB 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
Kubernetes-Components - This article is part of a series.
Part 6: This Article