Kubernetes

Kubernetes Services

What is a Service?

Pods in Kubernetes are ephemeral. They are assigned an IP address at start up, but when they die or are removed, and are replaced they get a new IP address, which means it’s difficult to find the new version of one. A pods IP address is determined by the node that it is deployed on. A node has a range of IPs that it has available for pods to use (these are internal IPs to the cluster).

A Service, however, has a stable IP address, that connects to the pod. A service also provides load balancing, passing a request to a service to any of the replicas that it knows about.

ClusterIP

This is the most commonly used service, and is the default if a service isn’t specified.

An Ingress will talk to the ClusterIP service at a predefined IP:Port - the Ingress reverse proxy rules route an endpoint address to a ClusterIP service by it’s name. The Kubernetes DNS service takes that name and converts it to an IP.

The service then forwards the request to one of the pods that are registered to service those endpoint(s).

How does the service know which pods, and which ports on those pods that it should send the request to?

Selectors. Pods are identified by selectors.

In the service specification:

apiVersion: v1
kind: Service
metadata:
  name: microservice-one-service
spec:
  selector:
    app: microservice-one
  ports:
    - protocol: TCP
      port: 3200 # This is the port that this service is listening on (it's arbitrary)
      targetPort: 3000 # this is the pod port, and the pods must be configured to the same port for this to work
...

The app key is essentially a label that the pod should have so that they match a given selector

The corresponding pod configuration file might look like:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: microservice-one
  ...
spec:
  replicas: 2
  ...
  template:
    metadata:
      labels:
        app: microservice-on # this is what the selector is looking for

The labels need to match all of the selectors, not just a subset.

Which port does the service forward the request to? The targetPort attribute in the service spec.

When a service is created kubernetes creates an endpoint object that is used to keep track of which pods are members of the service

kubectl get endpoints gets all the endpoints objects

In a scenario where the deployment of pods (meaning that they’re stateless), need to interact with a stateful application (a database perhaps), then the following would happen:

The ingress calls the service which manages the requests to the pods. The pods then call a service that manages requests to the stateful application which, in turn, passes the request on to one of the pods that holds the database.

A service can have more than one port open, one per endpoint that it is managing. For example, if the service managing the mongoDB pods also has to handle calls from prometheus to those pods for metrics, it would open another port and provide a different endpoint for that call.|

The ports need to be named. An example:

apiVersion: v1
kind: Service
metadata:
  name: mongodb-service
  ...
spec:
  selector:
    app: mongodb
  ports:
    - name: mongodb 
      protocol: TCP
      port: 27017 # receiving port on service
      targetPort: 27017
    - name: mongodb-exporter
      protocol: TCP
      port: 9216
      targetPort: 9216

Headless

Usecase: A pod wants to talk to another pod, a specific one, not one randomly selected. For example, when a stateful application starts and wants to talk to the previously created stateful pod to be able to synchronise their data.

In order to be able to achieve this, the new pod needs to figure out what the IP is of the previous pods.

Options:

  • API call to Kubernetes API (inefficient, you’d get all the pods back and have to aprse the info, also your pod is then strongly coupled to kubernetes)
  • DNS Lookup - but this gives us the service IP address, not the pods.if the service is a `ClusterIP
  • DNS Lookup on a headless service returns the pod address instead.
apiVersion: v1
kind: Service
metadata:
  name: mongodb-service-headless
spec:
  clusterIP: None # this makes the service Headless
  selector:
    app: mongodb
  ports:
    - protocol: TCP
      port: 27017
      targetPort: 27017

Does this mean multiple services per pod. and the answer is Yes.

NodePort

This exposes the node to outside of the cluster (so the public) via some port. The traffic doesn’t go through an ingress.

apiVersion: v1
kind: Service
metadata:
  name: ms-service-nodeport
spec:
  type: NodePort
  selector:
    app: microservice-one
  ports:
    - protocol: TCP
      port: 3200 # Port of the **service**
      nodePort: 30008 # This is the port that's open to the public

NodePorts aren’t secure, nor are they efficient. This approach isn’t used in production.

LoadBalancer

apiVersion: v1
kind: Service
metadata:
  name: ms-service-loadbalancer
spec:
  type: LoadBalancer
  selector:
    app: microservice-one
  ports:
    - protocol: TCP
      port: 3200 # Port of the **service**
      targetPort: 3000 # Port of the pod(s)
      nodePort: 30008 # This is the port that's open to the public

LoadBalancer is used with cloud provider loadbalancers, and it creates ClusterIP and NodePort services in order to fulfil its tasks.

In order to expose your application to the world, you would typically use a Loadbalancer if you were on the cloud, or had a hardware load balancer in front of your cluster, or you would use an Ingress service.

Published:
comments powered by Disqus