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.