Skip to content

Service Discovery

  • In this lab we will learn what is a Service and go over the different Service types.

What will we learn?

  • What a Kubernetes Service is and why you need one
  • How to create and test ClusterIP, NodePort, and LoadBalancer services
  • How to use Kubernetes internal DNS (FQDN) to access services
  • The differences between the service types

Prerequisites

  • A running Kubernetes cluster (kubectl cluster-info should work)
  • kubectl configured against the cluster

01. Some General Notes on What is a Service

  • Service is a unit of application behavior bound to a unique name in a service registry.
  • Service consist of multiple network endpoints implemented by workload instances running on pods, containers, VMs etc.
  • Service allow us to gain access to any given pod or container (e.g., a web service).
  • A service is (normally) created on top of an existing deployment and exposing it to the “world”, using IP(s) & port(s).
  • K8S define 3 main ways (+FQDN internally) to define a service, which means that we have 4 different ways to access Pods.
  • There are several proxy mode which inplements diffrent behaviour, for example in user proxy mode for each Service kube-proxy opens a port (randomly chosen) on the local node. Any connections to this “proxy port” are proxied to one of the Service’s backend Pods (as reported via Endpoints).
  • All the service types are assigned with a Cluster-IP.
  • Every service also creates Endoint(s), which point to the actual pods. Endpoints are usually referred to as back-ends of a particular service.

01. Create namespace and clear previous data if there is any

# If the namespace already exists and contains data form previous steps, let's clean it
kubectl delete namespace codewizard

# Create the desired namespace [codewizard]
kubectl create namespace codewizard
namespace/codewizard created

02. Create the required resources for this hand-on

# Network tools pod
kubectl create deployment -n codewizard multitool --image=praqma/network-multitool
deployment.apps/multitool created

# nginx pod
kubectl create deployment -n codewizard nginx --image=nginx
deployment.apps/nginx created

# Verify that the pods running
kubectl get all -n codewizard

NAME                             READY   STATUS    RESTARTS   AGE
pod/multitool-74477484b8-bdrwr   1/1     Running   0          29s
pod/nginx-6799fc88d8-p2fjn       1/1     Running   0          7s
NAME                        READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/multitool   1/1     1            1           30s
deployment.apps/nginx       1/1     1            1           8s
NAME                                   DESIRED   CURRENT   READY   AGE
replicaset.apps/multitool-74477484b8   1         1         1       30s
replicaset.apps/nginx-6799fc88d8       1         1         1       8s

Service Type: ClusterIP

  • If not specified, the default service type is ClusterIP.
  • In order to expose the deployment as a service, use: --type=ClusterIP
  • ClusterIP will expose the pods within the cluster. Since we don’t have an external IP, it will not be reachable from outside the cluster.
  • When the service is created K8S attaches a DNS record to the service in the following format: <service name>.<namespace>.svc.cluster.local

03. Expose the nginx with ClusterIP

# Expose the service on port 80
kubectl expose deployment nginx -n codewizard --port 80 --type ClusterIP
service/nginx exposed

# Check the services and see it's type
# Grab the ClusterIP - we will use it in the next steps
kubectl get services -n codewizard

NAME         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)
nginx        ClusterIP   10.109.78.182   <none>        80/TCP

04. Test the nginx with ClusterIP

  • Since the service is a ClusterIP, we will test if we can access the service using the multitool pod.
# Get the name of the multitool pod to be used
kubectl get pods -n codewizard
NAME
multitool-XXXXXX-XXXXX

# Run an interactive shell inside the network-multitool-container (same concept as with Docker)
kubectl exec -it <pod name> -n codewizard -- sh
  • Connect to the service in any of the following ways:

Test the nginx with ClusterIP

1. using the IP from the services output. grab the server response:
bash-5.0# curl -s <ClusterIP>
# Expected output:
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>


