Skip to content

Service⚓︎

This tutorial uses a Service to route traffic to one of the pods created by the Deployment last time. It assumes that you've already created the deployment and have multiple pods running.

Quick Setup

Links to sections of previous lessons which are required to set up your environment.

  1. Create cluster
  2. Build docker image
  3. Create deployment

Purpose⚓︎

A Deployment creates multiple pods, each of which have their own IP address. However, the pods have unpredictable names such as hello-node-7f746ffc8d-4v4dq, so it's not easy to connect to them from other applications. A Service has exactly the metadata.name: it specifies, and can route network traffic it receives to pods. We'll investigate each of the service types Kubernetes supports below.

Service Types⚓︎

We're going to test out all 4 of the Kubernetes Service types:

  • ClusterIP
  • NodePort
  • ExternalName
  • LoadBalancer

ClusterIP Service Type⚓︎

ClusterIP Service Definition⚓︎

Create the file hello-node-service-clusterip.yaml and paste the contents below.

hello-node-service-clusterip.yaml
apiVersion: v1
kind: Service
metadata:
  name: hello-node-clusterip #(1)!
  labels:
    app: hello-node #(2)!
spec:
  selector: #(3)!
    app: hello-node
  type: ClusterIP #(4)!
  ports:
    - port: 80 #(5)!
      targetPort: 3000 #(6)!
  1. The name of the service can also be used to find the service's IP using the cluster's DNS. The DNS name hello-node-clusterip can be used to reach the service from other pods in the same namespace. Assuming the service is deployed to the default namespace, any of the names below can be used to reach the service from other pods in any namespace:
    • hello-node-clusterip.default
    • hello-node-clusterip.default.svc
    • hello-node-clusterip.default.svc.cluster
    • hello-node-clusterip.default.svc.cluster.local
  2. The Service's metadata.labels: will make it easier for us to inspect all of the application's resources with a single command. Otherwise, they aren't related to the Service's spec.selector:.
  3. The selector determines which pods should receive traffic from the service. Every item in the Service's spec.selector: must match an item in the Pod's metadata.labels:, or the Deployments's spec.template.metadata.labels: which creates the Pod. It's fine if the Pod has other labels which aren't mentioned in the Service's selector.
  4. In later sections, we'll test out other Service type:s.
  5. Other applications will connect to the Service's port:
  6. The service will send requests to the targetPort: on selected Pods. The Service's targetPort: should match the Pod's containerPort:.

Apply ClusterIP Service⚓︎

Apply the service definition to create the service in the cluster.

kubectl apply -f hello-node-service-clusterip.yaml
Output
service/hello-node-clusterip created

Get ClusterIP Service⚓︎

kubectl get service hello-node-clusterip
Output
NAME                   TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
hello-node-clusterip   ClusterIP   10.96.100.181   <none>        80/TCP    67s

Test ClusterIP Service⚓︎

Unfortunately, a Service with spec.type: ClusterIP has just what it sounds like, an IP address that's only valid inside the cluster. To access the service, we need to use kubectl port-forward in a similar way to accessing a Deployment in previous chapters.

kubectl port-forward service/hello-node-clusterip 8000:80

Note

The port-forward destination port changed to 80 to match the port: 80 in the Service definition.

Open http://127.0.0.1:8000/ in the browser, and we can see the Hello World from pod ... greeting!

Hello World

If you press refresh a few times, the message still doesn't change because the same pod handles every request. Unfortunately, kubectl port-forward doesn't really send requests to the Service, it simply selects the Deployment's first pod.

Press Ctrl+C in your terminal to stop the kubectl port-forward.

NodePort Service Type⚓︎

A NodePort service exposes a high humbered port (30000-32767) on all nodes. This will finally allow us to access the Service/Deployment without using kubectl port-forward.

NodePort Service Definition⚓︎

Create the file hello-node-service-nodeport.yaml and paste the contents below.

hello-node-service-nodeport.yaml
apiVersion: v1
kind: Service
metadata:
  name: hello-node-nodeport #(1)!
  labels:
    app: hello-node
spec:
  selector:
    app: hello-node
  type: NodePort #(2)!
  ports:
    - port: 80
      targetPort: 3000
      nodePort: 30001 #(3)!
  1. This Service uses a different name, so we can deploy it without removing the hello-node-clusterip service.
  2. Note the type: has changed relative to the previous service.
  3. If nodePort: is not specified, one will be assigned automatically. Setting the port ourselves makes it easier to follow examples.

Apply NodePort Service⚓︎

Apply the service definition to create the service in the cluster.

kubectl apply -f hello-node-service-nodeport.yaml
Output
service/hello-node-nodeport created

Get NodePort Service⚓︎

kubectl get service hello-node-nodeport
Output
NAME                  TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
hello-node-nodeport   NodePort   10.96.196.195   <none>        80:30001/TCP   43s

Test NodePort Service⚓︎

First, we need to find the address of the kind kubernetes node, so that we can connect to it from the browser.

NODE_ADDRESS=$(kubectl get nodes --output=jsonpath='{.items[*].status.addresses[?(@.type=="InternalIP")].address}')

Tip

The jsonpath expression is a bit complicated. If you wanted to write a similar expression, you can save the JSON output to a file to inspect its structure.

kubectl get nodes --output=json > nodes.json

If you'd like to see the node address, you can run:

echo $NODE_ADDRESS

Instead of opening the address in a browser, we're going to send 10 requests quickly using curl. We'll use the NODE_ADDRESS environment variable set above, along with the port :30001 specified by nodePort: in the NodePort Service Definition.

for x in {1..10}; do curl -s $NODE_ADDRESS:30001; done

This should give you output similar to the below. Note that we can see all 3 pods handle the request.

Hello World from pod hello-node-7944c56f54-qsw47
Hello World from pod hello-node-7944c56f54-bxgk7
Hello World from pod hello-node-7944c56f54-qsw47
Hello World from pod hello-node-7944c56f54-qsw47
Hello World from pod hello-node-7944c56f54-bxgk7
Hello World from pod hello-node-7944c56f54-vskkf
Hello World from pod hello-node-7944c56f54-qsw47
Hello World from pod hello-node-7944c56f54-vskkf
Hello World from pod hello-node-7944c56f54-qsw47
Hello World from pod hello-node-7944c56f54-vskkf

We're one step closer to connecting to our application in a nice way. However, this still isn't a very good experience since we're not able to use the standard http:// port 80.