Kubernetes Services: Exposed!

Paul Czarkowski
4 min readNov 27, 2017

--

If you have minikube installed you can follow along with the examples provided using a simple hello world web service.

When you want to run an application in Kubernetes you do so by declaring a Pod which describes the container that you want to run. Each Pod is given an IP address that is internal to the Kubernetes cluster but this IP is not accessible from outside of Kubernetes. Even from inside Kubernetes you’d have to know the IP of the Pod to access it which is inconvenient at best.

You can create a simple Deployment to manage a Pod as easily as:

$ kubectl run hello --image=paulczar/hello-world
deployment "hello" created

However in order to make a pod accessible you can create a Service which is an abstraction that creates a logical group of Pods together and provides a way to access them. A Service uses the metadata labels assigned to Pods to determine its constituents.

Each Service has a setting called ServiceType that defines how that service is exposed. You can set this to ClusterIP, NodePort, LoadBalancer, or ExternalName depending on your particular deployment scenario.

ClusterIP

ClusterIP is the default ServiceType and it creates a single IP address that can be used to access its Pods which can only be accessed from inside the cluster. If KubeDNS is enabled it will also get a series of DNS records assigned to it include an A record to match its IP. This is very useful for exposing microservices running inside the same Kubernetes cluster to each other.

You can expose your application via a ClusterIP service and demonstrate accessing it from another Pod like so:

$ kubectl expose deployment hello --port=8080 --type=ClusterIP
service "hello" exposed
$ kubectl run -i --tty --rm debug --image=alpine \
--restart=Never -- wget -qO - hello:8080
<html><head><title>hello world</title></head><body>hello world!</body></html>
$ kubectl delete service hello
service "hello" deleted

Since KubeDNS is enabled in minikube by default you can access the service via DNS using the name of the service.

NodePort

NodePort builds on top of ClusterIP to create a mapping from each Worker Node’s static IP on a specified (or Kubernetes chosen) Port. A Service exposed as a NodePort can be accessed via <node-ip-address>:<node-port>. This ServiceType can be useful when developing applications with minikube or for exposing a specific Port to an application via an unmanaged load balancer or round robin DNS.

$ kubectl expose deployment hello --port=8080 --type=NodePort
service "hello" exposed
$ kubectl get service hello
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
hello NodePort 10.0.0.231 <none> 8080:30259/TCP 21s
$ minikube ip
192.168.99.100
$ curl 192.168.99.100:30259
<html><head><title>hello world</title></head><body>hello world!</body></html>
$ kubectl delete service hello
service "hello" deleted

LoadBalancer

LoadBalancer builds on top of NodePort and is used to automatically configure a supported external Load Balancer (for instance an ELB in Amazon) to route traffic through to the NodePort of the Service. This is the most versatile of the ServiceTypes but requires that you have a supported Load Balancer in your infrastructure of which most major cloud providers have.

In minikube this would produce the same result as a NodePort as minikube does not have a load balancer. However we can demonstrate it on Google Cloud quite easily if you have an account:

$ gcloud container clusters get-credentials cluster-1 \
--zone us-central1-a --project XXXX
$ kubectl run hello --image=paulczar/hello-world
deployment "hello" created
$ kubectl expose deployment hello --port=8080 --type=LoadBalancer
service "hello" exposed
$ kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
hello LoadBalancer 10.11.251.34 35.192.25.113 8080:32107/TCP 2m
$ curl 35.192.25.113:8080
<html><head><title>hello world</title></head><body>hello world!</body></html>

ExternalName

ExternalName creates DNS records in KubeDNS to direct the Service’s DNS to an external service specified in the field ExternalName. It provides no other routing or load balancing services.

Headless Services

You can also create a headless service. This is a service that does not get a ClusterIP and is created when you specify a Service with ClusterIP set to None. This is often used when a deployer wants to decouple their service from Kubernetes and use an alternative method of service discovery and load balancing, however, DNS is still created for the service.

So which one should I use?

  • If you only want applications in the same Kubernetes cluster to talk to each other then you would use ClusterIP as there is no need to expose it to the outside world.
  • If you have some sort of service registration and external load balancing, or you do not have a supported Load Balancer you should look at NodePort.
  • If you are doing development against a simple Kubernetes install like minikube you’ll want to use NodePort.
  • If you are running in Amazon, Google Cloud, or one of the other well supported cloud providers and you want to utilize their load balancing services you should use LoadBalancer.
  • If you want to use KubeDNS for service discovery but want to use an external database like Amazon RDS then you may want to use ExternalName.
  • If you are running a clustering application (like Consul) you may choose to use a headless service combined with running the application as a StatefulSet so that your minions can connect to the master via DNS (consul-0.service )

Hopefully this has helped clear up the various methods you can expose your application in Kubernetes using the Service resource. There is one other method worth mentioning called Ingress which gives you layer7 routing for your applications, however this is a much larger topic that deserves its own post.

--

--