Skip to content

Services

Services

A Kubernetes Service is an abstraction layer which defines a logical set of Pods and enables external traffic exposure, load balancing and service discovery for those Pods.

  • A Service in Kubernetes is an abstraction which defines a logical set of Pods and a policy by which to access them.
  • Services enable a loose coupling between dependent Pods.
  • The set of Pods targeted by a Service is usually determined by a LabelSelector
  • Although each Pod has a unique IP address, those IPs are not exposed outside the cluster without a Service
  • Services allow your applications to receive traffic
  • You can create a Service at the same time you create a Deployment by using --expose in kubectl.

Image

Create a service

To create a new service and expose it to external traffic we’ll use the expose command with NodePort as parameter

$ kubectl get services
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP   4m

$ kubectl get deployments
NAME                  DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
kubernetes-bootcamp   1         1         1            1           1h


$ kubectl expose deployment/kubernetes-bootcamp --type="NodePort" --port 8080
service "kubernetes-bootcamp" exposed

$ kubectl get services
NAME                  TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)       AGE
kubernetes            ClusterIP   10.96.0.1        <none>        443/TCP       6m
kubernetes-bootcamp   NodePort    10.103.120.102   <none>        8080:31989/TCP   48s

We have now a running Service called kubernetes-bootcamp. Here we see that the Service received a unique cluster-IP, an internal port and an external-IP (the IP of the Node).

$ kubectl describe services/kubernetes-bootcamp
Name:                     kubernetes-bootcamp
Namespace:                default
Labels:                   run=kubernetes-bootcamp
Annotations:              <none>
Selector:                 run=kubernetes-bootcamp
Type:                     NodePort
IP:                       10.103.120.102
Port:                     <unset>  8080/TCP
TargetPort:               8080/TCP
NodePort:                 <unset>  31989/TCP
Endpoints:                172.18.0.4:8080
Session Affinity:         None
External Traffic Policy:  Cluster
Events:                   <none>
Create an environment variable called NODE_PORT that has the value of the Node port assigned

$ export NODE_PORT=$(kubectl get services/kubernetes-bootcamp -o go-template='{{(index .spec.ports 0).nodePort}}')

$ echo NODE_PORT=$NODE_PORT
NODE_PORT=31989

$ curl host01:$NODE_PORT
Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-5dbf48f7d4-m55fc | v=1

Note

kubectl port-forward can be used Forward one or more local ports to a pod

Deleting a service

To delete Services

$ kubectl delete service -l run=kubernetes-bootcamp
service "kubernetes-bootcamp" deleted

Confirm that the service is gone

$ kubectl get services
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP   13h

To confirm that route is not exposed anymore you can curl the previously exposed IP and port

$ curl host01:$NODE_PORT
curl: (6) Could not resolve host: host01

You can confirm that the app is still running with a curl inside the pod:

$ kubectl exec -ti $POD_NAME curl localhost:8080
Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-5d7f968ccb-njgzx | v=1

Services Types

  • ClusterIP
  • NodePort
  • LoadBalancer

ClusterIP

A ClusterIP service is the default Kubernetes service. It gives you a service inside your cluster that other apps inside your cluster can access. There is no external access. You can access it using the Kubernetes proxy

The YAML for a ClusterIP service looks like this:

apiVersion: v1
kind: Service
metadata:  
  name: my-internal-service
spec:
  selector:    
    app: my-app
  type: ClusterIP
  ports:  
  - name: http
    port: 80
    targetPort: 80
    protocol: TCP
Image

To access the ClusterIp Service, first start the Kubernets proxy

1
kubectl proxy --port=8080

Now, you can navigate through the Kubernetes API to access this service using this scheme: http://localhost:8080/api/v1/namespaces/<NAMESPACE>/services/<SERVICE-NAME>:<PORT-NAME>/proxy

http://localhost:8080/api/v1/namespaces/challenge/services/jupyter-service:80/proxy/

NodePort

A NodePort service is the most primitive way to get external traffic directly to your service. NodePort, as the name implies, opens a specific port on all the Nodes (the VMs), and any traffic that is sent to this port is forwarded to the service.

Image

The YAML for a NodePort service looks like this:

apiVersion: v1
kind: Service
metadata:  
  name: my-nodeport-service
spec:
  selector:    
    app: my-app
  type: NodePort
  ports:  
  - name: http
    port: 80
    targetPort: 80
    nodePort: 30036
    protocol: TCP

Basically, a NodePort service has two differences from a normal “ClusterIP” service. First, the type is “NodePort.” There is also an additional port called the nodePort that specifies which port to open on the nodes. If you don’t specify this port, it will pick a random port. Most of the time you should let Kubernetes choose the port

There are many downsides to this method:

  1. You can only have once service per port
  2. You can only use ports 30000–32767
  3. If your Node/VM IP address change, you need to deal with that

LoadBalancer

A LoadBalancer service is the standard way to expose a service to the internet. On AWS or GKE, this will spin up a Load Balancer that will give you a single IP address that will forward all traffic to your service

Image

If you want to directly expose a service, this is the default method. All traffic on the port you specify will be forwarded to the service. There is no filtering, no routing, etc. This means you can send almost any kind of traffic to it, like HTTP, TCP, UDP, Websockets, gRPC, or whatever.

Bug

The big downside of LoadBalancer Service is that each service you expose with a LoadBalancer will get its own IP address, and you have to pay for a LoadBalancer per exposed service, which can get expensive!

YAML for LoadbalancerSerivce

{
   "kind":"Service",
   "apiVersion":"v1",
   "metadata":{
      "name":"guestbook",
      "labels":{
         "app":"guestbook"
      }
   },
   "spec":{
      "ports": [
         {
           "port":3000,
           "targetPort":"http-server"
         }
      ],
      "selector":{
         "app":"guestbook"
      },
      "type": "LoadBalancer"
   }
}