2. Test the nginx using the deployment name - using the service name since its the DNS name behind the scenes
bash-5.0# curl -s nginx
# Expected output:
<!DOCTYPE html>
<html>
  <head>
    <title>Welcome to nginx!</title>
    <style>
      body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
      }
    </style>
  </head>
  <body>
    <h1>Welcome to nginx!</h1>
    <p>
      If you see this page, the nginx web server is successfully installed and
      working. Further configuration is required.
    </p>
    <p>
      For online documentation and support please refer to
      <a href="http://nginx.org/">nginx.org</a>.<br />
      Commercial support is available at
      <a href="http://nginx.com/">nginx.com</a>.
    </p>
    <p><em>Thank you for using nginx.</em></p>
  </body>
</html>


3. using the full DNS name - for every service we have a full FQDN (Fully qualified domain name) so we can use it as well
# bash-5.0# curl -s <service name>.<namespace>.svc.cluster.local
bash-5.0# curl -s nginx.codewizard.svc.cluster.local

Service Type: NodePort

  • NodePort: Exposes the Service on each Node’s IP at a static port (the NodePort).
  • A ClusterIP Service, to which the NodePort Service routes, is automatically created.
  • NodePort service is reachable from outside the cluster, by requesting <Node IP>:<Node Port>.
  • The NodePort is allocated from a flag-configured range (default: 30000-32767).

05. Create NodePort

1. Delete previous service
# Delete the existing service from previous steps
kubectl delete svc nginx -n codewizard
service "nginx" deleted


2. Create NodePort service
# As before but this time the type is a NodePort
kubectl expose deployment -n codewizard nginx --port 80 --type NodePort
service/nginx exposed

# Verify that the type is set to NodePort.
# This time you should see ClusterIP and port as well
kubectl get svc -n codewizard
NAME         TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)
nginx        NodePort    100.65.29.172  <none>        80:32593/TCP

Note the PORT(S) column: 80:32593/TCP. - 80 is the port the service exposes internally (ClusterIP). - 32593 is the NodePort (the port exposed on every node).


3. Test the NodePort service

To test the service from outside the cluster (e.g., from your local machine), we need two pieces of information: 1. The Node IP: The IP address of one of the cluster nodes. 2. The NodePort: The port allocated to the service (which we saw above).

Step 3.1: Get the Node Port

We can retrieve the allocated NodePort manually from the kubectl get svc output, or programmatically:

# Get the NodePort allocated to the 'nginx' service
kubectl get svc nginx -n codewizard -o jsonpath='{.spec.ports[0].nodePort}{"\n"}'
32593

Step 3.2: Get the Node IP

We need the IP address of a node. In a multi-node cluster, any node’s IP will work.

# List nodes and their IP addresses
kubectl get nodes -o wide
NAME           STATUS   ROLES           AGE   VERSION   INTERNAL-IP    EXTERNAL-IP   OS-IMAGE
minikube       Ready    control-plane   1d    v1.26.1   192.168.49.2   <none>        Buildroot 2021.02.4
Note: If you are using Minikube, you can also run minikube ip to get this IP directly.

Step 3.3: Access the Service

Now construct the URL using the format http://<NODE_IP>:<NODE_PORT>.

# Example: curl http://192.168.49.2:32593
# Replace with YOUR actual Node IP and Node Port
curl -s http://<NODE_IP>:<NODE_PORT>
# Expected output:
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...
<h1>Welcome to nginx!</h1>
...
<p><em>Thank you for using nginx.</em></p>
</body>
</html>

Service Type: LoadBalancer

Note

We cannot test a LoadBalancer service locally on a localhost, but only on a cluster which can provide an external-IP

06. Create LoadBalancer (only if you are on real cloud)


1. Delete previous service

# Delete the existing service from previous steps
kubectl delete svc nginx -n codewizard
service "nginx" deleted

2. Create LoadBalancer Service

# As before this time the type is a LoadBalancer
kubectl expose deployment nginx -n codewizard --port 80 --type LoadBalancer
service/nginx exposed

# In real cloud we should se an EXTERNAL-IP and we can access the service
# via the internet
kubectl get svc
NAME         TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)
nginx        LoadBalancer   100.69.15.89   35.205.60.29  80:31354/TCP

3. Test the LoadBalancer Service
# Testing load balancer only require us to use the EXTERNAL-IP
curl -s <EXTERNAL-IP>