Service
Learn how to create a Kubernetes service and how service discovery works.
This section continues from the previous section - make sure you do the tutorial in sequence.
Each Pod has a unique IP address - but the address is ephemeral. The Pod IP addresses are not stable and it can change when Pods start and/or restart. Moreover, if you have a Deployment that starts multiple Pods, and you need to consume an API from the Pods, you do not want to connect using an ephemeral IP address. Usually, you'll need to add a load balancer to distribute traffic to each individual instance.
In Kubernetes, a Service is a Network (L4) Load Balancer that'll provide you a single stable Service IP (and hostname) to load balance the traffic to a set of pods selected by using labels.
You can create a Service and deploy into Kubernetes using the
kubectl
CLI like in the Hello World tutorial. That's great to get a feel of Kubernetes. However, it's best that you create a YAML file first, and then deploy the YAML file.# Under the helloworld-springboot-tomcat directory , create a k8s directory
mkdir k8s/
kubectl create service clusterip helloworld \
--tcp=8080:8080 \
--dry-run \
-o yaml > k8s/service.yaml
You can open the
k8s/service.yaml
file to see the content. Below is a version of the YAML file that's slimmed down to the bare minimum.k8s/service.yaml
# API Version and Kind are important to indicate the type of resource
apiVersion: v1
kind: Service
metadata:
# Every Kubernetes resource has a name that's unique within a namespace
name: helloworld
# Every Kubernetes can have labels, label key/value pairs can be queried later.
labels:
app: helloworld
spec:
# The type of the service - this one is an internal only service
type: ClusterIP
# Any Pods that matches these labels will be load balanced through
# this Service (L4 load balancer)
selector:
app: helloworld
ports:
# A port can have a name, it can be renamed to be more descriptive
# such as "http", "jmx", "metrics", etc.
- name: 8080-8080
# The port to listen on by the Service (L4 Load Balancer)
port: 8080
# The port to forward traffic to on the destination Pod
targetPort: 8080
# TCP or UDP protocol
protocol: TCP
Use
kubectl
command line to deploy the YAML file:kubectl apply -f k8s/service.yaml
Verify that the service is configured:
kubectl get svc helloworld
You should see that the Service has a Cluster IP address:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
helloworld ClusterIP ... <none> 8080/TCP 7s
In a traditional Spring Cloud application, service discovery is done using an external service registry like Eureka, and client-side load balancing with Ribbon.
In Kubernetes, the Kubernetes Service itself can act as the service registry:
- You can discover all the endpoints associated with a service
- Kubernetes Service's Cluster IP is a built-in L4 load balancer, and it's automatically associated with a DNS name
Each Service has a
selector
block that is used to find Pods with matching labels and enlisting them as an Endpoint of the Service (or, you can think of it as a backend instance of a load balancer).In the example above, the selector is
app: helloworld
, you can also find the matching Pods using kubectl
:# Scale out the # of instances so we can see more than one pod
kubectl scale deployment helloworld --replicas=2
# Find Pods using a selector
kubectl get pods -lapp=helloworld
Describe the Service to see the Endpoint IP addresses that it's currently enlisted,, and look for the
Endpoints
output:kubectl describe svc helloworld
It is possible to continue to use Ribbon for client-side load balancing by retrieving these endpoints using the Kubernetes API. This is an advanced usage and not covered in this documentation. However, you can achieve a similar result if you use Spring Cloud Kubernetes to replace Spring Cloud Eureka.
The newly created Kubernetes Service is also a L4 Load Balancer, and it can be accessed using the Cluster IP, or the hostname
helloworld
, or a Fully Qualified Name (FQN) of helloworld.default.svc.cluster.local
.See Kubernetes DNS for Services and Pods documentation for more detail on how a DNS name is associated with a Service.
This service is currently only accessible from within the Kubernetes cluster. I.e., there is no public IP address. You can start a new Pod within the cluster that has a shell, and execute commands within the cluster. This accurately simulates a client application sending request to another backend service.
nginx
container image has a shell that we can use, so deploy one instance of nginx
.kubectl create deployment nginx --image=nginx
Attach to the
nginx
Pod:POD_NAME=$(kubectl get pods -lapp=nginx -o jsonpath='{.items[0].metadata.name}')
kubectl exec -ti ${POD_NAME} /bin/bash
From within the Pod,
curl
the service:# From within the nginx Pod
curl http://helloworld:8080
exit
If you have a client service that needs to reach the
helloworld
service within the same cluster, you can simply use the DNS name. This will resolve to the Cluster IP, and subsequently, L4 load balanced to one of the backend endpoints.Last modified 3yr ago