Part 3: Services

Concept

As you have seen in the previous section, even though you have deployed a Pod, that doesn’t mean you can access it easily.

Actually, there is a reason for that: Pods are ephemeral in nature. So you shouldn’t try to access a pod through it’s individual IP. That is when a Service object comes in handy.

A Service object has it’s own IP, DNS and Port and they never change. It enables you to access pods through the mechanism of selectors and labels.

Service Types

There are several types of Services. The default value is clusterIP, which only exposes your Service internally.

In our example, since we want to access our application from outside the cluster, we will go for the type NodePort. It exposes the Service object on the Node’s IP at a specific port. (Reminder: Node is a minion VM).

If you are running your cluster on the cloud, consider the type LoadBalancer, which exposes your Service on your cloud provider’s load-balancer. However, how the type LoadBalancer works varies with each cloud provider.

Manifest

Below is an example of a NodePort Service:

It is made accessible on port 8080, forwards requests to Pod’s 4567.

The IP of this Service object is auto-generated and nodePort will be automatically assigned to a port in the range of 30000-32767, unless we specify one ourselves.

# service-example.yml
apiVersion: v1
kind: Service
metadata:
  name: tasman                         # name of your Service
spec:                                  # defines what is in this resource
  type: NodePort                       # ClusterIP by default (which is only accessible internally)
  ports:
  - port: 8080                         # Allow Service access at this port
    targetPort: 4567                   # Let your Service target a specific port of your Pods
    # nodePort: 30001                  # Expose the Service object to the external world on a port of Node's IP (30000-32767)
    # protocol: TCP                    # TCP by default
  selector:
    app: tasman                        # targets pods with label app=tasman

A few attributes to pay special attention to:

Note: By default the targetPort will be set to the same value as the port field.

Example: If your application is running on port 4567, you could just specify:

port: 4567
# no need to specify targetPort

And this will expose your service on 4567 and forward requests to Pod’s 4567 automatically.

With the manifest above, together with the Pod that you have previously spun up, you can now deploy your service object with:

kubectl apply -f {service-manifest.yml}

Once deployed, inspect the service object with kubectl describe svc {svc-name} and pay attention to the field Endpoints. Do you know which IPs it refers to?

Mini Challenge 1 - Access your Service from outside the cluster

Since the Service was of the type NodePort, you should be able to access your app through your browser (outside of the cluster). Do you know which IP and Port to hit?

Possible Solution:

If you are on Minikube, you would have only 1 Node and it's IP is the same as returned by the command

$(minikube ip)
The port can be obtained from nodePort as shown in kubectl describe svc {svc-name}.
If you are on GKE, you can get Node IP's from your Cloud Console.


Mini Challenge 2 - Access your Service from inside the cluster

Apart from IP, your Service object can also be reached with its DNS from within the cluster. The hostname for your Service object is the same as the name you have provided in the manifest.

Could you spin up a Busybox to

Possible Solution:
First, get the Name, IP and Port of your Service through kubectl describe svc {svc-name}.
Then:
kubectl run -it busybox --image=busybox --restart=Never -- /bin/sh
wget {Service's IP}:{Service's Port}
wget {Service's Name}:{Service's Port}


Mini Challenge 3 - Access your Service with your ClusterIP

The Service manifest allows you to specify externalIPs.

As described by K8s documentation:

If there are external IPs that route to one or more cluster nodes,
Kubernetes services can be exposed on those externalIPs.
Traffic that ingresses into the cluster with the external IP (as destination IP),
on the service port, will be routed to one of the service endpoints.

If you are running on minikube, this externalIP is actually your minikube ip.

Could you make the required adjustments to the manifest, to allow you to access the application with only the IP address from your browser (no port required)?

Possible Solution: </br> Obtain the ip through.
minikube ip
</br> The key is now to add this IP as externalIP and make your service accessible on port 80 (so that clients don't have to specify the port) </br>
apiVersion: v1
kind: Service
metadata:
  name: tasman
spec:
  type: NodePort
  ports:
  - port: 80                           # <= expose service to port 80.
    targetPort: 4567
  selector:
    app: tasman
  externalIPs:
    - 192.168.99.100                    # <= my minikube-ip
</br> Apply the change with:
kubectl apply -f {service-manifest.yml}
</br> Then access your application the specified externalIP from your browser.

What you have learned in this section

Congrats! You have just learned how to make your Pods accessible, from outside and inside the cluster.

Because Pods are ephemeral, they should never be accessed directly through their IP. But rather, they should be accessed through a Service object.

Until now, we have only targeted one single pod with our Service object.

In the next chapter, you will find out how we can ensure that a number of pod replica’s are always running with ReplicationControllers / ReplicaSets. And the good news: all these replica’s will be targeted by our Service object, automagically!